@swarmify/agents-cli 1.12.0 → 1.13.1

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 +33 -0
  2. package/LICENSE +21 -0
  3. package/README.md +212 -232
  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 +700 -263
  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 +36 -1
  139. package/dist/commands/view.d.ts.map +1 -1
  140. package/dist/commands/view.js +375 -15
  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 +728 -0
  320. package/dist/lib/models.js.map +1 -0
  321. package/dist/lib/permissions.d.ts +24 -1
  322. package/dist/lib/permissions.d.ts.map +1 -1
  323. package/dist/lib/permissions.js +52 -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 +101 -2
  442. package/dist/lib/shims.d.ts.map +1 -1
  443. package/dist/lib/shims.js +282 -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 +106 -14
  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 +124 -41
  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,88 +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 = options.json ? null : 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
  });
207
+ tracker.stop();
30
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
+ }
31
234
  if (options.json) {
32
- const serializable = sessions.map(s => {
33
- const { _contentIndex, _userTerms, _matchedTerms, _bm25Score, ...rest } = s;
235
+ const filtered = searchQuery ? filterSessionsByQuery(sessions, searchQuery) : sessions;
236
+ const serializable = filtered.map(s => {
237
+ const { _matchedTerms, _bm25Score, ...rest } = s;
34
238
  return rest;
35
239
  });
36
240
  process.stdout.write(JSON.stringify(serializable, null, 2) + '\n');
37
241
  return;
38
242
  }
39
243
  if (sessions.length === 0) {
40
- 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
+ }
41
253
  return;
42
254
  }
43
- if (shouldUseFilteredSessionSearch(options)) {
44
- 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);
45
260
  if (picked) {
46
- await renderSession(picked, 'summary');
261
+ await handlePickedSession(picked);
47
262
  return;
48
263
  }
264
+ return;
49
265
  }
50
- // Print header
51
- console.log(chalk.gray(padRight('ID', 10) +
52
- padRight('Account', 20) +
53
- padRight('Agent', 18) +
54
- padRight('Project', 16) +
55
- padRight('When', 14) +
56
- padRight('Msgs', 8) +
57
- padRight('Tokens', 10) +
58
- 'Topic'));
59
- for (const session of sessions) {
60
- const agentColor = colorAgent(session.agent);
61
- const when = formatRelativeTime(session.timestamp);
62
- const project = session.project || '-';
63
- const account = session.account || '';
64
- const agentLabel = session.version
65
- ? `${session.agent}@${session.version}`
66
- : session.agent;
67
- const topic = session.topic || '';
68
- console.log(chalk.white(padRight(session.shortId, 10)) +
69
- chalk.gray(padRight(truncate(account, 18), 20)) +
70
- agentColor(padRight(truncate(agentLabel, 16), 18)) +
71
- chalk.cyan(padRight(truncate(project, 14), 16)) +
72
- chalk.gray(padRight(when, 14)) +
73
- chalk.gray(padRight(formatCompactMetric(session.messageCount), 8)) +
74
- chalk.gray(padRight(formatCompactMetric(session.tokenCount), 10)) +
75
- chalk.white(truncate(topic, 40)));
76
- }
77
- 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);
78
269
  }
79
270
  catch (err) {
271
+ tracker.stop();
80
272
  spinner?.stop();
81
273
  console.error(chalk.red(`Failed to discover sessions: ${err.message}`));
82
274
  process.exit(1);
83
275
  }
84
276
  }
