@swarmify/agents-cli 1.11.3 → 1.13.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 (539) hide show
  1. package/CHANGELOG.md +40 -1
  2. package/LICENSE +21 -0
  3. package/README.md +209 -233
  4. package/dist/commands/__tests__/sessions.test.js +90 -20
  5. package/dist/commands/__tests__/sessions.test.js.map +1 -1
  6. package/dist/commands/cloud.d.ts +11 -0
  7. package/dist/commands/cloud.d.ts.map +1 -0
  8. package/dist/commands/cloud.js +363 -0
  9. package/dist/commands/cloud.js.map +1 -0
  10. package/dist/commands/commands.d.ts +9 -0
  11. package/dist/commands/commands.d.ts.map +1 -1
  12. package/dist/commands/commands.js +330 -139
  13. package/dist/commands/commands.js.map +1 -1
  14. package/dist/commands/daemon.d.ts +8 -0
  15. package/dist/commands/daemon.d.ts.map +1 -1
  16. package/dist/commands/daemon.js +38 -222
  17. package/dist/commands/daemon.js.map +1 -1
  18. package/dist/commands/drive.d.ts +8 -0
  19. package/dist/commands/drive.d.ts.map +1 -1
  20. package/dist/commands/drive.js +70 -7
  21. package/dist/commands/drive.js.map +1 -1
  22. package/dist/commands/exec.d.ts +9 -1
  23. package/dist/commands/exec.d.ts.map +1 -1
  24. package/dist/commands/exec.js +198 -24
  25. package/dist/commands/exec.js.map +1 -1
  26. package/dist/commands/factory.d.ts +11 -0
  27. package/dist/commands/factory.d.ts.map +1 -0
  28. package/dist/commands/factory.js +445 -0
  29. package/dist/commands/factory.js.map +1 -0
  30. package/dist/commands/fork.d.ts +8 -0
  31. package/dist/commands/fork.d.ts.map +1 -1
  32. package/dist/commands/fork.js +38 -4
  33. package/dist/commands/fork.js.map +1 -1
  34. package/dist/commands/hooks.d.ts +9 -0
  35. package/dist/commands/hooks.d.ts.map +1 -1
  36. package/dist/commands/hooks.js +252 -18
  37. package/dist/commands/hooks.js.map +1 -1
  38. package/dist/commands/init.d.ts +15 -0
  39. package/dist/commands/init.d.ts.map +1 -0
  40. package/dist/commands/init.js +137 -0
  41. package/dist/commands/init.js.map +1 -0
  42. package/dist/commands/mcp.d.ts +9 -0
  43. package/dist/commands/mcp.d.ts.map +1 -1
  44. package/dist/commands/mcp.js +250 -169
  45. package/dist/commands/mcp.js.map +1 -1
  46. package/dist/commands/models.d.ts +11 -0
  47. package/dist/commands/models.d.ts.map +1 -0
  48. package/dist/commands/models.js +170 -0
  49. package/dist/commands/models.js.map +1 -0
  50. package/dist/commands/packages.d.ts +8 -0
  51. package/dist/commands/packages.d.ts.map +1 -1
  52. package/dist/commands/packages.js +155 -14
  53. package/dist/commands/packages.js.map +1 -1
  54. package/dist/commands/permissions.d.ts +9 -0
  55. package/dist/commands/permissions.d.ts.map +1 -1
  56. package/dist/commands/permissions.js +72 -13
  57. package/dist/commands/permissions.js.map +1 -1
  58. package/dist/commands/plugins.d.ts +8 -0
  59. package/dist/commands/plugins.d.ts.map +1 -1
  60. package/dist/commands/plugins.js +265 -44
  61. package/dist/commands/plugins.js.map +1 -1
  62. package/dist/commands/profiles.d.ts +12 -0
  63. package/dist/commands/profiles.d.ts.map +1 -0
  64. package/dist/commands/profiles.js +255 -0
  65. package/dist/commands/profiles.js.map +1 -0
  66. package/dist/commands/pty.d.ts +1 -0
  67. package/dist/commands/pty.d.ts.map +1 -1
  68. package/dist/commands/pty.js +133 -22
  69. package/dist/commands/pty.js.map +1 -1
  70. package/dist/commands/pull.d.ts +8 -0
  71. package/dist/commands/pull.d.ts.map +1 -1
  72. package/dist/commands/pull.js +103 -14
  73. package/dist/commands/pull.js.map +1 -1
  74. package/dist/commands/push.d.ts +8 -0
  75. package/dist/commands/push.d.ts.map +1 -1
  76. package/dist/commands/push.js +37 -3
  77. package/dist/commands/push.js.map +1 -1
  78. package/dist/commands/refresh-memory.d.ts +16 -0
  79. package/dist/commands/refresh-memory.d.ts.map +1 -0
  80. package/dist/commands/refresh-memory.js +52 -0
  81. package/dist/commands/refresh-memory.js.map +1 -0
  82. package/dist/commands/resource-view.d.ts +39 -0
  83. package/dist/commands/resource-view.d.ts.map +1 -0
  84. package/dist/commands/resource-view.js +197 -0
  85. package/dist/commands/resource-view.js.map +1 -0
  86. package/dist/commands/routines.d.ts +8 -0
  87. package/dist/commands/routines.d.ts.map +1 -1
  88. package/dist/commands/routines.js +163 -34
  89. package/dist/commands/routines.js.map +1 -1
  90. package/dist/commands/rules.d.ts +9 -0
  91. package/dist/commands/rules.d.ts.map +1 -1
  92. package/dist/commands/rules.js +83 -12
  93. package/dist/commands/rules.js.map +1 -1
  94. package/dist/commands/secrets.d.ts +11 -0
  95. package/dist/commands/secrets.d.ts.map +1 -0
  96. package/dist/commands/secrets.js +352 -0
  97. package/dist/commands/secrets.js.map +1 -0
  98. package/dist/commands/sessions-picker.d.ts +18 -0
  99. package/dist/commands/sessions-picker.d.ts.map +1 -0
  100. package/dist/commands/sessions-picker.js +265 -0
  101. package/dist/commands/sessions-picker.js.map +1 -0
  102. package/dist/commands/sessions.d.ts +15 -0
  103. package/dist/commands/sessions.d.ts.map +1 -1
  104. package/dist/commands/sessions.js +708 -258
  105. package/dist/commands/sessions.js.map +1 -1
  106. package/dist/commands/skills.d.ts +9 -0
  107. package/dist/commands/skills.d.ts.map +1 -1
  108. package/dist/commands/skills.js +355 -233
  109. package/dist/commands/skills.js.map +1 -1
  110. package/dist/commands/status.d.ts +7 -0
  111. package/dist/commands/status.d.ts.map +1 -1
  112. package/dist/commands/status.js +7 -0
  113. package/dist/commands/status.js.map +1 -1
  114. package/dist/commands/subagents.d.ts +8 -0
  115. package/dist/commands/subagents.d.ts.map +1 -1
  116. package/dist/commands/subagents.js +214 -74
  117. package/dist/commands/subagents.js.map +1 -1
  118. package/dist/commands/sync.d.ts +8 -0
  119. package/dist/commands/sync.d.ts.map +1 -1
  120. package/dist/commands/sync.js +15 -7
  121. package/dist/commands/sync.js.map +1 -1
  122. package/dist/commands/teams-picker.d.ts +18 -0
  123. package/dist/commands/teams-picker.d.ts.map +1 -0
  124. package/dist/commands/teams-picker.js +290 -0
  125. package/dist/commands/teams-picker.js.map +1 -0
  126. package/dist/commands/teams.d.ts +18 -0
  127. package/dist/commands/teams.d.ts.map +1 -0
  128. package/dist/commands/teams.js +1098 -0
  129. package/dist/commands/teams.js.map +1 -0
  130. package/dist/commands/utils.d.ts +20 -0
  131. package/dist/commands/utils.d.ts.map +1 -1
  132. package/dist/commands/utils.js +34 -0
  133. package/dist/commands/utils.js.map +1 -1
  134. package/dist/commands/versions.d.ts +8 -0
  135. package/dist/commands/versions.d.ts.map +1 -1
  136. package/dist/commands/versions.js +71 -8
  137. package/dist/commands/versions.js.map +1 -1
  138. package/dist/commands/view.d.ts +34 -1
  139. package/dist/commands/view.d.ts.map +1 -1
  140. package/dist/commands/view.js +207 -13
  141. package/dist/commands/view.js.map +1 -1
  142. package/dist/index.d.ts +6 -0
  143. package/dist/index.d.ts.map +1 -1
  144. package/dist/index.js +136 -51
  145. package/dist/index.js.map +1 -1
  146. package/dist/lib/__tests__/bugfixes.test.js +3 -3
  147. package/dist/lib/__tests__/bugfixes.test.js.map +1 -1
  148. package/dist/lib/__tests__/exec.test.js +125 -19
  149. package/dist/lib/__tests__/exec.test.js.map +1 -1
  150. package/dist/lib/__tests__/hooks.test.d.ts +2 -0
  151. package/dist/lib/__tests__/hooks.test.d.ts.map +1 -0
  152. package/dist/lib/__tests__/hooks.test.js +203 -0
  153. package/dist/lib/__tests__/hooks.test.js.map +1 -0
  154. package/dist/lib/__tests__/memory-compile.test.d.ts +2 -0
  155. package/dist/lib/__tests__/memory-compile.test.d.ts.map +1 -0
  156. package/dist/lib/__tests__/memory-compile.test.js +95 -0
  157. package/dist/lib/__tests__/memory-compile.test.js.map +1 -0
  158. package/dist/lib/__tests__/models.test.d.ts +2 -0
  159. package/dist/lib/__tests__/models.test.d.ts.map +1 -0
  160. package/dist/lib/__tests__/models.test.js +239 -0
  161. package/dist/lib/__tests__/models.test.js.map +1 -0
  162. package/dist/lib/__tests__/rotate.test.d.ts +2 -0
  163. package/dist/lib/__tests__/rotate.test.d.ts.map +1 -0
  164. package/dist/lib/__tests__/rotate.test.js +80 -0
  165. package/dist/lib/__tests__/rotate.test.js.map +1 -0
  166. package/dist/lib/__tests__/secrets-bundles.test.d.ts +2 -0
  167. package/dist/lib/__tests__/secrets-bundles.test.d.ts.map +1 -0
  168. package/dist/lib/__tests__/secrets-bundles.test.js +104 -0
  169. package/dist/lib/__tests__/secrets-bundles.test.js.map +1 -0
  170. package/dist/lib/__tests__/secrets.test.d.ts +2 -0
  171. package/dist/lib/__tests__/secrets.test.d.ts.map +1 -0
  172. package/dist/lib/__tests__/secrets.test.js +90 -0
  173. package/dist/lib/__tests__/secrets.test.js.map +1 -0
  174. package/dist/lib/__tests__/shims.test.d.ts +2 -0
  175. package/dist/lib/__tests__/shims.test.d.ts.map +1 -0
  176. package/dist/lib/__tests__/shims.test.js +39 -0
  177. package/dist/lib/__tests__/shims.test.js.map +1 -0
  178. package/dist/lib/__tests__/usage.test.js +4 -2
  179. package/dist/lib/__tests__/usage.test.js.map +1 -1
  180. package/dist/lib/__tests__/versions.test.d.ts +2 -0
  181. package/dist/lib/__tests__/versions.test.d.ts.map +1 -0
  182. package/dist/lib/__tests__/versions.test.js +63 -0
  183. package/dist/lib/__tests__/versions.test.js.map +1 -0
  184. package/dist/lib/agents.d.ts +53 -1
  185. package/dist/lib/agents.d.ts.map +1 -1
  186. package/dist/lib/agents.js +178 -37
  187. package/dist/lib/agents.js.map +1 -1
  188. package/dist/lib/artifact-actions.d.ts +8 -3
  189. package/dist/lib/artifact-actions.d.ts.map +1 -1
  190. package/dist/lib/artifact-actions.js +8 -5
  191. package/dist/lib/artifact-actions.js.map +1 -1
  192. package/dist/lib/cloud/codex.d.ts +26 -0
  193. package/dist/lib/cloud/codex.d.ts.map +1 -0
  194. package/dist/lib/cloud/codex.js +237 -0
  195. package/dist/lib/cloud/codex.js.map +1 -0
  196. package/dist/lib/cloud/factory.d.ts +32 -0
  197. package/dist/lib/cloud/factory.d.ts.map +1 -0
  198. package/dist/lib/cloud/factory.js +43 -0
  199. package/dist/lib/cloud/factory.js.map +1 -0
  200. package/dist/lib/cloud/registry.d.ts +16 -0
  201. package/dist/lib/cloud/registry.d.ts.map +1 -0
  202. package/dist/lib/cloud/registry.js +68 -0
  203. package/dist/lib/cloud/registry.js.map +1 -0
  204. package/dist/lib/cloud/rush.d.ts +37 -0
  205. package/dist/lib/cloud/rush.d.ts.map +1 -0
  206. package/dist/lib/cloud/rush.js +230 -0
  207. package/dist/lib/cloud/rush.js.map +1 -0
  208. package/dist/lib/cloud/rush.test.d.ts +2 -0
  209. package/dist/lib/cloud/rush.test.d.ts.map +1 -0
  210. package/dist/lib/cloud/rush.test.js +63 -0
  211. package/dist/lib/cloud/rush.test.js.map +1 -0
  212. package/dist/lib/cloud/store.d.ts +23 -0
  213. package/dist/lib/cloud/store.d.ts.map +1 -0
  214. package/dist/lib/cloud/store.js +116 -0
  215. package/dist/lib/cloud/store.js.map +1 -0
  216. package/dist/lib/cloud/stream.d.ts +24 -0
  217. package/dist/lib/cloud/stream.d.ts.map +1 -0
  218. package/dist/lib/cloud/stream.js +145 -0
  219. package/dist/lib/cloud/stream.js.map +1 -0
  220. package/dist/lib/cloud/types.d.ts +109 -0
  221. package/dist/lib/cloud/types.d.ts.map +1 -0
  222. package/dist/lib/cloud/types.js +33 -0
  223. package/dist/lib/cloud/types.js.map +1 -0
  224. package/dist/lib/cloud/types.test.d.ts +2 -0
  225. package/dist/lib/cloud/types.test.d.ts.map +1 -0
  226. package/dist/lib/cloud/types.test.js +41 -0
  227. package/dist/lib/cloud/types.test.js.map +1 -0
  228. package/dist/lib/commands.d.ts +67 -0
  229. package/dist/lib/commands.d.ts.map +1 -1
  230. package/dist/lib/commands.js +161 -2
  231. package/dist/lib/commands.js.map +1 -1
  232. package/dist/lib/convert.d.ts +10 -0
  233. package/dist/lib/convert.d.ts.map +1 -1
  234. package/dist/lib/convert.js +9 -0
  235. package/dist/lib/convert.js.map +1 -1
  236. package/dist/lib/daemon.d.ts +21 -0
  237. package/dist/lib/daemon.d.ts.map +1 -1
  238. package/dist/lib/daemon.js +21 -0
  239. package/dist/lib/daemon.js.map +1 -1
  240. package/dist/lib/drive-sync.d.ts +18 -0
  241. package/dist/lib/drive-sync.d.ts.map +1 -1
  242. package/dist/lib/drive-sync.js +16 -0
  243. package/dist/lib/drive-sync.js.map +1 -1
  244. package/dist/lib/exec.d.ts +64 -3
  245. package/dist/lib/exec.d.ts.map +1 -1
  246. package/dist/lib/exec.js +218 -80
  247. package/dist/lib/exec.js.map +1 -1
  248. package/dist/lib/factory/__tests__/config.test.d.ts +2 -0
  249. package/dist/lib/factory/__tests__/config.test.d.ts.map +1 -0
  250. package/dist/lib/factory/__tests__/config.test.js +128 -0
  251. package/dist/lib/factory/__tests__/config.test.js.map +1 -0
  252. package/dist/lib/factory/config.d.ts +49 -0
  253. package/dist/lib/factory/config.d.ts.map +1 -0
  254. package/dist/lib/factory/config.js +127 -0
  255. package/dist/lib/factory/config.js.map +1 -0
  256. package/dist/lib/factory.js +1 -1
  257. package/dist/lib/factory.js.map +1 -1
  258. package/dist/lib/git.d.ts +16 -1
  259. package/dist/lib/git.d.ts.map +1 -1
  260. package/dist/lib/git.js +33 -4
  261. package/dist/lib/git.js.map +1 -1
  262. package/dist/lib/help.d.ts +7 -0
  263. package/dist/lib/help.d.ts.map +1 -1
  264. package/dist/lib/help.js +3 -0
  265. package/dist/lib/help.js.map +1 -1
  266. package/dist/lib/hooks.d.ts +59 -3
  267. package/dist/lib/hooks.d.ts.map +1 -1
  268. package/dist/lib/hooks.js +413 -33
  269. package/dist/lib/hooks.js.map +1 -1
  270. package/dist/lib/ledger/__tests__/local.test.d.ts +2 -0
  271. package/dist/lib/ledger/__tests__/local.test.d.ts.map +1 -0
  272. package/dist/lib/ledger/__tests__/local.test.js +177 -0
  273. package/dist/lib/ledger/__tests__/local.test.js.map +1 -0
  274. package/dist/lib/ledger/__tests__/sync.test.d.ts +2 -0
  275. package/dist/lib/ledger/__tests__/sync.test.d.ts.map +1 -0
  276. package/dist/lib/ledger/__tests__/sync.test.js +117 -0
  277. package/dist/lib/ledger/__tests__/sync.test.js.map +1 -0
  278. package/dist/lib/ledger/index.d.ts +18 -0
  279. package/dist/lib/ledger/index.d.ts.map +1 -0
  280. package/dist/lib/ledger/index.js +32 -0
  281. package/dist/lib/ledger/index.js.map +1 -0
  282. package/dist/lib/ledger/local.d.ts +22 -0
  283. package/dist/lib/ledger/local.d.ts.map +1 -0
  284. package/dist/lib/ledger/local.js +333 -0
  285. package/dist/lib/ledger/local.js.map +1 -0
  286. package/dist/lib/ledger/r2.d.ts +41 -0
  287. package/dist/lib/ledger/r2.d.ts.map +1 -0
  288. package/dist/lib/ledger/r2.js +335 -0
  289. package/dist/lib/ledger/r2.js.map +1 -0
  290. package/dist/lib/ledger/sync.d.ts +33 -0
  291. package/dist/lib/ledger/sync.d.ts.map +1 -0
  292. package/dist/lib/ledger/sync.js +106 -0
  293. package/dist/lib/ledger/sync.js.map +1 -0
  294. package/dist/lib/ledger/types.d.ts +100 -0
  295. package/dist/lib/ledger/types.d.ts.map +1 -0
  296. package/dist/lib/ledger/types.js +21 -0
  297. package/dist/lib/ledger/types.js.map +1 -0
  298. package/dist/lib/manifest.d.ts +6 -0
  299. package/dist/lib/manifest.d.ts.map +1 -1
  300. package/dist/lib/manifest.js +12 -0
  301. package/dist/lib/manifest.js.map +1 -1
  302. package/dist/lib/markdown.d.ts.map +1 -1
  303. package/dist/lib/markdown.js +6 -0
  304. package/dist/lib/markdown.js.map +1 -1
  305. package/dist/lib/mcp.d.ts +0 -9
  306. package/dist/lib/mcp.d.ts.map +1 -1
  307. package/dist/lib/mcp.js +0 -20
  308. package/dist/lib/mcp.js.map +1 -1
  309. package/dist/lib/memory-compile.d.ts +65 -0
  310. package/dist/lib/memory-compile.d.ts.map +1 -0
  311. package/dist/lib/memory-compile.js +174 -0
  312. package/dist/lib/memory-compile.js.map +1 -0
  313. package/dist/lib/memory.d.ts +8 -0
  314. package/dist/lib/memory.d.ts.map +1 -1
  315. package/dist/lib/memory.js +8 -0
  316. package/dist/lib/memory.js.map +1 -1
  317. package/dist/lib/models.d.ts +98 -0
  318. package/dist/lib/models.d.ts.map +1 -0
  319. package/dist/lib/models.js +722 -0
  320. package/dist/lib/models.js.map +1 -0
  321. package/dist/lib/permissions.d.ts +3 -1
  322. package/dist/lib/permissions.d.ts.map +1 -1
  323. package/dist/lib/permissions.js +13 -3
  324. package/dist/lib/permissions.js.map +1 -1
  325. package/dist/lib/picker.d.ts +27 -0
  326. package/dist/lib/picker.d.ts.map +1 -0
  327. package/dist/lib/picker.js +110 -0
  328. package/dist/lib/picker.js.map +1 -0
  329. package/dist/lib/plugins.d.ts +22 -0
  330. package/dist/lib/plugins.d.ts.map +1 -1
  331. package/dist/lib/plugins.js +114 -0
  332. package/dist/lib/plugins.js.map +1 -1
  333. package/dist/lib/profiles-keychain.d.ts +11 -0
  334. package/dist/lib/profiles-keychain.d.ts.map +1 -0
  335. package/dist/lib/profiles-keychain.js +14 -0
  336. package/dist/lib/profiles-keychain.js.map +1 -0
  337. package/dist/lib/profiles-presets.d.ts +25 -0
  338. package/dist/lib/profiles-presets.d.ts.map +1 -0
  339. package/dist/lib/profiles-presets.js +104 -0
  340. package/dist/lib/profiles-presets.js.map +1 -0
  341. package/dist/lib/profiles.d.ts +70 -0
  342. package/dist/lib/profiles.d.ts.map +1 -0
  343. package/dist/lib/profiles.js +145 -0
  344. package/dist/lib/profiles.js.map +1 -0
  345. package/dist/lib/pty-client.d.ts +1 -0
  346. package/dist/lib/pty-client.d.ts.map +1 -1
  347. package/dist/lib/pty-client.js.map +1 -1
  348. package/dist/lib/pty-server.d.ts +5 -0
  349. package/dist/lib/pty-server.d.ts.map +1 -1
  350. package/dist/lib/pty-server.js +5 -0
  351. package/dist/lib/pty-server.js.map +1 -1
  352. package/dist/lib/registry.d.ts +17 -0
  353. package/dist/lib/registry.d.ts.map +1 -1
  354. package/dist/lib/registry.js +17 -0
  355. package/dist/lib/registry.js.map +1 -1
  356. package/dist/lib/resources.d.ts +5 -0
  357. package/dist/lib/resources.d.ts.map +1 -1
  358. package/dist/lib/resources.js.map +1 -1
  359. package/dist/lib/rotate.d.ts +58 -0
  360. package/dist/lib/rotate.d.ts.map +1 -0
  361. package/dist/lib/rotate.js +93 -0
  362. package/dist/lib/rotate.js.map +1 -0
  363. package/dist/lib/routines.d.ts +30 -1
  364. package/dist/lib/routines.d.ts.map +1 -1
  365. package/dist/lib/routines.js +31 -4
  366. package/dist/lib/routines.js.map +1 -1
  367. package/dist/lib/runner.d.ts +14 -0
  368. package/dist/lib/runner.d.ts.map +1 -1
  369. package/dist/lib/runner.js +45 -11
  370. package/dist/lib/runner.js.map +1 -1
  371. package/dist/lib/sandbox.d.ts +16 -0
  372. package/dist/lib/sandbox.d.ts.map +1 -1
  373. package/dist/lib/sandbox.js +19 -2
  374. package/dist/lib/sandbox.js.map +1 -1
  375. package/dist/lib/scheduler.d.ts +8 -0
  376. package/dist/lib/scheduler.d.ts.map +1 -1
  377. package/dist/lib/scheduler.js +8 -0
  378. package/dist/lib/scheduler.js.map +1 -1
  379. package/dist/lib/secrets-bundles.d.ts +38 -0
  380. package/dist/lib/secrets-bundles.d.ts.map +1 -0
  381. package/dist/lib/secrets-bundles.js +176 -0
  382. package/dist/lib/secrets-bundles.js.map +1 -0
  383. package/dist/lib/secrets.d.ts +53 -0
  384. package/dist/lib/secrets.d.ts.map +1 -0
  385. package/dist/lib/secrets.js +140 -0
  386. package/dist/lib/secrets.js.map +1 -0
  387. package/dist/lib/session/__tests__/db.test.d.ts +2 -0
  388. package/dist/lib/session/__tests__/db.test.d.ts.map +1 -0
  389. package/dist/lib/session/__tests__/db.test.js +54 -0
  390. package/dist/lib/session/__tests__/db.test.js.map +1 -0
  391. package/dist/lib/session/__tests__/discover.test.js +54 -91
  392. package/dist/lib/session/__tests__/discover.test.js.map +1 -1
  393. package/dist/lib/session/__tests__/prompt.test.d.ts +2 -0
  394. package/dist/lib/session/__tests__/prompt.test.d.ts.map +1 -0
  395. package/dist/lib/session/__tests__/prompt.test.js +44 -0
  396. package/dist/lib/session/__tests__/prompt.test.js.map +1 -0
  397. package/dist/lib/session/__tests__/render.test.d.ts +2 -0
  398. package/dist/lib/session/__tests__/render.test.d.ts.map +1 -0
  399. package/dist/lib/session/__tests__/render.test.js +602 -0
  400. package/dist/lib/session/__tests__/render.test.js.map +1 -0
  401. package/dist/lib/session/artifacts.d.ts +15 -0
  402. package/dist/lib/session/artifacts.d.ts.map +1 -0
  403. package/dist/lib/session/artifacts.js +86 -0
  404. package/dist/lib/session/artifacts.js.map +1 -0
  405. package/dist/lib/session/db.d.ts +140 -0
  406. package/dist/lib/session/db.d.ts.map +1 -0
  407. package/dist/lib/session/db.js +599 -0
  408. package/dist/lib/session/db.js.map +1 -0
  409. package/dist/lib/session/discover.d.ts +44 -32
  410. package/dist/lib/session/discover.d.ts.map +1 -1
  411. package/dist/lib/session/discover.js +418 -464
  412. package/dist/lib/session/discover.js.map +1 -1
  413. package/dist/lib/session/parse.d.ts +11 -0
  414. package/dist/lib/session/parse.d.ts.map +1 -1
  415. package/dist/lib/session/parse.js +50 -0
  416. package/dist/lib/session/parse.js.map +1 -1
  417. package/dist/lib/session/prompt.d.ts +10 -0
  418. package/dist/lib/session/prompt.d.ts.map +1 -1
  419. package/dist/lib/session/prompt.js +38 -2
  420. package/dist/lib/session/prompt.js.map +1 -1
  421. package/dist/lib/session/prompt.test.d.ts +2 -0
  422. package/dist/lib/session/prompt.test.d.ts.map +1 -0
  423. package/dist/lib/session/prompt.test.js +57 -0
  424. package/dist/lib/session/prompt.test.js.map +1 -0
  425. package/dist/lib/session/render.d.ts +91 -10
  426. package/dist/lib/session/render.d.ts.map +1 -1
  427. package/dist/lib/session/render.js +708 -180
  428. package/dist/lib/session/render.js.map +1 -1
  429. package/dist/lib/session/team-filter.d.ts +35 -0
  430. package/dist/lib/session/team-filter.d.ts.map +1 -0
  431. package/dist/lib/session/team-filter.js +75 -0
  432. package/dist/lib/session/team-filter.js.map +1 -0
  433. package/dist/lib/session/team-filter.test.d.ts +2 -0
  434. package/dist/lib/session/team-filter.test.d.ts.map +1 -0
  435. package/dist/lib/session/team-filter.test.js +157 -0
  436. package/dist/lib/session/team-filter.test.js.map +1 -0
  437. package/dist/lib/session/types.d.ts +48 -6
  438. package/dist/lib/session/types.d.ts.map +1 -1
  439. package/dist/lib/session/types.js +9 -0
  440. package/dist/lib/session/types.js.map +1 -1
  441. package/dist/lib/shims.d.ts +73 -2
  442. package/dist/lib/shims.d.ts.map +1 -1
  443. package/dist/lib/shims.js +171 -25
  444. package/dist/lib/shims.js.map +1 -1
  445. package/dist/lib/skills.d.ts +68 -0
  446. package/dist/lib/skills.d.ts.map +1 -1
  447. package/dist/lib/skills.js +267 -1
  448. package/dist/lib/skills.js.map +1 -1
  449. package/dist/lib/state.d.ts +41 -2
  450. package/dist/lib/state.d.ts.map +1 -1
  451. package/dist/lib/state.js +63 -4
  452. package/dist/lib/state.js.map +1 -1
  453. package/dist/lib/subagents.d.ts +9 -0
  454. package/dist/lib/subagents.d.ts.map +1 -1
  455. package/dist/lib/subagents.js +9 -1
  456. package/dist/lib/subagents.js.map +1 -1
  457. package/dist/lib/teams/__tests__/oracle.test.d.ts +2 -0
  458. package/dist/lib/teams/__tests__/oracle.test.d.ts.map +1 -0
  459. package/dist/lib/teams/__tests__/oracle.test.js +89 -0
  460. package/dist/lib/teams/__tests__/oracle.test.js.map +1 -0
  461. package/dist/lib/teams/__tests__/supervisor.test.d.ts +2 -0
  462. package/dist/lib/teams/__tests__/supervisor.test.d.ts.map +1 -0
  463. package/dist/lib/teams/__tests__/supervisor.test.js +179 -0
  464. package/dist/lib/teams/__tests__/supervisor.test.js.map +1 -0
  465. package/dist/lib/teams/agents.d.ts +247 -0
  466. package/dist/lib/teams/agents.d.ts.map +1 -0
  467. package/dist/lib/teams/agents.js +1244 -0
  468. package/dist/lib/teams/agents.js.map +1 -0
  469. package/dist/lib/teams/api.d.ts +91 -0
  470. package/dist/lib/teams/api.d.ts.map +1 -0
  471. package/dist/lib/teams/api.js +239 -0
  472. package/dist/lib/teams/api.js.map +1 -0
  473. package/dist/lib/teams/cloud.d.ts +11 -0
  474. package/dist/lib/teams/cloud.d.ts.map +1 -0
  475. package/dist/lib/teams/cloud.js +169 -0
  476. package/dist/lib/teams/cloud.js.map +1 -0
  477. package/dist/lib/teams/debug.d.ts +8 -0
  478. package/dist/lib/teams/debug.d.ts.map +1 -0
  479. package/dist/lib/teams/debug.js +12 -0
  480. package/dist/lib/teams/debug.js.map +1 -0
  481. package/dist/lib/teams/file_ops.d.ts +13 -0
  482. package/dist/lib/teams/file_ops.d.ts.map +1 -0
  483. package/dist/lib/teams/file_ops.js +66 -0
  484. package/dist/lib/teams/file_ops.js.map +1 -0
  485. package/dist/lib/teams/index.d.ts +16 -0
  486. package/dist/lib/teams/index.d.ts.map +1 -0
  487. package/dist/lib/teams/index.js +15 -0
  488. package/dist/lib/teams/index.js.map +1 -0
  489. package/dist/lib/teams/oracle.d.ts +20 -0
  490. package/dist/lib/teams/oracle.d.ts.map +1 -0
  491. package/dist/lib/teams/oracle.js +59 -0
  492. package/dist/lib/teams/oracle.js.map +1 -0
  493. package/dist/lib/teams/parsers.d.ts +9 -0
  494. package/dist/lib/teams/parsers.d.ts.map +1 -0
  495. package/dist/lib/teams/parsers.js +837 -0
  496. package/dist/lib/teams/parsers.js.map +1 -0
  497. package/dist/lib/teams/persistence.d.ts +43 -0
  498. package/dist/lib/teams/persistence.d.ts.map +1 -0
  499. package/dist/lib/teams/persistence.js +299 -0
  500. package/dist/lib/teams/persistence.js.map +1 -0
  501. package/dist/lib/teams/ralph.d.ts +8 -0
  502. package/dist/lib/teams/ralph.d.ts.map +1 -0
  503. package/dist/lib/teams/ralph.js +59 -0
  504. package/dist/lib/teams/ralph.js.map +1 -0
  505. package/dist/lib/teams/registry.d.ts +18 -0
  506. package/dist/lib/teams/registry.d.ts.map +1 -0
  507. package/dist/lib/teams/registry.js +68 -0
  508. package/dist/lib/teams/registry.js.map +1 -0
  509. package/dist/lib/teams/summarizer.d.ts +73 -0
  510. package/dist/lib/teams/summarizer.d.ts.map +1 -0
  511. package/dist/lib/teams/summarizer.js +780 -0
  512. package/dist/lib/teams/summarizer.js.map +1 -0
  513. package/dist/lib/teams/supervisor.d.ts +49 -0
  514. package/dist/lib/teams/supervisor.d.ts.map +1 -0
  515. package/dist/lib/teams/supervisor.js +74 -0
  516. package/dist/lib/teams/supervisor.js.map +1 -0
  517. package/dist/lib/template.d.ts +8 -5
  518. package/dist/lib/template.d.ts.map +1 -1
  519. package/dist/lib/template.js +8 -5
  520. package/dist/lib/template.js.map +1 -1
  521. package/dist/lib/types.d.ts +58 -1
  522. package/dist/lib/types.d.ts.map +1 -1
  523. package/dist/lib/types.js +16 -1
  524. package/dist/lib/types.js.map +1 -1
  525. package/dist/lib/usage.d.ts +48 -0
  526. package/dist/lib/usage.d.ts.map +1 -1
  527. package/dist/lib/usage.js +103 -11
  528. package/dist/lib/usage.js.map +1 -1
  529. package/dist/lib/versions.d.ts +12 -1
  530. package/dist/lib/versions.d.ts.map +1 -1
  531. package/dist/lib/versions.js +91 -35
  532. package/dist/lib/versions.js.map +1 -1
  533. package/package.json +20 -6
  534. package/scripts/postinstall.js +1 -1
  535. package/scripts/rebuild-sqlite.sh +46 -0
  536. package/dist/commands/sessions.test.d.ts +0 -2
  537. package/dist/commands/sessions.test.d.ts.map +0 -1
  538. package/dist/commands/sessions.test.js +0 -53
  539. package/dist/commands/sessions.test.js.map +0 -1
