@phnx-labs/agents-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (486) hide show
  1. package/CHANGELOG.md +316 -0
  2. package/LICENSE +21 -0
  3. package/README.md +537 -0
  4. package/dist/commands/__tests__/sessions.test.d.ts +2 -0
  5. package/dist/commands/__tests__/sessions.test.d.ts.map +1 -0
  6. package/dist/commands/__tests__/sessions.test.js +636 -0
  7. package/dist/commands/__tests__/sessions.test.js.map +1 -0
  8. package/dist/commands/cloud.d.ts +3 -0
  9. package/dist/commands/cloud.d.ts.map +1 -0
  10. package/dist/commands/cloud.js +322 -0
  11. package/dist/commands/cloud.js.map +1 -0
  12. package/dist/commands/commands.d.ts +3 -0
  13. package/dist/commands/commands.d.ts.map +1 -0
  14. package/dist/commands/commands.js +628 -0
  15. package/dist/commands/commands.js.map +1 -0
  16. package/dist/commands/daemon.d.ts +3 -0
  17. package/dist/commands/daemon.d.ts.map +1 -0
  18. package/dist/commands/daemon.js +110 -0
  19. package/dist/commands/daemon.js.map +1 -0
  20. package/dist/commands/drive.d.ts +3 -0
  21. package/dist/commands/drive.d.ts.map +1 -0
  22. package/dist/commands/drive.js +166 -0
  23. package/dist/commands/drive.js.map +1 -0
  24. package/dist/commands/exec.d.ts +3 -0
  25. package/dist/commands/exec.d.ts.map +1 -0
  26. package/dist/commands/exec.js +241 -0
  27. package/dist/commands/exec.js.map +1 -0
  28. package/dist/commands/fork.d.ts +3 -0
  29. package/dist/commands/fork.d.ts.map +1 -0
  30. package/dist/commands/fork.js +139 -0
  31. package/dist/commands/fork.js.map +1 -0
  32. package/dist/commands/hooks.d.ts +3 -0
  33. package/dist/commands/hooks.d.ts.map +1 -0
  34. package/dist/commands/hooks.js +689 -0
  35. package/dist/commands/hooks.js.map +1 -0
  36. package/dist/commands/init.d.ts +6 -0
  37. package/dist/commands/init.d.ts.map +1 -0
  38. package/dist/commands/init.js +127 -0
  39. package/dist/commands/init.js.map +1 -0
  40. package/dist/commands/mcp.d.ts +3 -0
  41. package/dist/commands/mcp.d.ts.map +1 -0
  42. package/dist/commands/mcp.js +581 -0
  43. package/dist/commands/mcp.js.map +1 -0
  44. package/dist/commands/models.d.ts +3 -0
  45. package/dist/commands/models.d.ts.map +1 -0
  46. package/dist/commands/models.js +158 -0
  47. package/dist/commands/models.js.map +1 -0
  48. package/dist/commands/packages.d.ts +3 -0
  49. package/dist/commands/packages.d.ts.map +1 -0
  50. package/dist/commands/packages.js +541 -0
  51. package/dist/commands/packages.js.map +1 -0
  52. package/dist/commands/permissions.d.ts +3 -0
  53. package/dist/commands/permissions.d.ts.map +1 -0
  54. package/dist/commands/permissions.js +723 -0
  55. package/dist/commands/permissions.js.map +1 -0
  56. package/dist/commands/plugins.d.ts +3 -0
  57. package/dist/commands/plugins.d.ts.map +1 -0
  58. package/dist/commands/plugins.js +382 -0
  59. package/dist/commands/plugins.js.map +1 -0
  60. package/dist/commands/profiles.d.ts +3 -0
  61. package/dist/commands/profiles.d.ts.map +1 -0
  62. package/dist/commands/profiles.js +242 -0
  63. package/dist/commands/profiles.js.map +1 -0
  64. package/dist/commands/pty.d.ts +20 -0
  65. package/dist/commands/pty.d.ts.map +1 -0
  66. package/dist/commands/pty.js +389 -0
  67. package/dist/commands/pty.js.map +1 -0
  68. package/dist/commands/pull.d.ts +3 -0
  69. package/dist/commands/pull.d.ts.map +1 -0
  70. package/dist/commands/pull.js +448 -0
  71. package/dist/commands/pull.js.map +1 -0
  72. package/dist/commands/push.d.ts +3 -0
  73. package/dist/commands/push.d.ts.map +1 -0
  74. package/dist/commands/push.js +180 -0
  75. package/dist/commands/push.js.map +1 -0
  76. package/dist/commands/refresh-memory.d.ts +9 -0
  77. package/dist/commands/refresh-memory.d.ts.map +1 -0
  78. package/dist/commands/refresh-memory.js +45 -0
  79. package/dist/commands/refresh-memory.js.map +1 -0
  80. package/dist/commands/resource-view.d.ts +31 -0
  81. package/dist/commands/resource-view.d.ts.map +1 -0
  82. package/dist/commands/resource-view.js +183 -0
  83. package/dist/commands/resource-view.js.map +1 -0
  84. package/dist/commands/routines.d.ts +3 -0
  85. package/dist/commands/routines.d.ts.map +1 -0
  86. package/dist/commands/routines.js +579 -0
  87. package/dist/commands/routines.js.map +1 -0
  88. package/dist/commands/rules.d.ts +3 -0
  89. package/dist/commands/rules.d.ts.map +1 -0
  90. package/dist/commands/rules.js +488 -0
  91. package/dist/commands/rules.js.map +1 -0
  92. package/dist/commands/secrets.d.ts +3 -0
  93. package/dist/commands/secrets.d.ts.map +1 -0
  94. package/dist/commands/secrets.js +339 -0
  95. package/dist/commands/secrets.js.map +1 -0
  96. package/dist/commands/sessions-picker.d.ts +16 -0
  97. package/dist/commands/sessions-picker.d.ts.map +1 -0
  98. package/dist/commands/sessions-picker.js +256 -0
  99. package/dist/commands/sessions-picker.js.map +1 -0
  100. package/dist/commands/sessions.d.ts +16 -0
  101. package/dist/commands/sessions.d.ts.map +1 -0
  102. package/dist/commands/sessions.js +1077 -0
  103. package/dist/commands/sessions.js.map +1 -0
  104. package/dist/commands/skills.d.ts +3 -0
  105. package/dist/commands/skills.d.ts.map +1 -0
  106. package/dist/commands/skills.js +716 -0
  107. package/dist/commands/skills.js.map +1 -0
  108. package/dist/commands/status.d.ts +3 -0
  109. package/dist/commands/status.d.ts.map +1 -0
  110. package/dist/commands/status.js +19 -0
  111. package/dist/commands/status.js.map +1 -0
  112. package/dist/commands/subagents.d.ts +3 -0
  113. package/dist/commands/subagents.d.ts.map +1 -0
  114. package/dist/commands/subagents.js +350 -0
  115. package/dist/commands/subagents.js.map +1 -0
  116. package/dist/commands/sync.d.ts +3 -0
  117. package/dist/commands/sync.d.ts.map +1 -0
  118. package/dist/commands/sync.js +62 -0
  119. package/dist/commands/sync.js.map +1 -0
  120. package/dist/commands/teams-picker.d.ts +14 -0
  121. package/dist/commands/teams-picker.d.ts.map +1 -0
  122. package/dist/commands/teams-picker.js +278 -0
  123. package/dist/commands/teams-picker.js.map +1 -0
  124. package/dist/commands/teams.d.ts +3 -0
  125. package/dist/commands/teams.d.ts.map +1 -0
  126. package/dist/commands/teams.js +917 -0
  127. package/dist/commands/teams.js.map +1 -0
  128. package/dist/commands/utils.d.ts +39 -0
  129. package/dist/commands/utils.d.ts.map +1 -0
  130. package/dist/commands/utils.js +100 -0
  131. package/dist/commands/utils.js.map +1 -0
  132. package/dist/commands/versions.d.ts +3 -0
  133. package/dist/commands/versions.d.ts.map +1 -0
  134. package/dist/commands/versions.js +700 -0
  135. package/dist/commands/versions.js.map +1 -0
  136. package/dist/commands/view.d.ts +8 -0
  137. package/dist/commands/view.d.ts.map +1 -0
  138. package/dist/commands/view.js +626 -0
  139. package/dist/commands/view.js.map +1 -0
  140. package/dist/index.d.ts +3 -0
  141. package/dist/index.d.ts.map +1 -0
  142. package/dist/index.js +484 -0
  143. package/dist/index.js.map +1 -0
  144. package/dist/lib/__tests__/bugfixes.test.d.ts +2 -0
  145. package/dist/lib/__tests__/bugfixes.test.d.ts.map +1 -0
  146. package/dist/lib/__tests__/bugfixes.test.js +192 -0
  147. package/dist/lib/__tests__/bugfixes.test.js.map +1 -0
  148. package/dist/lib/__tests__/exec.test.d.ts +2 -0
  149. package/dist/lib/__tests__/exec.test.d.ts.map +1 -0
  150. package/dist/lib/__tests__/exec.test.js +446 -0
  151. package/dist/lib/__tests__/exec.test.js.map +1 -0
  152. package/dist/lib/__tests__/git-sync.test.d.ts +2 -0
  153. package/dist/lib/__tests__/git-sync.test.d.ts.map +1 -0
  154. package/dist/lib/__tests__/git-sync.test.js +138 -0
  155. package/dist/lib/__tests__/git-sync.test.js.map +1 -0
  156. package/dist/lib/__tests__/hooks.test.d.ts +2 -0
  157. package/dist/lib/__tests__/hooks.test.d.ts.map +1 -0
  158. package/dist/lib/__tests__/hooks.test.js +203 -0
  159. package/dist/lib/__tests__/hooks.test.js.map +1 -0
  160. package/dist/lib/__tests__/memory-compile.test.d.ts +2 -0
  161. package/dist/lib/__tests__/memory-compile.test.d.ts.map +1 -0
  162. package/dist/lib/__tests__/memory-compile.test.js +95 -0
  163. package/dist/lib/__tests__/memory-compile.test.js.map +1 -0
  164. package/dist/lib/__tests__/models.test.d.ts +2 -0
  165. package/dist/lib/__tests__/models.test.d.ts.map +1 -0
  166. package/dist/lib/__tests__/models.test.js +239 -0
  167. package/dist/lib/__tests__/models.test.js.map +1 -0
  168. package/dist/lib/__tests__/rotate.test.d.ts +2 -0
  169. package/dist/lib/__tests__/rotate.test.d.ts.map +1 -0
  170. package/dist/lib/__tests__/rotate.test.js +80 -0
  171. package/dist/lib/__tests__/rotate.test.js.map +1 -0
  172. package/dist/lib/__tests__/secrets-bundles.test.d.ts +2 -0
  173. package/dist/lib/__tests__/secrets-bundles.test.d.ts.map +1 -0
  174. package/dist/lib/__tests__/secrets-bundles.test.js +104 -0
  175. package/dist/lib/__tests__/secrets-bundles.test.js.map +1 -0
  176. package/dist/lib/__tests__/secrets.test.d.ts +2 -0
  177. package/dist/lib/__tests__/secrets.test.d.ts.map +1 -0
  178. package/dist/lib/__tests__/secrets.test.js +90 -0
  179. package/dist/lib/__tests__/secrets.test.js.map +1 -0
  180. package/dist/lib/__tests__/shims.test.d.ts +2 -0
  181. package/dist/lib/__tests__/shims.test.d.ts.map +1 -0
  182. package/dist/lib/__tests__/shims.test.js +39 -0
  183. package/dist/lib/__tests__/shims.test.js.map +1 -0
  184. package/dist/lib/__tests__/usage.test.d.ts +2 -0
  185. package/dist/lib/__tests__/usage.test.d.ts.map +1 -0
  186. package/dist/lib/__tests__/usage.test.js +220 -0
  187. package/dist/lib/__tests__/usage.test.js.map +1 -0
  188. package/dist/lib/__tests__/versions.test.d.ts +2 -0
  189. package/dist/lib/__tests__/versions.test.d.ts.map +1 -0
  190. package/dist/lib/__tests__/versions.test.js +63 -0
  191. package/dist/lib/__tests__/versions.test.js.map +1 -0
  192. package/dist/lib/agents.d.ts +107 -0
  193. package/dist/lib/agents.d.ts.map +1 -0
  194. package/dist/lib/agents.js +1096 -0
  195. package/dist/lib/agents.js.map +1 -0
  196. package/dist/lib/artifact-actions.d.ts +22 -0
  197. package/dist/lib/artifact-actions.d.ts.map +1 -0
  198. package/dist/lib/artifact-actions.js +55 -0
  199. package/dist/lib/artifact-actions.js.map +1 -0
  200. package/dist/lib/cloud/codex.d.ts +19 -0
  201. package/dist/lib/cloud/codex.d.ts.map +1 -0
  202. package/dist/lib/cloud/codex.js +210 -0
  203. package/dist/lib/cloud/codex.js.map +1 -0
  204. package/dist/lib/cloud/factory.d.ts +26 -0
  205. package/dist/lib/cloud/factory.d.ts.map +1 -0
  206. package/dist/lib/cloud/factory.js +37 -0
  207. package/dist/lib/cloud/factory.js.map +1 -0
  208. package/dist/lib/cloud/registry.d.ts +6 -0
  209. package/dist/lib/cloud/registry.d.ts.map +1 -0
  210. package/dist/lib/cloud/registry.js +56 -0
  211. package/dist/lib/cloud/registry.js.map +1 -0
  212. package/dist/lib/cloud/rush.d.ts +15 -0
  213. package/dist/lib/cloud/rush.d.ts.map +1 -0
  214. package/dist/lib/cloud/rush.js +185 -0
  215. package/dist/lib/cloud/rush.js.map +1 -0
  216. package/dist/lib/cloud/store.d.ts +10 -0
  217. package/dist/lib/cloud/store.d.ts.map +1 -0
  218. package/dist/lib/cloud/store.js +96 -0
  219. package/dist/lib/cloud/store.js.map +1 -0
  220. package/dist/lib/cloud/stream.d.ts +18 -0
  221. package/dist/lib/cloud/stream.d.ts.map +1 -0
  222. package/dist/lib/cloud/stream.js +138 -0
  223. package/dist/lib/cloud/stream.js.map +1 -0
  224. package/dist/lib/cloud/types.d.ts +60 -0
  225. package/dist/lib/cloud/types.d.ts.map +1 -0
  226. package/dist/lib/cloud/types.js +2 -0
  227. package/dist/lib/cloud/types.js.map +1 -0
  228. package/dist/lib/commands.d.ts +121 -0
  229. package/dist/lib/commands.d.ts.map +1 -0
  230. package/dist/lib/commands.js +499 -0
  231. package/dist/lib/commands.js.map +1 -0
  232. package/dist/lib/convert.d.ts +11 -0
  233. package/dist/lib/convert.d.ts.map +1 -0
  234. package/dist/lib/convert.js +45 -0
  235. package/dist/lib/convert.js.map +1 -0
  236. package/dist/lib/daemon.d.ts +22 -0
  237. package/dist/lib/daemon.d.ts.map +1 -0
  238. package/dist/lib/daemon.js +311 -0
  239. package/dist/lib/daemon.js.map +1 -0
  240. package/dist/lib/drive-sync.d.ts +28 -0
  241. package/dist/lib/drive-sync.d.ts.map +1 -0
  242. package/dist/lib/drive-sync.js +193 -0
  243. package/dist/lib/drive-sync.js.map +1 -0
  244. package/dist/lib/exec.d.ts +85 -0
  245. package/dist/lib/exec.d.ts.map +1 -0
  246. package/dist/lib/exec.js +423 -0
  247. package/dist/lib/exec.js.map +1 -0
  248. package/dist/lib/factory.d.ts +57 -0
  249. package/dist/lib/factory.d.ts.map +1 -0
  250. package/dist/lib/factory.js +110 -0
  251. package/dist/lib/factory.js.map +1 -0
  252. package/dist/lib/git.d.ts +146 -0
  253. package/dist/lib/git.d.ts.map +1 -0
  254. package/dist/lib/git.js +635 -0
  255. package/dist/lib/git.js.map +1 -0
  256. package/dist/lib/help.d.ts +3 -0
  257. package/dist/lib/help.d.ts.map +1 -0
  258. package/dist/lib/help.js +63 -0
  259. package/dist/lib/help.js.map +1 -0
  260. package/dist/lib/hooks.d.ts +116 -0
  261. package/dist/lib/hooks.d.ts.map +1 -0
  262. package/dist/lib/hooks.js +837 -0
  263. package/dist/lib/hooks.js.map +1 -0
  264. package/dist/lib/manifest.d.ts +8 -0
  265. package/dist/lib/manifest.d.ts.map +1 -0
  266. package/dist/lib/manifest.js +36 -0
  267. package/dist/lib/manifest.js.map +1 -0
  268. package/dist/lib/markdown.d.ts +5 -0
  269. package/dist/lib/markdown.d.ts.map +1 -0
  270. package/dist/lib/markdown.js +11 -0
  271. package/dist/lib/markdown.js.map +1 -0
  272. package/dist/lib/mcp.d.ts +64 -0
  273. package/dist/lib/mcp.d.ts.map +1 -0
  274. package/dist/lib/mcp.js +327 -0
  275. package/dist/lib/mcp.js.map +1 -0
  276. package/dist/lib/memory-compile.d.ts +56 -0
  277. package/dist/lib/memory-compile.d.ts.map +1 -0
  278. package/dist/lib/memory-compile.js +167 -0
  279. package/dist/lib/memory-compile.js.map +1 -0
  280. package/dist/lib/memory.d.ts +56 -0
  281. package/dist/lib/memory.d.ts.map +1 -0
  282. package/dist/lib/memory.js +267 -0
  283. package/dist/lib/memory.js.map +1 -0
  284. package/dist/lib/models.d.ts +91 -0
  285. package/dist/lib/models.d.ts.map +1 -0
  286. package/dist/lib/models.js +706 -0
  287. package/dist/lib/models.js.map +1 -0
  288. package/dist/lib/permissions.d.ts +204 -0
  289. package/dist/lib/permissions.d.ts.map +1 -0
  290. package/dist/lib/permissions.js +1022 -0
  291. package/dist/lib/permissions.js.map +1 -0
  292. package/dist/lib/picker.d.ts +17 -0
  293. package/dist/lib/picker.d.ts.map +1 -0
  294. package/dist/lib/picker.js +95 -0
  295. package/dist/lib/picker.js.map +1 -0
  296. package/dist/lib/plugins.d.ts +73 -0
  297. package/dist/lib/plugins.d.ts.map +1 -0
  298. package/dist/lib/plugins.js +549 -0
  299. package/dist/lib/plugins.js.map +1 -0
  300. package/dist/lib/profiles-keychain.d.ts +3 -0
  301. package/dist/lib/profiles-keychain.d.ts.map +1 -0
  302. package/dist/lib/profiles-keychain.js +10 -0
  303. package/dist/lib/profiles-keychain.js.map +1 -0
  304. package/dist/lib/profiles-presets.d.ts +15 -0
  305. package/dist/lib/profiles-presets.d.ts.map +1 -0
  306. package/dist/lib/profiles-presets.js +95 -0
  307. package/dist/lib/profiles-presets.js.map +1 -0
  308. package/dist/lib/profiles.d.ts +35 -0
  309. package/dist/lib/profiles.d.ts.map +1 -0
  310. package/dist/lib/profiles.js +123 -0
  311. package/dist/lib/profiles.js.map +1 -0
  312. package/dist/lib/pty-client.d.ts +22 -0
  313. package/dist/lib/pty-client.d.ts.map +1 -0
  314. package/dist/lib/pty-client.js +181 -0
  315. package/dist/lib/pty-client.js.map +1 -0
  316. package/dist/lib/pty-server.d.ts +16 -0
  317. package/dist/lib/pty-server.d.ts.map +1 -0
  318. package/dist/lib/pty-server.js +422 -0
  319. package/dist/lib/pty-server.js.map +1 -0
  320. package/dist/lib/registry.d.ts +28 -0
  321. package/dist/lib/registry.d.ts.map +1 -0
  322. package/dist/lib/registry.js +203 -0
  323. package/dist/lib/registry.js.map +1 -0
  324. package/dist/lib/resources.d.ts +50 -0
  325. package/dist/lib/resources.d.ts.map +1 -0
  326. package/dist/lib/resources.js +103 -0
  327. package/dist/lib/resources.js.map +1 -0
  328. package/dist/lib/rotate.d.ts +52 -0
  329. package/dist/lib/rotate.d.ts.map +1 -0
  330. package/dist/lib/rotate.js +87 -0
  331. package/dist/lib/rotate.js.map +1 -0
  332. package/dist/lib/routines.d.ts +70 -0
  333. package/dist/lib/routines.d.ts.map +1 -0
  334. package/dist/lib/routines.js +325 -0
  335. package/dist/lib/routines.js.map +1 -0
  336. package/dist/lib/runner.d.ts +12 -0
  337. package/dist/lib/runner.d.ts.map +1 -0
  338. package/dist/lib/runner.js +311 -0
  339. package/dist/lib/runner.js.map +1 -0
  340. package/dist/lib/sandbox.d.ts +10 -0
  341. package/dist/lib/sandbox.d.ts.map +1 -0
  342. package/dist/lib/sandbox.js +201 -0
  343. package/dist/lib/sandbox.js.map +1 -0
  344. package/dist/lib/scheduler.d.ts +18 -0
  345. package/dist/lib/scheduler.d.ts.map +1 -0
  346. package/dist/lib/scheduler.js +69 -0
  347. package/dist/lib/scheduler.js.map +1 -0
  348. package/dist/lib/secrets-bundles.d.ts +29 -0
  349. package/dist/lib/secrets-bundles.d.ts.map +1 -0
  350. package/dist/lib/secrets-bundles.js +168 -0
  351. package/dist/lib/secrets-bundles.js.map +1 -0
  352. package/dist/lib/secrets.d.ts +27 -0
  353. package/dist/lib/secrets.d.ts.map +1 -0
  354. package/dist/lib/secrets.js +127 -0
  355. package/dist/lib/secrets.js.map +1 -0
  356. package/dist/lib/session/__tests__/db.test.d.ts +2 -0
  357. package/dist/lib/session/__tests__/db.test.d.ts.map +1 -0
  358. package/dist/lib/session/__tests__/db.test.js +54 -0
  359. package/dist/lib/session/__tests__/db.test.js.map +1 -0
  360. package/dist/lib/session/__tests__/discover.test.d.ts +2 -0
  361. package/dist/lib/session/__tests__/discover.test.d.ts.map +1 -0
  362. package/dist/lib/session/__tests__/discover.test.js +63 -0
  363. package/dist/lib/session/__tests__/discover.test.js.map +1 -0
  364. package/dist/lib/session/__tests__/prompt.test.d.ts +2 -0
  365. package/dist/lib/session/__tests__/prompt.test.d.ts.map +1 -0
  366. package/dist/lib/session/__tests__/prompt.test.js +44 -0
  367. package/dist/lib/session/__tests__/prompt.test.js.map +1 -0
  368. package/dist/lib/session/__tests__/render.test.d.ts +2 -0
  369. package/dist/lib/session/__tests__/render.test.d.ts.map +1 -0
  370. package/dist/lib/session/__tests__/render.test.js +602 -0
  371. package/dist/lib/session/__tests__/render.test.js.map +1 -0
  372. package/dist/lib/session/artifacts.d.ts +5 -0
  373. package/dist/lib/session/artifacts.d.ts.map +1 -0
  374. package/dist/lib/session/artifacts.js +75 -0
  375. package/dist/lib/session/artifacts.js.map +1 -0
  376. package/dist/lib/session/db.d.ts +118 -0
  377. package/dist/lib/session/db.d.ts.map +1 -0
  378. package/dist/lib/session/db.js +576 -0
  379. package/dist/lib/session/db.js.map +1 -0
  380. package/dist/lib/session/discover.d.ts +60 -0
  381. package/dist/lib/session/discover.d.ts.map +1 -0
  382. package/dist/lib/session/discover.js +1272 -0
  383. package/dist/lib/session/discover.js.map +1 -0
  384. package/dist/lib/session/parse.d.ts +23 -0
  385. package/dist/lib/session/parse.d.ts.map +1 -0
  386. package/dist/lib/session/parse.js +650 -0
  387. package/dist/lib/session/parse.js.map +1 -0
  388. package/dist/lib/session/prompt.d.ts +4 -0
  389. package/dist/lib/session/prompt.d.ts.map +1 -0
  390. package/dist/lib/session/prompt.js +64 -0
  391. package/dist/lib/session/prompt.js.map +1 -0
  392. package/dist/lib/session/prompt.test.d.ts +2 -0
  393. package/dist/lib/session/prompt.test.d.ts.map +1 -0
  394. package/dist/lib/session/prompt.test.js +57 -0
  395. package/dist/lib/session/prompt.test.js.map +1 -0
  396. package/dist/lib/session/render.d.ts +90 -0
  397. package/dist/lib/session/render.d.ts.map +1 -0
  398. package/dist/lib/session/render.js +778 -0
  399. package/dist/lib/session/render.js.map +1 -0
  400. package/dist/lib/session/team-filter.d.ts +26 -0
  401. package/dist/lib/session/team-filter.d.ts.map +1 -0
  402. package/dist/lib/session/team-filter.js +66 -0
  403. package/dist/lib/session/team-filter.js.map +1 -0
  404. package/dist/lib/session/team-filter.test.d.ts +2 -0
  405. package/dist/lib/session/team-filter.test.d.ts.map +1 -0
  406. package/dist/lib/session/team-filter.test.js +157 -0
  407. package/dist/lib/session/team-filter.test.js.map +1 -0
  408. package/dist/lib/session/types.d.ts +69 -0
  409. package/dist/lib/session/types.d.ts.map +1 -0
  410. package/dist/lib/session/types.js +2 -0
  411. package/dist/lib/session/types.js.map +1 -0
  412. package/dist/lib/shims.d.ts +228 -0
  413. package/dist/lib/shims.d.ts.map +1 -0
  414. package/dist/lib/shims.js +1170 -0
  415. package/dist/lib/shims.js.map +1 -0
  416. package/dist/lib/skills.d.ts +134 -0
  417. package/dist/lib/skills.d.ts.map +1 -0
  418. package/dist/lib/skills.js +783 -0
  419. package/dist/lib/skills.js.map +1 -0
  420. package/dist/lib/state.d.ts +53 -0
  421. package/dist/lib/state.d.ts.map +1 -0
  422. package/dist/lib/state.js +299 -0
  423. package/dist/lib/state.js.map +1 -0
  424. package/dist/lib/subagents.d.ts +75 -0
  425. package/dist/lib/subagents.d.ts.map +1 -0
  426. package/dist/lib/subagents.js +402 -0
  427. package/dist/lib/subagents.js.map +1 -0
  428. package/dist/lib/teams/agents.d.ts +146 -0
  429. package/dist/lib/teams/agents.d.ts.map +1 -0
  430. package/dist/lib/teams/agents.js +1072 -0
  431. package/dist/lib/teams/agents.js.map +1 -0
  432. package/dist/lib/teams/api.d.ts +77 -0
  433. package/dist/lib/teams/api.d.ts.map +1 -0
  434. package/dist/lib/teams/api.js +229 -0
  435. package/dist/lib/teams/api.js.map +1 -0
  436. package/dist/lib/teams/cloud.d.ts +11 -0
  437. package/dist/lib/teams/cloud.d.ts.map +1 -0
  438. package/dist/lib/teams/cloud.js +169 -0
  439. package/dist/lib/teams/cloud.js.map +1 -0
  440. package/dist/lib/teams/debug.d.ts +2 -0
  441. package/dist/lib/teams/debug.d.ts.map +1 -0
  442. package/dist/lib/teams/debug.js +6 -0
  443. package/dist/lib/teams/debug.js.map +1 -0
  444. package/dist/lib/teams/file_ops.d.ts +6 -0
  445. package/dist/lib/teams/file_ops.d.ts.map +1 -0
  446. package/dist/lib/teams/file_ops.js +59 -0
  447. package/dist/lib/teams/file_ops.js.map +1 -0
  448. package/dist/lib/teams/parsers.d.ts +5 -0
  449. package/dist/lib/teams/parsers.d.ts.map +1 -0
  450. package/dist/lib/teams/parsers.js +826 -0
  451. package/dist/lib/teams/parsers.js.map +1 -0
  452. package/dist/lib/teams/persistence.d.ts +28 -0
  453. package/dist/lib/teams/persistence.d.ts.map +1 -0
  454. package/dist/lib/teams/persistence.js +289 -0
  455. package/dist/lib/teams/persistence.js.map +1 -0
  456. package/dist/lib/teams/ralph.d.ts +8 -0
  457. package/dist/lib/teams/ralph.d.ts.map +1 -0
  458. package/dist/lib/teams/ralph.js +59 -0
  459. package/dist/lib/teams/ralph.js.map +1 -0
  460. package/dist/lib/teams/registry.d.ts +11 -0
  461. package/dist/lib/teams/registry.d.ts.map +1 -0
  462. package/dist/lib/teams/registry.js +56 -0
  463. package/dist/lib/teams/registry.js.map +1 -0
  464. package/dist/lib/teams/summarizer.d.ts +58 -0
  465. package/dist/lib/teams/summarizer.d.ts.map +1 -0
  466. package/dist/lib/teams/summarizer.js +766 -0
  467. package/dist/lib/teams/summarizer.js.map +1 -0
  468. package/dist/lib/template.d.ts +24 -0
  469. package/dist/lib/template.d.ts.map +1 -0
  470. package/dist/lib/template.js +57 -0
  471. package/dist/lib/template.js.map +1 -0
  472. package/dist/lib/types.d.ts +282 -0
  473. package/dist/lib/types.d.ts.map +1 -0
  474. package/dist/lib/types.js +18 -0
  475. package/dist/lib/types.js.map +1 -0
  476. package/dist/lib/usage.d.ts +73 -0
  477. package/dist/lib/usage.d.ts.map +1 -0
  478. package/dist/lib/usage.js +623 -0
  479. package/dist/lib/usage.js.map +1 -0
  480. package/dist/lib/versions.d.ts +248 -0
  481. package/dist/lib/versions.d.ts.map +1 -0
  482. package/dist/lib/versions.js +1737 -0
  483. package/dist/lib/versions.js.map +1 -0
  484. package/package.json +82 -0
  485. package/scripts/postinstall.js +72 -0
  486. package/scripts/rebuild-sqlite.sh +46 -0