85
- 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) {
86
352
  // OpenCode stores sessions in SQLite; filePath is "db_path#session_id"
87
353
  const realPath = session.filePath.split('#')[0];
88
354
  if (!fs.existsSync(realPath)) {
@@ -97,81 +363,115 @@ async function renderSession(session, mode) {
97
363
  console.log(chalk.gray(`Time: ${session.timestamp}`));
98
364
  return;
99
365
  }
100
- // Session header
101
- const agentColor = colorAgent(session.agent);
102
- console.log('');
103
- console.log(agentColor(session.agent) +
104
- (session.version ? chalk.yellow(` ${session.version}`) : '') +
105
- (session.project ? chalk.cyan(` ${session.project}`) : '') +
106
- chalk.gray(` ${formatRelativeTime(session.timestamp)}`) +
107
- (session.account ? chalk.gray(` (${session.account})`) : ''));
108
- console.log(chalk.gray('─'.repeat(60)));
109
366
  const spinner = ora(`Parsing ${session.agent} session...`).start();
110
- const events = parseSession(session.filePath, session.agent);
367
+ let events = parseSession(session.filePath, session.agent);
111
368
  spinner.stop();
112
- let output;
113
- switch (mode) {
114
- case 'transcript':
115
- output = renderTranscript(events);
116
- break;
117
- case 'summary':
118
- output = renderMarkdown(renderSummary(events));
119
- break;
120
- case 'trace':
121
- output = renderMarkdown(renderTrace(events));
122
- break;
123
- case 'json':
124
- output = renderJson(events);
125
- break;
126
- }
127
- process.stdout.write(output);
128
- }
129
- 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) {
130
443
  const agentColor = colorAgent(s.agent);
131
444
  const when = formatRelativeTime(s.timestamp);
132
445
  const project = s.project || '-';
133
- const account = s.account || '';
134
- const agentLabel = s.version ? `${s.agent}@${s.version}` : s.agent;
135
- const topic = s.topic || '';
136
- const matchTerms = s._matchedTerms;
137
- // Append matched terms badge
138
- const matchBadge = matchTerms && matchTerms.length > 0
139
- ? chalk.yellow(` [${matchTerms.join(', ')}]`)
140
- : '';
446
+ const tag = teamTag(s);
447
+ const label = s.label;
448
+ const topic = tag ? `${tag}${s.topic ?? ''}` : s.topic;
449
+ const versionStr = s.version || '-';
141
450
  return (chalk.white(padRight(s.shortId, 10)) +
142
- chalk.gray(padRight(truncate(account, 18), 20)) +
143
- agentColor(padRight(truncate(agentLabel, 16), 18)) +
451
+ agentColor(padRight(truncate(s.agent, 8), 9)) +
452
+ chalk.yellow(padRight(truncate(versionStr, 7), 8)) +
144
453
  chalk.cyan(padRight(truncate(project, 14), 16)) +
145
- chalk.gray(padRight(when, 14)) +
146
- chalk.gray(padRight(formatCompactMetric(s.messageCount), 8)) +
147
- chalk.gray(padRight(formatCompactMetric(s.tokenCount), 10)) +
148
- chalk.white(truncate(topic, 30)) +
149
- matchBadge);
454
+ renderTopicCell(label, topic, query, 48, 50) +
455
+ chalk.gray(when));
150
456
  }