@@ -1,80 +1,354 @@
1
+ /**
2
+ * Session discovery, search, and rendering commands.
3
+ *
4
+ * Implements `agents sessions` -- the unified interface for finding, browsing,
5
+ * and reading agent conversation transcripts across Claude, Codex, Gemini,
6
+ * and OpenCode. Supports interactive picker mode, text/path search, markdown
7
+ * and JSON rendering, role/turn filtering, artifact inspection, and session
8
+ * resume via agent-native CLI flags.
9
+ */
1
10
  import * as fs from 'fs';
2
11
  import * as os from 'os';
3
12
  import * as path from 'path';
13
+ import { spawn } from 'child_process';
4
14
  import chalk from 'chalk';
5
15
  import ora from 'ora';
6
- import { search } from '@inquirer/prompts';
7
16
  import { SESSION_AGENTS } from '../lib/session/types.js';
8
- import { discoverSessions, resolveSessionById, searchContentIndex } from '../lib/session/discover.js';
17
+ import { discoverArtifacts, readArtifact, resolveArtifact } from '../lib/session/artifacts.js';
18
+ import { discoverSessions, countSessionsInScope, resolveSessionById, searchContentIndex } from '../lib/session/discover.js';
19
+ import { filterTeamSessions } from '../lib/session/team-filter.js';
9
20
  import { parseSession } from '../lib/session/parse.js';