@@ -0,0 +1,1737 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import * as os from 'os';
4
+ import * as yaml from 'yaml';
5
+ import { exec } from 'child_process';
6
+ import { promisify } from 'util';
7
+ import chalk from 'chalk';
8
+ import * as TOML from 'smol-toml';
9
+ import { checkbox, select } from '@inquirer/prompts';
10
+ import { getVersionsDir, ensureAgentsDir, readMeta, writeMeta, getCommandsDir, getSkillsDir, getHooksDir, getMemoryDir, clearVersionResources, recordVersionResources, getProjectAgentsDir, getPromptcutsPath } from './state.js';
11
+ import { AGENTS, getAccountEmail, MCP_CAPABLE_AGENTS, COMMANDS_CAPABLE_AGENTS, getMcpConfigPathForHome, parseMcpConfig, resolveAgentName, formatAgentError, CODEX_HOOKS_MIN_VERSION } from './agents.js';
12
+ import { applyPermissionsToVersion as applyPermsToVersion, PERMISSIONS_CAPABLE_AGENTS, discoverPermissionGroups, buildPermissionsFromGroups, CODEX_RULES_FILENAME } from './permissions.js';
13
+ import { installMcpServers } from './mcp.js';
14
+ import { markdownToToml } from './convert.js';
15
+ import { createVersionedAlias, removeVersionedAlias, getConfigSymlinkVersion } from './shims.js';
16
+ import { listInstalledSubagents, transformSubagentForClaude, syncSubagentToOpenclaw, SUBAGENT_CAPABLE_AGENTS } from './subagents.js';
17
+ import { registerHooksToSettings } from './hooks.js';
18
+ import { discoverPlugins, syncPluginToVersion, isPluginSynced, pluginSupportsAgent, cleanOrphanedPluginSkills } from './plugins.js';
19
+ import { compileMemoryForAgent } from './memory-compile.js';
20
+ import { PLUGINS_CAPABLE_AGENTS } from './agents.js';
21
+ const execAsync = promisify(exec);
22
+ /**
23
+ * Get all available resources from ~/.agents/.
24
+ */
25
+ export function getAvailableResources(cwd = process.cwd()) {
26
+ const result = {
27
+ commands: [],
28
+ skills: [],
29
+ hooks: [],
30
+ memory: [],
31
+ mcp: [],
32
+ permissions: [],
33
+ subagents: [],
34
+ plugins: [],
35
+ promptcuts: false,
36
+ };
37
+ const projectAgentsDir = getProjectAgentsDir(cwd);
38
+ const userBase = path.dirname(getCommandsDir());
39
+ const resourceBases = [];
40
+ if (projectAgentsDir) {
41
+ resourceBases.push({ scope: 'project', base: projectAgentsDir });
42
+ }
43
+ resourceBases.push({ scope: 'user', base: userBase });
44
+ // Commands (*.md files)
45
+ const commandNames = new Set();
46
+ for (const { base } of resourceBases) {
47
+ const commandsDir = path.join(base, 'commands');
48
+ if (!fs.existsSync(commandsDir))
49
+ continue;
50
+ const names = fs.readdirSync(commandsDir)
51
+ .filter(f => f.endsWith('.md'))
52
+ .map(f => f.replace(/\.md$/, ''));
53
+ for (const name of names) {
54
+ commandNames.add(name);
55
+ }
56
+ }
57
+ result.commands = Array.from(commandNames);
58
+ // Skills (directories, excluding hidden)
59
+ const skillNames = new Set();
60
+ for (const { base } of resourceBases) {
61
+ const skillsDir = path.join(base, 'skills');
62
+ if (!fs.existsSync(skillsDir))
63
+ continue;
64
+ const names = fs.readdirSync(skillsDir, { withFileTypes: true })
65
+ .filter(d => d.isDirectory() && !d.name.startsWith('.'))
66
+ .map(d => d.name);
67
+ for (const name of names) {
68
+ skillNames.add(name);
69
+ }
70
+ }
71
+ result.skills = Array.from(skillNames);
72
+ // Hooks (files)
73
+ const hookNames = new Set();
74
+ for (const { base } of resourceBases) {
75
+ const hooksDir = path.join(base, 'hooks');
76
+ if (!fs.existsSync(hooksDir))
77
+ continue;
78
+ const names = fs.readdirSync(hooksDir).filter(f => !f.startsWith('.'));
79
+ for (const name of names) {
80
+ hookNames.add(name);
81
+ }
82
+ }
83
+ result.hooks = Array.from(hookNames);
84
+ // Memory (*.md files, excluding symlinks)
85
+ const memoryNames = new Set();
86
+ for (const { base } of resourceBases) {
87
+ const memoryDir = path.join(base, 'memory');
88
+ if (!fs.existsSync(memoryDir))
89
+ continue;
90
+ const names = fs.readdirSync(memoryDir)
91
+ .filter(f => {
92
+ if (!f.endsWith('.md'))
93
+ return false;
94
+ const stat = fs.lstatSync(path.join(memoryDir, f));
95
+ return !stat.isSymbolicLink();
96
+ })
97
+ .map(f => f.replace(/\.md$/, ''));
98
+ for (const name of names) {
99
+ memoryNames.add(name);
100
+ }
101
+ }
102
+ result.memory = Array.from(memoryNames);
103
+ // MCP servers (*.yaml files)
104
+ const mcpNames = new Set();
105
+ for (const { base } of resourceBases) {
106
+ const mcpDir = path.join(base, 'mcp');
107
+ if (!fs.existsSync(mcpDir))
108
+ continue;
109
+ const names = fs.readdirSync(mcpDir)
110
+ .filter(f => f.endsWith('.yaml') || f.endsWith('.yml'))
111
+ .map(f => f.replace(/\.(yaml|yml)$/, ''));
112
+ for (const name of names) {
113
+ mcpNames.add(name);
114
+ }
115
+ }
116
+ result.mcp = Array.from(mcpNames);
117
+ // Permission groups (from permissions/groups/*.yaml)
118
+ const permissionNames = new Set();
119
+ for (const { base } of resourceBases) {
120
+ const permsGroupsDir = path.join(base, 'permissions', 'groups');
121
+ if (!fs.existsSync(permsGroupsDir))
122
+ continue;
123
+ const names = fs.readdirSync(permsGroupsDir)
124
+ .filter(f => f.endsWith('.yaml') || f.endsWith('.yml'))
125
+ .map(f => f.replace(/\.(yaml|yml)$/, ''));
126
+ for (const name of names) {
127
+ permissionNames.add(name);
128
+ }
129
+ }
130
+ result.permissions = Array.from(permissionNames);
131
+ // Subagents (directories with AGENT.md)
132
+ const subagentNames = new Set();
133
+ for (const { base } of resourceBases) {
134
+ const subagentsDir = path.join(base, 'subagents');
135
+ if (!fs.existsSync(subagentsDir))
136
+ continue;
137
+ const names = fs.readdirSync(subagentsDir, { withFileTypes: true })
138
+ .filter(d => d.isDirectory() && fs.existsSync(path.join(subagentsDir, d.name, 'AGENT.md')))
139
+ .map(d => d.name);
140
+ for (const name of names) {
141
+ subagentNames.add(name);
142
+ }
143
+ }
144
+ result.subagents = Array.from(subagentNames);
145
+ // Plugins (directories with .claude-plugin/plugin.json)
146
+ const allPlugins = discoverPlugins();
147
+ result.plugins = allPlugins.map(p => p.name);
148
+ // Promptcuts — single file at ~/.agents/promptcuts.yaml, not per-agent.
149
+ // Project-scoped .agents/promptcuts.yaml is intentionally not supported
150
+ // (user-global shortcuts only — they follow the user, not the repo).
151
+ result.promptcuts = fs.existsSync(getPromptcutsPath());
152
+ return result;
153
+ }
154
+ /**
155
+ * Recursively compare two directories: every file in src must exist in dest with identical content.
156
+ */
157
+ function skillDirsMatch(src, dest) {
158
+ const entries = fs.readdirSync(src, { withFileTypes: true });
159
+ for (const entry of entries) {
160
+ const srcPath = path.join(src, entry.name);
161
+ const destPath = path.join(dest, entry.name);
162
+ if (entry.isDirectory()) {
163
+ if (!fs.existsSync(destPath))
164
+ return false;
165
+ if (!skillDirsMatch(srcPath, destPath))
166
+ return false;
167
+ }
168
+ else {
169
+ if (!fs.existsSync(destPath))
170
+ return false;
171
+ const srcContent = fs.readFileSync(srcPath, 'utf-8');
172
+ const destContent = fs.readFileSync(destPath, 'utf-8');
173
+ if (srcContent !== destContent)
174
+ return false;
175
+ }
176
+ }
177
+ return true;
178
+ }
179
+ /**
180
+ * Get what's ACTUALLY synced to a version by inspecting the version home.
181
+ * This is the source of truth - not the tracking in agents.yaml.
182
+ */
183
+ export function getActuallySyncedResources(agent, version, options = {}) {
184
+ const agentConfig = AGENTS[agent];
185
+ const versionHome = path.join(getVersionsDir(), agent, version, 'home');
186
+ const configDir = path.join(versionHome, `.${agent}`);
187
+ const projectAgentsDir = getProjectAgentsDir(options.cwd || process.cwd());
188
+ const result = {
189
+ commands: [],
190
+ skills: [],
191
+ hooks: [],
192
+ memory: [],
193
+ mcp: [],
194
+ permissions: [],
195
+ subagents: [],
196
+ plugins: [],
197
+ promptcuts: false,
198
+ };
199
+ // Commands - check what files exist in version home
200
+ const commandsDir = path.join(configDir, agentConfig.commandsSubdir);
201
+ if (fs.existsSync(commandsDir)) {
202
+ const ext = agentConfig.format === 'toml' ? '.toml' : '.md';
203
+ result.commands = fs.readdirSync(commandsDir)
204
+ .filter(f => f.endsWith(ext))
205
+ .map(f => f.replace(new RegExp(`\\${ext}$`), ''));
206
+ }
207
+ // Skills - check what directories exist AND content matches central source
208
+ const skillsDir = path.join(configDir, 'skills');
209
+ const centralSkillsDir = getSkillsDir();
210
+ const projectSkillsDir = projectAgentsDir ? path.join(projectAgentsDir, 'skills') : null;
211
+ if (fs.existsSync(skillsDir)) {
212
+ const installedSkills = fs.readdirSync(skillsDir, { withFileTypes: true })
213
+ .filter(d => d.isDirectory() && !d.name.startsWith('.'))
214
+ .map(d => d.name);
215
+ for (const skill of installedSkills) {
216
+ const versionSkillDir = path.join(skillsDir, skill);
217
+ const projectSourceDir = projectSkillsDir ? path.join(projectSkillsDir, skill) : null;
218
+ const centralSkillDir = path.join(centralSkillsDir, skill);
219
+ const hasProjectSource = projectSourceDir ? fs.existsSync(projectSourceDir) : false;
220
+ const hasUserSource = fs.existsSync(centralSkillDir);
221
+ if (!hasProjectSource && !hasUserSource) {
222
+ result.skills.push(skill);
223
+ continue;
224
+ }
225
+ const sourceDir = hasProjectSource ? projectSourceDir : centralSkillDir;
226
+ const allMatch = skillDirsMatch(sourceDir, versionSkillDir);
227
+ if (allMatch) {
228
+ result.skills.push(skill);
229
+ }
230
+ }
231
+ }
232
+ // Hooks - check what files exist AND content matches central source
233
+ const hooksDir = path.join(configDir, 'hooks');
234
+ const centralHooksDir = getHooksDir();
235
+ const projectHooksDir = projectAgentsDir ? path.join(projectAgentsDir, 'hooks') : null;
236
+ if (fs.existsSync(hooksDir)) {
237
+ const installedHooks = fs.readdirSync(hooksDir).filter(f => !f.startsWith('.'));
238
+ for (const hook of installedHooks) {
239
+ const projectFile = projectHooksDir ? path.join(projectHooksDir, hook) : null;
240
+ const centralFile = path.join(centralHooksDir, hook);
241
+ const versionFile = path.join(hooksDir, hook);
242
+ const hasProject = projectFile ? fs.existsSync(projectFile) : false;
243
+ const hasCentral = fs.existsSync(centralFile);
244
+ const sourceFile = hasProject ? projectFile : centralFile;
245
+ if (!hasProject && !hasCentral) {
246
+ result.hooks.push(hook);
247
+ continue;
248
+ }
249
+ try {
250
+ const centralContent = fs.readFileSync(sourceFile, 'utf-8');
251
+ const versionContent = fs.readFileSync(versionFile, 'utf-8');
252
+ if (centralContent === versionContent) {
253
+ result.hooks.push(hook);
254
+ }
255
+ }
256
+ catch {
257
+ // If read fails, consider not synced
258
+ }
259
+ }
260
+ }
261
+ // Memory - check which memory files are actually in sync (content matches)
262
+ const memoryDir = getMemoryDir();
263
+ const projectMemoryDir = projectAgentsDir ? path.join(projectAgentsDir, 'memory') : null;
264
+ const memoryFiles = new Set();
265
+ if (fs.existsSync(memoryDir)) {
266
+ fs.readdirSync(memoryDir).filter(f => f.endsWith('.md')).forEach(f => memoryFiles.add(f));
267
+ }
268
+ if (projectMemoryDir && fs.existsSync(projectMemoryDir)) {
269
+ fs.readdirSync(projectMemoryDir).filter(f => f.endsWith('.md')).forEach(f => memoryFiles.add(f));
270
+ }
271
+ for (const file of memoryFiles) {
272
+ const memName = file.replace(/\.md$/, '');
273
+ const targetName = file === 'AGENTS.md' ? agentConfig.instructionsFile : file;
274
+ const versionFile = path.join(configDir, targetName);
275
+ if (!fs.existsSync(versionFile))
276
+ continue;
277
+ const projectFile = projectMemoryDir ? path.join(projectMemoryDir, file) : null;
278
+ const centralFile = path.join(memoryDir, file);
279
+ const hasProject = projectFile ? fs.existsSync(projectFile) : false;
280
+ const hasCentral = fs.existsSync(centralFile);
281
+ const sourceFile = hasProject ? projectFile : centralFile;
282
+ if (!hasProject && !hasCentral) {
283
+ result.memory.push(memName);
284
+ continue;
285
+ }
286
+ try {
287
+ const centralContent = fs.readFileSync(sourceFile, 'utf-8');
288
+ const versionContent = fs.readFileSync(versionFile, 'utf-8');
289
+ if (centralContent === versionContent) {
290
+ result.memory.push(memName);
291
+ }
292
+ }
293
+ catch {
294
+ // Ignore
295
+ }
296
+ }
297
+ // MCP - use canonical config path + parser per agent
298
+ if (MCP_CAPABLE_AGENTS.includes(agent)) {
299
+ const mcpConfigPath = getMcpConfigPathForHome(agent, versionHome);
300
+ if (fs.existsSync(mcpConfigPath)) {
301
+ try {
302
+ const servers = parseMcpConfig(agent, mcpConfigPath);
303
+ result.mcp = Object.keys(servers);
304
+ }
305
+ catch {
306
+ // Ignore parse errors
307
+ }
308
+ }
309
+ }
310
+ // Permissions - check agent-specific config files
311
+ const settingsPath = path.join(configDir, 'settings.json');
312
+ if (PERMISSIONS_CAPABLE_AGENTS.includes(agent)) {
313
+ if (agent === 'claude' && fs.existsSync(settingsPath)) {
314
+ // Claude: check settings.json permissions.allow and deny
315
+ try {
316
+ const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
317
+ const allowRules = settings.permissions?.allow || [];
318
+ const denyRules = settings.permissions?.deny || [];
319
+ if (allowRules.length > 0 || denyRules.length > 0) {
320
+ const permGroups = discoverPermissionGroups();
321
+ const appliedGroups = [];
322
+ for (const group of permGroups) {
323
+ const groupSet = buildPermissionsFromGroups([group.name]);
324
+ // Empty groups (like header files) are considered synced if ANY permissions are applied
325
+ if (groupSet.allow.length === 0 && (!groupSet.deny || groupSet.deny.length === 0)) {
326
+ appliedGroups.push(group.name);
327
+ continue;
328
+ }
329
+ const hasAllowRule = groupSet.allow.some(rule => allowRules.includes(rule));
330
+ const hasDenyRule = groupSet.deny?.some(rule => denyRules.includes(rule)) || false;
331
+ if (hasAllowRule || hasDenyRule) {
332
+ appliedGroups.push(group.name);
333
+ }
334
+ }
335
+ result.permissions = appliedGroups;
336
+ }
337
+ }
338
+ catch {
339
+ // Ignore parse errors
340
+ }
341
+ }
342
+ else if (agent === 'codex') {
343
+ // Codex: config.toml for approval_policy/sandbox_mode, .rules for deny
344
+ const codexConfigPath = path.join(configDir, 'config.toml');
345
+ const codexRulesPath = path.join(configDir, 'rules', CODEX_RULES_FILENAME);
346
+ const hasConfig = fs.existsSync(codexConfigPath);
347
+ const hasRules = fs.existsSync(codexRulesPath);
348
+ if (hasConfig || hasRules) {
349
+ try {
350
+ // Codex format is lossy — all groups merge into a few keys.
351
+ // If any permission artifacts exist, all groups were applied together.
352
+ let hasPermKeys = false;
353
+ if (hasConfig) {
354
+ const content = fs.readFileSync(codexConfigPath, 'utf-8');
355
+ const config = TOML.parse(content);
356
+ hasPermKeys = !!(config.approval_policy || config.sandbox_mode || config.sandbox_workspace_write);
357
+ }
358
+ if (hasPermKeys || hasRules) {
359
+ result.permissions = discoverPermissionGroups().map(g => g.name);
360
+ }
361
+ }
362
+ catch {
363
+ // Ignore parse errors
364
+ }
365
+ }
366
+ }
367
+ else if (agent === 'opencode') {
368
+ // OpenCode: opencode.jsonc for permission.bash
369
+ const opencodeConfigPath = path.join(configDir, 'opencode.jsonc');
370
+ if (fs.existsSync(opencodeConfigPath)) {
371
+ try {
372
+ const content = fs.readFileSync(opencodeConfigPath, 'utf-8');
373
+ const stripped = content.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '');
374
+ const config = JSON.parse(stripped);
375
+ if (config.permission && Object.keys(config.permission.bash || {}).length > 0) {
376
+ result.permissions = discoverPermissionGroups().map(g => g.name);
377
+ }
378
+ }
379
+ catch {
380
+ // Ignore parse errors
381
+ }
382
+ }
383
+ }
384
+ }
385
+ // Subagents - check agent-specific locations
386
+ if (SUBAGENT_CAPABLE_AGENTS.includes(agent)) {
387
+ if (agent === 'claude') {
388
+ const agentsDir = path.join(configDir, 'agents');
389
+ if (fs.existsSync(agentsDir)) {
390
+ result.subagents = fs.readdirSync(agentsDir)
391
+ .filter(f => f.endsWith('.md'))
392
+ .map(f => f.replace('.md', ''));
393
+ }
394
+ }
395
+ else if (agent === 'openclaw') {
396
+ // OpenClaw: directories with AGENTS.md
397
+ const openclawDir = path.join(versionHome, '.openclaw');
398
+ if (fs.existsSync(openclawDir)) {
399
+ result.subagents = fs.readdirSync(openclawDir, { withFileTypes: true })
400
+ .filter(d => d.isDirectory() && fs.existsSync(path.join(openclawDir, d.name, 'AGENTS.md')))
401
+ .map(d => d.name);
402
+ }
403
+ }
404
+ }
405
+ // Plugins - check which discovered plugins have their skills in the version
406
+ if (PLUGINS_CAPABLE_AGENTS.includes(agent)) {
407
+ const allPlugins = discoverPlugins();
408
+ for (const plugin of allPlugins) {
409
+ if (isPluginSynced(plugin, agent, versionHome)) {
410
+ result.plugins.push(plugin.name);
411
+ }
412
+ }
413
+ }
414
+ return result;
415
+ }
416
+ /**
417
+ * Compare available resources with what's ACTUALLY synced to version home.
418
+ * Returns only NEW resources that haven't been synced yet.
419
+ * Source of truth: the actual files/config, NOT agents.yaml tracking.
420
+ */
421
+ export function getNewResources(available, actuallySynced) {
422
+ return {
423
+ commands: available.commands.filter(c => !actuallySynced.commands.includes(c)),
424
+ skills: available.skills.filter(s => !actuallySynced.skills.includes(s)),
425
+ hooks: available.hooks.filter(h => !actuallySynced.hooks.includes(h)),
426
+ memory: available.memory.filter(m => !actuallySynced.memory.includes(m)),
427
+ mcp: available.mcp.filter(m => !actuallySynced.mcp.includes(m)),
428
+ permissions: available.permissions.filter(p => !actuallySynced.permissions.includes(p)),
429
+ subagents: available.subagents.filter(s => !actuallySynced.subagents.includes(s)),
430
+ plugins: available.plugins.filter(p => !actuallySynced.plugins.includes(p)),
431
+ // Promptcuts aren't version-scoped — the hook reads ~/.agents/promptcuts.yaml
432
+ // directly, so there is never a "new" per-version state to reconcile.
433
+ promptcuts: false,
434
+ };
435
+ }
436
+ /**
437
+ * Check if there are any new resources to sync.
438
+ */
439
+ export function hasNewResources(diff, agent) {
440
+ const commandsApply = agent ? COMMANDS_CAPABLE_AGENTS.includes(agent) : true;
441
+ const hooksApply = agent ? AGENTS[agent].supportsHooks : true;
442
+ const mcpApply = agent ? MCP_CAPABLE_AGENTS.includes(agent) : true;
443
+ const permsApply = agent ? PERMISSIONS_CAPABLE_AGENTS.includes(agent) : true;
444
+ const subagentsApply = agent ? SUBAGENT_CAPABLE_AGENTS.includes(agent) : true;
445
+ const pluginsApply = agent ? PLUGINS_CAPABLE_AGENTS.includes(agent) : true;
446
+ return ((diff.commands.length > 0 && commandsApply) ||
447
+ diff.skills.length > 0 ||
448
+ (diff.hooks.length > 0 && hooksApply) ||
449
+ (diff.memory.length > 0 && commandsApply) ||
450
+ (diff.mcp.length > 0 && mcpApply) ||
451
+ (diff.permissions.length > 0 && permsApply) ||
452
+ (diff.subagents.length > 0 && subagentsApply) ||
453
+ (diff.plugins.length > 0 && pluginsApply));
454
+ }
455
+ /**
456
+ * Build a summary string of new resources.
457
+ * E.g., "2 commands, 5 permission groups"
458
+ */
459
+ function buildNewResourcesSummary(newResources, agent) {
460
+ const agentConfig = AGENTS[agent];
461
+ const parts = [];
462
+ if (newResources.commands.length > 0 && COMMANDS_CAPABLE_AGENTS.includes(agent)) {
463
+ parts.push(`${newResources.commands.length} command${newResources.commands.length === 1 ? '' : 's'}`);
464
+ }
465
+ if (newResources.skills.length > 0) {
466
+ parts.push(`${newResources.skills.length} skill${newResources.skills.length === 1 ? '' : 's'}`);
467
+ }
468
+ if (newResources.hooks.length > 0 && agentConfig.supportsHooks) {
469
+ parts.push(`${newResources.hooks.length} hook${newResources.hooks.length === 1 ? '' : 's'}`);
470
+ }
471
+ if (newResources.memory.length > 0 && COMMANDS_CAPABLE_AGENTS.includes(agent)) {
472
+ parts.push(`${newResources.memory.length} rule file${newResources.memory.length === 1 ? '' : 's'}`);
473
+ }
474
+ if (newResources.mcp.length > 0 && MCP_CAPABLE_AGENTS.includes(agent)) {
475
+ parts.push(`${newResources.mcp.length} MCP${newResources.mcp.length === 1 ? '' : 's'}`);
476
+ }
477
+ if (newResources.permissions.length > 0 && PERMISSIONS_CAPABLE_AGENTS.includes(agent)) {
478
+ parts.push(`${newResources.permissions.length} permission group${newResources.permissions.length === 1 ? '' : 's'}`);
479
+ }
480
+ if (newResources.subagents.length > 0 && SUBAGENT_CAPABLE_AGENTS.includes(agent)) {
481
+ parts.push(`${newResources.subagents.length} subagent${newResources.subagents.length === 1 ? '' : 's'}`);
482
+ }
483
+ if (newResources.plugins.length > 0 && PLUGINS_CAPABLE_AGENTS.includes(agent)) {
484
+ parts.push(`${newResources.plugins.length} plugin${newResources.plugins.length === 1 ? '' : 's'}`);
485
+ }
486
+ return parts.join(', ');
487
+ }
488
+ /**
489
+ * Prompt user to select which NEW resources to sync.
490
+ * Only shows resources that haven't been synced yet.
491
+ */
492
+ export async function promptNewResourceSelection(agent, newResources) {
493
+ const agentConfig = AGENTS[agent];
494
+ const selection = {};
495
+ // Get permission group info for display
496
+ const permissionGroups = discoverPermissionGroups();
497
+ const newPermissionGroups = permissionGroups.filter(g => newResources.permissions.includes(g.name));
498
+ const totalNewPermissionRules = newPermissionGroups.reduce((sum, g) => sum + g.ruleCount, 0);
499
+ // Build the summary
500
+ const summary = buildNewResourcesSummary(newResources, agent);
501
+ console.log(chalk.cyan(`\nNew resources available:`));
502
+ console.log(chalk.gray(` ${summary}`));
503
+ // Ask how to handle new resources
504
+ const action = await select({
505
+ message: 'Sync new resources?',
506
+ choices: [
507
+ { value: 'all', name: 'Yes, sync all new' },
508
+ { value: 'specific', name: 'Select specific items' },
509
+ { value: 'skip', name: 'Skip' },
510
+ ],
511
+ default: 'all',
512
+ });
513
+ if (action === 'skip') {
514
+ return null;
515
+ }
516
+ if (action === 'all') {
517
+ // Sync all new resources
518
+ if (newResources.commands.length > 0 && COMMANDS_CAPABLE_AGENTS.includes(agent))
519
+ selection.commands = newResources.commands;
520
+ if (newResources.skills.length > 0)
521
+ selection.skills = newResources.skills;
522
+ if (newResources.hooks.length > 0 && agentConfig.supportsHooks)
523
+ selection.hooks = newResources.hooks;
524
+ if (newResources.memory.length > 0 && COMMANDS_CAPABLE_AGENTS.includes(agent))
525
+ selection.memory = newResources.memory;
526
+ if (newResources.mcp.length > 0 && MCP_CAPABLE_AGENTS.includes(agent))
527
+ selection.mcp = newResources.mcp;
528
+ if (newResources.permissions.length > 0 && PERMISSIONS_CAPABLE_AGENTS.includes(agent))
529
+ selection.permissions = newResources.permissions;
530
+ if (newResources.subagents.length > 0 && SUBAGENT_CAPABLE_AGENTS.includes(agent))
531
+ selection.subagents = newResources.subagents;
532
+ if (newResources.plugins.length > 0 && PLUGINS_CAPABLE_AGENTS.includes(agent))
533
+ selection.plugins = newResources.plugins;
534
+ return selection;
535
+ }
536
+ // Select specific items for each category
537
+ if (newResources.commands.length > 0 && COMMANDS_CAPABLE_AGENTS.includes(agent)) {
538
+ const selected = await checkbox({
539
+ message: 'Select new commands to sync:',
540
+ choices: newResources.commands.map(c => ({ name: c, value: c, checked: true })),
541
+ });
542
+ if (selected.length > 0)
543
+ selection.commands = selected;
544
+ }
545
+ if (newResources.skills.length > 0) {
546
+ const selected = await checkbox({
547
+ message: 'Select new skills to sync:',
548
+ choices: newResources.skills.map(s => ({ name: s, value: s, checked: true })),
549
+ });
550
+ if (selected.length > 0)
551
+ selection.skills = selected;
552
+ }
553
+ if (newResources.hooks.length > 0 && agentConfig.supportsHooks) {
554
+ const selected = await checkbox({
555
+ message: 'Select new hooks to sync:',
556
+ choices: newResources.hooks.map(h => ({ name: h, value: h, checked: true })),
557
+ });
558
+ if (selected.length > 0)
559
+ selection.hooks = selected;
560
+ }
561
+ if (newResources.memory.length > 0 && COMMANDS_CAPABLE_AGENTS.includes(agent)) {
562
+ const selected = await checkbox({
563
+ message: 'Select new rule files to sync:',
564
+ choices: newResources.memory.map(m => ({ name: m, value: m, checked: true })),
565
+ });
566
+ if (selected.length > 0)
567
+ selection.memory = selected;
568
+ }
569
+ if (newResources.mcp.length > 0 && MCP_CAPABLE_AGENTS.includes(agent)) {
570
+ const selected = await checkbox({
571
+ message: 'Select new MCPs to sync:',
572
+ choices: newResources.mcp.map(m => ({ name: m, value: m, checked: true })),
573
+ });
574
+ if (selected.length > 0)
575
+ selection.mcp = selected;
576
+ }
577
+ if (newResources.permissions.length > 0 && PERMISSIONS_CAPABLE_AGENTS.includes(agent)) {
578
+ const selected = await checkbox({
579
+ message: 'Select new permission groups to sync:',
580
+ choices: newPermissionGroups.map(g => ({
581
+ name: `${g.name} (${g.ruleCount} rules)`,
582
+ value: g.name,
583
+ checked: true,
584
+ })),
585
+ });
586
+ if (selected.length > 0)
587
+ selection.permissions = selected;
588
+ }
589
+ if (newResources.subagents.length > 0 && SUBAGENT_CAPABLE_AGENTS.includes(agent)) {
590
+ const selected = await checkbox({
591
+ message: 'Select new subagents to sync:',
592
+ choices: newResources.subagents.map(s => ({ name: s, value: s, checked: true })),
593
+ });
594
+ if (selected.length > 0)
595
+ selection.subagents = selected;
596
+ }
597
+ if (newResources.plugins.length > 0 && PLUGINS_CAPABLE_AGENTS.includes(agent)) {
598
+ const allPlugins = discoverPlugins();
599
+ const pluginMap = new Map(allPlugins.map(p => [p.name, p]));
600
+ const selected = await checkbox({
601
+ message: 'Select new plugins to sync:',
602
+ choices: newResources.plugins.map(name => {
603
+ const plugin = pluginMap.get(name);
604
+ const desc = plugin?.manifest.description;
605
+ return { name: desc ? `${name} - ${desc}` : name, value: name, checked: true };
606
+ }),
607
+ });
608
+ if (selected.length > 0)
609
+ selection.plugins = selected;
610
+ }
611
+ return selection;
612
+ }
613
+ /**
614
+ * Prompt user to select which resources to sync from ~/.agents/.
615
+ * Returns the selection, or null if user cancels.
616
+ */
617
+ export async function promptResourceSelection(agent) {
618
+ const available = getAvailableResources();
619
+ const agentConfig = AGENTS[agent];
620
+ const selection = {};
621
+ // Get permission group info for display
622
+ const permissionGroups = discoverPermissionGroups();
623
+ const totalPermissionRules = permissionGroups.reduce((sum, g) => sum + g.ruleCount, 0);
624
+ const categories = [
625
+ { key: 'commands', label: 'Commands', available: COMMANDS_CAPABLE_AGENTS.includes(agent) && available.commands.length > 0, displayCount: `${available.commands.length} available` },
626
+ { key: 'skills', label: 'Skills', available: available.skills.length > 0, displayCount: `${available.skills.length} available` },
627
+ { key: 'hooks', label: 'Hooks', available: agentConfig.supportsHooks && available.hooks.length > 0, displayCount: `${available.hooks.length} available` },
628
+ { key: 'memory', label: 'Rules', available: COMMANDS_CAPABLE_AGENTS.includes(agent) && available.memory.length > 0, displayCount: `${available.memory.length} available` },
629
+ { key: 'mcp', label: 'MCPs', available: MCP_CAPABLE_AGENTS.includes(agent) && available.mcp.length > 0, displayCount: `${available.mcp.length} available` },
630
+ { key: 'permissions', label: 'Permissions', available: PERMISSIONS_CAPABLE_AGENTS.includes(agent) && permissionGroups.length > 0, displayCount: `${permissionGroups.length} groups, ${totalPermissionRules} rules` },
631
+ { key: 'subagents', label: 'Subagents', available: SUBAGENT_CAPABLE_AGENTS.includes(agent) && available.subagents.length > 0, displayCount: `${available.subagents.length} available` },
632
+ { key: 'plugins', label: 'Plugins', available: PLUGINS_CAPABLE_AGENTS.includes(agent) && available.plugins.length > 0, displayCount: `${available.plugins.length} available` },
633
+ ];
634
+ const availableCategories = categories.filter(c => c.available);
635
+ if (availableCategories.length === 0) {
636
+ console.log(chalk.gray('No resources available in ~/.agents/'));
637
+ return {};
638
+ }
639
+ // Step 1: Select categories (with "Select All" shortcut at the top)
640
+ console.log();
641
+ const SELECT_ALL_KEY = '__select_all__';
642
+ const selectedCategories = await checkbox({
643
+ message: 'Which resources from ~/.agents/ would you like to sync?',
644
+ choices: [
645
+ { name: chalk.bold('Select All (sync everything)'), value: SELECT_ALL_KEY, checked: false },
646
+ ...availableCategories.map(c => ({
647
+ name: `${c.label} (${c.displayCount})`,
648
+ value: c.key,
649
+ checked: true, // Default all checked
650
+ })),
651
+ ],
652
+ });
653
+ if (selectedCategories.length === 0) {
654
+ return {};
655
+ }
656
+ // If "Select All" was picked, sync everything without per-category prompts
657
+ if (selectedCategories.includes(SELECT_ALL_KEY)) {
658
+ for (const c of availableCategories) {
659
+ selection[c.key] = 'all';
660
+ }
661
+ return selection;
662
+ }
663
+ // Step 2: For each selected category, ask all/specific/skip
664
+ for (const category of selectedCategories) {
665
+ const categoryLabel = categories.find(c => c.key === category).label;
666
+ // Special handling for permissions - show groups
667
+ if (category === 'permissions') {
668
+ const choice = await select({
669
+ message: `${categoryLabel}:`,
670
+ choices: [
671
+ { name: `Select all (${permissionGroups.length} groups)`, value: 'all' },
672
+ { name: 'Select specific groups', value: 'specific' },
673
+ { name: 'Skip', value: 'skip' },
674
+ ],
675
+ default: 'all',
676
+ });
677
+ if (choice === 'all') {
678
+ selection.permissions = 'all';
679
+ }
680
+ else if (choice === 'specific') {
681
+ const selected = await checkbox({
682
+ message: 'Select permission groups to sync:',
683
+ choices: permissionGroups.map(g => ({
684
+ name: `${g.name} (${g.ruleCount} rules)`,
685
+ value: g.name,
686
+ checked: true,
687
+ })),
688
+ });
689
+ if (selected.length > 0) {
690
+ selection.permissions = selected;
691
+ }
692
+ }
693
+ }
694
+ else {
695
+ // Standard handling for other categories
696
+ const items = available[category];
697
+ const choice = await select({
698
+ message: `${categoryLabel}:`,
699
+ choices: [
700
+ { name: `Select all (${items.length})`, value: 'all' },
701
+ { name: 'Select specific', value: 'specific' },
702
+ { name: 'Skip', value: 'skip' },
703
+ ],
704
+ default: 'all',
705
+ });
706
+ if (choice === 'all') {
707
+ selection[category] = 'all';
708
+ }
709
+ else if (choice === 'specific') {
710
+ const selected = await checkbox({
711
+ message: `Select ${categoryLabel.toLowerCase()} to sync:`,
712
+ choices: items.map(item => ({
713
+ name: item,
714
+ value: item,
715
+ checked: true,
716
+ })),
717
+ });
718
+ if (selected.length > 0) {
719
+ selection[category] = selected;
720
+ }
721
+ }
722
+ }
723
+ // 'skip' means we don't set anything for this category
724
+ }
725
+ return selection;
726
+ }
727
+ /**
728
+ * Parse agent@version syntax.
729
+ * Examples:
730
+ * "claude@1.5.0" -> { agent: "claude", version: "1.5.0" }
731
+ * "claude" -> { agent: "claude", version: "latest" }
732
+ * "codex@latest" -> { agent: "codex", version: "latest" }
733
+ */
734
+ export function parseAgentSpec(spec) {
735
+ const parts = spec.split('@');
736
+ const agentName = parts[0].toLowerCase();
737
+ const version = parts[1] || 'latest';
738
+ if (!AGENTS[agentName]) {
739
+ return null;
740
+ }
741
+ return {
742
+ agent: agentName,
743
+ version,
744
+ };
745
+ }
746
+ /**
747
+ * Get the directory where a specific version is installed.
748
+ */
749
+ export function getVersionDir(agent, version) {
750
+ return path.join(getVersionsDir(), agent, version);
751
+ }
752
+ /**
753
+ * Get the binary path for a specific agent version.
754
+ */
755
+ export function getBinaryPath(agent, version) {
756
+ const versionDir = getVersionDir(agent, version);
757
+ const agentConfig = AGENTS[agent];
758
+ return path.join(versionDir, 'node_modules', '.bin', agentConfig.cliCommand);
759
+ }
760
+ /**
761
+ * Get the isolated HOME directory for a specific agent version.
762
+ * Each version has its own config isolation (like jobs sandbox).
763
+ */
764
+ export function getVersionHomePath(agent, version) {
765
+ return path.join(getVersionDir(agent, version), 'home');
766
+ }
767
+ /**
768
+ * Check if a specific version is installed.
769
+ */
770
+ export function isVersionInstalled(agent, version) {
771
+ const binaryPath = getBinaryPath(agent, version);
772
+ return fs.existsSync(binaryPath);
773
+ }
774
+ /**
775
+ * Get the latest available version from npm for an agent.
776
+ */
777
+ export async function getLatestNpmVersion(agent) {
778
+ const agentConfig = AGENTS[agent];
779
+ if (!agentConfig.npmPackage)
780
+ return null;
781
+ try {
782
+ const { stdout } = await execAsync(`npm view ${agentConfig.npmPackage} version`);
783
+ return stdout.trim();
784
+ }
785
+ catch {
786
+ return null;
787
+ }
788
+ }
789
+ /**
790
+ * Check if 'latest' version is already installed (by resolving to actual version).
791
+ */
792
+ export async function isLatestInstalled(agent) {
793
+ const latestVersion = await getLatestNpmVersion(agent);
794
+ if (!latestVersion) {
795
+ return { installed: false, version: null };
796
+ }
797
+ return { installed: isVersionInstalled(agent, latestVersion), version: latestVersion };
798
+ }
799
+ /**
800
+ * List all installed versions for an agent.
801
+ */
802
+ export function listInstalledVersions(agent) {
803
+ const agentVersionsDir = path.join(getVersionsDir(), agent);
804
+ if (!fs.existsSync(agentVersionsDir)) {
805
+ return [];
806
+ }
807
+ const entries = fs.readdirSync(agentVersionsDir, { withFileTypes: true });
808
+ const versions = [];
809
+ for (const entry of entries) {
810
+ if (entry.isDirectory()) {
811
+ const binaryPath = getBinaryPath(agent, entry.name);
812
+ if (fs.existsSync(binaryPath)) {
813
+ versions.push(entry.name);
814
+ }
815
+ }
816
+ }
817
+ return versions.sort(compareVersions);
818
+ }
819
+ /**
820
+ * Get the global default version for an agent.
821
+ */
822
+ export function getGlobalDefault(agent) {
823
+ const meta = readMeta();
824
+ return meta.agents?.[agent] || null;
825
+ }
826
+ /**
827
+ * Set the global default version for an agent.
828
+ */
829
+ export function setGlobalDefault(agent, version) {
830
+ const meta = readMeta();
831
+ if (!meta.agents) {
832
+ meta.agents = {};
833
+ }
834
+ if (version === undefined) {
835
+ delete meta.agents[agent];
836
+ }
837
+ else {
838
+ meta.agents[agent] = version;
839
+ }
840
+ writeMeta(meta);
841
+ }
842
+ /**
843
+ * Install a specific version of an agent.
844
+ */
845
+ export async function installVersion(agent, version, onProgress) {
846
+ const agentConfig = AGENTS[agent];
847
+ if (!agentConfig.npmPackage) {
848
+ return { success: false, installedVersion: version, error: 'Agent has no npm package' };
849
+ }
850
+ ensureAgentsDir();
851
+ const versionDir = getVersionDir(agent, version);
852
+ // Create version directory and isolated home
853
+ fs.mkdirSync(versionDir, { recursive: true });
854
+ fs.mkdirSync(path.join(versionDir, 'home'), { recursive: true });
855
+ // Initialize package.json
856
+ const packageJson = {
857
+ name: `agents-${agent}-${version}`,
858
+ version: '1.0.0',
859
+ private: true,
860
+ };
861
+ fs.writeFileSync(path.join(versionDir, 'package.json'), JSON.stringify(packageJson, null, 2));
862
+ // Install the package
863
+ const packageSpec = version === 'latest'
864
+ ? agentConfig.npmPackage
865
+ : `${agentConfig.npmPackage}@${version}`;
866
+ try {
867
+ onProgress?.(`Installing ${packageSpec}...`);
868
+ const { stdout } = await execAsync(`npm install ${packageSpec}`, { cwd: versionDir });
869
+ // Determine the actual installed version
870
+ let installedVersion = version;
871
+ if (version === 'latest') {
872
+ const pkgJsonPath = path.join(versionDir, 'node_modules', agentConfig.npmPackage.replace(/^@/, '').split('/')[0], 'package.json');
873
+ // Try to read the actual version from installed package
874
+ try {
875
+ const installedPkgPath = path.join(versionDir, 'node_modules', agentConfig.npmPackage, 'package.json');
876
+ if (fs.existsSync(installedPkgPath)) {
877
+ const installedPkg = JSON.parse(fs.readFileSync(installedPkgPath, 'utf-8'));
878
+ installedVersion = installedPkg.version;
879
+ // Rename the directory to the actual version
880
+ if (installedVersion !== 'latest') {
881
+ const actualVersionDir = getVersionDir(agent, installedVersion);
882
+ if (!fs.existsSync(actualVersionDir)) {
883
+ fs.renameSync(versionDir, actualVersionDir);
884
+ }
885
+ else {
886
+ // Already exists, remove the 'latest' dir
887
+ fs.rmSync(versionDir, { recursive: true, force: true });
888
+ }
889
+ }
890
+ }
891
+ }
892
+ catch (e) {
893
+ // Failed to determine version - this shouldn't happen
894
+ throw new Error(`Failed to determine installed version: ${e.message}`);
895
+ }
896
+ }
897
+ // Create versioned alias (e.g., claude@2.0.65)
898
+ createVersionedAlias(agent, installedVersion);
899
+ return { success: true, installedVersion };
900
+ }
901
+ catch (err) {
902
+ // Clean up on failure
903
+ if (fs.existsSync(versionDir)) {
904
+ fs.rmSync(versionDir, { recursive: true, force: true });
905
+ }
906
+ return { success: false, installedVersion: version, error: err.message };
907
+ }
908
+ }
909
+ /**
910
+ * Remove a specific version of an agent.
911
+ */
912
+ export function removeVersion(agent, version) {
913
+ const versionDir = getVersionDir(agent, version);
914
+ if (!fs.existsSync(versionDir)) {
915
+ return false;
916
+ }
917
+ fs.rmSync(versionDir, { recursive: true, force: true });
918
+ // Remove versioned alias (e.g., claude@2.0.65)
919
+ removeVersionedAlias(agent, version);
920
+ // Clear resource tracking for this version
921
+ clearVersionResources(agent, version);
922
+ // Clear default if it was the removed version - user must explicitly pick a new one
923
+ if (getGlobalDefault(agent) === version) {
924
+ const meta = readMeta();
925
+ if (meta.agents?.[agent]) {
926
+ delete meta.agents[agent];
927
+ writeMeta(meta);
928
+ }
929
+ const remaining = listInstalledVersions(agent);
930
+ if (remaining.length > 0) {
931
+ console.log(chalk.yellow(`Default version removed. Run: agents use ${agent}@<version> to set a new default`));
932
+ }
933
+ }
934
+ // Clean up dangling config symlink if it pointed to the removed version
935
+ const symlinkVersion = getConfigSymlinkVersion(agent);
936
+ if (symlinkVersion === version) {
937
+ const configPath = path.join(os.homedir(), `.${agent}`);
938
+ try {
939
+ fs.unlinkSync(configPath);
940
+ }
941
+ catch {
942
+ // Ignore if already gone
943
+ }
944
+ }
945
+ return true;
946
+ }
947
+ /**
948
+ * Remove all versions of an agent.
949
+ */
950
+ export function removeAllVersions(agent) {
951
+ const versions = listInstalledVersions(agent);
952
+ let removed = 0;
953
+ for (const version of versions) {
954
+ if (removeVersion(agent, version)) {
955
+ removed++;
956
+ }
957
+ }
958
+ // Clean up the agent directory
959
+ const agentDir = path.join(getVersionsDir(), agent);
960
+ if (fs.existsSync(agentDir)) {
961
+ fs.rmSync(agentDir, { recursive: true, force: true });
962
+ }
963
+ return removed;
964
+ }
965
+ /**
966
+ * Get the resolved version for an agent in the current context.
967
+ * Checks project manifest first, then global default.
968
+ */
969
+ export function resolveVersion(agent, projectPath) {
970
+ // Check project manifest
971
+ if (projectPath) {
972
+ const version = getProjectVersion(agent, projectPath);
973
+ if (version) {
974
+ return version;
975
+ }
976
+ }
977
+ // Fall back to global default
978
+ return getGlobalDefault(agent);
979
+ }
980
+ /**
981
+ * Get version specified in a project-root agents.yaml (not the user ~/.agents/agents.yaml).
982
+ */
983
+ export function getProjectVersion(agent, startPath) {
984
+ const userAgentsYaml = path.join(os.homedir(), '.agents', 'agents.yaml');
985
+ let dir = path.resolve(startPath);
986
+ while (dir !== path.dirname(dir)) {
987
+ const manifestPath = path.join(dir, 'agents.yaml');
988
+ if (manifestPath !== userAgentsYaml && fs.existsSync(manifestPath)) {
989
+ try {
990
+ const content = fs.readFileSync(manifestPath, 'utf-8');
991
+ const parsed = yaml.parse(content);
992
+ const version = parsed?.agents?.[agent];
993
+ if (typeof version === 'string' && version.trim()) {
994
+ return version.trim();
995
+ }
996
+ }
997
+ catch {
998
+ // Ignore parsing errors
999
+ }
1000
+ }
1001
+ dir = path.dirname(dir);
1002
+ }
1003
+ return null;
1004
+ }
1005
+ /**
1006
+ * Compare semver versions for sorting.
1007
+ */
1008
+ export function compareVersions(a, b) {
1009
+ const aParts = a.split('.').map((n) => parseInt(n, 10) || 0);
1010
+ const bParts = b.split('.').map((n) => parseInt(n, 10) || 0);
1011
+ for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
1012
+ const aVal = aParts[i] || 0;
1013
+ const bVal = bParts[i] || 0;
1014
+ if (aVal !== bVal) {
1015
+ return aVal - bVal;
1016
+ }
1017
+ }
1018
+ return 0;
1019
+ }
1020
+ /**
1021
+ * Get actual version from an installed 'latest' directory.
1022
+ */
1023
+ export async function getInstalledVersion(agent, version) {
1024
+ const binaryPath = getBinaryPath(agent, version);
1025
+ if (!fs.existsSync(binaryPath)) {
1026
+ return null;
1027
+ }
1028
+ try {
1029
+ const { stdout } = await execAsync(`${binaryPath} --version`);
1030
+ const match = stdout.match(/(\d+\.\d+\.\d+)/);
1031
+ return match ? match[1] : version;
1032
+ }
1033
+ catch {
1034
+ return version;
1035
+ }
1036
+ }
1037
+ /**
1038
+ * Get the diff between central resources (~/.agents/) and what's synced to a version.
1039
+ * Uses filesystem state - no tracking needed.
1040
+ */
1041
+ export function getResourceDiff(agent, version) {
1042
+ const agentConfig = AGENTS[agent];
1043
+ const versionHome = getVersionHomePath(agent, version);
1044
+ const agentDir = path.join(versionHome, `.${agent}`);
1045
+ const diff = {
1046
+ commands: { added: [], dangling: [] },
1047
+ skills: { added: [], dangling: [] },
1048
+ hooks: { added: [], dangling: [] },
1049
+ memory: { added: [], dangling: [] },
1050
+ totalAdded: 0,
1051
+ totalDangling: 0,
1052
+ };
1053
+ // Helper to check symlink status
1054
+ const getSymlinkStatus = (linkPath) => {
1055
+ try {
1056
+ const stat = fs.lstatSync(linkPath);
1057
+ if (!stat.isSymbolicLink())
1058
+ return 'none';
1059
+ // Check if target exists
1060
+ try {
1061
+ fs.statSync(linkPath);
1062
+ return 'valid';
1063
+ }
1064
+ catch {
1065
+ return 'dangling';
1066
+ }
1067
+ }
1068
+ catch {
1069
+ return 'none';
1070
+ }
1071
+ };
1072
+ // Commands: check directory symlink (or individual files for Gemini)
1073
+ const centralCommands = getCommandsDir();
1074
+ const commandsTarget = path.join(agentDir, agentConfig.commandsSubdir);
1075
+ if (agentConfig.format === 'toml') {
1076
+ // Gemini: compare .md files in central vs .toml files in version
1077
+ if (fs.existsSync(centralCommands)) {
1078
+ const centralFiles = fs.readdirSync(centralCommands).filter(f => f.endsWith('.md'));
1079
+ const versionFiles = fs.existsSync(commandsTarget)
1080
+ ? fs.readdirSync(commandsTarget).filter(f => f.endsWith('.toml'))
1081
+ : [];
1082
+ const versionNames = new Set(versionFiles.map(f => f.replace('.toml', '')));
1083
+ for (const file of centralFiles) {
1084
+ const name = file.replace('.md', '');
1085
+ if (!versionNames.has(name)) {
1086
+ diff.commands.added.push(file);
1087
+ }
1088
+ }
1089
+ // Check for dangling (toml exists but no md source)
1090
+ const centralNames = new Set(centralFiles.map(f => f.replace('.md', '')));
1091
+ for (const file of versionFiles) {
1092
+ const name = file.replace('.toml', '');
1093
+ if (!centralNames.has(name)) {
1094
+ diff.commands.dangling.push(file);
1095
+ }
1096
+ }
1097
+ }
1098
+ }
1099
+ else {
1100
+ // Other agents: check directory symlink
1101
+ const status = getSymlinkStatus(commandsTarget);
1102
+ if (status === 'none' && fs.existsSync(centralCommands)) {
1103
+ const files = fs.readdirSync(centralCommands).filter(f => f.endsWith('.md'));
1104
+ diff.commands.added = files;
1105
+ }
1106
+ else if (status === 'dangling') {
1107
+ diff.commands.dangling = ['commands/'];
1108
+ }
1109
+ }
1110
+ // Skills: check directory symlink (skip if agent natively reads ~/.agents/skills/)
1111
+ if (!agentConfig.nativeAgentsSkillsDir) {
1112
+ const centralSkills = getSkillsDir();
1113
+ const skillsTarget = path.join(agentDir, 'skills');
1114
+ const skillsStatus = getSymlinkStatus(skillsTarget);
1115
+ if (skillsStatus === 'none' && fs.existsSync(centralSkills)) {
1116
+ const dirs = fs.readdirSync(centralSkills).filter(f => {
1117
+ const stat = fs.statSync(path.join(centralSkills, f));
1118
+ return stat.isDirectory() && !f.startsWith('.');
1119
+ });
1120
+ diff.skills.added = dirs;
1121
+ }
1122
+ else if (skillsStatus === 'dangling') {
1123
+ diff.skills.dangling = ['skills/'];
1124
+ }
1125
+ }
1126
+ // Hooks: check directory symlink (if agent supports hooks)
1127
+ if (agentConfig.supportsHooks) {
1128
+ const centralHooks = getHooksDir();
1129
+ const hooksTarget = path.join(agentDir, 'hooks');
1130
+ const hooksStatus = getSymlinkStatus(hooksTarget);
1131
+ if (hooksStatus === 'none' && fs.existsSync(centralHooks)) {
1132
+ const files = fs.readdirSync(centralHooks).filter(f => !f.startsWith('.'));
1133
+ diff.hooks.added = files;
1134
+ }
1135
+ else if (hooksStatus === 'dangling') {
1136
+ diff.hooks.dangling = ['hooks/'];
1137
+ }
1138
+ }
1139
+ // Memory: check individual file symlinks
1140
+ const centralMemory = getMemoryDir();
1141
+ if (fs.existsSync(centralMemory)) {
1142
+ const memoryFiles = fs.readdirSync(centralMemory).filter(f => f.endsWith('.md'));
1143
+ for (const file of memoryFiles) {
1144
+ const targetName = file === 'AGENTS.md' ? agentConfig.instructionsFile : file;
1145
+ const targetPath = path.join(agentDir, targetName);
1146
+ const status = getSymlinkStatus(targetPath);
1147
+ if (status === 'none') {
1148
+ diff.memory.added.push(file);
1149
+ }
1150
+ else if (status === 'dangling') {
1151
+ diff.memory.dangling.push(targetName);
1152
+ }
1153
+ }
1154
+ }
1155
+ // Calculate totals
1156
+ diff.totalAdded = diff.commands.added.length + diff.skills.added.length +
1157
+ diff.hooks.added.length + diff.memory.added.length;
1158
+ diff.totalDangling = diff.commands.dangling.length + diff.skills.dangling.length +
1159
+ diff.hooks.dangling.length + diff.memory.dangling.length;
1160
+ return diff;
1161
+ }
1162
+ /**
1163
+ * Sync central resources (~/.agents/) into a specific version's config directory.
1164
+ * Copies selected resources from central storage into {versionHome}/.{agent}/.
1165
+ *
1166
+ * @param agent - The agent ID
1167
+ * @param version - The version string
1168
+ * @param selection - Optional resource selection. If not provided, syncs all resources.
1169
+ *
1170
+ * For Gemini: commands are converted from markdown to TOML.
1171
+ */
1172
+ export function syncResourcesToVersion(agent, version, selection, options = {}) {
1173
+ const agentConfig = AGENTS[agent];
1174
+ const versionHome = getVersionHomePath(agent, version);
1175
+ const agentDir = path.join(versionHome, `.${agent}`);
1176
+ fs.mkdirSync(agentDir, { recursive: true });
1177
+ const result = { commands: false, skills: false, hooks: false, memory: [], permissions: false, mcp: [], subagents: [], plugins: [] };
1178
+ const cwd = options.cwd || process.cwd();
1179
+ const projectAgentsDir = options.projectDir || getProjectAgentsDir(cwd);
1180
+ const available = getAvailableResources(cwd);
1181
+ // Helper: remove a path (symlink or real) if it exists
1182
+ const removePath = (p) => {
1183
+ try {
1184
+ const stat = fs.lstatSync(p);
1185
+ if (stat.isSymbolicLink() || stat.isFile()) {
1186
+ fs.unlinkSync(p);
1187
+ }
1188
+ else if (stat.isDirectory()) {
1189
+ fs.rmSync(p, { recursive: true, force: true });
1190
+ }
1191
+ }
1192
+ catch { /* file already removed or inaccessible */ }
1193
+ };
1194
+ // Helper: copy a directory recursively
1195
+ const copyDir = (src, dest) => {
1196
+ fs.mkdirSync(dest, { recursive: true });
1197
+ const entries = fs.readdirSync(src, { withFileTypes: true });
1198
+ for (const entry of entries) {
1199
+ const srcPath = path.join(src, entry.name);
1200
+ const destPath = path.join(dest, entry.name);
1201
+ if (entry.isDirectory()) {
1202
+ copyDir(srcPath, destPath);
1203
+ }
1204
+ else {
1205
+ fs.copyFileSync(srcPath, destPath);
1206
+ }
1207
+ }
1208
+ };
1209
+ // Helper: resolve selection to list of items
1210
+ const resolveSelection = (sel, available) => {
1211
+ if (sel === 'all')
1212
+ return available;
1213
+ if (Array.isArray(sel))
1214
+ return sel;
1215
+ return [];
1216
+ };
1217
+ // Sync commands
1218
+ const commandsToSync = selection
1219
+ ? resolveSelection(selection.commands, available.commands)
1220
+ : available.commands; // No selection = sync all
1221
+ if (commandsToSync.length > 0 && COMMANDS_CAPABLE_AGENTS.includes(agent)) {
1222
+ const centralCommands = getCommandsDir();
1223
+ const projectCommandsDir = projectAgentsDir ? path.join(projectAgentsDir, 'commands') : null;
1224
+ const commandsTarget = path.join(agentDir, agentConfig.commandsSubdir);
1225
+ fs.mkdirSync(commandsTarget, { recursive: true });
1226
+ const syncedCommands = [];
1227
+ for (const cmd of commandsToSync) {
1228
+ const projectSource = projectCommandsDir ? path.join(projectCommandsDir, `${cmd}.md`) : null;
1229
+ const userSource = path.join(centralCommands, `${cmd}.md`);
1230
+ const srcFile = projectSource && fs.existsSync(projectSource) ? projectSource : userSource;
1231
+ if (!fs.existsSync(srcFile))
1232
+ continue;
1233
+ if (agentConfig.format === 'toml') {
1234
+ const content = fs.readFileSync(srcFile, 'utf-8');
1235
+ const tomlContent = markdownToToml(cmd, content);
1236
+ fs.writeFileSync(path.join(commandsTarget, `${cmd}.toml`), tomlContent);
1237
+ }
1238
+ else {
1239
+ fs.copyFileSync(srcFile, path.join(commandsTarget, `${cmd}.md`));
1240
+ }
1241
+ syncedCommands.push(cmd);
1242
+ }
1243
+ result.commands = syncedCommands.length > 0;
1244
+ if (syncedCommands.length > 0) {
1245
+ recordVersionResources(agent, version, 'commands', syncedCommands);
1246
+ }
1247
+ }
1248
+ // Sync skills (skip if agent natively reads ~/.agents/skills/)
1249
+ if (agentConfig.nativeAgentsSkillsDir) {
1250
+ // Clean up stale skills symlink/dir — agent reads from ~/.agents/skills/ directly
1251
+ const skillsTarget = path.join(agentDir, 'skills');
1252
+ removePath(skillsTarget);
1253
+ }
1254
+ else {
1255
+ const skillsToSync = selection
1256
+ ? resolveSelection(selection.skills, available.skills)
1257
+ : available.skills;
1258
+ if (skillsToSync.length > 0) {
1259
+ const centralSkills = getSkillsDir();
1260
+ const projectSkills = projectAgentsDir ? path.join(projectAgentsDir, 'skills') : null;
1261
+ const skillsTarget = path.join(agentDir, 'skills');
1262
+ fs.mkdirSync(skillsTarget, { recursive: true });
1263
+ const syncedSkills = [];
1264
+ for (const skill of skillsToSync) {
1265
+ const projectSource = projectSkills ? path.join(projectSkills, skill) : null;
1266
+ const srcDir = projectSource && fs.existsSync(projectSource) ? projectSource : path.join(centralSkills, skill);
1267
+ if (!fs.existsSync(srcDir))
1268
+ continue;
1269
+ const destDir = path.join(skillsTarget, skill);
1270
+ removePath(destDir);
1271
+ copyDir(srcDir, destDir);
1272
+ syncedSkills.push(skill);
1273
+ }
1274
+ result.skills = syncedSkills.length > 0;
1275
+ if (syncedSkills.length > 0) {
1276
+ recordVersionResources(agent, version, 'skills', syncedSkills);
1277
+ }
1278
+ }
1279
+ }
1280
+ // Sync hooks (if agent supports them)
1281
+ if (agentConfig.supportsHooks) {
1282
+ // Version gate: Codex hooks require >= CODEX_HOOKS_MIN_VERSION
1283
+ if (agent === 'codex' && compareVersions(version, CODEX_HOOKS_MIN_VERSION) < 0) {
1284
+ console.warn(`hooks skipped: codex@${version} < ${CODEX_HOOKS_MIN_VERSION}`);
1285
+ }
1286
+ else {
1287
+ const hooksToSync = selection
1288
+ ? resolveSelection(selection.hooks, available.hooks)
1289
+ : available.hooks;
1290
+ if (hooksToSync.length > 0) {
1291
+ const centralHooks = getHooksDir();
1292
+ const projectHooksDir = projectAgentsDir ? path.join(projectAgentsDir, 'hooks') : null;
1293
+ const hooksTarget = path.join(agentDir, 'hooks');
1294
+ fs.mkdirSync(hooksTarget, { recursive: true });
1295
+ const syncedHooks = [];
1296
+ for (const hook of hooksToSync) {
1297
+ const projectSource = projectHooksDir ? path.join(projectHooksDir, hook) : null;
1298
+ const srcFile = projectSource && fs.existsSync(projectSource) ? projectSource : path.join(centralHooks, hook);
1299
+ if (!fs.existsSync(srcFile))
1300
+ continue;
1301
+ const destFile = path.join(hooksTarget, hook);
1302
+ fs.copyFileSync(srcFile, destFile);
1303
+ fs.chmodSync(destFile, 0o755);
1304
+ syncedHooks.push(hook);
1305
+ }
1306
+ // Remove orphan hook files that exist in version home but not in central
1307
+ const centralHookNames = new Set(fs.existsSync(getHooksDir())
1308
+ ? fs.readdirSync(getHooksDir()).filter(f => !f.startsWith('.'))
1309
+ : []);
1310
+ if (fs.existsSync(hooksTarget)) {
1311
+ for (const file of fs.readdirSync(hooksTarget).filter(f => !f.startsWith('.'))) {
1312
+ if (!centralHookNames.has(file)) {
1313
+ removePath(path.join(hooksTarget, file));
1314
+ }
1315
+ }
1316
+ }
1317
+ result.hooks = syncedHooks.length > 0;
1318
+ if (syncedHooks.length > 0) {
1319
+ recordVersionResources(agent, version, 'hooks', syncedHooks);
1320
+ }
1321
+ if (agent === 'claude' || agent === 'codex') {
1322
+ registerHooksToSettings(agent, versionHome);
1323
+ }
1324
+ }
1325
+ }
1326
+ }
1327
+ // Sync memory files
1328
+ const memoryToSync = selection
1329
+ ? resolveSelection(selection.memory, available.memory)
1330
+ : available.memory;
1331
+ if (memoryToSync.length > 0 && COMMANDS_CAPABLE_AGENTS.includes(agent)) {
1332
+ const centralMemory = getMemoryDir();
1333
+ const projectMemoryDir = projectAgentsDir ? path.join(projectAgentsDir, 'memory') : null;
1334
+ const syncedMemory = [];
1335
+ const agentSupportsImports = !!agentConfig.capabilities.memoryImports;
1336
+ for (const mem of memoryToSync) {
1337
+ const projectSource = projectMemoryDir ? path.join(projectMemoryDir, `${mem}.md`) : null;
1338
+ const srcFile = projectSource && fs.existsSync(projectSource)
1339
+ ? projectSource
1340
+ : path.join(centralMemory, `${mem}.md`);
1341
+ if (!fs.existsSync(srcFile))
1342
+ continue;
1343
+ const targetName = mem === 'AGENTS' ? agentConfig.instructionsFile : `${mem}.md`;
1344
+ const destFile = path.join(agentDir, targetName);
1345
+ removePath(destFile);
1346
+ // For the primary memory file (AGENTS.md), agents that don't natively
1347
+ // resolve @-imports get a compiled (inlined) copy + sidecar manifest.
1348
+ // Everything else (secondary memory files, @-capable agents) gets a
1349
+ // straight copy.
1350
+ if (mem === 'AGENTS' && !agentSupportsImports) {
1351
+ compileMemoryForAgent(agent, version);
1352
+ }
1353
+ else {
1354
+ fs.copyFileSync(srcFile, destFile);
1355
+ }
1356
+ result.memory.push(targetName);
1357
+ syncedMemory.push(mem);
1358
+ }
1359
+ if (syncedMemory.length > 0) {
1360
+ recordVersionResources(agent, version, 'memory', syncedMemory);
1361
+ }
1362
+ }
1363
+ // Apply permissions (if agent supports them)
1364
+ // Permissions are now stored as groups in ~/.agents/permissions/groups/
1365
+ const permissionGroups = discoverPermissionGroups();
1366
+ const allGroupNames = permissionGroups.map(g => g.name);
1367
+ const permsToSync = selection
1368
+ ? resolveSelection(selection.permissions, allGroupNames)
1369
+ : (PERMISSIONS_CAPABLE_AGENTS.includes(agent) ? allGroupNames : []);
1370
+ if (permsToSync.length > 0 && PERMISSIONS_CAPABLE_AGENTS.includes(agent)) {
1371
+ // Build permissions from selected groups
1372
+ const builtPerms = buildPermissionsFromGroups(permsToSync);
1373
+ if (builtPerms.allow.length > 0 || (builtPerms.deny && builtPerms.deny.length > 0)) {
1374
+ const permResult = applyPermsToVersion(agent, builtPerms, versionHome, true);
1375
+ result.permissions = permResult.success;
1376
+ if (permResult.success) {
1377
+ recordVersionResources(agent, version, 'permissions', permsToSync);
1378
+ }
1379
+ }
1380
+ }
1381
+ // Install MCP servers (if agent supports them)
1382
+ // For Claude/Codex: uses CLI commands (claude mcp add, codex mcp add)
1383
+ // For others: edits config files directly
1384
+ const mcpToSync = selection
1385
+ ? resolveSelection(selection.mcp, available.mcp)
1386
+ : (MCP_CAPABLE_AGENTS.includes(agent) ? available.mcp : []);
1387
+ if (mcpToSync.length > 0 && MCP_CAPABLE_AGENTS.includes(agent)) {
1388
+ const mcpResult = installMcpServers(agent, version, versionHome, mcpToSync, { cwd });
1389
+ result.mcp = mcpResult.applied;
1390
+ if (mcpResult.applied.length > 0) {
1391
+ recordVersionResources(agent, version, 'mcp', mcpResult.applied);
1392
+ }
1393
+ }
1394
+ // Sync subagents (claude and openclaw only)
1395
+ const subagentsToSync = selection
1396
+ ? resolveSelection(selection.subagents, available.subagents)
1397
+ : (SUBAGENT_CAPABLE_AGENTS.includes(agent) ? available.subagents : []);
1398
+ if (subagentsToSync.length > 0 && SUBAGENT_CAPABLE_AGENTS.includes(agent)) {
1399
+ const allSubagents = listInstalledSubagents();
1400
+ const subagentsMap = new Map(allSubagents.map(s => [s.name, s]));
1401
+ for (const name of subagentsToSync) {
1402
+ const subagent = subagentsMap.get(name);
1403
+ if (!subagent)
1404
+ continue;
1405
+ try {
1406
+ if (agent === 'claude') {
1407
+ // Claude: flatten to single .md file
1408
+ const agentsDir = path.join(agentDir, 'agents');
1409
+ fs.mkdirSync(agentsDir, { recursive: true });
1410
+ const transformed = transformSubagentForClaude(subagent.path);
1411
+ fs.writeFileSync(path.join(agentsDir, `${subagent.name}.md`), transformed);
1412
+ result.subagents.push(subagent.name);
1413
+ }
1414
+ else if (agent === 'openclaw') {
1415
+ // OpenClaw: copy full directory, rename AGENT.md -> AGENTS.md
1416
+ const targetDir = path.join(versionHome, '.openclaw', subagent.name);
1417
+ const syncResult = syncSubagentToOpenclaw(subagent.path, targetDir);
1418
+ if (syncResult.success) {
1419
+ result.subagents.push(subagent.name);
1420
+ }
1421
+ }
1422
+ }
1423
+ catch { /* resource sync failed for this item */ }
1424
+ }
1425
+ if (result.subagents.length > 0) {
1426
+ recordVersionResources(agent, version, 'subagents', result.subagents);
1427
+ }
1428
+ }
1429
+ // Sync plugins (claude and openclaw)
1430
+ const pluginsToSync = selection
1431
+ ? resolveSelection(selection.plugins, available.plugins)
1432
+ : (PLUGINS_CAPABLE_AGENTS.includes(agent) ? available.plugins : []);
1433
+ if (pluginsToSync.length > 0 && PLUGINS_CAPABLE_AGENTS.includes(agent)) {
1434
+ const allPlugins = discoverPlugins();
1435
+ const pluginMap = new Map(allPlugins.map(p => [p.name, p]));
1436
+ // Clean orphaned plugin skills from plugins that no longer exist
1437
+ const activePluginNames = new Set(allPlugins.map(p => p.name));
1438
+ cleanOrphanedPluginSkills(agent, versionHome, activePluginNames);
1439
+ for (const name of pluginsToSync) {
1440
+ const plugin = pluginMap.get(name);
1441
+ if (!plugin || !pluginSupportsAgent(plugin, agent))
1442
+ continue;
1443
+ const pluginResult = syncPluginToVersion(plugin, agent, versionHome);
1444
+ if (pluginResult.success) {
1445
+ result.plugins.push(name);
1446
+ }
1447
+ }
1448
+ if (result.plugins.length > 0) {
1449
+ recordVersionResources(agent, version, 'plugins', result.plugins);
1450
+ }
1451
+ }
1452
+ return result;
1453
+ }
1454
+ /**
1455
+ * Get the effective HOME directory for an agent.
1456
+ * If version-managed with a resolved version, returns the version's home directory.
1457
+ * Otherwise returns the real HOME.
1458
+ */
1459
+ export function getEffectiveHome(agentId) {
1460
+ const resolved = resolveVersion(agentId, process.cwd());
1461
+ if (resolved && isVersionInstalled(agentId, resolved)) {
1462
+ return getVersionHomePath(agentId, resolved);
1463
+ }
1464
+ return os.homedir();
1465
+ }
1466
+ /**
1467
+ * Resolve a comma-separated --agents list into concrete version selections.
1468
+ * Bare agents target the default version, or the newest installed version when no default exists.
1469
+ * Explicit agent@version targets only that installed version.
1470
+ */
1471
+ export function resolveAgentVersionTargets(value, availableAgents, options = {}) {
1472
+ const selectedAgents = [];
1473
+ const versionSelections = new Map();
1474
+ const explicitSelections = new Set();
1475
+ const targets = value
1476
+ .split(',')
1477
+ .map((item) => item.trim())
1478
+ .filter(Boolean);
1479
+ for (const target of targets) {
1480
+ const atIndex = target.indexOf('@');
1481
+ const agentToken = (atIndex === -1 ? target : target.slice(0, atIndex)).trim();
1482
+ const versionToken = atIndex === -1 ? null : target.slice(atIndex + 1).trim();
1483
+ if (!agentToken) {
1484
+ continue;
1485
+ }
1486
+ if (atIndex !== -1 && !versionToken) {
1487
+ throw new Error(`Missing version in --agents entry '${target}'. Use agent@x.y.z or agent@default.`);
1488
+ }
1489
+ const agentId = resolveAgentName(agentToken);
1490
+ if (!agentId || !availableAgents.includes(agentId)) {
1491
+ throw new Error(formatAgentError(agentToken, [...availableAgents]));
1492
+ }
1493
+ if (!selectedAgents.includes(agentId)) {
1494
+ selectedAgents.push(agentId);
1495
+ }
1496
+ if (explicitSelections.has(agentId) && !versionToken) {
1497
+ continue;
1498
+ }
1499
+ const installedVersions = listInstalledVersions(agentId);
1500
+ const defaultVersion = getGlobalDefault(agentId);
1501
+ if (!versionToken) {
1502
+ if (installedVersions.length === 0) {
1503
+ continue;
1504
+ }
1505
+ versionSelections.set(agentId, options.allVersions
1506
+ ? [...installedVersions]
1507
+ : [defaultVersion || installedVersions[installedVersions.length - 1]]);
1508
+ continue;
1509
+ }
1510
+ if (installedVersions.length === 0) {
1511
+ throw new Error(`No managed versions are installed for ${AGENTS[agentId].name}. Run: agents add ${agentId}@latest`);
1512
+ }
1513
+ if (versionToken === 'default') {
1514
+ if (!defaultVersion) {
1515
+ throw new Error(`No default version set for ${AGENTS[agentId].name}. Run: agents use ${agentId}@<version>`);
1516
+ }
1517
+ const explicitVersions = explicitSelections.has(agentId)
1518
+ ? (versionSelections.get(agentId) || [])
1519
+ : [];
1520
+ if (!explicitVersions.includes(defaultVersion)) {
1521
+ explicitVersions.push(defaultVersion);
1522
+ }
1523
+ versionSelections.set(agentId, explicitVersions);
1524
+ explicitSelections.add(agentId);
1525
+ continue;
1526
+ }
1527
+ if (!installedVersions.includes(versionToken)) {
1528
+ throw new Error(`Version ${versionToken} is not installed for ${AGENTS[agentId].name}. Installed versions: ${installedVersions.join(', ')}`);
1529
+ }
1530
+ const explicitVersions = explicitSelections.has(agentId)
1531
+ ? (versionSelections.get(agentId) || [])
1532
+ : [];
1533
+ if (!explicitVersions.includes(versionToken)) {
1534
+ explicitVersions.push(versionToken);
1535
+ }
1536
+ versionSelections.set(agentId, explicitVersions);
1537
+ explicitSelections.add(agentId);
1538
+ }
1539
+ return { selectedAgents, versionSelections };
1540
+ }
1541
+ /**
1542
+ * Resolve a comma-separated --agents list into install/apply targets.
1543
+ * Bare agents target the default version (or newest installed version) when managed,
1544
+ * and fall back to the agent's effective HOME when unmanaged.
1545
+ * Explicit agent@version targets only that installed version.
1546
+ */
1547
+ export function resolveInstalledAgentTargets(value, availableAgents, options = {}) {
1548
+ const selectedAgents = [];
1549
+ const directAgents = [];
1550
+ const versionSelections = new Map();
1551
+ const targets = value
1552
+ .split(',')
1553
+ .map((item) => item.trim())
1554
+ .filter(Boolean);
1555
+ const addVersionTarget = (agentId, version) => {
1556
+ const versions = versionSelections.get(agentId) || [];
1557
+ if (!versions.includes(version)) {
1558
+ versions.push(version);
1559
+ versionSelections.set(agentId, versions);
1560
+ }
1561
+ const directIndex = directAgents.indexOf(agentId);
1562
+ if (directIndex !== -1) {
1563
+ directAgents.splice(directIndex, 1);
1564
+ }
1565
+ };
1566
+ for (const target of targets) {
1567
+ const atIndex = target.indexOf('@');
1568
+ const agentToken = (atIndex === -1 ? target : target.slice(0, atIndex)).trim();
1569
+ const versionToken = atIndex === -1 ? null : target.slice(atIndex + 1).trim();
1570
+ if (!agentToken) {
1571
+ continue;
1572
+ }
1573
+ if (atIndex !== -1 && !versionToken) {
1574
+ throw new Error(`Missing version in --agents entry '${target}'. Use agent@x.y.z or agent@default.`);
1575
+ }
1576
+ const agentId = resolveAgentName(agentToken);
1577
+ if (!agentId || !availableAgents.includes(agentId)) {
1578
+ throw new Error(formatAgentError(agentToken, [...availableAgents]));
1579
+ }
1580
+ if (!selectedAgents.includes(agentId)) {
1581
+ selectedAgents.push(agentId);
1582
+ }
1583
+ const installedVersions = listInstalledVersions(agentId);
1584
+ const defaultVersion = getGlobalDefault(agentId);
1585
+ if (!versionToken) {
1586
+ if (installedVersions.length === 0) {
1587
+ if (!directAgents.includes(agentId)) {
1588
+ directAgents.push(agentId);
1589
+ }
1590
+ continue;
1591
+ }
1592
+ const targetVersions = options.allVersions
1593
+ ? [...installedVersions]
1594
+ : [defaultVersion || installedVersions[installedVersions.length - 1]];
1595
+ for (const version of targetVersions) {
1596
+ addVersionTarget(agentId, version);
1597
+ }
1598
+ continue;
1599
+ }
1600
+ if (versionToken === 'default') {
1601
+ if (!defaultVersion) {
1602
+ throw new Error(`No default version set for ${AGENTS[agentId].name}. Run: agents use ${agentId}@<version>`);
1603
+ }
1604
+ addVersionTarget(agentId, defaultVersion);
1605
+ continue;
1606
+ }
1607
+ if (installedVersions.length === 0) {
1608
+ throw new Error(`No managed versions are installed for ${AGENTS[agentId].name}. Run: agents add ${agentId}@latest`);
1609
+ }
1610
+ if (!installedVersions.includes(versionToken)) {
1611
+ throw new Error(`Version ${versionToken} is not installed for ${AGENTS[agentId].name}. Installed versions: ${installedVersions.join(', ')}`);
1612
+ }
1613
+ addVersionTarget(agentId, versionToken);
1614
+ }
1615
+ return { selectedAgents, directAgents, versionSelections };
1616
+ }
1617
+ /**
1618
+ * Resolve configured manifest targets into direct homes and managed versions.
1619
+ */
1620
+ export function resolveConfiguredAgentTargets(agents, agentVersions, availableAgents, options = {}) {
1621
+ const targetSpecs = [];
1622
+ const broadTargets = agents ? [...agents] : [...availableAgents];
1623
+ for (const agentId of broadTargets) {
1624
+ if (availableAgents.includes(agentId)) {
1625
+ targetSpecs.push(agentId);
1626
+ }
1627
+ }
1628
+ if (agentVersions) {
1629
+ for (const [agentId, versions] of Object.entries(agentVersions)) {
1630
+ if (!availableAgents.includes(agentId) || !versions)
1631
+ continue;
1632
+ for (const version of versions) {
1633
+ targetSpecs.push(`${agentId}@${version}`);
1634
+ }
1635
+ }
1636
+ }
1637
+ if (targetSpecs.length === 0) {
1638
+ return {
1639
+ selectedAgents: [],
1640
+ directAgents: [],
1641
+ versionSelections: new Map(),
1642
+ };
1643
+ }
1644
+ return resolveInstalledAgentTargets(targetSpecs.join(','), availableAgents, options);
1645
+ }
1646
+ /**
1647
+ * Prompt user to select agents and versions for resource installation.
1648
+ * Returns selected agents and their version selections.
1649
+ */
1650
+ export async function promptAgentVersionSelection(availableAgents, options = {}) {
1651
+ const versionSelections = new Map();
1652
+ // Filter to installed agents (only those with versions managed by agents CLI)
1653
+ const installedAgents = availableAgents.filter((id) => {
1654
+ const versions = listInstalledVersions(id);
1655
+ return versions.length > 0;
1656
+ });
1657
+ if (installedAgents.length === 0) {
1658
+ return { selectedAgents: [], versionSelections };
1659
+ }
1660
+ const formatAgentLabel = (agentId) => {
1661
+ const versions = listInstalledVersions(agentId);
1662
+ const defaultVer = getGlobalDefault(agentId);
1663
+ if (versions.length === 0)
1664
+ return `${AGENTS[agentId].name} ${chalk.gray('(not installed)')}`;
1665
+ if (defaultVer)
1666
+ return `${AGENTS[agentId].name} ${chalk.gray(`(active: ${defaultVer})`)}`;
1667
+ return `${AGENTS[agentId].name} ${chalk.gray(`(${versions[0]})`)}`;
1668
+ };
1669
+ let selectedAgents;
1670
+ if (options.skipPrompts) {
1671
+ // Auto-select all installed agents with default versions
1672
+ selectedAgents = [...installedAgents];
1673
+ for (const agentId of selectedAgents) {
1674
+ const versions = listInstalledVersions(agentId);
1675
+ if (versions.length > 0) {
1676
+ const defaultVer = getGlobalDefault(agentId);
1677
+ versionSelections.set(agentId, defaultVer ? [defaultVer] : [versions[versions.length - 1]]);
1678
+ }
1679
+ }
1680
+ }
1681
+ else {
1682
+ // Prompt for agent selection
1683
+ const checkboxResult = await checkbox({
1684
+ message: 'Which agents should receive these resources?',
1685
+ choices: [
1686
+ { name: chalk.bold('All'), value: 'all', checked: true },
1687
+ ...installedAgents.map((id) => ({
1688
+ name: ` ${formatAgentLabel(id)}`,
1689
+ value: id,
1690
+ checked: false,
1691
+ })),
1692
+ ],
1693
+ });
1694
+ if (checkboxResult.includes('all')) {
1695
+ selectedAgents = [...installedAgents];
1696
+ }
1697
+ else {
1698
+ selectedAgents = checkboxResult;
1699
+ }
1700
+ // Version selection per agent
1701
+ for (const agentId of selectedAgents) {
1702
+ const versions = listInstalledVersions(agentId);
1703
+ if (versions.length === 0)
1704
+ continue;
1705
+ if (versions.length === 1) {
1706
+ versionSelections.set(agentId, [versions[0]]);
1707
+ continue;
1708
+ }
1709
+ const defaultVer = getGlobalDefault(agentId);
1710
+ const versionEmails = await Promise.all(versions.map((v) => getAccountEmail(agentId, getVersionHomePath(agentId, v)).then((email) => ({ v, email }))));
1711
+ const versionEmailMap = new Map(versionEmails.map((e) => [e.v, e.email]));
1712
+ const maxLabelLen = Math.max(...versions.map((v) => (v === defaultVer ? `${v} (default)` : v).length));
1713
+ const versionResult = await checkbox({
1714
+ message: `Which versions of ${AGENTS[agentId].name} should receive these resources?`,
1715
+ choices: [
1716
+ { name: chalk.bold('All versions'), value: 'all', checked: false },
1717
+ ...versions.map((v) => {
1718
+ const base = v === defaultVer ? `${v} (default)` : v;
1719
+ let label = base.padEnd(maxLabelLen);
1720
+ const email = versionEmailMap.get(v);
1721
+ if (email)
1722
+ label += chalk.cyan(` ${email}`);
1723
+ return { name: label, value: v, checked: v === defaultVer };
1724
+ }),
1725
+ ],
1726
+ });
1727
+ if (versionResult.includes('all')) {
1728
+ versionSelections.set(agentId, [...versions]);
1729
+ }
1730
+ else {
1731
+ versionSelections.set(agentId, versionResult);
1732
+ }
1733
+ }
1734
+ }
1735
+ return { selectedAgents, versionSelections };
1736
+ }
1737
+ //# sourceMappingURL=versions.js.map