151
- 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
+ }
152
461
  try {
153
- return await search({
462
+ return await sessionPicker({
154
463
  message,
155
- pageSize: 12,
156
- source: async (input) => {
157
- const matches = filterSessionsByQuery(sessions, input).slice(0, 30);
158
- if (matches.length === 0) {
159
- return [{
160
- name: input?.trim()
161
- ? `No sessions found for "${input.trim()}"`
162
- : 'No sessions found',
163
- value: null,
164
- disabled: 'Keep typing',
165
- }];
166
- }
167
- return matches.map(s => ({
168
- name: formatPickerLabel(s),
169
- value: s,
170
- description: formatSearchDescription(s),
171
- short: s.shortId,
172
- }));
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);
173
471
  },
174
- validate: (value) => value ? true : 'No matching sessions.',
472
+ labelFor: (s, query) => formatPickerLabel(s, query),
473
+ pageSize: PICKER_RECENT_COUNT,
474
+ initialSearch,
175
475
  });
176
476
  }
177
477
  catch (err) {
@@ -180,16 +480,81 @@ async function pickSession(sessions, message = 'Search sessions:') {
180
480
  throw err;
181
481
  }
182
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
+ }
183
548
  function parseAgentFilter(agentName) {
184
- const agent = agentName;
185
- if (agent && !SESSION_AGENTS.includes(agent)) {
186
- 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(', ')}`));
187
555
  process.exit(1);
188
556
  }
189
- return agent;
190
- }
191
- function shouldUseFilteredSessionSearch(options) {
192
- return isInteractiveTerminal() && Boolean(options.agent || options.project);
557
+ return { agent, version };
193
558
  }
194
559
  function formatSearchMessage(options) {
195
560
  const filters = [];
@@ -201,15 +566,21 @@ function formatSearchMessage(options) {
201
566
  return 'Search sessions:';
202
567
  return `Search sessions (${filters.join(', ')}):`;
203
568
  }
204
- function formatSearchDescription(session) {
205
- return [session.id, session.cwd].filter(Boolean).join(' ');
206
- }
207
- 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) {
208
571
  const trimmed = query?.trim().toLowerCase() || '';
209
572
  if (!trimmed)
210
573
  return sessions;
211
574
  const terms = trimmed.split(/\s+/).filter(Boolean);
212
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
+ }
213
584
  return sessions
214
585
  .map(session => ({ session, score: scoreSessionQuery(session, terms) }))
215
586
  .filter(entry => {
@@ -282,102 +653,131 @@ function scoreSessionQuery(session, terms) {
282
653
  }
283
654
  return score;
284
655
  }
285
- async function viewAction(idQuery, options) {
286
- // Default to summary, opt into full transcript
287
- let mode = 'summary';
288
- if (options.transcript)
289
- mode = 'transcript';
290
- else if (options.trace)
291
- mode = 'trace';
292
- else if (options.json)
293
- mode = 'json';
294
- const agent = parseAgentFilter(options.agent);
295
- 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);
296
688
  try {
297
- const allSessions = await discoverSessions({
298
- agent,
299
- all: Boolean(idQuery) || options.all,
689
+ const discovered = await discoverSessions({
690
+ all: true,
300
691
  cwd: process.cwd(),
301
- project: options.project,
302
692
  limit: 5000,
303
- since: options.since,
304
- until: options.until,
693
+ onProgress: tracker.onProgress,
305
694
  });
306
- let session;
307
- if (!idQuery) {
308
- // 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) {
309
700
  spinner.stop();
310
- if (allSessions.length === 0) {
311
- console.log(chalk.gray(formatNoSessionsMessage(options.all, true)));
312
- return;
313
- }
314
- if (!isInteractiveTerminal()) {
315
- requireInteractiveSelection('Selecting a session to view', [
316
- 'agents sessions list',
317
- 'agents sessions view <id>',
318
- ]);
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 ?? ''}`));
319
709
  }
320
- const picked = await pickSession(allSessions, formatSearchMessage(options));
321
- if (!picked)
322
- return;
323
- session = picked;
710
+ console.error(chalk.gray('Pass a longer ID to narrow it down.'));
711
+ process.exit(1);
324
712
  }