10
- import { renderTranscript, renderSummary, renderTrace, renderJson } from '../lib/session/render.js';
21
+ import { renderConversationMarkdown, renderSummary, renderSummaryHeader, computeSummaryStats, renderJson, filterEvents, parseRoleList } from '../lib/session/render.js';
11
22
  import { renderMarkdown } from '../lib/markdown.js';
12
23
  import { colorAgent } from '../lib/agents.js';
13
- import { isInteractiveTerminal, isPromptCancelled, requireInteractiveSelection } from './utils.js';
14
- const SESSION_AGENT_FILTER_HELP = `Filter by agent (${SESSION_AGENTS.join(', ')})`;
24
+ import { resolveVersion } from '../lib/versions.js';
25
+ import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
26
+ import { sessionPicker } from './sessions-picker.js';
27
+ const SESSION_AGENT_FILTER_HELP = `Filter by agent, e.g. claude, codex, claude@2.0.65`;
15
28
  const CLAUDE_RESUME_MATCH_WINDOW_MS = 10 * 60_000;
16
- async function listAction(options) {
17
- const agent = parseAgentFilter(options.agent);
18
- const limit = parseInt(options.limit || '20', 10);
19
- const spinner = ora('Scanning sessions...').start();
29
+ const LOAD_VERBS = ['Loading', 'Scanning', 'Gathering', 'Indexing', 'Reading'];
30
+ const FIND_VERBS = ['Finding', 'Searching', 'Locating', 'Matching'];
31
+ /** Build a spinner-backed progress tracker that cycles through verbs while scanning sessions. */
32
+ function createScanProgressTracker(verbs, suffix, spinner) {
33
+ const counts = new Map();
34
+ let verbIndex = 0;
35
+ const render = () => {
36
+ if (!spinner)
37
+ return;
38
+ const verb = verbs[verbIndex % verbs.length];
39
+ const parts = [];
40
+ for (const agent of SESSION_AGENTS) {
41
+ const c = counts.get(agent);
42
+ if (!c || c.total === 0)
43
+ continue;
44
+ parts.push(`${agent} ${c.parsed}/${c.total}`);
45
+ }
46
+ const base = `${verb} ${suffix}...`;
47
+ spinner.text = parts.length > 0 ? `${base} (${parts.join(' · ')})` : base;
48
+ };
49
+ const interval = spinner
50
+ ? setInterval(() => {
51
+ verbIndex++;
52
+ render();
53
+ }, 900)
54
+ : null;
55
+ render();
56
+ return {
57
+ onProgress: (progress) => {
58
+ counts.set(progress.agent, { parsed: progress.parsed, total: progress.total });
59
+ render();
60
+ },
61
+ stop: () => {
62
+ if (interval)
63
+ clearInterval(interval);
64
+ },
65
+ };
66
+ }
67
+ const PICKER_RECENT_COUNT = 15;
68
+ const PICKER_POOL_LIMIT = 200;
69
+ /**
70
+ * Detect whether a positional argument looks like a filesystem path.
71
+ * Naked paths (., ./, ../, /, ~) filter sessions by project directory.
72
+ * Everything else is treated as a search query string.
73
+ */
74
+ function isPathLike(query) {
75
+ return query === '.' || query.startsWith('./') || query.startsWith('../')
76
+ || query.startsWith('/') || query.startsWith('~');
77
+ }
78
+ /**
79
+ * Resolve a path-like query to an absolute directory path.
80
+ */
81
+ function resolvePathFilter(query) {
82
+ const expanded = query.startsWith('~')
83
+ ? path.join(os.homedir(), query.slice(1))
84
+ : query;
85
+ return path.resolve(expanded);
86
+ }
87
+ function formatBytes(n) {
88
+ if (n < 1024)
89
+ return `${n} B`;
90
+ if (n < 1024 * 1024)
91
+ return `${(n / 1024).toFixed(1)} KB`;
92
+ return `${(n / (1024 * 1024)).toFixed(1)} MB`;
93
+ }
94
+ async function renderArtifactsForSession(session, listAll, name) {
95
+ const artifacts = discoverArtifacts(session);
96
+ if (name !== undefined) {
97
+ const artifact = resolveArtifact(artifacts, name);
98
+ if (!artifact) {
99
+ console.error(chalk.red(`No artifact matching "${name}" in session ${session.shortId}.`));
100
+ if (artifacts.length > 0) {
101
+ console.error(chalk.gray('Available artifacts:'));
102
+ for (const a of artifacts) {
103
+ console.error(chalk.gray(` ${a.path}`));
104
+ }
105
+ }
106
+ process.exit(1);
107
+ }
108
+ if (!artifact.exists) {
109
+ console.error(chalk.red(`Artifact exists in session history but the file is no longer on disk: ${artifact.path}`));
110
+ process.exit(1);
111
+ }
112
+ process.stdout.write(readArtifact(artifact));
113
+ return;
114
+ }
115
+ if (artifacts.length === 0) {
116
+ console.log(chalk.gray('No file-write artifacts found in this session.'));
117
+ return;
118
+ }
119
+ const agentColor = colorAgent(session.agent);
120
+ console.log('');
121
+ console.log(agentColor(session.agent) +
122
+ chalk.gray(` · ${session.shortId} · ${formatRelativeTime(session.timestamp)}`));
123
+ console.log(chalk.gray('─'.repeat(72)));
124
+ for (const a of artifacts) {
125
+ const exists = a.exists ? chalk.green('yes') : chalk.red('no');
126
+ const size = a.exists && a.sizeBytes !== undefined ? chalk.cyan(formatBytes(a.sizeBytes)) : chalk.gray('-');
127
+ const tool = chalk.yellow(padRight(a.tool, 10));
128
+ const when = chalk.gray(formatRelativeTime(a.timestamp));
129
+ const p = chalk.white(a.path);
130
+ console.log(` ${exists} ${size.padEnd(10)} ${tool} ${when.padEnd(16)} ${p}`);
131
+ }
132
+ console.log(chalk.gray(`\n${artifacts.length} artifact${artifacts.length !== 1 ? 's' : ''}.`));
133
+ }
134
+ /** Main action handler for `agents sessions`. Routes to picker, table, or single-session render. */
135
+ async function sessionsAction(query, options) {
136
+ let filterOpts;
137
+ try {
138
+ filterOpts = buildFilterOptions(options);
139
+ }
140
+ catch (err) {
141
+ console.error(chalk.red(err.message));
142
+ process.exit(1);
143
+ }
144
+ const { agent, version } = parseAgentFilter(options.agent);
145
+ // Path-like queries filter by project directory instead of text search.
146
+ let pathFilter;
147
+ let searchQuery;
148
+ if (query && isPathLike(query)) {
149
+ const resolved = resolvePathFilter(query);
150
+ if (!fs.existsSync(resolved)) {
151
+ console.log(chalk.yellow(`Path not found: ${resolved}`));
152
+ console.log(chalk.gray('Did you mean to search? Use quotes: agents sessions "' + query + '"'));
153
+ return;
154
+ }
155
+ pathFilter = fs.realpathSync(resolved);
156
+ }
157
+ else {
158
+ searchQuery = query;
159
+ }
160
+ // Artifact flags require a session query.
161
+ if ((options.artifacts || options.artifact !== undefined) && !query) {
162
+ console.error(chalk.red('--artifacts and --artifact require a session ID or query.'));
163
+ process.exit(1);
164
+ }
165
+ const mode = resolveViewMode(options, filterOpts);
166
+ // --markdown or any filter flag forces single-session render.
167
+ const wantsRender = mode === 'markdown' || hasAnyFilter(filterOpts);
168
+ // Artifact-list or artifact-read paths: widen scope and resolve session globally.
169
+ if ((options.artifacts || options.artifact !== undefined) && searchQuery) {
170
+ await renderArtifactsGlobal(searchQuery, options.artifacts ?? false, options.artifact, { agent: options.agent, project: options.project });
171
+ return;
172
+ }
173
+ // When the user explicitly asks to render (via mode flag), resolve the
174
+ // query globally so sessions outside the default cwd/30d window are found.
175
+ if (wantsRender && searchQuery) {
176
+ await renderOneSession(searchQuery, mode, { agent: options.agent, project: options.project, filter: filterOpts });
177
+ return;
178
+ }
179
+ // Interactive picker loads a deep pool but shows only recent sessions
180
+ // until the user starts typing. Non-interactive/JSON uses the explicit limit.
181
+ const isInteractive = !options.json && isInteractiveTerminal();
182
+ const limit = parseInt(options.limit || (isInteractive ? String(PICKER_POOL_LIMIT) : '50'), 10);
183
+ const since = options.since ?? (isInteractive && !options.all ? '30d' : undefined);
184
+ const spinner = options.json ? null : ora().start();
185
+ const tracker = createScanProgressTracker(LOAD_VERBS, 'sessions', spinner);
20
186
  try {
21
- const sessions = await discoverSessions({
187
+ // Team-origin filter is pushed down to SQL so the LIMIT applies AFTER it.
188
+ // Without this, a dev dir with heavy SDK spawn activity (Task subagents,
189
+ // `agents run`, team agents) can fill the top-N window entirely with
190
+ // hidden rows and make real CLI sessions appear to vanish.
191
+ const scope = {
22
192
  agent,
23
- all: options.all,
193
+ version,
194
+ all: pathFilter ? undefined : options.all,
24
195
  cwd: process.cwd(),
196
+ cwdPrefix: pathFilter,
25
197
  project: options.project,
26
- limit,
27
- since: options.since,
198
+ since,
28
199
  until: options.until,
200
+ };
201
+ let sessions = await discoverSessions({
202
+ ...scope,
203
+ limit,
204
+ excludeTeamOrigin: !options.teams,
205
+ onProgress: tracker.onProgress,
29
206
  });
30
- spinner.stop();
207
+ tracker.stop();
208
+ spinner?.stop();
209
+ // Version filter is pushed down to SQL via scope.version above; no
210
+ // post-filter needed. Defensive: the team-origin SQL filter covers the
211
+ // ~100% case, but classifyTeamSession also recognizes sessions with a
212
+ // meta.json in ~/.agents/teams/agents whose is_team_origin flag was
213
+ // never set (legacy rows). Keep the in-memory pass so those are still
214
+ // enriched/hidden.
215
+ const { visible: visibleSessions } = filterTeamSessions(sessions, !!options.teams);
216
+ sessions = visibleSessions;
217
+ const hiddenCount = options.teams
218
+ ? 0
219
+ : countSessionsInScope({ ...scope, onlyTeamOrigin: true });
220
+ // Smart ID routing: a bare query that resolves to one session renders
221
+ // directly. If nothing matches in the scoped window and the query looks
222
+ // like a session ID, widen to global scope (incl. Claude /resume history).
223
+ if (searchQuery) {
224
+ const idMatches = resolveSessionById(sessions, searchQuery);
225
+ if (idMatches.length === 1) {
226
+ await renderSession(idMatches[0], mode, filterOpts);
227
+ return;
228
+ }
229
+ if (idMatches.length === 0 && looksLikeSessionId(searchQuery)) {
230
+ await renderOneSession(searchQuery, mode, { agent: options.agent, project: options.project, filter: filterOpts });
231
+ return;
232
+ }
233
+ }
234
+ if (options.json) {
235
+ const filtered = searchQuery ? filterSessionsByQuery(sessions, searchQuery) : sessions;
236
+ const serializable = filtered.map(s => {
237
+ const { _matchedTerms, _bm25Score, ...rest } = s;
238
+ return rest;
239
+ });
240
+ process.stdout.write(JSON.stringify(serializable, null, 2) + '\n');
241
+ return;
242
+ }
31
243
  if (sessions.length === 0) {
32
- console.log(chalk.gray(formatNoSessionsMessage(options.all, false, options.project)));
244
+ if (pathFilter) {
245
+ console.log(chalk.gray(`No sessions found for ${pathFilter}.`));
246
+ }
247
+ else {
248
+ console.log(chalk.gray(formatNoSessionsMessage(options.all, options.project)));
249
+ }
250
+ if (hiddenCount > 0) {
251
+ console.log(chalk.gray(formatTeamHiddenFooter(hiddenCount)));
252
+ }
33
253
  return;
34
254
  }
35
- if (shouldUseFilteredSessionSearch(options)) {
36
- const picked = await pickSession(sessions, formatSearchMessage(options));
255
+ if (isInteractiveTerminal()) {
256
+ const message = pathFilter
257
+ ? `Search sessions (${path.basename(pathFilter)}):`
258
+ : formatSearchMessage(options);
259
+ const picked = await pickSessionInteractive(sessions, message, searchQuery, hiddenCount);
37
260
  if (picked) {
38
- await renderSession(picked, 'summary');
261
+ await handlePickedSession(picked);
39
262
  return;
40
263
  }
264
+ return;
41
265
  }
42
- // Print header
43
- console.log(chalk.gray(padRight('ID', 10) +
44
- padRight('Account', 20) +
45
- padRight('Agent', 18) +
46
- padRight('Project', 16) +
47
- padRight('When', 14) +
48
- padRight('Msgs', 8) +
49
- padRight('Tokens', 10) +
50
- 'Topic'));
51
- for (const session of sessions) {
52
- const agentColor = colorAgent(session.agent);
53
- const when = formatRelativeTime(session.timestamp);
54
- const project = session.project || '-';
55
- const account = session.account || '';
56
- const agentLabel = session.version
57
- ? `${session.agent}@${session.version}`
58
- : session.agent;
59
- const topic = session.topic || '';
60
- console.log(chalk.white(padRight(session.shortId, 10)) +
61
- chalk.gray(padRight(truncate(account, 18), 20)) +
62
- agentColor(padRight(truncate(agentLabel, 16), 18)) +
63
- chalk.cyan(padRight(truncate(project, 14), 16)) +
64
- chalk.gray(padRight(when, 14)) +
65
- chalk.gray(padRight(formatCompactMetric(session.messageCount), 8)) +
66
- chalk.gray(padRight(formatCompactMetric(session.tokenCount), 10)) +
67
- chalk.white(truncate(topic, 40)));
68
- }
69
- console.log(chalk.gray(`\n${sessions.length} session${sessions.length === 1 ? '' : 's'}. Use 'agents sessions view <id>' to read.`));
266
+ // Non-interactive fallback (piped output)
267
+ const filtered = searchQuery ? filterSessionsByQuery(sessions, searchQuery) : sessions;
268
+ printSessionTable(filtered, hiddenCount);
70
269
  }
71
270
  catch (err) {
72
- spinner.stop();
271
+ tracker.stop();
272
+ spinner?.stop();
73
273
  console.error(chalk.red(`Failed to discover sessions: ${err.message}`));
74
274
  process.exit(1);
75
275
  }
76
276
  }
77
- async function renderSession(session, mode) {
277
+ function looksLikeSessionId(query) {
278
+ return /^[0-9a-f-]{6,}$/i.test(query.trim());
279
+ }
280
+ function teamTag(session) {
281
+ const origin = session.teamOrigin;
282
+ if (!origin)
283
+ return '';
284
+ const parts = [origin.handle, origin.mode].filter(Boolean).join(' · ');
285
+ return parts ? `[${parts}] ` : '[team] ';
286
+ }
287
+ function printSessionTable(sessions, hiddenCount = 0) {
288
+ for (const session of sessions) {
289
+ const agentColor = colorAgent(session.agent);
290
+ const when = formatRelativeTime(session.timestamp);
291
+ const project = session.project || '-';
292
+ const tag = teamTag(session);
293
+ const label = session.label;
294
+ const topic = tag ? `${tag}${session.topic ?? ''}` : session.topic;
295
+ const versionStr = session.version || '-';
296
+ console.log(chalk.white(padRight(session.shortId, 10)) +
297
+ agentColor(padRight(truncate(session.agent, 8), 9)) +
298
+ chalk.yellow(padRight(truncate(versionStr, 7), 8)) +
299
+ chalk.cyan(padRight(truncate(project, 14), 16)) +
300
+ renderTopicCell(label, topic, '', 48, 50) +
301
+ chalk.gray(when));
302
+ }
303
+ const countLine = `${sessions.length} session${sessions.length === 1 ? '' : 's'}.`;
304
+ console.log(chalk.gray(`\n${countLine}`));
305
+ if (hiddenCount > 0) {
306
+ console.log(chalk.gray(formatTeamHiddenFooter(hiddenCount)));
307
+ }
308
+ }
309
+ function buildFilterOptions(options) {
310
+ const opts = {};
311
+ if (options.include)
312
+ opts.include = parseRoleList(options.include, '--include');
313
+ if (options.exclude)
314
+ opts.exclude = parseRoleList(options.exclude, '--exclude');
315
+ if (opts.include && opts.exclude) {
316
+ throw new Error('--include and --exclude are mutually exclusive');
317
+ }
318
+ const parseCount = (raw, flag) => {
319
+ const n = Number(raw);
320
+ if (!Number.isFinite(n) || !Number.isInteger(n) || n <= 0) {
321
+ throw new Error(`${flag} expects a positive integer, got "${raw}"`);
322
+ }
323
+ return n;
324
+ };
325
+ if (options.first !== undefined)
326
+ opts.first = parseCount(options.first, '--first');
327
+ if (options.last !== undefined)
328
+ opts.last = parseCount(options.last, '--last');
329
+ if (opts.first !== undefined && opts.last !== undefined) {
330
+ throw new Error('--first and --last are mutually exclusive');
331
+ }
332
+ return opts;
333
+ }
334
+ function hasAnyFilter(opts) {
335
+ return !!(opts.include?.length || opts.exclude?.length || opts.first !== undefined || opts.last !== undefined);
336
+ }
337
+ /**
338
+ * Default is summary. Any explicit format flag wins. When filters are present
339
+ * without a format, default to markdown since summary is an aggregate view
340
+ * that filters don't meaningfully narrow.
341
+ */
342
+ function resolveViewMode(options, filters) {
343
+ if (options.markdown)
344
+ return 'markdown';
345
+ if (options.json)
346
+ return 'json';
347
+ if (hasAnyFilter(filters))
348
+ return 'markdown';
349
+ return 'summary';
350
+ }
351
+ async function renderSession(session, mode, filters) {
78
352
  // OpenCode stores sessions in SQLite; filePath is "db_path#session_id"
79
353
  const realPath = session.filePath.split('#')[0];
80
354
  if (!fs.existsSync(realPath)) {
@@ -89,81 +363,115 @@ async function renderSession(session, mode) {
89
363
  console.log(chalk.gray(`Time: ${session.timestamp}`));
90
364
  return;
91
365
  }
92
- // Session header
93
- const agentColor = colorAgent(session.agent);
94
- console.log('');
95
- console.log(agentColor(session.agent) +
96
- (session.version ? chalk.yellow(` ${session.version}`) : '') +
97
- (session.project ? chalk.cyan(` ${session.project}`) : '') +
98
- chalk.gray(` ${formatRelativeTime(session.timestamp)}`) +
99
- (session.account ? chalk.gray(` (${session.account})`) : ''));
100
- console.log(chalk.gray('─'.repeat(60)));
101
366
  const spinner = ora(`Parsing ${session.agent} session...`).start();
102
- const events = parseSession(session.filePath, session.agent);
367
+ let events = parseSession(session.filePath, session.agent);
103
368
  spinner.stop();
104
- let output;
105
- switch (mode) {
106
- case 'transcript':
107
- output = renderTranscript(events);
108
- break;
109
- case 'summary':
110
- output = renderMarkdown(renderSummary(events));
111
- break;
112
- case 'trace':
113
- output = renderMarkdown(renderTrace(events));
114
- break;
115
- case 'json':
116
- output = renderJson(events);
117
- break;
118
- }
119
- process.stdout.write(output);
120
- }
121
- function formatPickerLabel(s) {
369
+ events = filterEvents(events, filters);
370
+ const agentColor = colorAgent(session.agent);
371
+ console.log('');
372
+ if (mode === 'summary') {
373
+ const stats = computeSummaryStats(events);
374
+ const modelStr = stats.models.length > 0 ? chalk.yellow(` ${stats.models.join(', ')}`) : '';
375
+ const branchStr = session.gitBranch ? chalk.gray(` (${session.gitBranch})`) : '';
376
+ const absTime = formatAbsoluteTime(session.timestamp);
377
+ console.log(agentColor(session.agent) +
378
+ (session.version ? chalk.yellow(` ${session.version}`) : '') +
379
+ modelStr +
380
+ (session.project ? chalk.cyan(` ${session.project}`) + branchStr : branchStr) +
381
+ chalk.gray(` ${absTime} (${formatRelativeTime(session.timestamp)})`) +
382
+ (session.account ? chalk.gray(` · ${session.account}`) : ''));
383
+ const statsLine = renderSummaryHeader(stats);
384
+ if (statsLine)
385
+ console.log(chalk.gray(statsLine));
386
+ console.log(chalk.gray('─'.repeat(60)));
387
+ process.stdout.write(renderSummary(events, session.cwd));
388
+ return;
389
+ }
390
+ if (mode === 'markdown') {
391
+ console.log(agentColor(session.agent) +
392
+ (session.version ? chalk.yellow(` ${session.version}`) : '') +
393
+ (session.project ? chalk.cyan(` ${session.project}`) : '') +
394
+ chalk.gray(` ${formatRelativeTime(session.timestamp)}`) +
395
+ (session.account ? chalk.gray(` (${session.account})`) : ''));
396
+ console.log(chalk.gray('─'.repeat(60)));
397
+ process.stdout.write(renderMarkdown(renderConversationMarkdown(events)));
398
+ return;
399
+ }
400
+ // json — no header, raw events only (pipeable)
401
+ process.stdout.write(renderJson(events));
402
+ }
403
+ function renderTopicCell(label, topic, query, visibleWidth, paddedWidth) {
404
+ const lbl = (label ?? '').trim();
405
+ const tpc = (topic ?? '').trim();
406
+ const sep = ' · ';
407
+ const raw = lbl && tpc ? `${lbl}${sep}${tpc}` : (lbl || tpc);
408
+ const visible = truncate(raw, visibleWidth);
409
+ const padding = ' '.repeat(Math.max(0, paddedWidth - visible.length));
410
+ const labelEnd = lbl ? Math.min(lbl.length, visible.length) : 0;
411
+ let matchStart = -1, matchEnd = -1;
412
+ const q = query.trim().toLowerCase();
413
+ if (q) {
414
+ const lower = visible.toLowerCase();
415
+ for (const term of q.split(/\s+/).filter(Boolean)) {
416
+ const idx = lower.indexOf(term);
417
+ if (idx !== -1) {
418
+ matchStart = idx;
419
+ matchEnd = idx + term.length;
420
+ break;
421
+ }
422
+ }
423
+ }
424
+ const cuts = new Set([0, labelEnd, visible.length]);
425
+ if (matchStart >= 0) {
426
+ cuts.add(matchStart);
427
+ cuts.add(matchEnd);
428
+ }
429
+ const boundaries = [...cuts].sort((a, b) => a - b);
430
+ let out = '';
431
+ for (let i = 0; i < boundaries.length - 1; i++) {
432
+ const s = boundaries[i], e = boundaries[i + 1];
433
+ if (s >= e)
434
+ continue;
435
+ const text = visible.slice(s, e);
436
+ const isLabel = s < labelEnd;
437
+ const isMatch = matchStart >= 0 && s >= matchStart && e <= matchEnd;
438
+ out += (isMatch || isLabel) ? chalk.bold.white(text) : chalk.white(text);
439
+ }
440
+ return out + padding;
441
+ }
442
+ function formatPickerLabel(s, query) {
122
443
  const agentColor = colorAgent(s.agent);
123
444
  const when = formatRelativeTime(s.timestamp);
124
445
  const project = s.project || '-';
125
- const account = s.account || '';
126
- const agentLabel = s.version ? `${s.agent}@${s.version}` : s.agent;
127
- const topic = s.topic || '';
128
- const matchTerms = s._matchedTerms;
129
- // Append matched terms badge
130
- const matchBadge = matchTerms && matchTerms.length > 0
131
- ? chalk.yellow(` [${matchTerms.join(', ')}]`)
132
- : '';
446
+ const tag = teamTag(s);
447
+ const label = s.label;
448
+ const topic = tag ? `${tag}${s.topic ?? ''}` : s.topic;
449
+ const versionStr = s.version || '-';
133
450
  return (chalk.white(padRight(s.shortId, 10)) +
134
- chalk.gray(padRight(truncate(account, 18), 20)) +
135
- agentColor(padRight(truncate(agentLabel, 16), 18)) +
451
+ agentColor(padRight(truncate(s.agent, 8), 9)) +
452
+ chalk.yellow(padRight(truncate(versionStr, 7), 8)) +
136
453
  chalk.cyan(padRight(truncate(project, 14), 16)) +
137
- chalk.gray(padRight(when, 14)) +
138
- chalk.gray(padRight(formatCompactMetric(s.messageCount), 8)) +
139
- chalk.gray(padRight(formatCompactMetric(s.tokenCount), 10)) +
140
- chalk.white(truncate(topic, 30)) +
141
- matchBadge);
454
+ renderTopicCell(label, topic, query, 48, 50) +
455
+ chalk.gray(when));
142
456
  }
143
- async function pickSession(sessions, message = 'Search sessions:') {
457
+ async function pickSessionInteractive(sessions, message = 'Search sessions:', initialSearch, hiddenCount = 0) {
458
+ if (hiddenCount > 0) {
459
+ console.log(chalk.gray(formatTeamHiddenFooter(hiddenCount)));
460
+ }
144
461
  try {
145
- return await search({
462
+ return await sessionPicker({
146
463
  message,
147
- pageSize: 12,
148
- source: async (input) => {
149
- const matches = filterSessionsByQuery(sessions, input).slice(0, 30);
150
- if (matches.length === 0) {
151
- return [{
152
- name: input?.trim()
153
- ? `No sessions found for "${input.trim()}"`
154
- : 'No sessions found',
155
- value: null,
156
- disabled: 'Keep typing',
157
- }];
158
- }
159
- return matches.map(s => ({
160
- name: formatPickerLabel(s),
161
- value: s,
162
- description: formatSearchDescription(s),
163
- short: s.shortId,
164
- }));
464
+ sessions,
465
+ filter: (query) => {
466
+ // No query: show the full pool (picker viewport still paginates via pageSize).
467
+ // Typing: search the full pool.
468
+ if (!query.trim())
469
+ return sessions;
470
+ return filterSessionsByQuery(sessions, query);
165
471
  },
166
- validate: (value) => value ? true : 'No matching sessions.',
472
+ labelFor: (s, query) => formatPickerLabel(s, query),
473
+ pageSize: PICKER_RECENT_COUNT,
474
+ initialSearch,
167
475
  });
168
476
  }
169
477
  catch (err) {
@@ -172,16 +480,81 @@ async function pickSession(sessions, message = 'Search sessions:') {
172
480
  throw err;
173
481
  }
174
482
  }
483
+ async function handlePickedSession(picked) {
484
+ if (picked.action === 'view') {
485
+ await renderSession(picked.session, 'summary', {});
486
+ return;
487
+ }
488
+ const cwd = picked.session.cwd && fs.existsSync(picked.session.cwd)
489
+ ? picked.session.cwd
490
+ : process.cwd();
491
+ const activeVersion = resolveVersion(picked.session.agent, cwd) ?? undefined;
492
+ const resume = buildResumeCommand(picked.session, activeVersion);
493
+ if (!resume) {
494
+ console.log(chalk.yellow(`Resume is not supported for ${picked.session.agent} sessions yet. Showing summary instead.`));
495
+ await renderSession(picked.session, 'summary', {});
496
+ return;
497
+ }
498
+ if (picked.session.version && activeVersion && picked.session.version !== activeVersion) {
499
+ console.log(chalk.gray(`Cross-version handoff: session is ${picked.session.agent} ${picked.session.version}, ` +
500
+ `default is ${activeVersion}. Starting fresh and passing /continue so the new agent ` +
501
+ `reads the prior transcript via 'agents sessions'.`));
502
+ }
503
+ console.log(chalk.gray(`Resuming: ${resume.join(' ')} (cwd: ${cwd})`));
504
+ await new Promise((resolve) => {
505
+ const child = spawn(resume[0], resume.slice(1), {
506
+ cwd,
507
+ stdio: 'inherit',
508
+ shell: false,
509
+ });
510
+ child.on('error', (err) => {
511
+ console.error(chalk.red(`Failed to launch ${resume[0]}: ${err.message}`));
512
+ if (err.code === 'ENOENT') {
513
+ console.error(chalk.gray(`Make sure '${resume[0]}' is on your PATH.`));
514
+ }
515
+ resolve();
516
+ });
517
+ child.on('close', () => resolve());
518
+ });
519
+ }
520
+ /**
521
+ * Build the shell command that resumes a picked session.
522
+ *
523
+ * Cross-version handoff: when the session was created on a different version
524
+ * than the one the shim will launch (activeVersion), the session file lives in
525
+ * the other version's isolated home and `--resume <id>` would silently fail to
526
+ * find it. Fall back to a fresh session seeded with `/continue <id>`, which is
527
+ * wired (~/.claude/commands/continue.md) to read the prior transcript via
528
+ * `agents sessions <id>` — that reader is version-agnostic.
529
+ */
530
+ export function buildResumeCommand(session, activeVersion) {
531
+ const versionMismatch = !!(session.version && activeVersion && session.version !== activeVersion);
532
+ switch (session.agent) {
533
+ case 'claude':
534
+ if (versionMismatch)
535
+ return ['claude', `/continue ${session.id}`];
536
+ return ['claude', '--resume', session.id];
537
+ case 'codex':
538
+ if (versionMismatch)
539
+ return ['codex', `/continue ${session.id}`];
540
+ return ['codex', 'resume', session.id];
541
+ case 'opencode':
542
+ return ['opencode', '--session', session.id];
543
+ case 'gemini':
544
+ case 'openclaw':
545
+ return null;
546
+ }
547
+ }
175
548
  function parseAgentFilter(agentName) {
176
- const agent = agentName;
177
- if (agent && !SESSION_AGENTS.includes(agent)) {
178
- console.error(chalk.red(`Unknown agent: ${agent}. Use: ${SESSION_AGENTS.join(', ')}`));
549
+ if (!agentName)
550
+ return {};
551
+ const [name, version] = agentName.split('@', 2);
552
+ const agent = name;
553
+ if (!SESSION_AGENTS.includes(agent)) {
554
+ console.error(chalk.red(`Unknown agent: ${name}. Use: ${SESSION_AGENTS.join(', ')}`));
179
555
  process.exit(1);
180
556
  }
181
- return agent;
182
- }
183
- function shouldUseFilteredSessionSearch(options) {
184
- return isInteractiveTerminal() && Boolean(options.agent || options.project);
557
+ return { agent, version };
185
558
  }
186
559
  function formatSearchMessage(options) {
187
560
  const filters = [];
@@ -193,15 +566,21 @@ function formatSearchMessage(options) {
193
566
  return 'Search sessions:';
194
567
  return `Search sessions (${filters.join(', ')}):`;
195
568
  }
196
- function formatSearchDescription(session) {
197
- return [session.id, session.cwd].filter(Boolean).join(' ');
198
- }
199
- function filterSessionsByQuery(sessions, query) {
569
+ /** Filter and rank sessions by a multi-term search query across metadata and content. */
570
+ export function filterSessionsByQuery(sessions, query) {
200
571
  const trimmed = query?.trim().toLowerCase() || '';
201
572
  if (!trimmed)
202
573
  return sessions;
203
574
  const terms = trimmed.split(/\s+/).filter(Boolean);
204
575
  const contentIndex = searchContentIndex(sessions, trimmed);
576
+ // If the query exactly matches a session label, short-circuit the structural
577
+ // scorer (which would otherwise surface every session whose topic happens to
578
+ // contain the same words) and return only the label hits.
579
+ const EXACT_LABEL_SCORE = 1_000_000;
580
+ const exactLabelHits = [...contentIndex.values()].filter(s => (s._bm25Score ?? 0) >= EXACT_LABEL_SCORE);
581
+ if (exactLabelHits.length > 0) {
582
+ return exactLabelHits.sort((a, b) => (b._bm25Score ?? 0) - (a._bm25Score ?? 0));
583
+ }
205
584
  return sessions
206
585
  .map(session => ({ session, score: scoreSessionQuery(session, terms) }))
207
586
  .filter(entry => {
@@ -274,102 +653,131 @@ function scoreSessionQuery(session, terms) {
274
653
  }
275
654
  return score;
276
655
  }
277
- async function viewAction(idQuery, options) {
278
- // Default to summary, opt into full transcript
279
- let mode = 'summary';
280
- if (options.transcript)
281
- mode = 'transcript';
282
- else if (options.trace)
283
- mode = 'trace';
284
- else if (options.json)
285
- mode = 'json';
286
- const agent = parseAgentFilter(options.agent);
287
- const spinner = ora('Finding session...').start();
656
+ /**
657
+ * Narrow a session list by --project and --agent before search resolution.
658
+ * Without this, a query like "scoped search" could match sessions in BOTH
659
+ * the project you specified AND elsewhere, producing an ambiguity error
660
+ * even though the user already pointed at the correct scope.
661
+ */
662
+ function applyScopeFilters(sessions, scope) {
663
+ let filtered = sessions;
664
+ if (scope.project) {
665
+ const projectQuery = scope.project.toLowerCase();
666
+ filtered = filtered.filter((s) => {
667
+ const project = (s.project || '').toLowerCase();
668
+ const cwd = (s.cwd || '').toLowerCase();
669
+ return project.includes(projectQuery) || cwd.includes(projectQuery);
670
+ });
671
+ }
672
+ if (scope.agent) {
673
+ // Accept "claude" or "claude@2.1.112". Version suffix narrows further.
674
+ const [wantAgent, wantVersion] = scope.agent.split('@');
675
+ filtered = filtered.filter((s) => {
676
+ if (s.agent !== wantAgent)
677
+ return false;
678
+ if (wantVersion && s.version !== wantVersion)
679
+ return false;
680
+ return true;
681
+ });
682
+ }
683
+ return filtered;
684
+ }
685
+ async function renderArtifactsGlobal(query, listAll, name, scope) {
686
+ const spinner = ora().start();
687
+ const tracker = createScanProgressTracker(FIND_VERBS, 'session', spinner);
288
688
  try {
289
- const allSessions = await discoverSessions({
290
- agent,
291
- all: Boolean(idQuery) || options.all,
689
+ const discovered = await discoverSessions({
690
+ all: true,
292
691
  cwd: process.cwd(),
293
- project: options.project,
294
692
  limit: 5000,
295
- since: options.since,
296
- until: options.until,
693
+ onProgress: tracker.onProgress,
297
694
  });
298
- let session;
299
- if (!idQuery) {
300
- // No ID provided -- show interactive picker
695
+ tracker.stop();
696
+ const allSessions = applyScopeFilters(discovered, scope);
697
+ const matches = resolveSessionById(allSessions, query);
698
+ const queryMatches = matches.length > 0 ? matches : filterSessionsByQuery(allSessions, query);
699
+ if (queryMatches.length === 0) {
301
700
  spinner.stop();
302
- if (allSessions.length === 0) {
303
- console.log(chalk.gray(formatNoSessionsMessage(options.all, true)));
304
- return;
305
- }
306
- if (!isInteractiveTerminal()) {
307
- requireInteractiveSelection('Selecting a session to view', [
308
- 'agents sessions list',
309
- 'agents sessions view <id>',
310
- ]);
701
+ console.error(chalk.red(`No session found matching: ${query}`));
702
+ process.exit(1);
703
+ }
704
+ if (queryMatches.length > 1) {
705
+ spinner.stop();
706
+ console.error(chalk.red(`Multiple sessions match "${query}":`));
707
+ for (const m of queryMatches.slice(0, 10)) {
708
+ console.error(chalk.cyan(` ${m.shortId} ${m.id} ${m.label ?? m.topic ?? ''}`));
311
709
  }
312
- const picked = await pickSession(allSessions, formatSearchMessage(options));
313
- if (!picked)
314
- return;
315
- session = picked;
710
+ console.error(chalk.gray('Pass a longer ID to narrow it down.'));
711
+ process.exit(1);
316
712
  }
317
- else {
318
- const matches = resolveSessionById(allSessions, idQuery);
319
- let queryMatches = matches.length > 0 ? matches : filterSessionsByQuery(allSessions, idQuery);
320
- // Content search fallback when no title/topic/project matches
321
- if (queryMatches.length === 0) {
322
- const contentResults = searchContentIndex(allSessions, idQuery);
323
- if (contentResults.size > 0) {
324
- const matchedSessions = Array.from(contentResults.values())
325
- .sort((a, b) => (b._bm25Score ?? 0) - (a._bm25Score ?? 0));
326
- if (matchedSessions.length === 1) {
327
- session = matchedSessions[0];
328
- console.log(chalk.gray(`Found by content match (terms: ${matchedSessions[0]._matchedTerms?.join(', ')})`));
329
- }
330
- else {
331
- queryMatches = matchedSessions;
332
- }
713
+ spinner.stop();
714
+ await renderArtifactsForSession(queryMatches[0], listAll, name);
715
+ }
716
+ catch (err) {
717
+ if (isPromptCancelled(err))
718
+ return;
719
+ tracker.stop();
720
+ spinner.stop();
721
+ console.error(chalk.red(`Failed to read session: ${err.message}`));
722
+ process.exit(1);
723
+ }
724
+ }
725
+ async function renderOneSession(query, mode, scope) {
726
+ const spinner = ora().start();
727
+ const tracker = createScanProgressTracker(FIND_VERBS, 'session', spinner);
728
+ try {
729
+ const discovered = await discoverSessions({
730
+ all: true,
731
+ cwd: process.cwd(),
732
+ limit: 5000,
733
+ onProgress: tracker.onProgress,
734
+ });
735
+ tracker.stop();
736
+ const allSessions = applyScopeFilters(discovered, scope);
737
+ let session;
738
+ const matches = resolveSessionById(allSessions, query);
739
+ let queryMatches = matches.length > 0 ? matches : filterSessionsByQuery(allSessions, query);
740
+ if (queryMatches.length === 0) {
741
+ const contentResults = searchContentIndex(allSessions, query);
742
+ if (contentResults.size > 0) {
743
+ const matchedSessions = Array.from(contentResults.values())
744
+ .sort((a, b) => (b._bm25Score ?? 0) - (a._bm25Score ?? 0));
745
+ if (matchedSessions.length === 1) {
746
+ session = matchedSessions[0];
747
+ }
748
+ else {
749
+ queryMatches = matchedSessions;
333
750
  }
334
751
  }
335
- if (queryMatches.length === 0) {
336
- spinner.stop();
337
- const historyEntry = findClaudeHistoryEntry(idQuery);
338
- if (historyEntry) {
339
- const resumeMatch = resolveClaudeHistoryEntryToTranscript(historyEntry, allSessions);
340
- if (resumeMatch) {
341
- session = resumeMatch.session;
342
- console.log(chalk.gray(`Resolved Claude history entry ${idQuery} to transcript ${resumeMatch.session.id}.`));
343
- }
344
- else {
345
- renderClaudeHistoryOnlyId(idQuery, historyEntry, allSessions);
346
- process.exit(1);
347
- }
752
+ }
753
+ if (queryMatches.length === 0 && !session) {
754
+ spinner.stop();
755
+ const historyEntry = findClaudeHistoryEntry(query);
756
+ if (historyEntry) {
757
+ const resumeMatch = resolveClaudeHistoryEntryToTranscript(historyEntry, allSessions);
758
+ if (resumeMatch) {
759
+ session = resumeMatch.session;
348
760
  }
349
761
  else {
350
- console.error(chalk.red(`No session found matching: ${idQuery}`));
351
- console.error(chalk.gray('Run "agents sessions" to list available sessions.'));
762
+ renderClaudeHistoryOnlyId(query, historyEntry, allSessions);
352
763
  process.exit(1);
353
764
  }
354
765
  }
355
- if (queryMatches.length === 0) {
356
- // session already resolved from history fallback
766
+ else {
767
+ console.error(chalk.red(`No session found matching: ${query}`));
768
+ console.error(chalk.gray('Run "agents sessions" to browse sessions.'));
769
+ process.exit(1);
357
770
  }
358
- else if (queryMatches.length > 1) {
771
+ }
772
+ if (!session) {
773
+ if (queryMatches.length > 1) {
359
774
  spinner.stop();
360
- if (!isInteractiveTerminal()) {
361
- console.error(chalk.red(`Multiple sessions match: ${idQuery}`));
362
- console.error(chalk.gray('Pass a longer ID/query or one of these exact IDs:'));
363
- for (const match of queryMatches.slice(0, 10)) {
364
- console.error(chalk.cyan(` ${match.id}`));
365
- }
366
- process.exit(1);
775
+ console.error(chalk.red(`Multiple sessions match "${query}":`));
776
+ for (const match of queryMatches.slice(0, 10)) {
777
+ console.error(chalk.cyan(` ${match.shortId} ${match.id} ${match.label ?? match.topic ?? ''}`));
367
778
  }
368
- // Multiple matches -- let the user pick
369
- const picked = await pickSession(queryMatches, formatSearchMessage(options));
370
- if (!picked)
371
- return;
372
- session = picked;
779
+ console.error(chalk.gray('Pass a longer ID to narrow it down.'));
780
+ process.exit(1);
373
781
  }
374
782
  else {
375
783
  session = queryMatches[0];
@@ -379,69 +787,108 @@ async function viewAction(idQuery, options) {
379
787
  throw new Error('Session resolution failed');
380
788
  }
381
789
  spinner.stop();
382
- await renderSession(session, mode);
790
+ await renderSession(session, mode, scope.filter);
383
791
  }
384
792
  catch (err) {
385
793
  if (isPromptCancelled(err))
386
794
  return;
795
+ tracker.stop();
387
796
  spinner.stop();
388
797
  console.error(chalk.red(`Failed to read session: ${err.message}`));
389
798
  process.exit(1);
390
799
  }
391
800
  }
801
+ /** Register the `agents sessions` command with all its options and help text. */
392
802
  export function registerSessionsCommands(program) {
393
- const sessionsCmd = program
803
+ program
394
804
  .command('sessions')
395
- .description('List and view agent sessions for the current directory by default')
396
- .option('--agent <agent>', SESSION_AGENT_FILTER_HELP)
397
- .option('--all', 'Show sessions from every directory')
398
- .option('--project <name>', 'Filter by project name across all directories')
399
- .option('--since <time>', 'Filter sessions newer than time (e.g., "7d", "30d", ISO timestamp)')
400
- .option('--until <time>', 'Filter sessions older than time (ISO timestamp)')
401
- .option('-n, --limit <n>', 'Max sessions to show', '20')
402
- .action(async (options) => {
403
- await listAction(options);
404
- });
405
- sessionsCmd
406
- .command('list')
407
- .description('List sessions for the current directory by default')
408
- .option('--agent <agent>', SESSION_AGENT_FILTER_HELP)
409
- .option('--all', 'Show sessions from every directory')
410
- .option('--project <name>', 'Filter by project name across all directories')
411
- .option('--since <time>', 'Filter sessions newer than time (e.g., "7d", "30d", ISO timestamp)')
412
- .option('--until <time>', 'Filter sessions older than time (ISO timestamp)')
413
- .option('-n, --limit <n>', 'Max sessions to show', '20')
414
- .action(async (options) => {
415
- await listAction(options);
416
- });
417
- sessionsCmd
418
- .command('view [id]')
419
- .description('View a session by ID or search query (picker defaults to live search)')
420
- .option('--agent <agent>', SESSION_AGENT_FILTER_HELP)
421
- .option('--all', 'Show sessions from every directory')
422
- .option('--project <name>', 'Filter by project name across all directories')
423
- .option('--since <time>', 'Filter sessions newer than time (e.g., "7d", "30d", ISO timestamp)')
424
- .option('--until <time>', 'Filter sessions older than time (ISO timestamp)')
425
- .option('--transcript', 'Show full conversation transcript')
426
- .option('--trace', 'Show reasoning trace as markdown')
427
- .option('--json', 'Output normalized events as JSON')
428
- .action(async (id, options, command) => {
429
- const parentOptions = typeof command?.parent?.opts === 'function'
430
- ? command.parent.opts()
431
- : {};
432
- await viewAction(id, { ...parentOptions, ...options });
805
+ .argument('[query]', 'Session ID, search query, or path (., ../, /path) to filter by project')
806
+ .description('Find, browse, and read agent conversation transcripts across Claude, Codex, Gemini, and OpenCode.')
807
+ .option('-a, --agent <agent>', 'Filter by agent type and version (e.g., claude, codex@0.116.0)')
808
+ .option('--all', 'Include sessions from every directory (not just current project)')
809
+ .option('--teams', 'Include team-spawned sessions (hidden by default)')
810
+ .option('--project <name>', 'Filter by project name (searches across all directories)')
811
+ .option('--since <time>', 'Only sessions newer than this (e.g., 2h, 7d, 4w, or ISO date)')
812
+ .option('--until <time>', 'Only sessions older than this (ISO timestamp)')
813
+ .option('-n, --limit <n>', 'Maximum number of sessions to return', '50')
814
+ .option('--markdown', 'Render the session as markdown (user, assistant, thinking, tool calls)')
815
+ .option('--json', 'Output JSON (session list when browsing, event array when rendering one session)')
816
+ .option('--include <roles>', 'Only include these roles (comma-separated): user, assistant, thinking, tools')
817
+ .option('--exclude <roles>', 'Exclude these roles (comma-separated): user, assistant, thinking, tools')
818
+ .option('--first <n>', 'Keep only the first N turns (a turn starts at each user message)')
819
+ .option('--last <n>', 'Keep only the last N turns (a turn starts at each user message)')
820
+ .option('--artifacts', 'List all files written or edited during a session')
821
+ .option('--artifact <name>', 'Read a specific artifact by filename or path (outputs to stdout)')
822
+ .addHelpText('after', `
823
+ Examples:
824
+ # Interactive picker: browse and search recent sessions (TTY only)
825
+ agents sessions
826
+
827
+ # List sessions from current project (last 30 days, piped output shows table)
828
+ agents sessions | head -20
829
+
830
+ # Search sessions by text (topic, file paths, commands)
831
+ agents sessions "add auth middleware"
832
+
833
+ # Filter by project across all directories
834
+ agents sessions --project agents-cli --all
835
+
836
+ # Filter by agent and time window
837
+ agents sessions --agent claude --since 7d
838
+
839
+ # Filter sessions in a specific directory
840
+ agents sessions /Users/muqsit/src/my-project
841
+
842
+ # Default summary view for one session
843
+ agents sessions a1b2c3d4
844
+
845
+ # Full conversation (user + assistant + thinking + tools) as markdown
846
+ agents sessions a1b2c3d4 --markdown
847
+
848
+ # Same conversation as structured JSON events
849
+ agents sessions a1b2c3d4 --json
850
+
851
+ # Only user messages (filter flags auto-select markdown)
852
+ agents sessions a1b2c3d4 --include user
853
+
854
+ # Everything except thinking, as markdown
855
+ agents sessions a1b2c3d4 --exclude thinking --markdown
856
+
857
+ # Last 3 turns as markdown
858
+ agents sessions a1b2c3d4 --last 3
859
+
860
+ # First 10 turns, user messages only, as JSON
861
+ agents sessions a1b2c3d4 --first 10 --include user --json
862
+
863
+ # Export all recent sessions as JSON for analysis
864
+ agents sessions --since 30d --limit 200 --json > sessions.json
865
+
866
+ # Include team-spawned sessions in results
867
+ agents sessions --teams
868
+
869
+ Notes:
870
+ - --include and --exclude are mutually exclusive.
871
+ - --first and --last are mutually exclusive.
872
+ - A filter flag without --markdown/--json defaults to --markdown output.
873
+ `)
874
+ .action(async (query, options) => {
875
+ await sessionsAction(query, options);
433
876
  });
434
877
  }
435
- function formatNoSessionsMessage(showAll, picker = false, project) {
878
+ function formatNoSessionsMessage(showAll, project) {
436
879
  const projectQuery = project?.trim();
437
880
  if (projectQuery) {
438
881
  return `No sessions found for project "${projectQuery}".`;
439
882
  }
440
883
  if (showAll)
441
884
  return 'No sessions found.';
442
- const command = picker ? 'agents sessions view --all' : 'agents sessions --all';
885
+ const command = 'agents sessions --all';
443
886
  return `No sessions found for ${process.cwd()}. Run "${command}" to see sessions from every directory.`;
444
887
  }
888
+ function formatTeamHiddenFooter(hiddenCount) {
889
+ const noun = hiddenCount === 1 ? 'team session' : 'team sessions';
890
+ return `(${hiddenCount} ${noun} hidden — use --teams to show, or \`agents teams status\`)`;
891
+ }
445
892
  function findClaudeHistoryEntry(idQuery) {
446
893
  const historyPath = path.join(os.homedir(), '.claude', 'history.jsonl');
447
894
  if (!fs.existsSync(historyPath))
@@ -499,7 +946,7 @@ function renderClaudeHistoryOnlyId(idQuery, historyEntry, allSessions) {
499
946
  for (const session of relatedSessions) {
500
947
  console.error(chalk.gray(` ${session.shortId} ${session.id} ${session.project || '-'} ${formatRelativeTime(session.timestamp)}`));
501
948
  }
502
- console.error(chalk.gray('Use one of the transcript IDs above with "agents sessions view <id>".'));
949
+ console.error(chalk.gray('Use one of the transcript IDs above with "agents sessions <id>".'));
503
950
  return;
504
951
  }
505
952
  if (historyEntry.display === '/resume') {
@@ -516,7 +963,13 @@ function findClaudeSessionsInProject(sessions, historyEntry) {
516
963
  function findClaudeProjectSessions(sessions, historyEntry) {
517
964
  if (!historyEntry.project)
518
965
  return [];
519
- const projectRoot = historyEntry.project;
966
+ // Resolve symlinks (e.g. macOS /var -> /private/var) so we match sessions
967
+ // whose cwd was canonicalized at scan time.
968
+ let projectRoot = historyEntry.project;
969
+ try {
970
+ projectRoot = fs.realpathSync(projectRoot);
971
+ }
972
+ catch { /* dir gone */ }
520
973
  return sessions.filter(session => session.agent === 'claude' &&
521
974
  typeof session.cwd === 'string' &&
522
975
  isWithinProject(session.cwd, projectRoot));
@@ -597,24 +1050,21 @@ function sessionDistance(session, historyEntry) {
597
1050
  // ---------------------------------------------------------------------------
598
1051
  // Formatting helpers
599
1052
  // ---------------------------------------------------------------------------
1053
+ function formatAbsoluteTime(isoTimestamp) {
1054
+ const d = new Date(isoTimestamp);
1055
+ if (isNaN(d.getTime()))
1056
+ return isoTimestamp;
1057
+ const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
1058
+ const hh = String(d.getHours()).padStart(2, '0');
1059
+ const mm = String(d.getMinutes()).padStart(2, '0');
1060
+ return `${months[d.getMonth()]} ${d.getDate()} ${hh}:${mm}`;
1061
+ }
600
1062
  function padRight(s, width) {
601
1063
  return s.length >= width ? s + ' ' : s + ' '.repeat(width - s.length);
602
1064
  }
603
1065
  function truncate(s, max) {
604
1066
  return s.length > max ? s.slice(0, max - 1) + '.' : s;
605
1067
  }
606
- function formatCompactMetric(value) {
607
- if (value === undefined)
608
- return '-';
609
- if (value < 1000)
610
- return String(value);
611
- if (value < 1_000_000) {
612
- const compact = value / 1000;
613
- return compact >= 100 ? `${Math.round(compact)}k` : `${compact.toFixed(1).replace(/\.0$/, '')}k`;
614
- }
615
- const compact = value / 1_000_000;
616
- return compact >= 100 ? `${Math.round(compact)}m` : `${compact.toFixed(1).replace(/\.0$/, '')}m`;
617
- }
618
1068
  function formatRelativeTime(isoTimestamp) {
619
1069
  const now = Date.now();
620
1070
  const then = new Date(isoTimestamp).getTime();