325
- else {
326
- const matches = resolveSessionById(allSessions, idQuery);
327
- let queryMatches = matches.length > 0 ? matches : filterSessionsByQuery(allSessions, idQuery);
328
- // Content search fallback when no title/topic/project matches
329
- if (queryMatches.length === 0) {
330
- const contentResults = searchContentIndex(allSessions, idQuery);
331
- if (contentResults.size > 0) {
332
- const matchedSessions = Array.from(contentResults.values())
333
- .sort((a, b) => (b._bm25Score ?? 0) - (a._bm25Score ?? 0));
334
- if (matchedSessions.length === 1) {
335
- session = matchedSessions[0];
336
- console.log(chalk.gray(`Found by content match (terms: ${matchedSessions[0]._matchedTerms?.join(', ')})`));
337
- }
338
- else {
339
- queryMatches = matchedSessions;
340
- }
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;
341
750
  }
342
751
  }
343
- if (queryMatches.length === 0) {
344
- spinner.stop();
345
- const historyEntry = findClaudeHistoryEntry(idQuery);
346
- if (historyEntry) {
347
- const resumeMatch = resolveClaudeHistoryEntryToTranscript(historyEntry, allSessions);
348
- if (resumeMatch) {
349
- session = resumeMatch.session;
350
- console.log(chalk.gray(`Resolved Claude history entry ${idQuery} to transcript ${resumeMatch.session.id}.`));
351
- }
352
- else {
353
- renderClaudeHistoryOnlyId(idQuery, historyEntry, allSessions);
354
- process.exit(1);
355
- }
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;
356
760
  }
357
761
  else {
358
- console.error(chalk.red(`No session found matching: ${idQuery}`));
359
- console.error(chalk.gray('Run "agents sessions" to list available sessions.'));
762
+ renderClaudeHistoryOnlyId(query, historyEntry, allSessions);
360
763
  process.exit(1);
361
764
  }
362
765
  }
363
- if (queryMatches.length === 0) {
364
- // 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);
365
770
  }
366
- else if (queryMatches.length > 1) {
771
+ }
772
+ if (!session) {
773
+ if (queryMatches.length > 1) {
367
774
  spinner.stop();
368
- if (!isInteractiveTerminal()) {
369
- console.error(chalk.red(`Multiple sessions match: ${idQuery}`));
370
- console.error(chalk.gray('Pass a longer ID/query or one of these exact IDs:'));
371
- for (const match of queryMatches.slice(0, 10)) {
372
- console.error(chalk.cyan(` ${match.id}`));
373
- }
374
- 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 ?? ''}`));
375
778
  }
376
- // Multiple matches -- let the user pick
377
- const picked = await pickSession(queryMatches, formatSearchMessage(options));
378
- if (!picked)
379
- return;
380
- session = picked;
779
+ console.error(chalk.gray('Pass a longer ID to narrow it down.'));
780
+ process.exit(1);
381
781
  }
382
782
  else {
383
783
  session = queryMatches[0];
@@ -387,74 +787,108 @@ async function viewAction(idQuery, options) {
387
787
  throw new Error('Session resolution failed');
388
788
  }
389
789
  spinner.stop();
390
- await renderSession(session, mode);
790
+ await renderSession(session, mode, scope.filter);
391
791
  }
392
792
  catch (err) {
393
793
  if (isPromptCancelled(err))
394
794
  return;
795
+ tracker.stop();
395
796
  spinner.stop();
396
797
  console.error(chalk.red(`Failed to read session: ${err.message}`));
397
798
  process.exit(1);
398
799
  }
399
800
  }
801
+ /** Register the `agents sessions` command with all its options and help text. */
400
802
  export function registerSessionsCommands(program) {
401
- const sessionsCmd = program
803
+ program
402
804
  .command('sessions')
403
- .description('List and view agent sessions for the current directory by default')
404
- .option('--agent <agent>', SESSION_AGENT_FILTER_HELP)
405
- .option('--all', 'Show sessions from every directory')
406
- .option('--project <name>', 'Filter by project name across all directories')
407
- .option('--since <time>', 'Filter sessions newer than time (e.g., "7d", "30d", ISO timestamp)')
408
- .option('--until <time>', 'Filter sessions older than time (ISO timestamp)')
409
- .option('-n, --limit <n>', 'Max sessions to show', '20')
410
- .option('--json', 'Output sessions as JSON array')
411
- .action(async (options) => {
412
- await listAction(options);
413
- });
414
- sessionsCmd
415
- .command('list')
416
- .description('List sessions for the current directory by default')
417
- .option('--agent <agent>', SESSION_AGENT_FILTER_HELP)
418
- .option('--all', 'Show sessions from every directory')
419
- .option('--project <name>', 'Filter by project name across all directories')
420
- .option('--since <time>', 'Filter sessions newer than time (e.g., "7d", "30d", ISO timestamp)')
421
- .option('--until <time>', 'Filter sessions older than time (ISO timestamp)')
422
- .option('-n, --limit <n>', 'Max sessions to show', '20')
423
- .option('--json', 'Output sessions as JSON array')
424
- .action(async (options, command) => {
425
- const parentOptions = typeof command?.parent?.opts === 'function'
426
- ? command.parent.opts()
427
- : {};
428
- await listAction({ ...parentOptions, ...options });
429
- });
430
- sessionsCmd
431
- .command('view [id]')
432
- .description('View a session by ID or search query (picker defaults to live search)')
433
- .option('--agent <agent>', SESSION_AGENT_FILTER_HELP)
434
- .option('--all', 'Show sessions from every directory')
435
- .option('--project <name>', 'Filter by project name across all directories')
436
- .option('--since <time>', 'Filter sessions newer than time (e.g., "7d", "30d", ISO timestamp)')
437
- .option('--until <time>', 'Filter sessions older than time (ISO timestamp)')
438
- .option('--transcript', 'Show full conversation transcript')
439
- .option('--trace', 'Show reasoning trace as markdown')
440
- .option('--json', 'Output normalized events as JSON')
441
- .action(async (id, options, command) => {
442
- const parentOptions = typeof command?.parent?.opts === 'function'
443
- ? command.parent.opts()
444
- : {};
445
- 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);
446
876
  });
447
877
  }
448
- function formatNoSessionsMessage(showAll, picker = false, project) {
878
+ function formatNoSessionsMessage(showAll, project) {
449
879
  const projectQuery = project?.trim();
450
880
  if (projectQuery) {
451
881
  return `No sessions found for project "${projectQuery}".`;
452
882
  }
453
883
  if (showAll)
454
884
  return 'No sessions found.';
455
- const command = picker ? 'agents sessions view --all' : 'agents sessions --all';
885
+ const command = 'agents sessions --all';
456
886
  return `No sessions found for ${process.cwd()}. Run "${command}" to see sessions from every directory.`;
457
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
+ }
458
892
  function findClaudeHistoryEntry(idQuery) {
459
893
  const historyPath = path.join(os.homedir(), '.claude', 'history.jsonl');
460
894
  if (!fs.existsSync(historyPath))
@@ -512,7 +946,7 @@ function renderClaudeHistoryOnlyId(idQuery, historyEntry, allSessions) {
512
946
  for (const session of relatedSessions) {
513
947
  console.error(chalk.gray(` ${session.shortId} ${session.id} ${session.project || '-'} ${formatRelativeTime(session.timestamp)}`));
514
948
  }
515
- 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>".'));
516
950
  return;
517
951
  }
518
952
  if (historyEntry.display === '/resume') {
@@ -529,7 +963,13 @@ function findClaudeSessionsInProject(sessions, historyEntry) {
529
963
  function findClaudeProjectSessions(sessions, historyEntry) {
530
964
  if (!historyEntry.project)
531
965
  return [];
532
- 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 */ }
533
973
  return sessions.filter(session => session.agent === 'claude' &&
534
974
  typeof session.cwd === 'string' &&
535
975
  isWithinProject(session.cwd, projectRoot));
@@ -610,24 +1050,21 @@ function sessionDistance(session, historyEntry) {
610
1050
  // ---------------------------------------------------------------------------
611
1051
  // Formatting helpers
612
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
+ }
613
1062
  function padRight(s, width) {
614
1063
  return s.length >= width ? s + ' ' : s + ' '.repeat(width - s.length);
615
1064
  }
616
1065
  function truncate(s, max) {
617
1066
  return s.length > max ? s.slice(0, max - 1) + '.' : s;
618
1067
  }
619
- function formatCompactMetric(value) {
620
- if (value === undefined)
621
- return '-';
622
- if (value < 1000)
623
- return String(value);
624
- if (value < 1_000_000) {
625
- const compact = value / 1000;
626
- return compact >= 100 ? `${Math.round(compact)}k` : `${compact.toFixed(1).replace(/\.0$/, '')}k`;
627
- }
628
- const compact = value / 1_000_000;
629
- return compact >= 100 ? `${Math.round(compact)}m` : `${compact.toFixed(1).replace(/\.0$/, '')}m`;
630
- }
631
1068
  function formatRelativeTime(isoTimestamp) {
632
1069
  const now = Date.now();
633
1070
  const then = new Date(isoTimestamp).getTime();