@vibe-agent-toolkit/cli 0.2.0-rc.10 → 0.2.0-rc.12

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 (755) hide show
  1. package/README.md +3 -3
  2. package/dist/bin/supervise-cli.d.ts +45 -0
  3. package/dist/bin/supervise-cli.d.ts.map +1 -0
  4. package/dist/bin/supervise-cli.js +83 -0
  5. package/dist/bin/supervise-cli.js.map +1 -0
  6. package/dist/bin/vat +94 -40
  7. package/dist/bin/vat.d.ts +15 -4
  8. package/dist/bin/vat.d.ts.map +1 -1
  9. package/dist/bin/vat.js +94 -40
  10. package/dist/bin/vat.js.map +1 -1
  11. package/dist/bin.js +21 -29
  12. package/dist/bin.js.map +1 -1
  13. package/dist/command-tree.d.ts +45 -0
  14. package/dist/command-tree.d.ts.map +1 -0
  15. package/dist/command-tree.js +60 -0
  16. package/dist/command-tree.js.map +1 -0
  17. package/dist/commands/agent/build-schema.d.ts +100 -0
  18. package/dist/commands/agent/build-schema.d.ts.map +1 -0
  19. package/dist/commands/agent/build-schema.js +32 -0
  20. package/dist/commands/agent/build-schema.js.map +1 -0
  21. package/dist/commands/agent/build.d.ts +1 -0
  22. package/dist/commands/agent/build.d.ts.map +1 -1
  23. package/dist/commands/agent/build.js +63 -33
  24. package/dist/commands/agent/build.js.map +1 -1
  25. package/dist/commands/agent/import-schema.d.ts +75 -0
  26. package/dist/commands/agent/import-schema.d.ts.map +1 -0
  27. package/dist/commands/agent/import-schema.js +25 -0
  28. package/dist/commands/agent/import-schema.js.map +1 -0
  29. package/dist/commands/agent/import.d.ts.map +1 -1
  30. package/dist/commands/agent/import.js +33 -27
  31. package/dist/commands/agent/import.js.map +1 -1
  32. package/dist/commands/agent/index.d.ts.map +1 -1
  33. package/dist/commands/agent/index.js +132 -37
  34. package/dist/commands/agent/index.js.map +1 -1
  35. package/dist/commands/agent/install-path.d.ts.map +1 -1
  36. package/dist/commands/agent/install-path.js +2 -1
  37. package/dist/commands/agent/install-path.js.map +1 -1
  38. package/dist/commands/agent/install-schema.d.ts +89 -0
  39. package/dist/commands/agent/install-schema.d.ts.map +1 -0
  40. package/dist/commands/agent/install-schema.js +29 -0
  41. package/dist/commands/agent/install-schema.js.map +1 -0
  42. package/dist/commands/agent/install.d.ts +7 -1
  43. package/dist/commands/agent/install.d.ts.map +1 -1
  44. package/dist/commands/agent/install.js +78 -108
  45. package/dist/commands/agent/install.js.map +1 -1
  46. package/dist/commands/agent/installed-schema.d.ts +140 -0
  47. package/dist/commands/agent/installed-schema.d.ts.map +1 -0
  48. package/dist/commands/agent/installed-schema.js +36 -0
  49. package/dist/commands/agent/installed-schema.js.map +1 -0
  50. package/dist/commands/agent/installed.d.ts +1 -1
  51. package/dist/commands/agent/installed.d.ts.map +1 -1
  52. package/dist/commands/agent/installed.js +104 -75
  53. package/dist/commands/agent/installed.js.map +1 -1
  54. package/dist/commands/agent/list-schema.d.ts +128 -0
  55. package/dist/commands/agent/list-schema.d.ts.map +1 -0
  56. package/dist/commands/agent/list-schema.js +34 -0
  57. package/dist/commands/agent/list-schema.js.map +1 -0
  58. package/dist/commands/agent/list.d.ts +4 -3
  59. package/dist/commands/agent/list.d.ts.map +1 -1
  60. package/dist/commands/agent/list.js +50 -37
  61. package/dist/commands/agent/list.js.map +1 -1
  62. package/dist/commands/agent/run.d.ts +3 -0
  63. package/dist/commands/agent/run.d.ts.map +1 -1
  64. package/dist/commands/agent/run.js +8 -3
  65. package/dist/commands/agent/run.js.map +1 -1
  66. package/dist/commands/agent/uninstall-schema.d.ts +89 -0
  67. package/dist/commands/agent/uninstall-schema.d.ts.map +1 -0
  68. package/dist/commands/agent/uninstall-schema.js +29 -0
  69. package/dist/commands/agent/uninstall-schema.js.map +1 -0
  70. package/dist/commands/agent/uninstall.d.ts +5 -1
  71. package/dist/commands/agent/uninstall.d.ts.map +1 -1
  72. package/dist/commands/agent/uninstall.js +50 -38
  73. package/dist/commands/agent/uninstall.js.map +1 -1
  74. package/dist/commands/agent/validate-schema.d.ts +129 -0
  75. package/dist/commands/agent/validate-schema.d.ts.map +1 -0
  76. package/dist/commands/agent/validate-schema.js +35 -0
  77. package/dist/commands/agent/validate-schema.js.map +1 -0
  78. package/dist/commands/agent/validate.d.ts +6 -0
  79. package/dist/commands/agent/validate.d.ts.map +1 -1
  80. package/dist/commands/agent/validate.js +64 -36
  81. package/dist/commands/agent/validate.js.map +1 -1
  82. package/dist/commands/ard/emit-schema.d.ts +108 -0
  83. package/dist/commands/ard/emit-schema.d.ts.map +1 -0
  84. package/dist/commands/ard/emit-schema.js +37 -0
  85. package/dist/commands/ard/emit-schema.js.map +1 -0
  86. package/dist/commands/ard/emit.d.ts +31 -63
  87. package/dist/commands/ard/emit.d.ts.map +1 -1
  88. package/dist/commands/ard/emit.js +124 -68
  89. package/dist/commands/ard/emit.js.map +1 -1
  90. package/dist/commands/ard/index.d.ts.map +1 -1
  91. package/dist/commands/ard/index.js +17 -10
  92. package/dist/commands/ard/index.js.map +1 -1
  93. package/dist/commands/audit/compat-lane-findings.d.ts +65 -0
  94. package/dist/commands/audit/compat-lane-findings.d.ts.map +1 -0
  95. package/dist/commands/audit/compat-lane-findings.js +72 -0
  96. package/dist/commands/audit/compat-lane-findings.js.map +1 -0
  97. package/dist/commands/audit/distributed-tree.d.ts +2 -2
  98. package/dist/commands/audit/distributed-tree.d.ts.map +1 -1
  99. package/dist/commands/audit/distributed-tree.js +23 -12
  100. package/dist/commands/audit/distributed-tree.js.map +1 -1
  101. package/dist/commands/audit/git-url-clone.d.ts +15 -7
  102. package/dist/commands/audit/git-url-clone.d.ts.map +1 -1
  103. package/dist/commands/audit/git-url-clone.js +85 -42
  104. package/dist/commands/audit/git-url-clone.js.map +1 -1
  105. package/dist/commands/audit/hierarchical-output.d.ts +28 -4
  106. package/dist/commands/audit/hierarchical-output.d.ts.map +1 -1
  107. package/dist/commands/audit/hierarchical-output.js +44 -27
  108. package/dist/commands/audit/hierarchical-output.js.map +1 -1
  109. package/dist/commands/audit/scan-population.d.ts.map +1 -1
  110. package/dist/commands/audit/scan-population.js +2 -1
  111. package/dist/commands/audit/scan-population.js.map +1 -1
  112. package/dist/commands/audit-schema.d.ts +1247 -0
  113. package/dist/commands/audit-schema.d.ts.map +1 -0
  114. package/dist/commands/audit-schema.js +139 -0
  115. package/dist/commands/audit-schema.js.map +1 -0
  116. package/dist/commands/audit-settings-schema.d.ts +417 -0
  117. package/dist/commands/audit-settings-schema.d.ts.map +1 -0
  118. package/dist/commands/audit-settings-schema.js +77 -0
  119. package/dist/commands/audit-settings-schema.js.map +1 -0
  120. package/dist/commands/audit-settings.d.ts +28 -9
  121. package/dist/commands/audit-settings.d.ts.map +1 -1
  122. package/dist/commands/audit-settings.js +215 -151
  123. package/dist/commands/audit-settings.js.map +1 -1
  124. package/dist/commands/audit.d.ts +72 -33
  125. package/dist/commands/audit.d.ts.map +1 -1
  126. package/dist/commands/audit.js +415 -397
  127. package/dist/commands/audit.js.map +1 -1
  128. package/dist/commands/build.d.ts +61 -4
  129. package/dist/commands/build.d.ts.map +1 -1
  130. package/dist/commands/build.js +176 -190
  131. package/dist/commands/build.js.map +1 -1
  132. package/dist/commands/cache/clear-schema.d.ts +111 -0
  133. package/dist/commands/cache/clear-schema.d.ts.map +1 -0
  134. package/dist/commands/cache/clear-schema.js +37 -0
  135. package/dist/commands/cache/clear-schema.js.map +1 -0
  136. package/dist/commands/cache/clear.d.ts +28 -26
  137. package/dist/commands/cache/clear.d.ts.map +1 -1
  138. package/dist/commands/cache/clear.js +108 -103
  139. package/dist/commands/cache/clear.js.map +1 -1
  140. package/dist/commands/cache/index.d.ts.map +1 -1
  141. package/dist/commands/cache/index.js +13 -7
  142. package/dist/commands/cache/index.js.map +1 -1
  143. package/dist/commands/claude/claude-config.d.ts +23 -6
  144. package/dist/commands/claude/claude-config.d.ts.map +1 -1
  145. package/dist/commands/claude/claude-config.js +29 -15
  146. package/dist/commands/claude/claude-config.js.map +1 -1
  147. package/dist/commands/claude/context.d.ts +87 -2
  148. package/dist/commands/claude/context.d.ts.map +1 -1
  149. package/dist/commands/claude/context.js +116 -57
  150. package/dist/commands/claude/context.js.map +1 -1
  151. package/dist/commands/claude/marketplace/changelog-utils.js +2 -2
  152. package/dist/commands/claude/marketplace/changelog-utils.js.map +1 -1
  153. package/dist/commands/claude/marketplace/git-publish.d.ts +14 -1
  154. package/dist/commands/claude/marketplace/git-publish.d.ts.map +1 -1
  155. package/dist/commands/claude/marketplace/git-publish.js +135 -68
  156. package/dist/commands/claude/marketplace/git-publish.js.map +1 -1
  157. package/dist/commands/claude/marketplace/license-utils.d.ts +3 -5
  158. package/dist/commands/claude/marketplace/license-utils.d.ts.map +1 -1
  159. package/dist/commands/claude/marketplace/license-utils.js +16 -7
  160. package/dist/commands/claude/marketplace/license-utils.js.map +1 -1
  161. package/dist/commands/claude/marketplace/publish-schema.d.ts +137 -0
  162. package/dist/commands/claude/marketplace/publish-schema.d.ts.map +1 -0
  163. package/dist/commands/claude/marketplace/publish-schema.js +32 -0
  164. package/dist/commands/claude/marketplace/publish-schema.js.map +1 -0
  165. package/dist/commands/claude/marketplace/publish-tree.d.ts +6 -0
  166. package/dist/commands/claude/marketplace/publish-tree.d.ts.map +1 -1
  167. package/dist/commands/claude/marketplace/publish-tree.js +75 -17
  168. package/dist/commands/claude/marketplace/publish-tree.js.map +1 -1
  169. package/dist/commands/claude/marketplace/publish.d.ts +27 -0
  170. package/dist/commands/claude/marketplace/publish.d.ts.map +1 -1
  171. package/dist/commands/claude/marketplace/publish.js +84 -50
  172. package/dist/commands/claude/marketplace/publish.js.map +1 -1
  173. package/dist/commands/claude/marketplace/validate-schema.d.ts +362 -0
  174. package/dist/commands/claude/marketplace/validate-schema.d.ts.map +1 -0
  175. package/dist/commands/claude/marketplace/validate-schema.js +67 -0
  176. package/dist/commands/claude/marketplace/validate-schema.js.map +1 -0
  177. package/dist/commands/claude/marketplace/validate.d.ts +40 -53
  178. package/dist/commands/claude/marketplace/validate.d.ts.map +1 -1
  179. package/dist/commands/claude/marketplace/validate.js +358 -214
  180. package/dist/commands/claude/marketplace/validate.js.map +1 -1
  181. package/dist/commands/claude/org/api-keys.d.ts +3 -0
  182. package/dist/commands/claude/org/api-keys.d.ts.map +1 -1
  183. package/dist/commands/claude/org/api-keys.js +4 -5
  184. package/dist/commands/claude/org/api-keys.js.map +1 -1
  185. package/dist/commands/claude/org/code-analytics.js +1 -1
  186. package/dist/commands/claude/org/code-analytics.js.map +1 -1
  187. package/dist/commands/claude/org/cost.d.ts.map +1 -1
  188. package/dist/commands/claude/org/cost.js +11 -33
  189. package/dist/commands/claude/org/cost.js.map +1 -1
  190. package/dist/commands/claude/org/helpers.d.ts +40 -27
  191. package/dist/commands/claude/org/helpers.d.ts.map +1 -1
  192. package/dist/commands/claude/org/helpers.js +61 -66
  193. package/dist/commands/claude/org/helpers.js.map +1 -1
  194. package/dist/commands/claude/org/index.d.ts.map +1 -1
  195. package/dist/commands/claude/org/index.js +10 -4
  196. package/dist/commands/claude/org/index.js.map +1 -1
  197. package/dist/commands/claude/org/info.d.ts.map +1 -1
  198. package/dist/commands/claude/org/info.js +2 -3
  199. package/dist/commands/claude/org/info.js.map +1 -1
  200. package/dist/commands/claude/org/invites.d.ts +3 -0
  201. package/dist/commands/claude/org/invites.d.ts.map +1 -1
  202. package/dist/commands/claude/org/invites.js +5 -7
  203. package/dist/commands/claude/org/invites.js.map +1 -1
  204. package/dist/commands/claude/org/skills.d.ts +76 -21
  205. package/dist/commands/claude/org/skills.d.ts.map +1 -1
  206. package/dist/commands/claude/org/skills.js +273 -246
  207. package/dist/commands/claude/org/skills.js.map +1 -1
  208. package/dist/commands/claude/org/stubs-schema.d.ts +56 -0
  209. package/dist/commands/claude/org/stubs-schema.d.ts.map +1 -0
  210. package/dist/commands/claude/org/stubs-schema.js +23 -0
  211. package/dist/commands/claude/org/stubs-schema.js.map +1 -0
  212. package/dist/commands/claude/org/stubs.d.ts +8 -3
  213. package/dist/commands/claude/org/stubs.d.ts.map +1 -1
  214. package/dist/commands/claude/org/stubs.js +9 -9
  215. package/dist/commands/claude/org/stubs.js.map +1 -1
  216. package/dist/commands/claude/org/usage.d.ts.map +1 -1
  217. package/dist/commands/claude/org/usage.js +1 -2
  218. package/dist/commands/claude/org/usage.js.map +1 -1
  219. package/dist/commands/claude/org/users.d.ts +3 -0
  220. package/dist/commands/claude/org/users.d.ts.map +1 -1
  221. package/dist/commands/claude/org/users.js +6 -8
  222. package/dist/commands/claude/org/users.js.map +1 -1
  223. package/dist/commands/claude/org/workspaces.d.ts +3 -0
  224. package/dist/commands/claude/org/workspaces.d.ts.map +1 -1
  225. package/dist/commands/claude/org/workspaces.js +10 -15
  226. package/dist/commands/claude/org/workspaces.js.map +1 -1
  227. package/dist/commands/claude/plugin/build-schema.d.ts +642 -0
  228. package/dist/commands/claude/plugin/build-schema.d.ts.map +1 -0
  229. package/dist/commands/claude/plugin/build-schema.js +51 -0
  230. package/dist/commands/claude/plugin/build-schema.js.map +1 -0
  231. package/dist/commands/claude/plugin/build.d.ts +220 -45
  232. package/dist/commands/claude/plugin/build.d.ts.map +1 -1
  233. package/dist/commands/claude/plugin/build.js +466 -249
  234. package/dist/commands/claude/plugin/build.js.map +1 -1
  235. package/dist/commands/claude/plugin/helpers.d.ts +186 -24
  236. package/dist/commands/claude/plugin/helpers.d.ts.map +1 -1
  237. package/dist/commands/claude/plugin/helpers.js +110 -68
  238. package/dist/commands/claude/plugin/helpers.js.map +1 -1
  239. package/dist/commands/claude/plugin/install-schema.d.ts +144 -0
  240. package/dist/commands/claude/plugin/install-schema.d.ts.map +1 -0
  241. package/dist/commands/claude/plugin/install-schema.js +36 -0
  242. package/dist/commands/claude/plugin/install-schema.js.map +1 -0
  243. package/dist/commands/claude/plugin/install.d.ts +191 -11
  244. package/dist/commands/claude/plugin/install.d.ts.map +1 -1
  245. package/dist/commands/claude/plugin/install.js +688 -628
  246. package/dist/commands/claude/plugin/install.js.map +1 -1
  247. package/dist/commands/claude/plugin/kept-findings.d.ts +19 -0
  248. package/dist/commands/claude/plugin/kept-findings.d.ts.map +1 -0
  249. package/dist/commands/claude/plugin/kept-findings.js +24 -0
  250. package/dist/commands/claude/plugin/kept-findings.js.map +1 -0
  251. package/dist/commands/claude/plugin/list-schema.d.ts +223 -0
  252. package/dist/commands/claude/plugin/list-schema.d.ts.map +1 -0
  253. package/dist/commands/claude/plugin/list-schema.js +34 -0
  254. package/dist/commands/claude/plugin/list-schema.js.map +1 -0
  255. package/dist/commands/claude/plugin/list.d.ts +18 -0
  256. package/dist/commands/claude/plugin/list.d.ts.map +1 -1
  257. package/dist/commands/claude/plugin/list.js +53 -45
  258. package/dist/commands/claude/plugin/list.js.map +1 -1
  259. package/dist/commands/claude/plugin/npm-pack.d.ts +15 -0
  260. package/dist/commands/claude/plugin/npm-pack.d.ts.map +1 -0
  261. package/dist/commands/claude/plugin/npm-pack.js +39 -0
  262. package/dist/commands/claude/plugin/npm-pack.js.map +1 -0
  263. package/dist/commands/claude/plugin/plugin-changelog.d.ts +5 -1
  264. package/dist/commands/claude/plugin/plugin-changelog.d.ts.map +1 -1
  265. package/dist/commands/claude/plugin/plugin-changelog.js +9 -5
  266. package/dist/commands/claude/plugin/plugin-changelog.js.map +1 -1
  267. package/dist/commands/claude/plugin/plugin-files.d.ts +21 -2
  268. package/dist/commands/claude/plugin/plugin-files.d.ts.map +1 -1
  269. package/dist/commands/claude/plugin/plugin-files.js +65 -32
  270. package/dist/commands/claude/plugin/plugin-files.js.map +1 -1
  271. package/dist/commands/claude/plugin/plugin-validators.d.ts +13 -0
  272. package/dist/commands/claude/plugin/plugin-validators.d.ts.map +1 -1
  273. package/dist/commands/claude/plugin/plugin-validators.js +64 -16
  274. package/dist/commands/claude/plugin/plugin-validators.js.map +1 -1
  275. package/dist/commands/claude/plugin/tree-copy.d.ts.map +1 -1
  276. package/dist/commands/claude/plugin/tree-copy.js +63 -37
  277. package/dist/commands/claude/plugin/tree-copy.js.map +1 -1
  278. package/dist/commands/claude/plugin/uninstall-schema.d.ts +112 -0
  279. package/dist/commands/claude/plugin/uninstall-schema.d.ts.map +1 -0
  280. package/dist/commands/claude/plugin/uninstall-schema.js +28 -0
  281. package/dist/commands/claude/plugin/uninstall-schema.js.map +1 -0
  282. package/dist/commands/claude/plugin/uninstall.d.ts +57 -0
  283. package/dist/commands/claude/plugin/uninstall.d.ts.map +1 -1
  284. package/dist/commands/claude/plugin/uninstall.js +176 -64
  285. package/dist/commands/claude/plugin/uninstall.js.map +1 -1
  286. package/dist/commands/consistency-check.d.ts.map +1 -1
  287. package/dist/commands/consistency-check.js +8 -6
  288. package/dist/commands/consistency-check.js.map +1 -1
  289. package/dist/commands/corpus/index.d.ts.map +1 -1
  290. package/dist/commands/corpus/index.js +26 -10
  291. package/dist/commands/corpus/index.js.map +1 -1
  292. package/dist/commands/corpus/report.d.ts +34 -16
  293. package/dist/commands/corpus/report.d.ts.map +1 -1
  294. package/dist/commands/corpus/report.js +36 -18
  295. package/dist/commands/corpus/report.js.map +1 -1
  296. package/dist/commands/corpus/runner.d.ts +25 -20
  297. package/dist/commands/corpus/runner.d.ts.map +1 -1
  298. package/dist/commands/corpus/runner.js +123 -67
  299. package/dist/commands/corpus/runner.js.map +1 -1
  300. package/dist/commands/corpus/scan-schema.d.ts +127 -0
  301. package/dist/commands/corpus/scan-schema.d.ts.map +1 -0
  302. package/dist/commands/corpus/scan-schema.js +31 -0
  303. package/dist/commands/corpus/scan-schema.js.map +1 -0
  304. package/dist/commands/corpus/scan.d.ts +31 -0
  305. package/dist/commands/corpus/scan.d.ts.map +1 -1
  306. package/dist/commands/corpus/scan.js +124 -50
  307. package/dist/commands/corpus/scan.js.map +1 -1
  308. package/dist/commands/corpus/seed.d.ts +11 -8
  309. package/dist/commands/corpus/seed.d.ts.map +1 -1
  310. package/dist/commands/corpus/seed.js +29 -11
  311. package/dist/commands/corpus/seed.js.map +1 -1
  312. package/dist/commands/doctor-render.d.ts +52 -0
  313. package/dist/commands/doctor-render.d.ts.map +1 -0
  314. package/dist/commands/doctor-render.js +110 -0
  315. package/dist/commands/doctor-render.js.map +1 -0
  316. package/dist/commands/doctor-schema.d.ts +191 -0
  317. package/dist/commands/doctor-schema.d.ts.map +1 -0
  318. package/dist/commands/doctor-schema.js +44 -0
  319. package/dist/commands/doctor-schema.js.map +1 -0
  320. package/dist/commands/doctor.d.ts +7 -62
  321. package/dist/commands/doctor.d.ts.map +1 -1
  322. package/dist/commands/doctor.js +126 -165
  323. package/dist/commands/doctor.js.map +1 -1
  324. package/dist/commands/inventory-schema.d.ts +7297 -0
  325. package/dist/commands/inventory-schema.d.ts.map +1 -0
  326. package/dist/commands/inventory-schema.js +28 -0
  327. package/dist/commands/inventory-schema.js.map +1 -0
  328. package/dist/commands/inventory.d.ts +1 -1
  329. package/dist/commands/inventory.d.ts.map +1 -1
  330. package/dist/commands/inventory.js +89 -41
  331. package/dist/commands/inventory.js.map +1 -1
  332. package/dist/commands/mcp/index.d.ts.map +1 -1
  333. package/dist/commands/mcp/index.js +15 -0
  334. package/dist/commands/mcp/index.js.map +1 -1
  335. package/dist/commands/mcp/list-collections-schema.d.ts +101 -0
  336. package/dist/commands/mcp/list-collections-schema.d.ts.map +1 -0
  337. package/dist/commands/mcp/list-collections-schema.js +22 -0
  338. package/dist/commands/mcp/list-collections-schema.js.map +1 -0
  339. package/dist/commands/mcp/list-collections.d.ts +1 -1
  340. package/dist/commands/mcp/list-collections.d.ts.map +1 -1
  341. package/dist/commands/mcp/list-collections.js +19 -17
  342. package/dist/commands/mcp/list-collections.js.map +1 -1
  343. package/dist/commands/mcp/serve.d.ts +5 -0
  344. package/dist/commands/mcp/serve.d.ts.map +1 -1
  345. package/dist/commands/mcp/serve.js +29 -15
  346. package/dist/commands/mcp/serve.js.map +1 -1
  347. package/dist/commands/okf/index.d.ts.map +1 -1
  348. package/dist/commands/okf/index.js +13 -10
  349. package/dist/commands/okf/index.js.map +1 -1
  350. package/dist/commands/okf/validate-schema.d.ts +171 -0
  351. package/dist/commands/okf/validate-schema.d.ts.map +1 -0
  352. package/dist/commands/okf/validate-schema.js +44 -0
  353. package/dist/commands/okf/validate-schema.js.map +1 -0
  354. package/dist/commands/okf/validate.d.ts +16 -145
  355. package/dist/commands/okf/validate.d.ts.map +1 -1
  356. package/dist/commands/okf/validate.js +63 -52
  357. package/dist/commands/okf/validate.js.map +1 -1
  358. package/dist/commands/orchestrator-schema.d.ts +419 -0
  359. package/dist/commands/orchestrator-schema.d.ts.map +1 -0
  360. package/dist/commands/orchestrator-schema.js +91 -0
  361. package/dist/commands/orchestrator-schema.js.map +1 -0
  362. package/dist/commands/phase-utils.d.ts +188 -220
  363. package/dist/commands/phase-utils.d.ts.map +1 -1
  364. package/dist/commands/phase-utils.js +210 -258
  365. package/dist/commands/phase-utils.js.map +1 -1
  366. package/dist/commands/positional-args.d.ts +13 -11
  367. package/dist/commands/positional-args.d.ts.map +1 -1
  368. package/dist/commands/positional-args.js +16 -15
  369. package/dist/commands/positional-args.js.map +1 -1
  370. package/dist/commands/rag/admin-schema.d.ts +152 -0
  371. package/dist/commands/rag/admin-schema.d.ts.map +1 -0
  372. package/dist/commands/rag/admin-schema.js +31 -0
  373. package/dist/commands/rag/admin-schema.js.map +1 -0
  374. package/dist/commands/rag/clear-command.d.ts.map +1 -1
  375. package/dist/commands/rag/clear-command.js +30 -15
  376. package/dist/commands/rag/clear-command.js.map +1 -1
  377. package/dist/commands/rag/command-helpers.d.ts +31 -7
  378. package/dist/commands/rag/command-helpers.d.ts.map +1 -1
  379. package/dist/commands/rag/command-helpers.js +41 -21
  380. package/dist/commands/rag/command-helpers.js.map +1 -1
  381. package/dist/commands/rag/index-command.d.ts +37 -48
  382. package/dist/commands/rag/index-command.d.ts.map +1 -1
  383. package/dist/commands/rag/index-command.js +80 -65
  384. package/dist/commands/rag/index-command.js.map +1 -1
  385. package/dist/commands/rag/index-schema.d.ts +107 -0
  386. package/dist/commands/rag/index-schema.d.ts.map +1 -0
  387. package/dist/commands/rag/index-schema.js +34 -0
  388. package/dist/commands/rag/index-schema.js.map +1 -0
  389. package/dist/commands/rag/index.d.ts.map +1 -1
  390. package/dist/commands/rag/index.js +88 -41
  391. package/dist/commands/rag/index.js.map +1 -1
  392. package/dist/commands/rag/query-command.d.ts +18 -4
  393. package/dist/commands/rag/query-command.d.ts.map +1 -1
  394. package/dist/commands/rag/query-command.js +42 -27
  395. package/dist/commands/rag/query-command.js.map +1 -1
  396. package/dist/commands/rag/query-schema.d.ts +363 -0
  397. package/dist/commands/rag/query-schema.d.ts.map +1 -0
  398. package/dist/commands/rag/query-schema.js +59 -0
  399. package/dist/commands/rag/query-schema.js.map +1 -0
  400. package/dist/commands/rag/stats-command.d.ts.map +1 -1
  401. package/dist/commands/rag/stats-command.js +19 -18
  402. package/dist/commands/rag/stats-command.js.map +1 -1
  403. package/dist/commands/resources/check-progress.d.ts +79 -13
  404. package/dist/commands/resources/check-progress.d.ts.map +1 -1
  405. package/dist/commands/resources/check-progress.js +112 -11
  406. package/dist/commands/resources/check-progress.js.map +1 -1
  407. package/dist/commands/resources/check-schema.d.ts +335 -0
  408. package/dist/commands/resources/check-schema.d.ts.map +1 -0
  409. package/dist/commands/resources/check-schema.js +104 -0
  410. package/dist/commands/resources/check-schema.js.map +1 -0
  411. package/dist/commands/resources/check-supervisor.d.ts +329 -71
  412. package/dist/commands/resources/check-supervisor.d.ts.map +1 -1
  413. package/dist/commands/resources/check-supervisor.js +507 -107
  414. package/dist/commands/resources/check-supervisor.js.map +1 -1
  415. package/dist/commands/resources/check.d.ts +88 -254
  416. package/dist/commands/resources/check.d.ts.map +1 -1
  417. package/dist/commands/resources/check.js +537 -310
  418. package/dist/commands/resources/check.js.map +1 -1
  419. package/dist/commands/resources/index.d.ts.map +1 -1
  420. package/dist/commands/resources/index.js +189 -103
  421. package/dist/commands/resources/index.js.map +1 -1
  422. package/dist/commands/resources/query-schema.d.ts +188 -0
  423. package/dist/commands/resources/query-schema.d.ts.map +1 -0
  424. package/dist/commands/resources/query-schema.js +48 -0
  425. package/dist/commands/resources/query-schema.js.map +1 -0
  426. package/dist/commands/resources/query.d.ts +18 -13
  427. package/dist/commands/resources/query.d.ts.map +1 -1
  428. package/dist/commands/resources/query.js +61 -49
  429. package/dist/commands/resources/query.js.map +1 -1
  430. package/dist/commands/resources/scan-schema.d.ts +195 -0
  431. package/dist/commands/resources/scan-schema.d.ts.map +1 -0
  432. package/dist/commands/resources/scan-schema.js +53 -0
  433. package/dist/commands/resources/scan-schema.js.map +1 -0
  434. package/dist/commands/resources/scan.d.ts +26 -3
  435. package/dist/commands/resources/scan.d.ts.map +1 -1
  436. package/dist/commands/resources/scan.js +51 -53
  437. package/dist/commands/resources/scan.js.map +1 -1
  438. package/dist/commands/resources/validate-schema.d.ts +326 -0
  439. package/dist/commands/resources/validate-schema.d.ts.map +1 -0
  440. package/dist/commands/resources/validate-schema.js +60 -0
  441. package/dist/commands/resources/validate-schema.js.map +1 -0
  442. package/dist/commands/resources/validate.d.ts +45 -169
  443. package/dist/commands/resources/validate.d.ts.map +1 -1
  444. package/dist/commands/resources/validate.js +221 -485
  445. package/dist/commands/resources/validate.js.map +1 -1
  446. package/dist/commands/skill/review-checklist.d.ts.map +1 -1
  447. package/dist/commands/skill/review-checklist.js +0 -1
  448. package/dist/commands/skill/review-checklist.js.map +1 -1
  449. package/dist/commands/skill/review-schema.d.ts +278 -0
  450. package/dist/commands/skill/review-schema.d.ts.map +1 -0
  451. package/dist/commands/skill/review-schema.js +46 -0
  452. package/dist/commands/skill/review-schema.js.map +1 -0
  453. package/dist/commands/skill/review.d.ts +8 -234
  454. package/dist/commands/skill/review.d.ts.map +1 -1
  455. package/dist/commands/skill/review.js +54 -77
  456. package/dist/commands/skill/review.js.map +1 -1
  457. package/dist/commands/skill/test/auth-flags.d.ts +3 -0
  458. package/dist/commands/skill/test/auth-flags.d.ts.map +1 -1
  459. package/dist/commands/skill/test/auth-flags.js +6 -2
  460. package/dist/commands/skill/test/auth-flags.js.map +1 -1
  461. package/dist/commands/skill/test/configure-schema.d.ts +67 -0
  462. package/dist/commands/skill/test/configure-schema.d.ts.map +1 -0
  463. package/dist/commands/skill/test/configure-schema.js +27 -0
  464. package/dist/commands/skill/test/configure-schema.js.map +1 -0
  465. package/dist/commands/skill/test/configure.d.ts +12 -5
  466. package/dist/commands/skill/test/configure.d.ts.map +1 -1
  467. package/dist/commands/skill/test/configure.js +101 -31
  468. package/dist/commands/skill/test/configure.js.map +1 -1
  469. package/dist/commands/skill/test/run-schema.d.ts +107 -0
  470. package/dist/commands/skill/test/run-schema.d.ts.map +1 -0
  471. package/dist/commands/skill/test/run-schema.js +36 -0
  472. package/dist/commands/skill/test/run-schema.js.map +1 -0
  473. package/dist/commands/skill/test/run.d.ts +3 -2
  474. package/dist/commands/skill/test/run.d.ts.map +1 -1
  475. package/dist/commands/skill/test/run.js +316 -155
  476. package/dist/commands/skill/test/run.js.map +1 -1
  477. package/dist/commands/skills/build-schema.d.ts +199 -0
  478. package/dist/commands/skills/build-schema.d.ts.map +1 -0
  479. package/dist/commands/skills/build-schema.js +55 -0
  480. package/dist/commands/skills/build-schema.js.map +1 -0
  481. package/dist/commands/skills/build.d.ts +95 -298
  482. package/dist/commands/skills/build.d.ts.map +1 -1
  483. package/dist/commands/skills/build.js +415 -818
  484. package/dist/commands/skills/build.js.map +1 -1
  485. package/dist/commands/skills/command-helpers.d.ts +5 -11
  486. package/dist/commands/skills/command-helpers.d.ts.map +1 -1
  487. package/dist/commands/skills/command-helpers.js +7 -17
  488. package/dist/commands/skills/command-helpers.js.map +1 -1
  489. package/dist/commands/skills/index.js +12 -6
  490. package/dist/commands/skills/index.js.map +1 -1
  491. package/dist/commands/skills/install-schema.d.ts +153 -0
  492. package/dist/commands/skills/install-schema.d.ts.map +1 -0
  493. package/dist/commands/skills/install-schema.js +44 -0
  494. package/dist/commands/skills/install-schema.js.map +1 -0
  495. package/dist/commands/skills/install.d.ts +76 -8
  496. package/dist/commands/skills/install.d.ts.map +1 -1
  497. package/dist/commands/skills/install.js +230 -194
  498. package/dist/commands/skills/install.js.map +1 -1
  499. package/dist/commands/skills/list-schema.d.ts +145 -0
  500. package/dist/commands/skills/list-schema.d.ts.map +1 -0
  501. package/dist/commands/skills/list-schema.js +37 -0
  502. package/dist/commands/skills/list-schema.js.map +1 -0
  503. package/dist/commands/skills/list.d.ts +39 -8
  504. package/dist/commands/skills/list.d.ts.map +1 -1
  505. package/dist/commands/skills/list.js +110 -122
  506. package/dist/commands/skills/list.js.map +1 -1
  507. package/dist/commands/skills/package-schema.d.ts +98 -0
  508. package/dist/commands/skills/package-schema.d.ts.map +1 -0
  509. package/dist/commands/skills/package-schema.js +32 -0
  510. package/dist/commands/skills/package-schema.js.map +1 -0
  511. package/dist/commands/skills/package.d.ts +85 -14
  512. package/dist/commands/skills/package.d.ts.map +1 -1
  513. package/dist/commands/skills/package.js +288 -207
  514. package/dist/commands/skills/package.js.map +1 -1
  515. package/dist/commands/skills/scope-guard.d.ts +27 -20
  516. package/dist/commands/skills/scope-guard.d.ts.map +1 -1
  517. package/dist/commands/skills/scope-guard.js +43 -28
  518. package/dist/commands/skills/scope-guard.js.map +1 -1
  519. package/dist/commands/skills/skill-discovery.d.ts +1 -1
  520. package/dist/commands/skills/skill-discovery.d.ts.map +1 -1
  521. package/dist/commands/skills/skill-discovery.js +40 -19
  522. package/dist/commands/skills/skill-discovery.js.map +1 -1
  523. package/dist/commands/skills/source-resolvers.d.ts +58 -8
  524. package/dist/commands/skills/source-resolvers.d.ts.map +1 -1
  525. package/dist/commands/skills/source-resolvers.js +113 -38
  526. package/dist/commands/skills/source-resolvers.js.map +1 -1
  527. package/dist/commands/skills/validate-command.d.ts.map +1 -1
  528. package/dist/commands/skills/validate-command.js +31 -51
  529. package/dist/commands/skills/validate-command.js.map +1 -1
  530. package/dist/commands/skills/validate-schema.d.ts +199 -0
  531. package/dist/commands/skills/validate-schema.d.ts.map +1 -0
  532. package/dist/commands/skills/validate-schema.js +44 -0
  533. package/dist/commands/skills/validate-schema.js.map +1 -0
  534. package/dist/commands/skills/validate.d.ts +100 -136
  535. package/dist/commands/skills/validate.d.ts.map +1 -1
  536. package/dist/commands/skills/validate.js +257 -343
  537. package/dist/commands/skills/validate.js.map +1 -1
  538. package/dist/commands/validate.d.ts +17 -5
  539. package/dist/commands/validate.d.ts.map +1 -1
  540. package/dist/commands/validate.js +139 -94
  541. package/dist/commands/validate.js.map +1 -1
  542. package/dist/commands/verify.d.ts +121 -110
  543. package/dist/commands/verify.d.ts.map +1 -1
  544. package/dist/commands/verify.js +230 -294
  545. package/dist/commands/verify.js.map +1 -1
  546. package/dist/index.d.ts +1 -1
  547. package/dist/index.d.ts.map +1 -1
  548. package/dist/index.js +1 -1
  549. package/dist/index.js.map +1 -1
  550. package/dist/report-schemas.d.ts +12398 -35
  551. package/dist/report-schemas.d.ts.map +1 -1
  552. package/dist/report-schemas.js +624 -66
  553. package/dist/report-schemas.js.map +1 -1
  554. package/dist/skill-resolution/packaging-config.js +2 -2
  555. package/dist/skill-resolution/packaging-config.js.map +1 -1
  556. package/dist/skill-resolution/resolve-skill-reference.d.ts.map +1 -1
  557. package/dist/skill-resolution/resolve-skill-reference.js +20 -13
  558. package/dist/skill-resolution/resolve-skill-reference.js.map +1 -1
  559. package/dist/utils/agent-discovery.d.ts +34 -1
  560. package/dist/utils/agent-discovery.d.ts.map +1 -1
  561. package/dist/utils/agent-discovery.js +80 -38
  562. package/dist/utils/agent-discovery.js.map +1 -1
  563. package/dist/utils/claude-context-lens.d.ts +13 -6
  564. package/dist/utils/claude-context-lens.d.ts.map +1 -1
  565. package/dist/utils/claude-context-lens.js +11 -12
  566. package/dist/utils/claude-context-lens.js.map +1 -1
  567. package/dist/utils/command-error-codes.d.ts +12 -0
  568. package/dist/utils/command-error-codes.d.ts.map +1 -0
  569. package/dist/utils/command-error-codes.js +12 -0
  570. package/dist/utils/command-error-codes.js.map +1 -0
  571. package/dist/utils/command-refusal.d.ts +58 -0
  572. package/dist/utils/command-refusal.d.ts.map +1 -0
  573. package/dist/utils/command-refusal.js +170 -0
  574. package/dist/utils/command-refusal.js.map +1 -0
  575. package/dist/utils/commander-ending.d.ts +40 -0
  576. package/dist/utils/commander-ending.d.ts.map +1 -0
  577. package/dist/utils/commander-ending.js +42 -0
  578. package/dist/utils/commander-ending.js.map +1 -0
  579. package/dist/utils/config-loader.d.ts +13 -8
  580. package/dist/utils/config-loader.d.ts.map +1 -1
  581. package/dist/utils/config-loader.js +84 -45
  582. package/dist/utils/config-loader.js.map +1 -1
  583. package/dist/utils/corpus-target.d.ts +1 -1
  584. package/dist/utils/corpus-target.d.ts.map +1 -1
  585. package/dist/utils/corpus-target.js +44 -5
  586. package/dist/utils/corpus-target.js.map +1 -1
  587. package/dist/utils/debug-diagnostics.d.ts +18 -0
  588. package/dist/utils/debug-diagnostics.d.ts.map +1 -0
  589. package/dist/utils/debug-diagnostics.js +23 -0
  590. package/dist/utils/debug-diagnostics.js.map +1 -0
  591. package/dist/utils/document-writer.d.ts +191 -0
  592. package/dist/utils/document-writer.d.ts.map +1 -0
  593. package/dist/utils/document-writer.js +319 -0
  594. package/dist/utils/document-writer.js.map +1 -0
  595. package/dist/utils/help-loader.d.ts +2 -2
  596. package/dist/utils/help-loader.js +2 -2
  597. package/dist/utils/install-plan.d.ts +99 -0
  598. package/dist/utils/install-plan.d.ts.map +1 -0
  599. package/dist/utils/install-plan.js +146 -0
  600. package/dist/utils/install-plan.js.map +1 -0
  601. package/dist/utils/issue-anchor.d.ts +1 -1
  602. package/dist/utils/issue-anchor.d.ts.map +1 -1
  603. package/dist/utils/issue-anchor.js.map +1 -1
  604. package/dist/utils/issue-rendering.d.ts +8 -3
  605. package/dist/utils/issue-rendering.d.ts.map +1 -1
  606. package/dist/utils/issue-rendering.js +4 -0
  607. package/dist/utils/issue-rendering.js.map +1 -1
  608. package/dist/utils/lens-population.d.ts +20 -0
  609. package/dist/utils/lens-population.d.ts.map +1 -0
  610. package/dist/utils/lens-population.js +15 -0
  611. package/dist/utils/lens-population.js.map +1 -0
  612. package/dist/utils/marketplace-io.d.ts +63 -0
  613. package/dist/utils/marketplace-io.d.ts.map +1 -0
  614. package/dist/utils/marketplace-io.js +63 -0
  615. package/dist/utils/marketplace-io.js.map +1 -0
  616. package/dist/utils/operator-notices.d.ts +97 -0
  617. package/dist/utils/operator-notices.d.ts.map +1 -0
  618. package/dist/utils/operator-notices.js +115 -0
  619. package/dist/utils/operator-notices.js.map +1 -0
  620. package/dist/utils/optional-backend.d.ts +18 -15
  621. package/dist/utils/optional-backend.d.ts.map +1 -1
  622. package/dist/utils/optional-backend.js +22 -33
  623. package/dist/utils/optional-backend.js.map +1 -1
  624. package/dist/utils/output.d.ts +5 -43
  625. package/dist/utils/output.d.ts.map +1 -1
  626. package/dist/utils/output.js +8 -63
  627. package/dist/utils/output.js.map +1 -1
  628. package/dist/utils/package-json.d.ts +3 -4
  629. package/dist/utils/package-json.d.ts.map +1 -1
  630. package/dist/utils/package-json.js +8 -8
  631. package/dist/utils/package-json.js.map +1 -1
  632. package/dist/utils/population-wiring.d.ts +31 -38
  633. package/dist/utils/population-wiring.d.ts.map +1 -1
  634. package/dist/utils/population-wiring.js +38 -32
  635. package/dist/utils/population-wiring.js.map +1 -1
  636. package/dist/utils/project-root-policy.d.ts +74 -1
  637. package/dist/utils/project-root-policy.d.ts.map +1 -1
  638. package/dist/utils/project-root-policy.js +146 -8
  639. package/dist/utils/project-root-policy.js.map +1 -1
  640. package/dist/utils/projection-lenses.d.ts +25 -7
  641. package/dist/utils/projection-lenses.d.ts.map +1 -1
  642. package/dist/utils/projection-lenses.js +20 -6
  643. package/dist/utils/projection-lenses.js.map +1 -1
  644. package/dist/utils/projection-query.d.ts +18 -11
  645. package/dist/utils/projection-query.d.ts.map +1 -1
  646. package/dist/utils/projection-query.js +47 -28
  647. package/dist/utils/projection-query.js.map +1 -1
  648. package/dist/utils/projection-store.d.ts +2 -2
  649. package/dist/utils/projection-store.d.ts.map +1 -1
  650. package/dist/utils/projection-store.js +74 -45
  651. package/dist/utils/projection-store.js.map +1 -1
  652. package/dist/utils/rag-database.d.ts +61 -0
  653. package/dist/utils/rag-database.d.ts.map +1 -0
  654. package/dist/utils/rag-database.js +178 -0
  655. package/dist/utils/rag-database.js.map +1 -0
  656. package/dist/utils/relation-limits.d.ts +7 -2
  657. package/dist/utils/relation-limits.d.ts.map +1 -1
  658. package/dist/utils/relation-limits.js +6 -1
  659. package/dist/utils/relation-limits.js.map +1 -1
  660. package/dist/utils/relativize-paths.d.ts +23 -0
  661. package/dist/utils/relativize-paths.d.ts.map +1 -1
  662. package/dist/utils/relativize-paths.js +32 -1
  663. package/dist/utils/relativize-paths.js.map +1 -1
  664. package/dist/utils/resource-loader.d.ts +78 -32
  665. package/dist/utils/resource-loader.d.ts.map +1 -1
  666. package/dist/utils/resource-loader.js +134 -66
  667. package/dist/utils/resource-loader.js.map +1 -1
  668. package/dist/utils/run-integrity.d.ts +64 -31
  669. package/dist/utils/run-integrity.d.ts.map +1 -1
  670. package/dist/utils/run-integrity.js +76 -35
  671. package/dist/utils/run-integrity.js.map +1 -1
  672. package/dist/utils/scope-locations.d.ts +7 -5
  673. package/dist/utils/scope-locations.d.ts.map +1 -1
  674. package/dist/utils/scope-locations.js +37 -12
  675. package/dist/utils/scope-locations.js.map +1 -1
  676. package/dist/utils/staging-dir.d.ts +7 -0
  677. package/dist/utils/staging-dir.d.ts.map +1 -0
  678. package/dist/utils/staging-dir.js +12 -0
  679. package/dist/utils/staging-dir.js.map +1 -0
  680. package/dist/utils/url-redact.d.ts +9 -0
  681. package/dist/utils/url-redact.d.ts.map +1 -1
  682. package/dist/utils/url-redact.js +13 -0
  683. package/dist/utils/url-redact.js.map +1 -1
  684. package/dist/utils/user-context-scanner.d.ts +3 -2
  685. package/dist/utils/user-context-scanner.d.ts.map +1 -1
  686. package/dist/utils/user-context-scanner.js +24 -4
  687. package/dist/utils/user-context-scanner.js.map +1 -1
  688. package/dist/utils/validate-help-files.d.ts.map +1 -1
  689. package/dist/utils/validate-help-files.js +8 -14
  690. package/dist/utils/validate-help-files.js.map +1 -1
  691. package/dist/utils/vat-bin-path.d.ts.map +1 -1
  692. package/dist/utils/vat-bin-path.js +3 -3
  693. package/dist/utils/vat-bin-path.js.map +1 -1
  694. package/dist/utils/verdict-helpers.d.ts.map +1 -1
  695. package/dist/utils/verdict-helpers.js +5 -5
  696. package/dist/utils/verdict-helpers.js.map +1 -1
  697. package/docs/agent.md +347 -2
  698. package/docs/audit.md +312 -89
  699. package/docs/doctor.md +65 -19
  700. package/docs/index.md +571 -20
  701. package/docs/mcp.md +64 -12
  702. package/docs/rag.md +176 -48
  703. package/docs/resources.md +257 -71
  704. package/docs/skill-test.md +148 -19
  705. package/docs/skills.md +459 -174
  706. package/package.json +17 -20
  707. package/schemas/agent-build.json +268 -0
  708. package/schemas/agent-import.json +252 -0
  709. package/schemas/agent-install.json +260 -0
  710. package/schemas/agent-installed.json +287 -0
  711. package/schemas/agent-list.json +279 -0
  712. package/schemas/agent-uninstall.json +260 -0
  713. package/schemas/agent-validate.json +279 -0
  714. package/schemas/ard-emit.json +228 -107
  715. package/schemas/audit-settings.json +472 -0
  716. package/schemas/audit.json +635 -0
  717. package/schemas/cache-clear.json +273 -0
  718. package/schemas/claude-marketplace-publish.json +286 -0
  719. package/schemas/claude-marketplace-validate.json +352 -0
  720. package/schemas/claude-org-not-implemented.json +239 -0
  721. package/schemas/claude-plugin-build.json +446 -0
  722. package/schemas/claude-plugin-install.json +291 -0
  723. package/schemas/claude-plugin-list.json +326 -0
  724. package/schemas/claude-plugin-uninstall.json +272 -0
  725. package/schemas/corpus-audit.json +635 -0
  726. package/schemas/corpus-scan.json +293 -0
  727. package/schemas/doctor.json +299 -0
  728. package/schemas/inventory.json +982 -0
  729. package/schemas/mcp-list-collections.json +268 -0
  730. package/schemas/okf-validate.json +224 -106
  731. package/schemas/orchestrator.json +299 -0
  732. package/schemas/rag-clear.json +253 -0
  733. package/schemas/rag-index.json +273 -0
  734. package/schemas/rag-query.json +365 -0
  735. package/schemas/rag-stats.json +269 -0
  736. package/schemas/resources-check.json +280 -118
  737. package/schemas/resources-query.json +333 -0
  738. package/schemas/resources-scan.json +321 -0
  739. package/schemas/resources-validate.json +332 -0
  740. package/schemas/skill-review.json +224 -106
  741. package/schemas/skill-test-configure.json +256 -0
  742. package/schemas/skill-test-run.json +296 -0
  743. package/schemas/skills-build.json +325 -0
  744. package/schemas/skills-install.json +287 -0
  745. package/schemas/skills-list.json +283 -0
  746. package/schemas/skills-package.json +270 -0
  747. package/schemas/skills-validate.json +285 -0
  748. package/dist/commands/skills/shared.d.ts +0 -29
  749. package/dist/commands/skills/shared.d.ts.map +0 -1
  750. package/dist/commands/skills/shared.js +0 -33
  751. package/dist/commands/skills/shared.js.map +0 -1
  752. package/dist/utils/command-error.d.ts +0 -189
  753. package/dist/utils/command-error.d.ts.map +0 -1
  754. package/dist/utils/command-error.js +0 -230
  755. package/dist/utils/command-error.js.map +0 -1
@@ -11,69 +11,162 @@
11
11
  * When present, copies the pre-built directory tree to ~/.claude/plugins/
12
12
  * and updates Claude's plugin registry files. When absent, falls back to
13
13
  * copying dist/skills/ to ~/.claude/skills/.
14
+ *
15
+ * Every lane resolves what it installs from (staging an archive under $TMPDIR first),
16
+ * then calls {@link planInstall} and {@link executeInstall}: ONE plan of tree changes —
17
+ * the marketplace copy, each plugin's cache, each skill, everything `vat.replaces`
18
+ * removes — and the registry edit that agrees with it, applied as one transaction.
14
19
  */
15
- import { existsSync, lstatSync, readdirSync, readFileSync, cpSync } from 'node:fs';
16
- import { mkdir, mkdtemp, rm, symlink } from 'node:fs/promises';
20
+ import { readdirSync } from 'node:fs';
21
+ import fs from 'node:fs/promises';
17
22
  import { basename } from 'node:path';
18
23
  import { readDeclaredSkillName } from '@vibe-agent-toolkit/agent-skills';
19
- import { getClaudeUserPaths, installPlugin, uninstallPlugin } from '@vibe-agent-toolkit/claude-marketplace';
20
- import { ExitCode } from '@vibe-agent-toolkit/schema';
21
- import { direntKindFollowingSync, isPathAbsentError, isSingleFsSegment, normalizedTmpdir, toForwardSlash, safePath } from '@vibe-agent-toolkit/utils';
24
+ import { getClaudeUserPaths, linkDevSkills, PLUGIN_KEY_INVALID_CODE, planPackageInstall, requirePluginInstallNames, } from '@vibe-agent-toolkit/claude-marketplace';
25
+ import { buildReport, createRegistryIssue, toFindings } from '@vibe-agent-toolkit/schema';
26
+ import { applyTreePlan, classifyFsFault, copyTree, direntKindFollowingSync, everywhere, forEachInOrder, fsBoundary, isPathAbsentError, isSingleFsSegment, isTreeChangeResidue, isVatError, pathPresent, planTreeChanges, proveTreeReadable, requireConfirmedAbsent, safePath, TREE_DEST_NOT_OWNED_CODE, withFsFault, withFsFaultSync, } from '@vibe-agent-toolkit/utils';
27
+ import { ARCHIVE_LIMITS, extractArchive } from '@vibe-agent-toolkit/utils/archive';
22
28
  import { safeExecSync } from '@vibe-agent-toolkit/utils/process';
23
- import AdmZip from 'adm-zip';
24
29
  import { Command } from 'commander';
25
- import * as tar from 'tar';
26
- import { handleCommandError } from '../../../utils/command-error.js';
30
+ import { CommandRefusalError, refusalCodeOf } from '../../../utils/command-refusal.js';
31
+ import { endWithRefusal, endWithReport, leftoverIssue, NOTHING_FINISHED } from '../../../utils/document-writer.js';
32
+ import { extractSkillZip, inStaging, installLinks, installUnit, occupiedRefusal, skillCopyChange } from '../../../utils/install-plan.js';
27
33
  import { createLogger } from '../../../utils/logger.js';
28
- import { detectSource, downloadNpmPackage, isGlobalNpmInstall, readPackageJsonVatMetadata, writeYamlHeader, } from './helpers.js';
34
+ import { requireInputPath } from '../../../utils/project-root-policy.js';
35
+ import { detectSource, downloadNpmPackage, isGlobalNpmInstall, readPackageJson, readPackageJsonVatMetadata, } from './helpers.js';
36
+ import { keptForSiblingFindings } from './kept-findings.js';
29
37
  /** Relative path within a VAT npm package to its pre-built plugin structure. */
30
38
  const PLUGIN_MARKETPLACES_SUBPATH = safePath.join('dist', '.claude', 'plugins', 'marketplaces');
31
- const PACKAGE_JSON = 'package.json';
39
+ /** `vat claude plugin install` has no `--strict`: a warning never fails it. */
40
+ const GATE = { strict: false };
41
+ /** One install source resolved per run — the `--npm-postinstall` lane included. */
42
+ const INSTALL_SOURCES = 1;
32
43
  /**
33
- * List immediate subdirectory names inside a directory, following links.
34
- * Returns empty array when the directory does not exist.
44
+ * Where the child `vat build`'s streams go. Its stdout is sent straight to
45
+ * this process's stderr (fd 2): this verb's stdout carries only its own
46
+ * report, and a PIPE would buffer the build's document under spawnSync's
47
+ * 1 MiB `maxBuffer` — a larger one kills the child with ENOBUFS.
48
+ */
49
+ const BUILD_CHILD_STDIO = ['inherit', 2, 'inherit'];
50
+ /** A fresh run of `options`, nothing done yet. */
51
+ function newInstallRun(options, logger) {
52
+ return { options, logger, dryRun: options.dryRun === true, source: undefined, skills: [], issues: [], roots: { source: [], environment: [] } };
53
+ }
54
+ /** The classifier over the run's trees: a fault's side is decided by the path it names. */
55
+ function boundaryOf(run) {
56
+ return fsBoundary({ source: run.roots.source, environment: run.roots.environment }, { origin: 'content' });
57
+ }
58
+ /**
59
+ * One tree the run is about to copy ({@link installUnit}): a staged one as it is, one in the
60
+ * operator's own directory as its real path once no link in it leads outside it.
35
61
  *
36
- * Followed on purpose: a `--dev` install puts a plugin or marketplace here AS
37
- * a symlink, and `Dirent.isDirectory()` is false for a link — so every dev
38
- * install was invisible to the listing that uninstalls, lists and re-installs.
62
+ * @param dir - The tree: a skill, a marketplace or a plugin directory
63
+ * @param side - The side the lane resolved its package on
64
+ * @param what - What the tree is, for a refusal
39
65
  */
40
- function listSubdirectories(dir) {
41
- if (!existsSync(dir))
42
- return [];
43
- return readdirSync(dir, { withFileTypes: true })
44
- .filter(d => direntKindFollowingSync(dir, d) === 'directory')
45
- .map(d => d.name);
66
+ function unitOf(run, dir, side, what) {
67
+ // A lane that installs from the operator's directory names it before it plans anything.
68
+ const [root] = run.roots.source;
69
+ if (side !== 'environment' && root === undefined)
70
+ throw new Error('install lane planned a copy before naming its source directory');
71
+ return installUnit({ root: root ?? dir, side }, dir, what);
72
+ }
73
+ /** Name the source the run installs from. */
74
+ function setSource(run, label, type, symlink = false) {
75
+ run.source = { label, type, symlink };
76
+ }
77
+ /** The run as an outcome, or `undefined` before any lane named its source. */
78
+ function outcomeOf(run) {
79
+ if (run.source === undefined)
80
+ return undefined;
81
+ return { source: run.source.label, sourceType: run.source.type, symlink: run.source.symlink, skills: run.skills, issues: run.issues };
82
+ }
83
+ /** The report's `data` for an outcome. */
84
+ function installData(outcome, dryRun) {
85
+ return { source: outcome.source, sourceType: outcome.sourceType, dryRun, symlink: outcome.symlink, skills: outcome.skills };
86
+ }
87
+ /**
88
+ * What finished before a refusal: every skill already installed, and every
89
+ * finding already made — or {@link NOTHING_FINISHED} when there is none.
90
+ */
91
+ export function installFinished(outcome, dryRun) {
92
+ if (outcome === undefined || (outcome.skills.length === 0 && outcome.issues.length === 0))
93
+ return NOTHING_FINISHED;
94
+ return { examined: INSTALL_SOURCES, findings: toFindings(outcome.issues), data: installData(outcome, dryRun) };
95
+ }
96
+ /** A source FILE argument (a .zip, a .tgz): absent is the invocation's mistake, refused the input's. */
97
+ function assertSourceFile(path) {
98
+ requireInputPath(path, path, { origin: 'argument', message: `Path does not exist: ${path}` }, everywhere('source'));
99
+ }
100
+ /** The skills `--name` selects from a package's declared list; naming one it lacks is the invocation's mistake. */
101
+ function selectSkills(skills, name, packageName) {
102
+ if (name === undefined)
103
+ return [...skills];
104
+ const selected = skills.filter((skill) => skill === name);
105
+ if (selected.length === 0) {
106
+ throw new CommandRefusalError('USAGE_INVALID', `Skill "${name}" not found in package ${packageName}. Available: ${skills.join(', ')}`);
107
+ }
108
+ return selected;
46
109
  }
47
110
  /**
48
- * Register a plugin in the Claude plugin registry, logging a warning on failure.
111
+ * List immediate subdirectory names inside a directory of the PACKAGE being
112
+ * installed, on `side` (the operator's tree, or VAT's staging when it was extracted
113
+ * there), following links. An empty array when the directory is absent — believed
114
+ * only when its parent's listing agrees; a directory the OS will not list (or an
115
+ * entry it will not stat) is a fault on `side`, naming it.
116
+ *
117
+ * Followed on purpose: a built plugin tree may hold a plugin or a skill AS a
118
+ * symlink, and `Dirent.isDirectory()` is false for a link.
119
+ *
120
+ * A tree-change leftover (`.<name>.vat-staged-*`, what a killed build leaves beside
121
+ * what it was replacing) is never one of them: it is not a marketplace, a plugin or a skill.
49
122
  */
50
- async function registerPlugin(mpName, pluginName, pluginDir, version, packageName, paths, logger) {
123
+ function listSubdirectories(dir, side, within) {
124
+ const ctx = { side, action: 'list the package directory', path: dir };
125
+ let entries;
51
126
  try {
52
- await installPlugin({
53
- marketplaceName: mpName,
54
- pluginName,
55
- pluginDir,
56
- version,
57
- source: { source: 'npm', package: packageName, version },
58
- paths,
59
- });
60
- logger.info(` Registered plugin ${pluginName}@${mpName} in Claude plugin registry`);
127
+ entries = readdirSync(dir, { withFileTypes: true });
61
128
  }
62
129
  catch (error) {
63
- logger.info(` Warning: Could not register plugin ${pluginName}: ${String(error)}`);
130
+ if (!isPathAbsentError(error))
131
+ throw classifyFsFault(error, ctx);
132
+ requireConfirmedAbsent(dir, error, { action: ctx.action, path: dir }, { follows: true, sideOf: everywhere(side) });
133
+ return [];
64
134
  }
135
+ return withFsFaultSync(ctx, () => entries.filter((d) => !isTreeChangeResidue(d.name) && direntKindFollowingSync(dir, d, within) === 'directory').map((d) => d.name));
65
136
  }
66
137
  /**
67
- * Install from a pre-built plugin tree: read package.json, copy tree, output success.
68
- * Shared between npm and local install paths to avoid duplication.
138
+ * Refuse, before anything under ~/.claude changes, a package whose plugin tree
139
+ * or `vat.replaces` names something that cannot be installed or removed: a
140
+ * plugin or marketplace directory, the package version, a replaced plugin, or a
141
+ * replaced flat skill that is not one path segment (or a dot-led version). Each
142
+ * came from the PACKAGE, so the refusal is the input's (`INPUT_UNREADABLE`), not
143
+ * the invocation's. (`vat.replaces`' SHAPE was already refused by `readPackageJson`.)
69
144
  */
70
- async function installPluginTreeAndExit(rootDir, marketplacesDir, sourceLabel, sourceType, startTime, logger, dryRun) {
71
- const pkgRaw = readFileSync(safePath.join(rootDir, PACKAGE_JSON), 'utf-8');
72
- const packageJson = JSON.parse(pkgRaw);
73
- const installedSkills = await copyPluginTree(marketplacesDir, packageJson, logger, dryRun);
74
- const duration = Date.now() - startTime;
75
- outputInstallSuccess(installedSkills, sourceLabel, sourceType, duration, logger, dryRun);
76
- process.exit(ExitCode.OK);
145
+ function assertPackagePluginNames(marketplacesDir, marketplaceNames, packageJson, version, side, within) {
146
+ const check = (names, origin) => {
147
+ try {
148
+ requirePluginInstallNames({ ...names, version });
149
+ }
150
+ catch (error) {
151
+ if (!isVatError(error, PLUGIN_KEY_INVALID_CODE))
152
+ throw error;
153
+ throw new CommandRefusalError('INPUT_UNREADABLE', `Package ${packageJson.name} cannot be installed, nothing was changed: ${error.message} (${origin}).`, { cause: error });
154
+ }
155
+ };
156
+ for (const skillName of packageJson.vat?.replaces?.flatSkills ?? []) {
157
+ if (!isSingleFsSegment(skillName)) {
158
+ throw new CommandRefusalError('INPUT_UNREADABLE', `Package ${packageJson.name} cannot be installed, nothing was changed: the legacy flat skill "${skillName}" it ` +
159
+ 'replaces (package.json vat.replaces.flatSkills) must be a single path segment (no separators, not "." or "..").');
160
+ }
161
+ }
162
+ for (const marketplaceName of marketplaceNames) {
163
+ for (const pluginName of listSubdirectories(safePath.join(marketplacesDir, marketplaceName, 'plugins'), side, within)) {
164
+ check({ marketplaceName, pluginName }, 'its built plugin tree and package.json version');
165
+ }
166
+ for (const pluginName of packageJson.vat?.replaces?.plugins ?? []) {
167
+ check({ marketplaceName, pluginName }, 'package.json vat.replaces.plugins');
168
+ }
169
+ }
77
170
  }
78
171
  /**
79
172
  * Convert a skill name to a filesystem-safe path segment.
@@ -95,7 +188,7 @@ function skillNameToFsPath(name) {
95
188
  */
96
189
  export function assertSkillEntryName(name, origin) {
97
190
  if (!isSingleFsSegment(name)) {
98
- throw new Error(`Refusing to install "${name}" (${origin}): a skill name must be a single path segment ` +
191
+ throw new CommandRefusalError('INPUT_UNREADABLE', `Refusing to install "${name}" (${origin}): a skill name must be a single path segment ` +
99
192
  `(no separators, not "." or "..").`);
100
193
  }
101
194
  return name;
@@ -107,7 +200,7 @@ export function createPluginInstallCommand() {
107
200
  .argument('[source]', 'Source to install from (npm:package, ZIP file, or directory path)')
108
201
  .option('-s, --skills-dir <path>', 'Claude skills directory', getClaudeUserPaths().skillsDir)
109
202
  .option('-n, --name <name>', 'Custom name for installed skill (default: auto-detect from source)')
110
- .option('-f, --force', 'Overwrite existing skill if present', false)
203
+ .option('-f, --force', 'Overwrite an existing skill, or a marketplace directory VAT did not install from this package', false)
111
204
  .option('--dry-run', 'Preview installation without creating files', false)
112
205
  .option('--npm-postinstall', 'Run as npm postinstall hook (internal use)', false)
113
206
  .option('-d, --dev', 'Development mode: symlink skills from dist/skills/ (rebuilds reflected immediately)')
@@ -125,6 +218,29 @@ Description:
125
218
  the pre-built directory tree is copied to ~/.claude/plugins/ (dumb copy).
126
219
  Otherwise, falls back to copying dist/skills/ to ~/.claude/skills/.
127
220
 
221
+ A marketplace directory already at ~/.claude/plugins/marketplaces/<name> is
222
+ replaced only when VAT installed it from this same package (its .vat-marketplace
223
+ marker says so; for an install made by a VAT older than the marker, the
224
+ known_marketplaces.json entry naming this package does). Anything else there — a
225
+ marketplace Claude Code added, or one another package installed — is refused
226
+ with nothing changed; --force replaces it. So is a marketplace name whose
227
+ known_marketplaces.json entry another source registered, with no directory
228
+ (or an empty one) there.
229
+
230
+ Archives (.zip, .tgz, an npm tarball) are judged whole before anything is
231
+ written: one holding a symbolic or hard link, a device, a name that leaves the
232
+ archive (absolute, ".."), one name twice (or twice once letter case is
233
+ folded), or more than 50,000 entries / 256 MiB expanded is refused. A skill
234
+ ZIP holds SKILL.md at its top level, or exactly one top-level folder with
235
+ SKILL.md directly inside. A local directory may hold links, but only
236
+ relative ones to something that exists inside the plugin or skill being
237
+ installed, reached through no other link.
238
+
239
+ One transaction: the marketplace copy, each plugin's cache, each skill, the
240
+ registry files and everything vat.replaces removes change together, or not at
241
+ all. --dry-run prints the plan, one line per change ([dry-run] create|replace|
242
+ remove|keep|subsumed <what> <path>), and changes nothing.
243
+
128
244
  Supported sources:
129
245
  - npm package: npm:@scope/package-name
130
246
  - Local ZIP file: ./path/to/skill.zip
@@ -132,15 +248,44 @@ Description:
132
248
  - npm postinstall: --npm-postinstall (automatic during global install)
133
249
  - Dev mode: --dev (symlinks from dist/skills/)
134
250
 
135
- Output:
136
- - status: success/error
137
- - source: Original source
138
- - sourceType: npm/local/zip/npm-postinstall/dev
251
+ Output (YAML report on stdout):
252
+ - status: ok, findings (a warning below), or error when the run could not install
253
+ - examined: install sources resolved (1; a --npm-postinstall that skips is ok, skills: [])
254
+ - data.source / data.sourceType: what was installed from (npm/local/zip/tgz/dev/npm-postinstall)
255
+ - data.dryRun, data.symlink (true for --dev)
256
+ - data.skills[]: { name, installPath, sourcePath } — sourcePath is the --dev link target, else null
257
+ - findings: COMPONENT_DECLARED_BUT_MISSING (warning) for a --dev plugin skill whose build is missing;
258
+ TREE_CLEANUP_INCOMPLETE (warning), its link the path, when the install is complete but a
259
+ previous tree it replaced (left as .<name>.vat-staged-*.previous beside it) or its
260
+ $TMPDIR staging directory could not be removed;
261
+ PLUGIN_KEPT_SIBLING_UNEXAMINED (warning), its link the directory, for a plugin
262
+ vat.replaces kept because a directory it may be could not be examined
139
263
 
140
264
  Exit Codes:
141
- 0 - Installation successful
142
- 1 - Installation error (invalid source, skill exists, etc.)
143
- 2 - System error
265
+ 0 - Installed (a warning does not fail the run)
266
+ 2 - The run could not install: a missing or unknown source, a skill that exists
267
+ without --force (an EMPTY directory there is replaced without it), a marketplace
268
+ directory VAT did not install from this package (without --force), an unknown
269
+ --target, a plain directory with no SKILL.md (USAGE_INVALID); --target claude.ai (NOT_IMPLEMENTED); an unreadable
270
+ source (a package directory it cannot list, or a named pipe, socket or
271
+ device in the package, refused unopened, included), a link in a local
272
+ directory that leads outside what is being installed, a .zip, .tgz or
273
+ npm tarball VAT does not accept (see Archives above; not an archive,
274
+ or an entry that does not inflate, included), a ZIP not laid out
275
+ as a skill ZIP, a registry file that is not JSON, a
276
+ package whose vat.replaces is not { plugins?: string[], flatSkills?: string[] },
277
+ whose plugin or marketplace directory, version, vat.replaces.plugins or
278
+ vat.replaces.flatSkills entry is not one path segment (or whose version
279
+ begins with ".") (INPUT_UNREADABLE); npm pack failing, a full $TMPDIR during
280
+ its download included (EXTERNAL_API_FAILED); --build whose vat build failed,
281
+ a staging copy under $TMPDIR it could not create or extract into (full,
282
+ read-only, out of descriptors), a destination under ~/.claude (or -s) it
283
+ cannot examine or write (RUN_INCOMPLETE). Every such refusal changes
284
+ nothing under ~/.claude. Only a tree vat.replaces removes that the OS will
285
+ not delete once the registry is written refuses after the install
286
+ (RUN_INCOMPLETE naming it), and then lists the installed skills. A plugin
287
+ vat.replaces names that is the just-installed plugin on disk (Old -> old on a
288
+ case-insensitive filesystem) loses only its registry entry.
144
289
 
145
290
  Example:
146
291
  $ vat claude plugin install --dev # Symlink all skills from cwd
@@ -150,370 +295,464 @@ Example:
150
295
  `);
151
296
  return command;
152
297
  }
298
+ /**
299
+ * Build the report. Pure: no file system, no `process.exit`.
300
+ *
301
+ * @param outcome - What the install run did
302
+ * @param dryRun - Whether anything was actually written
303
+ * @param durationMs - How long the run took
304
+ */
305
+ export function buildPluginInstallReport(outcome, dryRun, durationMs) {
306
+ return buildReport({
307
+ examined: INSTALL_SOURCES,
308
+ findings: toFindings(outcome.issues),
309
+ data: installData(outcome, dryRun),
310
+ gate: GATE,
311
+ durationMs,
312
+ });
313
+ }
314
+ /** Refuse a `--target` this verb cannot install to. */
315
+ function assertInstallTarget(target) {
316
+ if (target === 'code')
317
+ return;
318
+ if (target === 'claude.ai') {
319
+ throw new CommandRefusalError('NOT_IMPLEMENTED', 'claude.ai org provisioning API not yet confirmed as public. Use the claude.ai admin console to upload a .zip manually, '
320
+ + 'or vat claude org skills install for workspace-scoped skill management.');
321
+ }
322
+ throw new CommandRefusalError('USAGE_INVALID', `Unsupported --target "${target}": use code (default) or claude.ai.`);
323
+ }
324
+ /** Route the invocation to the lane that installs it. */
325
+ async function runInstall(source, run) {
326
+ const { options, logger } = run;
327
+ assertInstallTarget(options.target ?? 'code');
328
+ // --build implies --dev
329
+ if (options.build || options.dev) {
330
+ await handleDevInstall(run);
331
+ return;
332
+ }
333
+ if (options.npmPostinstall) {
334
+ await handleNpmPostinstall(run);
335
+ return;
336
+ }
337
+ // Regular install - source is required
338
+ if (!source) {
339
+ throw new CommandRefusalError('USAGE_INVALID', 'Source argument required. Use npm:package, ./dir, ./file.zip or ./file.tgz');
340
+ }
341
+ const sourceType = detectSource(source);
342
+ logger.debug(`Detected source type: ${sourceType}`);
343
+ switch (sourceType) {
344
+ case 'npm':
345
+ return handleNpmInstall(source, run);
346
+ case 'local':
347
+ return handleLocalInstall(source, run);
348
+ case 'zip':
349
+ return handleZipInstall(source, run);
350
+ case 'tgz':
351
+ return handleTgzInstall(source, run);
352
+ case 'npm-postinstall':
353
+ case 'dev':
354
+ // detectSource never returns these for a positional source: each is a flag.
355
+ throw new Error(`${sourceType} source type is handled by its own flag`);
356
+ }
357
+ }
358
+ /** The human half, on stderr; a run that placed no skill gets no check mark. */
359
+ function logInstallOutcome(outcome, dryRun, logger) {
360
+ const count = outcome.skills.length;
361
+ if (count === 0) {
362
+ logger.info(dryRun ? '\n⚠️ Dry-run: the source holds no skill to install' : '\n⚠️ Nothing installed: the source holds no skill');
363
+ return;
364
+ }
365
+ if (outcome.symlink) {
366
+ logger.info(dryRun ? `\n✅ Dry-run complete: ${count} skill(s) would be symlinked` : `\n✅ Dev-installed ${count} skill(s) via symlink`);
367
+ if (!dryRun)
368
+ logger.info(` After rebuilding, run /reload-plugins in Claude Code`);
369
+ return;
370
+ }
371
+ if (dryRun) {
372
+ logger.info(`\n✅ Dry-run complete: ${count} skill(s) would be installed`);
373
+ return;
374
+ }
375
+ logger.info(`\n✅ Installed ${count} skill(s)`);
376
+ logger.info(`\n💡 Run 'vat claude plugin list' to verify installation`);
377
+ logger.info(` Restart Claude Code or run /reload-plugins to use the new skill`);
378
+ }
153
379
  async function installCommand(source, options) {
154
380
  const logger = createLogger(options.debug ? { debug: true } : {});
155
381
  const startTime = Date.now();
382
+ const run = newInstallRun(options, logger);
383
+ const { dryRun } = run;
384
+ let outcome;
156
385
  try {
157
- // Handle --target claude.ai stub
158
- const target = options.target ?? 'code';
159
- if (target === 'claude.ai') {
160
- // Write separator directly — cannot use writeYamlHeader() which outputs status: success
161
- process.stdout.write(`---\n`);
162
- process.stdout.write(`status: not-available\n`);
163
- process.stdout.write(`reason: "claude.ai org provisioning API not yet confirmed as public"\n`);
164
- process.stdout.write(`requestedTarget: claude.ai\n`);
165
- process.stdout.write(`guidance: "Use claude.ai admin console to upload a .zip manually, or use --target api.anthropic.com for workspace-scoped skill management"\n`);
166
- process.exit(ExitCode.ERROR);
167
- }
168
- // Handle --build (implies --dev)
169
- if (options.build) {
170
- options.dev = true;
171
- }
172
- // Handle --dev flag
173
- if (options.dev) {
174
- await handleDevInstall(options, logger, startTime);
175
- return;
176
- }
177
- // Handle --npm-postinstall flag
178
- if (options.npmPostinstall) {
179
- await handleNpmPostinstall(options, logger, startTime);
180
- return;
181
- }
182
- // Regular install - source is required
183
- if (!source) {
184
- throw new Error('Source argument required. Use npm:package, ./dir, or ./file.zip');
185
- }
186
- // Detect source type
187
- const sourceType = detectSource(source);
188
- logger.debug(`Detected source type: ${sourceType}`);
189
- // Route to appropriate handler
190
- switch (sourceType) {
191
- case 'npm': {
192
- await handleNpmInstall(source, options, logger, startTime);
193
- break;
194
- }
195
- case 'local': {
196
- await handleLocalInstall(source, options, logger, startTime);
197
- break;
198
- }
199
- case 'zip': {
200
- await handleZipInstall(source, options, logger, startTime);
201
- break;
202
- }
203
- case 'tgz': {
204
- await handleTgzInstall(source, options, logger, startTime);
205
- break;
206
- }
207
- case 'npm-postinstall': {
208
- throw new Error('npm-postinstall source type should be handled by --npm-postinstall flag');
209
- }
210
- case 'dev': {
211
- throw new Error('dev source type should be handled by --dev flag');
212
- }
213
- }
386
+ await runInstall(source, run);
387
+ outcome = outcomeOf(run);
388
+ // Every lane names its source before it can return; reaching here without one is a defect.
389
+ if (outcome === undefined)
390
+ throw new Error('install lane returned without naming its source');
214
391
  }
215
392
  catch (error) {
216
- handleCommandError(error, logger, startTime, 'PluginInstall');
217
- }
393
+ // A raw filesystem fault is classified by the path it names against the run's trees;
394
+ // anything already coded or classified passes through as it is.
395
+ const refusal = boundaryOf(run).classify(error, 'install the plugin', 'destination');
396
+ // Whatever installed before the refusal is still reported.
397
+ endWithRefusal('claude plugin install', refusalCodeOf(refusal), refusal, 'yaml', GATE, installFinished(outcomeOf(run), dryRun));
398
+ }
399
+ logInstallOutcome(outcome, dryRun, logger);
400
+ endWithReport('claude plugin install', buildPluginInstallReport(outcome, dryRun, Date.now() - startTime), 'yaml');
218
401
  }
219
- /**
220
- * Handle npm package installation
221
- */
222
- async function handleNpmInstall(source, options, logger, startTime) {
223
- const packageName = source.startsWith('npm:') ? source.slice(4) : source;
224
- logger.info(`📥 Installing skill from npm: ${packageName}`);
225
- // Create temp directory
226
- const tempDir = await mkdtemp(safePath.join(normalizedTmpdir(), 'vat-install-npm-'));
227
- try {
228
- // Download and extract npm package
229
- logger.info(' Downloading package...');
230
- const extractedPath = downloadNpmPackage(packageName, tempDir);
231
- // If the package ships a pre-built plugin tree, install via dumb copy.
232
- const marketplacesDir = safePath.join(extractedPath, PLUGIN_MARKETPLACES_SUBPATH);
233
- const hasPlugin = !options.userInstallWithoutPlugin && existsSync(marketplacesDir);
234
- if (hasPlugin) {
235
- logger.info(' Plugin detected — installing via Claude plugin system');
236
- await installPluginTreeAndExit(extractedPath, marketplacesDir, `npm:${packageName}`, 'npm', startTime, logger, options.dryRun);
237
- }
238
- // No plugin tree — install skills directly to ~/.claude/skills/
239
- const { skills } = await readPackageJsonVatMetadata(extractedPath);
240
- if (skills.length === 0) {
241
- throw new Error(`No skills found in package ${packageName}`);
242
- }
243
- // Filter by --name if specified
244
- const skillNames = options.name
245
- ? skills.filter(s => s === options.name)
246
- : skills;
247
- if (skillNames.length === 0) {
248
- throw new Error(`Skill "${options.name ?? ''}" not found in package ${packageName}. ` +
249
- `Available: ${skills.join(', ')}`);
250
- }
251
- const skillsDir = options.skillsDir ?? getClaudeUserPaths().skillsDir;
252
- for (const skillName of skillNames) {
253
- const skillPath = safePath.join(extractedPath, 'dist', 'skills', skillNameToFsPath(skillName));
254
- await installSkillFromPath(skillPath, skillName, options, logger);
402
+ // --- the one plan -----------------------------------------------------------------------------
403
+ /** The input kind of a package's built plugin tree. */
404
+ const PLUGIN_TREE = 'plugin-tree';
405
+ /** One skill copied into the skills directory: absent build is the invocation's mistake. */
406
+ function skillCopy(run, side, skill) {
407
+ const { path: skillPath, name: skillName, within } = skill;
408
+ // The source declares a skill whose build is absent or unreadable: absent is the
409
+ // invocation's mistake, as for every path argument; unreadable is classified on its side —
410
+ // the operator's tree, or VAT's own staging an archive was extracted into.
411
+ if (!pathPresent(skillPath, { follow: within }, everywhere(side), 'confirmed')) {
412
+ throw new CommandRefusalError('USAGE_INVALID', `Skill "${skillName}" has no build to install. Path does not exist: ${skillPath}`);
413
+ }
414
+ const skillsDir = run.options.skillsDir ?? getClaudeUserPaths().skillsDir;
415
+ // The declared name is author-controlled (SKILL.md `name:`, package.json `vat.skills[]`) and
416
+ // `--force` replaces whatever is at this path.
417
+ const installPath = safePath.join(skillsDir, assertSkillEntryName(skillName, 'skill name'));
418
+ return {
419
+ change: skillCopyChange({ name: skillName, source: unitOf(run, skillPath, side, 'the skill directory'), dest: installPath }, side, run.options.force === true),
420
+ skill: { name: skillName, installPath, sourcePath: null },
421
+ };
422
+ }
423
+ function planSkills(run, side, skills) {
424
+ const copies = skills.map((skill) => skillCopy(run, side, skill));
425
+ return { changes: copies.map((copy) => copy.change), registry: null, replaced: [], skills: copies.map((copy) => copy.skill), issues: [] };
426
+ }
427
+ /** Entries of a plugin other than its skills: what `--dev` copies (it links each skill instead). */
428
+ const notSkills = (relative) => relative !== 'skills';
429
+ /** Entries of a marketplace other than its plugins: what `--dev` copies before building each plugin. */
430
+ const notPlugins = (relative) => relative !== 'plugins';
431
+ /** Entries of a marketplace other than a plugin's skills: what a `--dev` install reads of it. */
432
+ const notPluginSkills = (relative) => !/^plugins\/[^/]+\/skills$/.test(relative);
433
+ /** The `--dev` links of one plugin: each skill its tree declares and `dist/skills/` holds; each one it does not, a finding. */
434
+ function devPlugin(plugin, rootDir) {
435
+ const skillsDir = safePath.join(plugin.srcPluginDir, 'skills');
436
+ const result = { plugin, hasSkills: pathPresent(skillsDir, { follow: rootDir }, everywhere(plugin.side), 'confirmed'), links: [], skills: [], issues: [] };
437
+ for (const name of listSubdirectories(skillsDir, plugin.side, rootDir)) {
438
+ const target = safePath.resolve(rootDir, 'dist', 'skills', name);
439
+ if (!pathPresent(target, { follow: rootDir }, everywhere(plugin.side), 'confirmed')) {
440
+ result.issues.push(createRegistryIssue('COMPONENT_DECLARED_BUT_MISSING', `Plugin "${plugin.pluginName}" declares skill "${name}", but it is not built — it was not linked. Run vat build, then re-install.`, { location: safePath.relative(rootDir, target) }));
441
+ continue;
255
442
  }
256
- const duration = Date.now() - startTime;
257
- outputInstallSuccess(skillNames.map(name => ({
258
- name,
259
- installPath: safePath.join(skillsDir, name),
260
- })), `npm:${packageName}`, 'npm', duration, logger, options.dryRun);
261
- process.exit(ExitCode.OK);
262
- }
263
- finally {
264
- await rm(tempDir, { recursive: true, force: true });
443
+ result.links.push({ name, target });
444
+ result.skills.push({ name: `${plugin.pluginName}:${name}`, installPath: safePath.join(plugin.destPluginDir, 'skills', name), sourcePath: target });
265
445
  }
446
+ return result;
266
447
  }
267
448
  /**
268
- * Handle local directory installation
449
+ * Fill `relative` under the staged directory (`''`: the directory itself) with one `--dev` plugin: its non-skill content copied, and a link per built
450
+ * skill to `dist/skills/<name>`, so a rebuild is picked up live.
269
451
  */
270
- async function handleLocalInstall(source, options, logger, startTime) {
271
- const sourcePath = safePath.resolve(source);
272
- logger.info(`📥 Installing skill from directory: ${sourcePath}`);
273
- const skillsDir = options.skillsDir ?? getClaudeUserPaths().skillsDir;
274
- // Check for pre-built plugin tree first
275
- const marketplacesDir = safePath.join(sourcePath, PLUGIN_MARKETPLACES_SUBPATH);
276
- if (!options.userInstallWithoutPlugin && existsSync(marketplacesDir)) {
277
- await installPluginTreeAndExit(sourcePath, marketplacesDir, `local:${sourcePath}`, 'local', startTime, logger, options.dryRun);
278
- }
279
- // Check if directory contains package.json with vat.skills
280
- const packageJsonPath = safePath.join(sourcePath, 'package.json');
281
- const hasPackageJson = existsSync(packageJsonPath);
282
- let installed;
283
- if (hasPackageJson) {
284
- const { packageJson, skills } = await readPackageJsonVatMetadata(sourcePath);
285
- const skillNames = options.name ? skills.filter(s => s === options.name) : skills;
286
- if (skillNames.length === 0) {
287
- throw new Error(`Skill "${options.name ?? ''}" not found in package ${packageJson.name}. ` +
288
- `Available: ${skills.join(', ')}`);
289
- }
290
- for (const skillName of skillNames) {
291
- const skillPath = safePath.join(sourcePath, 'dist', 'skills', skillNameToFsPath(skillName));
292
- await installSkillFromPath(skillPath, skillName, options, logger);
293
- }
294
- installed = skillNames.map(name => ({ name, installPath: safePath.join(skillsDir, name) }));
295
- }
296
- else {
297
- // Plain skill directory. The package.json branch above installs each skill
298
- // under its declared name; do the same here rather than under whatever the
299
- // source directory happens to be called.
300
- const skillName = options.name ??
301
- readDeclaredSkillName(safePath.join(sourcePath, 'SKILL.md')) ??
302
- basename(sourcePath);
303
- await installSkillFromPath(sourcePath, skillName, options, logger);
304
- installed = [{ name: skillName, installPath: safePath.join(skillsDir, skillName) }];
305
- }
306
- const duration = Date.now() - startTime;
307
- outputInstallSuccess(installed, `local:${sourcePath}`, 'local', duration, logger, options.dryRun);
308
- process.exit(ExitCode.OK);
452
+ async function writeDevPlugin(dev, staged, relative) {
453
+ await copyTree(dev.plugin.srcPluginDir, staged, relative, { links: installLinks(dev.plugin.side), side: dev.plugin.side, onto: 'fresh', filter: notSkills });
454
+ if (dev.hasSkills)
455
+ await linkDevSkills(relative === '' ? staged : safePath.join(staged, relative), dev.links);
456
+ }
457
+ /** The plugins one marketplace of the package ships, each as the tree its cache is copied from, with where it goes under `paths`. */
458
+ function pluginsOf(run, side, srcMpDir, marketplaceName, destMpDir, rootDir) {
459
+ return listSubdirectories(safePath.join(srcMpDir, 'plugins'), side, rootDir).map((pluginName) => ({
460
+ side,
461
+ marketplaceName,
462
+ pluginName,
463
+ // Its own unit: the cache holds this plugin alone, so a link from it into a sibling plugin would leave the copy.
464
+ srcPluginDir: unitOf(run, safePath.join(srcMpDir, 'plugins', pluginName), side, 'the plugin directory'),
465
+ destPluginDir: safePath.join(destMpDir, 'plugins', pluginName),
466
+ }));
467
+ }
468
+ /** One marketplace of a copy install: the package's tree copied whole, and each plugin's cache copied from it. */
469
+ function copiedMarketplace(marketplaceName, srcMpDir, plugins, side, rootDir) {
470
+ const skills = plugins.flatMap((plugin) => listSubdirectories(safePath.join(plugin.srcPluginDir, 'skills'), side, rootDir).map((name) => ({ name, installPath: safePath.join(plugin.destPluginDir, 'skills', name), sourcePath: null })));
471
+ return {
472
+ install: {
473
+ marketplaceName,
474
+ write: (staged) => copyTree(srcMpDir, staged, '', { links: installLinks(side), side, onto: 'fresh' }),
475
+ reads: [srcMpDir],
476
+ // A `write` fill, not a `copy`: the planner proves every `copy` source again, and the whole package was
477
+ // proven readable once already ({@link planPluginTree}). The holding check still sees it (`reads`).
478
+ plugins: plugins.map(({ pluginName, srcPluginDir }) => ({
479
+ pluginName,
480
+ cacheFill: { from: 'write', write: (staged) => copyTree(srcPluginDir, staged, '', { links: installLinks(side), side, onto: 'fresh' }), reads: [srcPluginDir] },
481
+ })),
482
+ },
483
+ skills,
484
+ issues: [],
485
+ };
486
+ }
487
+ /** One marketplace of a `--dev` install: its non-plugin content copied, each plugin built with links, the cache built the same way. */
488
+ function devMarketplace(marketplaceName, srcMpDir, plugins, rootDir, side) {
489
+ const linked = plugins.map((plugin) => devPlugin(plugin, rootDir));
490
+ return {
491
+ install: {
492
+ marketplaceName,
493
+ write: async (staged) => {
494
+ await copyTree(srcMpDir, staged, '', { links: installLinks(side), side, onto: 'fresh', filter: notPlugins });
495
+ // In order: each plugin is built into the one staged tree, and the first refusal stops the fill.
496
+ await forEachInOrder(linked, (dev) => writeDevPlugin(dev, staged, `plugins/${dev.plugin.pluginName}`));
497
+ },
498
+ reads: [srcMpDir],
499
+ plugins: linked.map((dev) => ({
500
+ pluginName: dev.plugin.pluginName,
501
+ cacheFill: { from: 'write', write: (staged) => writeDevPlugin(dev, staged, ''), reads: [dev.plugin.srcPluginDir] },
502
+ })),
503
+ },
504
+ skills: linked.flatMap((each) => each.skills),
505
+ issues: linked.flatMap((each) => each.issues),
506
+ };
309
507
  }
310
508
  /**
311
- * Handle ZIP file installation
509
+ * Plan a plugin-tree install: every marketplace the package ships replaced whole (with
510
+ * VAT's marker), each plugin's cache, each `vat.replaces` plugin's cache and flat skill
511
+ * removed, and the registry edit — one plan. Every name is checked and the whole source
512
+ * proven readable before anything is decided. A marketplace directory already there is
513
+ * replaced only when VAT installed it from this very package, or under `force` (`--force`).
312
514
  */
313
- async function handleZipInstall(source, options, logger, startTime) {
314
- const sourcePath = safePath.resolve(source);
315
- logger.info(`📥 Installing skill from ZIP: ${sourcePath}`);
316
- if (!existsSync(sourcePath)) {
317
- throw new Error(`ZIP file not found: ${sourcePath}`);
318
- }
319
- const skillName = options.name ?? basename(sourcePath, '.zip');
320
- const { installPath } = await prepareInstallation(skillName, options);
321
- if (!options.dryRun) {
322
- logger.info(' Extracting ZIP...');
323
- const zip = new AdmZip(sourcePath);
324
- zip.extractAllTo(installPath, /* overwrite */ true);
325
- }
326
- const duration = Date.now() - startTime;
327
- outputInstallSuccess([{ name: skillName, installPath }], sourcePath, 'zip', duration, logger, options.dryRun);
328
- process.exit(ExitCode.OK);
515
+ async function planPluginTree(run, input, mode) {
516
+ const { rootDir, packageJson, side } = input;
517
+ const force = run.options.force === true;
518
+ const marketplacesDir = safePath.join(rootDir, PLUGIN_MARKETPLACES_SUBPATH);
519
+ const paths = getClaudeUserPaths();
520
+ const version = packageJson.version ?? '0.0.0';
521
+ const marketplaceNames = listSubdirectories(marketplacesDir, side, rootDir);
522
+ assertPackagePluginNames(marketplacesDir, marketplaceNames, packageJson, version, side, rootDir);
523
+ // Each marketplace as the tree it is copied from, before anything reads it: one in the operator's
524
+ // own directory with a link leading out of it is the package's refusal (INPUT_UNREADABLE).
525
+ const marketplaces = marketplaceNames.map((mpName) => ({ mpName, srcMpDir: unitOf(run, safePath.join(marketplacesDir, mpName), side, 'the marketplace directory') }));
526
+ // Every file of the package, before anything changes — dry run or real: a file the
527
+ // install cannot read is the package's refusal (INPUT_UNREADABLE).
528
+ const proof = mode === 'dev' ? { links: installLinks(side), side, filter: notPluginSkills } : { links: installLinks(side), side };
529
+ // In order: the first unreadable entry named is the first marketplace's, deterministically.
530
+ await forEachInOrder(marketplaces, ({ srcMpDir }) => proveTreeReadable(srcMpDir, proof));
531
+ const built = marketplaces.map(({ mpName, srcMpDir }) => {
532
+ const plugins = pluginsOf(run, side, srcMpDir, mpName, safePath.join(paths.marketplacesDir, mpName), rootDir);
533
+ return mode === 'dev' ? devMarketplace(mpName, srcMpDir, plugins, rootDir, side) : copiedMarketplace(mpName, srcMpDir, plugins, side, rootDir);
534
+ });
535
+ const replaces = packageJson.vat?.replaces;
536
+ const shipped = new Set(built.flatMap(({ install }) => install.plugins.map(({ pluginName }) => `${pluginName}@${install.marketplaceName}`)));
537
+ const planned = planPackageInstall({
538
+ marketplaces: built.map((each) => each.install),
539
+ version,
540
+ source: { source: 'npm', package: packageJson.name, version },
541
+ replacedPluginKeys: marketplaceNames.flatMap((mp) => (replaces?.plugins ?? []).map((plugin) => `${plugin}@${mp}`)),
542
+ force,
543
+ paths,
544
+ });
545
+ const flatSkills = (replaces?.flatSkills ?? []).map((name) => ({
546
+ op: 'remove',
547
+ dest: safePath.join(paths.skillsDir, assertSkillEntryName(name, 'vat.replaces.flatSkills')),
548
+ // The package says it replaces this legacy install: whatever is there goes.
549
+ ownership: { kind: 'force' },
550
+ label: `legacy flat skill ${name}`,
551
+ }));
552
+ return {
553
+ changes: [...planned.changes, ...flatSkills],
554
+ registry: planned.registry,
555
+ replaced: planned.replaced.map((each) => ({ ...each, reinstalled: shipped.has(each.pluginKey) })),
556
+ skills: built.flatMap((each) => each.skills),
557
+ issues: built.flatMap((each) => each.issues),
558
+ };
329
559
  }
330
560
  /**
331
- * Handle npm tarball (.tgz / .tar.gz) installation.
332
- * npm pack format: all package files are under a `package/` subdirectory in the tarball.
333
- * Extracts to a temp directory then delegates to the local install path.
561
+ * Plan an install of `input`, with no side effect: the tree changes, the registry edit,
562
+ * and what the run reports once they are done. `mode` is `dev` only for a plugin tree
563
+ * whose skills are linked, not copied (`--dev`).
334
564
  */
335
- async function handleTgzInstall(source, options, logger, startTime) {
336
- const sourcePath = safePath.resolve(source);
337
- logger.info(`📥 Installing skill from tarball: ${sourcePath}`);
338
- if (!existsSync(sourcePath)) {
339
- throw new Error(`Tarball not found: ${sourcePath}`);
340
- }
341
- const tempDir = await mkdtemp(safePath.join(normalizedTmpdir(), 'vat-install-tgz-'));
342
- try {
343
- logger.info(' Extracting tarball...');
344
- await tar.extract({ file: sourcePath, cwd: tempDir });
345
- // npm pack tarballs extract under package/ subdirectory
346
- const packageDir = safePath.join(tempDir, 'package');
347
- const extractedDir = existsSync(packageDir) ? packageDir : tempDir;
348
- // Delegate to local install logic
349
- const marketplacesDir = safePath.join(extractedDir, PLUGIN_MARKETPLACES_SUBPATH);
350
- if (!options.userInstallWithoutPlugin && existsSync(marketplacesDir)) {
351
- await installPluginTreeAndExit(extractedDir, marketplacesDir, sourcePath, 'tgz', startTime, logger, options.dryRun);
352
- }
353
- // Fallback: skills-only install
354
- const { skills } = await readPackageJsonVatMetadata(extractedDir);
355
- const skillsDir = options.skillsDir ?? getClaudeUserPaths().skillsDir;
356
- const skillNames = options.name ? skills.filter(s => s === options.name) : skills;
357
- for (const skillName of skillNames) {
358
- const skillPath = safePath.join(extractedDir, 'dist', 'skills', skillNameToFsPath(skillName));
359
- await installSkillFromPath(skillPath, skillName, options, logger);
565
+ function planInstall(run, input, mode) {
566
+ if (input.kind === PLUGIN_TREE)
567
+ return planPluginTree(run, input, mode);
568
+ // A skills lane plans synchronously; a refusal still rejects the returned promise.
569
+ return Promise.resolve().then(() => planSkills(run, input.side, input.kind === 'skills'
570
+ ? input.skillNames.map((name) => ({ path: safePath.join(input.rootDir, 'dist', 'skills', skillNameToFsPath(name)), name, within: input.rootDir }))
571
+ // The skill directory IS the input (the operator's own, or VAT's staging of an archive): its own `within`.
572
+ : [{ path: input.skillPath, name: input.skillName, within: input.skillPath }]));
573
+ }
574
+ /** A package directory's plugin tree, as the input of {@link planInstall}. */
575
+ function pluginTree(side, rootDir, packageJson) {
576
+ return { kind: PLUGIN_TREE, side, rootDir, packageJson };
577
+ }
578
+ /** What the decided plan did with each replaced plugin: a warning per directory kept, and a finding per one kept for an unexaminable sibling. */
579
+ function replacedOutcome(run, planned, plan) {
580
+ const decided = planned.replaced.flatMap(({ pluginKey, index, reinstalled }) => {
581
+ const change = plan.changes[index];
582
+ return change === undefined ? [] : [{ pluginKey, change, reinstalled }];
583
+ });
584
+ for (const { pluginKey, change, reinstalled } of decided) {
585
+ // A key the package installs again stays registered: nothing to warn about.
586
+ if (change.action === 'keep' && change.existing !== 'absent' && !reinstalled) {
587
+ run.logger.warn(` Plugin "${pluginKey}" is removed from the registry, but ${change.change.dest} is kept (${change.reason ?? 'kept'})`);
360
588
  }
361
- const duration = Date.now() - startTime;
362
- outputInstallSuccess(skillNames.map(name => ({ name, installPath: safePath.join(skillsDir, name) })), sourcePath, 'tgz', duration, logger, options.dryRun);
363
- process.exit(ExitCode.OK);
364
- }
365
- finally {
366
- await rm(tempDir, { recursive: true, force: true });
367
589
  }
590
+ return keptForSiblingFindings(decided.flatMap(({ change }) => (change.action === 'keep' && change.unexaminedSibling !== undefined ? [{ path: change.change.dest, sibling: change.unexaminedSibling }] : [])));
591
+ }
592
+ /** Record what the run did: called once the transaction committed, or once a dry run is planned. */
593
+ function record(run, planned, findings) {
594
+ run.skills.push(...planned.skills);
595
+ run.issues.push(...planned.issues, ...findings);
368
596
  }
369
597
  /**
370
- * Prepare destination for a dev symlink: remove any existing entry if --force.
371
- * Throws if the path exists and --force was not passed.
598
+ * Carry out a planned install as ONE transaction: every tree staged and swapped, then the
599
+ * registry written (`afterSwap`); a failure before the registry is written puts every tree
600
+ * and registry file back. Under `--dry-run` the plan's lines are printed and nothing changes.
601
+ * What the run installed is recorded the moment the registry is written, so a failure after
602
+ * that (a replaced tree the OS will not delete) reports it.
372
603
  */
373
- async function prepareDevSymlinkDest(destPath, skillFsName, options) {
374
- // The probe is the ONLY thing whose failure may read as "nothing there". A
375
- // refusal on the probe, or a failed removal, stays loud: this used to catch
376
- // everything and recognise its own throw by the words in its message.
604
+ async function executeInstall(run, planned) {
605
+ let plan;
377
606
  try {
378
- lstatSync(destPath);
607
+ plan = await planTreeChanges(planned.changes);
379
608
  }
380
609
  catch (error) {
381
- if (isPathAbsentError(error))
382
- return;
383
- throw error;
384
- }
385
- if (!options.force) {
386
- throw new Error(`Skill "${skillFsName}" already installed at ${destPath}.\n` +
387
- `Use --force to overwrite.`);
388
- }
389
- if (!options.dryRun) {
390
- await rm(destPath, { recursive: true, force: true });
391
- }
610
+ throw notOwnedRefusal(occupiedRefusal(error));
611
+ }
612
+ const findings = replacedOutcome(run, planned, plan);
613
+ const prefix = run.dryRun ? '[dry-run] ' : '';
614
+ for (const line of plan.describe())
615
+ run.logger.info(` ${prefix}${line}`);
616
+ if (run.dryRun) {
617
+ record(run, planned, findings);
618
+ return;
619
+ }
620
+ const { warnings } = await applyTreePlan(plan, {
621
+ afterSwap: async () => {
622
+ await planned.registry?.apply();
623
+ record(run, planned, findings);
624
+ },
625
+ });
626
+ for (const { path, message } of warnings)
627
+ leftover(run, leftoverIssue(message, path));
628
+ }
629
+ /** A marketplace directory VAT cannot prove it installed from this package, as the invocation's refusal saying how to proceed; anything else as itself. */
630
+ function notOwnedRefusal(error) {
631
+ if (!isVatError(error, TREE_DEST_NOT_OWNED_CODE))
632
+ return error;
633
+ return new CommandRefusalError('USAGE_INVALID', `${error.message}. Nothing was changed. Uninstall what is there first — \`vat claude plugin uninstall <plugin>@<marketplace>\` when VAT installed it, Claude Code's /plugin when it did — or pass --force to replace it.`, { cause: error });
634
+ }
635
+ /** A leftover of a complete install, as a warning naming it. */
636
+ function leftover(run, issue) {
637
+ run.logger.warn(` ${issue.message}`);
638
+ run.issues.push(issue);
639
+ }
640
+ /** Plan and execute `input`: every lane's last step. */
641
+ async function install(run, input, mode) {
642
+ await executeInstall(run, await planInstall(run, input, mode));
643
+ }
644
+ /** Run `work` in a fresh `$TMPDIR` staging directory (VAT's own: `environment`), disposed of after: see {@link inStaging}. */
645
+ function stagedIn(run, prefix, work) {
646
+ return inStaging(prefix, async (dir) => {
647
+ run.roots.environment.push(dir);
648
+ await work(dir);
649
+ }, (issue) => leftover(run, issue));
392
650
  }
651
+ // --- the lanes --------------------------------------------------------------------------------
393
652
  /**
394
- * Symlink a single skill directory from dist/skills/{name} into the plugin tree.
395
- * Returns install info, or null when the skill is not built (skips with a warning).
653
+ * The package at `rootDir`, as `readPackageJson` is told of it: its side, and what the user named —
654
+ * the directory itself, or, where `rootDir` is VAT's staging (`environment`: a staging directory
655
+ * under $TMPDIR names nothing they have), the archive or npm spec the run was given.
396
656
  */
397
- async function symlinkDevSkill(skillFsName, destSkillsDir, cwd, pluginName, options, logger) {
398
- const srcSkillPath = safePath.resolve(cwd, 'dist', 'skills', skillFsName);
399
- const destSkillPath = safePath.join(destSkillsDir, skillFsName);
400
- if (!existsSync(srcSkillPath)) {
401
- logger.info(` Warning: skill not built at ${srcSkillPath} — skipping symlink`);
402
- return null;
403
- }
404
- await prepareDevSymlinkDest(destSkillPath, skillFsName, options);
405
- if (!options.dryRun) {
406
- try {
407
- // eslint-disable-next-line local/no-bare-symlink-in-tests -- eyes open: `handleDevInstall` refuses win32 upstream, so the Windows privilege hazard the rule names cannot be reached here.
408
- await symlink(srcSkillPath, destSkillPath, 'dir');
409
- }
410
- catch (error) {
411
- // ⚠️ POSIX-only in practice: `handleDevInstall` throws on win32 before
412
- // this call chain begins, so Windows-specific handling here would be dead
413
- // code — an earlier revision added exactly that. Making `--dev` work on
414
- // Windows means changing that guard (a junction needs no elevation), not
415
- // this catch. Not falling back to a copy either — dev mode exists so a
416
- // rebuild is picked up live, and a copy would quietly stop that.
417
- //
418
- // ⚠️ This throws mid-loop, after `devInstallMarketplace` already removed
419
- // the previous marketplace directory and `devInstallPlugin` copied the
420
- // non-skill content. Earlier skills are linked, later ones are not, and
421
- // `registerPlugin` never runs — so the tree is on disk but unregistered.
422
- // Recoverable by re-running, which starts over.
423
- const detail = error instanceof Error ? error.message : String(error);
424
- throw new Error(`Could not symlink ${skillFsName} to ${destSkillPath}: ${detail}\n` +
425
- 'Check that the destination directory is writable. This install is now ' +
426
- 'incomplete — re-run the same command to start over.');
427
- }
428
- }
429
- logger.info(` Symlinked: ${safePath.relative(cwd, destSkillPath)} → ${srcSkillPath}`);
430
- return {
431
- name: `${pluginName}:${skillFsName}`,
432
- installPath: destSkillPath,
433
- sourcePath: srcSkillPath,
434
- };
657
+ function packageOf(run, rootDir, side) {
658
+ const named = side === 'environment' && run.source !== undefined ? `The package ${run.source.label}` : `The package directory ${rootDir}`;
659
+ return { side, label: named };
660
+ }
661
+ /** A package directory's install: its plugin tree, or (none, or `--user-install-without-plugin`) its declared skills. */
662
+ async function installPackageDir(run, rootDir, side) {
663
+ if (run.options.userInstallWithoutPlugin !== true && pathPresent(safePath.join(rootDir, PLUGIN_MARKETPLACES_SUBPATH), { follow: rootDir }, everywhere(side), 'confirmed')) {
664
+ run.logger.info(' Plugin detected — installing via Claude plugin system');
665
+ await install(run, pluginTree(side, rootDir, await readPackageJson(rootDir, packageOf(run, rootDir, side))), 'copy');
666
+ return;
667
+ }
668
+ const { packageJson, skills } = await readPackageJsonVatMetadata(rootDir, packageOf(run, rootDir, side));
669
+ await install(run, { kind: 'skills', side, rootDir, skillNames: selectSkills(skills, run.options.name, packageJson.name) }, 'copy');
435
670
  }
436
671
  /**
437
- * Symlink all skill directories from a plugin's source skills dir into the destination.
672
+ * Handle npm package installation
438
673
  */
439
- async function symlinkPluginSkills(srcPluginDir, destPluginDir, pluginName, cwd, options, logger) {
440
- const installed = [];
441
- const srcSkillsDir = safePath.join(srcPluginDir, 'skills');
442
- if (!existsSync(srcSkillsDir)) {
443
- return installed;
444
- }
445
- const destSkillsDir = safePath.join(destPluginDir, 'skills');
446
- if (!options.dryRun) {
447
- await mkdir(destSkillsDir, { recursive: true });
448
- }
449
- // Followed: a skill directory that is itself a link is still a skill to link.
450
- const skillEntries = readdirSync(srcSkillsDir, { withFileTypes: true })
451
- .filter(d => direntKindFollowingSync(srcSkillsDir, d) === 'directory');
452
- for (const skillEntry of skillEntries) {
453
- const result = await symlinkDevSkill(skillEntry.name, destSkillsDir, cwd, pluginName, options, logger);
454
- if (result) {
455
- installed.push(result);
456
- }
457
- }
458
- return installed;
674
+ async function handleNpmInstall(source, run) {
675
+ const { logger } = run;
676
+ const packageName = source.startsWith('npm:') ? source.slice(4) : source;
677
+ setSource(run, `npm:${packageName}`, 'npm');
678
+ logger.info(`📥 Installing skill from npm: ${packageName}`);
679
+ await stagedIn(run, 'vat-install-npm-', async (tempDir) => {
680
+ logger.info(' Downloading package...');
681
+ const extractedPath = await downloadNpmPackage(packageName, tempDir);
682
+ await installPackageDir(run, extractedPath, 'environment');
683
+ });
459
684
  }
460
685
  /**
461
- * Dev-install a single plugin: copy non-skill content, symlink skills, register.
686
+ * Handle local directory installation
462
687
  */
463
- async function devInstallPlugin(ctx, cwd, options, logger) {
464
- const { mpName, pluginName, srcPluginDir, destPluginDir, packageName, version, paths } = ctx;
465
- if (!options.dryRun) {
466
- await mkdir(destPluginDir, { recursive: true });
467
- }
468
- // Copy non-skill entries (e.g. .claude-plugin/)
469
- for (const entry of readdirSync(srcPluginDir, { withFileTypes: true })) {
470
- if (entry.name !== 'skills' && !options.dryRun) {
471
- const srcEntry = safePath.join(srcPluginDir, entry.name);
472
- const destEntry = safePath.join(destPluginDir, entry.name);
473
- cpSync(srcEntry, destEntry, { recursive: true, force: true });
474
- }
475
- }
476
- const installed = await symlinkPluginSkills(srcPluginDir, destPluginDir, pluginName, cwd, options, logger);
477
- // Register plugin in Claude plugin registry
478
- if (!options.dryRun) {
479
- await registerPlugin(mpName, pluginName, destPluginDir, version, packageName, paths, logger);
480
- }
481
- return installed;
688
+ async function handleLocalInstall(source, run) {
689
+ const { options, logger } = run;
690
+ const sourcePath = safePath.resolve(source); // The operator's own argument: every probe beneath it passes it as `within`.
691
+ setSource(run, `local:${sourcePath}`, 'local');
692
+ run.roots.source.push(sourcePath);
693
+ logger.info(`📥 Installing skill from directory: ${sourcePath}`);
694
+ // A package: its pre-built plugin tree, or each skill its vat.skills declares.
695
+ const marketplacesDir = safePath.join(sourcePath, PLUGIN_MARKETPLACES_SUBPATH);
696
+ if ((!options.userInstallWithoutPlugin && pathPresent(marketplacesDir, { follow: sourcePath }, everywhere('source'), 'confirmed')) || pathPresent(safePath.join(sourcePath, 'package.json'), { follow: sourcePath }, everywhere('source'), 'confirmed')) {
697
+ await installPackageDir(run, sourcePath, 'source');
698
+ return;
699
+ }
700
+ // Plain skill directory: it must hold a SKILL.md, or any directory would
701
+ // install as a skill. A source that names nothing keeps its own refusal below.
702
+ if (pathPresent(sourcePath, { follow: sourcePath }, everywhere('source'), 'confirmed') && !pathPresent(safePath.join(sourcePath, 'SKILL.md'), { follow: sourcePath }, everywhere('source'), 'confirmed')) {
703
+ throw new CommandRefusalError('USAGE_INVALID', `No SKILL.md found at the root of: ${sourcePath}`);
704
+ }
705
+ // The package.json branch above installs each skill under its declared name;
706
+ // do the same here rather than under whatever the source directory is called.
707
+ const skillName = options.name ??
708
+ readDeclaredSkillName(safePath.join(sourcePath, 'SKILL.md')) ??
709
+ basename(sourcePath);
710
+ await install(run, { kind: 'skill-dir', side: 'source', skillPath: sourcePath, skillName }, 'copy');
482
711
  }
483
712
  /**
484
- * Dev-install a single marketplace: reset dir, copy non-plugin content, install each plugin.
713
+ * Handle ZIP file installation
485
714
  */
486
- async function devInstallMarketplace(mpName, srcMpDir, packageInfo, cwd, options, logger) {
487
- const paths = getClaudeUserPaths();
488
- const destMpDir = safePath.join(paths.marketplacesDir, mpName);
489
- const installed = [];
490
- if (!options.dryRun) {
491
- await rm(destMpDir, { recursive: true, force: true });
492
- await mkdir(destMpDir, { recursive: true });
493
- }
494
- logger.info(` Marketplace: ${mpName} → ${destMpDir}`);
495
- // Copy non-plugin content (e.g. .claude-plugin/marketplace.json)
496
- for (const entry of readdirSync(srcMpDir, { withFileTypes: true })) {
497
- if (entry.name !== 'plugins' && !options.dryRun) {
498
- const srcEntry = safePath.join(srcMpDir, entry.name);
499
- const destEntry = safePath.join(destMpDir, entry.name);
500
- cpSync(srcEntry, destEntry, { recursive: true, force: true });
501
- }
502
- }
503
- const pluginsDir = safePath.join(srcMpDir, 'plugins');
504
- for (const pluginName of listSubdirectories(pluginsDir)) {
505
- const results = await devInstallPlugin({
506
- mpName,
507
- pluginName,
508
- srcPluginDir: safePath.join(pluginsDir, pluginName),
509
- destPluginDir: safePath.join(destMpDir, 'plugins', pluginName),
510
- packageName: packageInfo.name,
511
- version: packageInfo.version,
512
- paths,
513
- }, cwd, options, logger);
514
- installed.push(...results);
515
- }
516
- return installed;
715
+ async function handleZipInstall(source, run) {
716
+ const { options, logger } = run;
717
+ const sourcePath = safePath.resolve(source);
718
+ setSource(run, sourcePath, 'zip');
719
+ run.roots.source.push(sourcePath);
720
+ logger.info(`📥 Installing skill from ZIP: ${sourcePath}`);
721
+ assertSourceFile(sourcePath);
722
+ // Extracted to staging first, through the one archive primitive: an archive it refuses — an
723
+ // entry that does not inflate, a link, two entries one name — is refused with nothing under
724
+ // ~/.claude changed.
725
+ await stagedIn(run, 'vat-install-zip-', async (tempDir) => {
726
+ logger.info(' Extracting ZIP...');
727
+ const extracted = safePath.join(tempDir, 'skill');
728
+ await withFsFault({ side: 'environment', action: 'create the staging directory', path: extracted }, () => fs.mkdir(extracted));
729
+ // The one extraction of a skill ZIP, shared with `vat skills install` and the org upload: the
730
+ // skill is at the top level or in the single top-level folder, installed under the name its SKILL.md declares.
731
+ const skillPath = await extractSkillZip(sourcePath, extracted);
732
+ const skillName = options.name ?? readDeclaredSkillName(safePath.join(skillPath, 'SKILL.md')) ?? basename(sourcePath, '.zip');
733
+ await install(run, { kind: 'skill-dir', side: 'environment', skillPath, skillName }, 'copy');
734
+ });
735
+ }
736
+ /**
737
+ * Handle npm tarball (.tgz / .tar.gz) installation.
738
+ * npm pack format: all package files are under a `package/` subdirectory in the tarball.
739
+ * Extracts to a temp directory then installs the plugin tree, or the declared skills.
740
+ */
741
+ async function handleTgzInstall(source, run) {
742
+ const { logger } = run;
743
+ const sourcePath = safePath.resolve(source);
744
+ setSource(run, sourcePath, 'tgz');
745
+ run.roots.source.push(sourcePath);
746
+ logger.info(`📥 Installing skill from tarball: ${sourcePath}`);
747
+ assertSourceFile(sourcePath);
748
+ await stagedIn(run, 'vat-install-tgz-', async (tempDir) => {
749
+ logger.info(' Extracting tarball...');
750
+ // One entry at a time: when the extraction throws, nothing is still being written under the staging.
751
+ await extractArchive({ file: sourcePath, side: 'source' }, tempDir, { format: 'tar', limits: ARCHIVE_LIMITS });
752
+ // npm pack tarballs extract under package/ subdirectory
753
+ const packageDir = safePath.join(tempDir, 'package');
754
+ await installPackageDir(run, pathPresent(packageDir, { follow: tempDir }, everywhere('environment'), 'confirmed') ? packageDir : tempDir, 'environment');
755
+ });
517
756
  }
518
757
  /**
519
758
  * Handle development mode installation via plugin tree (symlinks).
@@ -522,292 +761,113 @@ async function devInstallMarketplace(mpName, srcMpDir, packageInfo, cwd, options
522
761
  * - Copies non-skill content (.claude-plugin/ dirs) to ~/.claude/plugins/
523
762
  * - For each skill directory under plugins/{plugin}/skills/{name}/, creates a
524
763
  * symlink to the corresponding dist/skills/{name}/ directory
525
- * - Registers each plugin in the Claude plugin registry (same as copyPluginTree)
764
+ * - Registers each plugin in the Claude plugin registry
526
765
  *
527
- * Skills appear in Claude Code as {plugin}:{skill} instead of the flat {skill} name.
766
+ * Each marketplace is ONE replace, built whole in staging and swapped in: the installed
767
+ * marketplace is never deleted first. Skills appear in Claude Code as {plugin}:{skill}.
528
768
  */
529
- async function handleDevInstall(options, logger, startTime) {
769
+ async function handleDevInstall(run) {
770
+ const { options, logger } = run;
530
771
  if (process.platform === 'win32') {
531
- throw new Error('--dev (symlink) not supported on Windows.\n' +
772
+ throw new CommandRefusalError('NOT_IMPLEMENTED', '--dev (symlink) not supported on Windows.\n' +
532
773
  'Use copy mode (omit --dev) or WSL for development.');
533
774
  }
534
775
  // --cwd only applies to --dev mode; other paths (npm postinstall, etc.) use process.cwd()
535
- const cwd = options.cwd ? safePath.resolve(options.cwd) : process.cwd();
776
+ const cwd = options.cwd ? safePath.resolve(options.cwd) : process.cwd(); // The operator's own: every probe beneath it passes it as `within`.
777
+ run.roots.source.push(cwd);
536
778
  if (options.build) {
537
779
  runBuild(cwd, logger);
538
780
  }
539
781
  // Check for pre-built plugin tree
540
782
  const marketplacesDir = safePath.join(cwd, PLUGIN_MARKETPLACES_SUBPATH);
541
- if (!existsSync(marketplacesDir)) {
542
- throw new Error(`Plugin tree not found at ${marketplacesDir}\n` +
783
+ if (!pathPresent(marketplacesDir, { follow: cwd }, everywhere('source'), 'confirmed')) {
784
+ throw new CommandRefusalError('USAGE_INVALID', `Plugin tree not found at ${marketplacesDir}\n` +
543
785
  `Run: vat build first (builds the plugin tree)`);
544
786
  }
545
- // Read package.json for package name/version
546
- const pkgRaw = readFileSync(safePath.join(cwd, PACKAGE_JSON), 'utf-8');
547
- const packageJson = JSON.parse(pkgRaw);
787
+ const packageJson = await readPackageJson(cwd, packageOf(run, cwd, 'source'));
788
+ setSource(run, packageJson.name, 'dev', true);
548
789
  logger.info(`📥 Dev-installing plugin tree from ${packageJson.name}`);
549
- const packageInfo = { name: packageJson.name, version: packageJson.version ?? '0.0.0' };
550
- const installed = [];
551
- // Remove old plugins/flat skills this package replaces, before installing
552
- if (packageJson.vat?.replaces) {
553
- const paths = getClaudeUserPaths();
554
- const marketplaceNames = listSubdirectories(marketplacesDir);
555
- await executeReplaces(packageJson.vat.replaces, marketplaceNames, paths, options.dryRun ?? false, logger);
556
- }
557
- for (const mpName of listSubdirectories(marketplacesDir)) {
558
- const srcMpDir = safePath.join(marketplacesDir, mpName);
559
- const results = await devInstallMarketplace(mpName, srcMpDir, packageInfo, cwd, options, logger);
560
- installed.push(...results);
561
- }
562
- const duration = Date.now() - startTime;
563
- outputDevSuccess(installed, packageJson.name, duration, logger, options.dryRun);
564
- process.exit(ExitCode.OK);
790
+ await install(run, pluginTree('source', cwd, packageJson), 'dev');
565
791
  }
566
792
  /**
567
- * Shell out to vat build (skills + claude plugin tree)
793
+ * Shell out to `vat build` (skills + claude plugin tree) in `cwd`.
794
+ *
795
+ * Its stdout goes to this process's stderr ({@link BUILD_CHILD_STDIO}), never
796
+ * through a buffered pipe. A build that exits non-zero, or one that cannot be
797
+ * started, stops the install before anything is installed: `RUN_INCOMPLETE`.
798
+ *
799
+ * @throws A `RUN_INCOMPLETE` {@link CommandRefusalError}
568
800
  */
569
- function runBuild(cwd, logger) {
801
+ export function runBuild(cwd, logger) {
570
802
  logger.info('🔨 Building first (skills + plugin tree)...');
571
803
  const binPath = safePath.resolve(safePath.join(import.meta.dirname, '../../../../bin/vat.js'));
572
- safeExecSync('node', [binPath, 'build'], {
573
- cwd,
574
- stdio: ['inherit', 'inherit', 'inherit'],
575
- });
804
+ try {
805
+ safeExecSync(process.execPath, [binPath, 'build'], { cwd, stdio: BUILD_CHILD_STDIO });
806
+ }
807
+ catch (error) {
808
+ const why = isVatError(error, 'COMMAND_EXECUTION')
809
+ ? 'vat build failed — its output above says why'
810
+ : `vat build could not be run: ${String(error)}`;
811
+ throw new CommandRefusalError('RUN_INCOMPLETE', `${why} (in ${cwd}), so nothing was installed.`, { cause: error });
812
+ }
576
813
  logger.info('');
577
814
  }
578
815
  /**
579
816
  * Handle npm postinstall hook
580
817
  */
581
- async function handleNpmPostinstall(options, logger, startTime) {
818
+ async function handleNpmPostinstall(run) {
819
+ const { options, logger } = run;
582
820
  logger.info(`📥 Running npm postinstall hook`);
821
+ // The postinstall lane resolves its source (the package directory) whether or
822
+ // not it installs: a skip is an answer, `ok` with no skills — never a failure
823
+ // inside `npm install -g`.
824
+ const cwd = process.cwd(); // The package npm runs us in: every probe beneath it passes it as `within`.
825
+ setSource(run, cwd, 'npm-postinstall');
826
+ run.roots.source.push(cwd);
583
827
  if (!isGlobalNpmInstall()) {
584
828
  logger.info(' Skipping: Not a global npm install');
585
- process.exit(ExitCode.OK);
829
+ return;
586
830
  }
587
- const cwd = process.cwd();
588
- // Check for pre-built plugin tree (dist/.claude/plugins/marketplaces/)
589
- const marketplacesDir = safePath.join(cwd, PLUGIN_MARKETPLACES_SUBPATH);
590
- const hasPluginTree = existsSync(marketplacesDir);
591
831
  if (!options.userInstallWithoutPlugin) {
592
- if (hasPluginTree) {
593
- const pkgRaw = readFileSync(safePath.join(cwd, PACKAGE_JSON), 'utf-8');
594
- const packageJson = JSON.parse(pkgRaw);
595
- logger.info(` Package: ${packageJson.name}@${packageJson.version ?? 'unknown'}`);
596
- logger.info(` Plugin tree detected — copying to ~/.claude/plugins/`);
597
- const installedSkills = await copyPluginTree(marketplacesDir, packageJson, logger, options.dryRun);
598
- const duration = Date.now() - startTime;
599
- logger.info(`✅ Installed plugin from ${packageJson.name} (${installedSkills.length} skill(s))`);
600
- logger.info(` Duration: ${duration}ms`);
601
- }
602
- else {
832
+ // Check for pre-built plugin tree (dist/.claude/plugins/marketplaces/)
833
+ const marketplacesDir = safePath.join(cwd, PLUGIN_MARKETPLACES_SUBPATH);
834
+ if (!pathPresent(marketplacesDir, { follow: cwd }, everywhere('source'), 'confirmed')) {
603
835
  logger.info(` No plugin tree found at dist/.claude/plugins/marketplaces/`);
604
836
  logger.info(` Run 'vat build' to generate plugin artifacts before publishing.`);
605
837
  logger.info(` Skipping install — no skills registered.`);
838
+ return;
606
839
  }
607
- process.exit(ExitCode.OK);
840
+ const packageJson = await readPackageJson(cwd, packageOf(run, cwd, 'source'));
841
+ logger.info(` Package: ${packageJson.name}@${packageJson.version ?? 'unknown'}`);
842
+ logger.info(` Plugin tree detected — copying to ~/.claude/plugins/`);
843
+ await install(run, pluginTree('source', cwd, packageJson), 'copy');
844
+ return;
608
845
  }
609
846
  // --user-install-without-plugin: install skills directly to ~/.claude/skills/
610
- const { packageJson, skills } = await readPackageJsonVatMetadata(cwd);
611
- logger.info(` Package: ${packageJson.name}@${packageJson.version}`);
847
+ const { packageJson, skills } = await readPackageJsonVatMetadata(cwd, packageOf(run, cwd, 'source'));
848
+ logger.info(` Package: ${packageJson.name}@${packageJson.version ?? 'unknown'}`);
612
849
  logger.info(` Skills found: ${skills.length}`);
613
- for (const skillName of skills) {
614
- const skillPath = safePath.join(cwd, 'dist', 'skills', skillNameToFsPath(skillName));
615
- await installSkillFromPath(skillPath, skillName, options, logger);
616
- }
617
- const duration = Date.now() - startTime;
618
- logger.info(`✅ Installed ${skills.length} skill(s) from ${packageJson.name}`);
619
- logger.info(` Duration: ${duration}ms`);
620
- process.exit(ExitCode.OK);
621
- }
622
- /**
623
- * Execute vat.replaces cleanup before installing the new plugin.
624
- *
625
- * Removes old plugin registrations and legacy flat-skill installs that this
626
- * package now supersedes. Runs before the new plugin is copied/symlinked so
627
- * Claude Code never sees stale duplicate entries.
628
- *
629
- * Idempotent — uninstallPlugin handles "not found" gracefully.
630
- */
631
- export async function removeFlatSkill(skillPath, logger) {
632
- logger.info(` Removing legacy flat skill: ${toForwardSlash(skillPath)}`);
633
- await rm(skillPath, { recursive: true, force: true });
634
- }
635
- export function logFlatSkillRemoval(skillPath, logger) {
636
- logger.info(` [dry-run] Would remove legacy flat skill: ${toForwardSlash(skillPath)}`);
637
- }
638
- export async function removeOldPlugins(oldPlugins, marketplaceNames, paths, dryRun, logger) {
639
- for (const mp of marketplaceNames) {
640
- for (const oldPlugin of oldPlugins ?? []) {
641
- const pluginKey = `${oldPlugin}@${mp}`;
642
- if (dryRun) {
643
- logger.info(` [dry-run] Would uninstall old plugin: ${pluginKey}`);
644
- }
645
- else {
646
- logger.info(` Removing old plugin: ${pluginKey}`);
647
- await uninstallPlugin({ pluginKey, paths, dryRun: false });
648
- }
649
- }
650
- }
651
- }
652
- export async function executeReplaces(replaces, marketplaceNames, paths, dryRun, logger) {
653
- // Remove old plugin entries from all marketplaces this package ships into
654
- await removeOldPlugins(replaces.plugins, marketplaceNames, paths, dryRun, logger);
655
- // Remove legacy flat-skill installs from ~/.claude/skills/<name>
656
- for (const skillName of replaces.flatSkills ?? []) {
657
- // The entry comes from the INSTALLED package's package.json, and this is
658
- // an `rm -rf` with no flag in front of it: `"../victim"` used to remove a
659
- // sibling of the skills dir (with the default paths, `"../.."` is $HOME).
660
- const skillPath = safePath.join(paths.skillsDir, assertSkillEntryName(skillName, 'vat.replaces.flatSkills'));
661
- let pathExists = false;
662
- try {
663
- lstatSync(skillPath); // throws if path itself doesn't exist (doesn't follow symlinks)
664
- pathExists = true;
665
- }
666
- catch (error) {
667
- // Path doesn't exist — nothing to remove. One the OS refuses to examine
668
- // is not "nothing to remove": the legacy install is still there and a
669
- // silent skip leaves it beside its replacement.
670
- if (!isPathAbsentError(error))
671
- throw error;
672
- }
673
- if (pathExists) {
674
- if (dryRun) {
675
- logFlatSkillRemoval(skillPath, logger);
676
- }
677
- else {
678
- await removeFlatSkill(skillPath, logger);
679
- }
680
- }
681
- }
682
- }
683
- /**
684
- * Copy pre-built plugin tree to ~/.claude/plugins/ and update registry.
685
- *
686
- * This is a "dumb copy" — the dist/.claude/plugins/marketplaces/ tree mirrors
687
- * the target ~/.claude/plugins/marketplaces/ structure exactly. No path rewriting,
688
- * no assembly, no skill resolution. Just recursive copy + registry update.
689
- */
690
- async function copyPluginTree(marketplacesDir, packageJson, logger, dryRun) {
691
- const paths = getClaudeUserPaths();
692
- const version = packageJson.version ?? '0.0.0';
693
- const marketplaceNames = listSubdirectories(marketplacesDir);
694
- const installedSkills = [];
695
- // Remove any old plugins/flat skills this package replaces, before installing
696
- if (packageJson.vat?.replaces) {
697
- await executeReplaces(packageJson.vat.replaces, marketplaceNames, paths, dryRun ?? false, logger);
698
- }
699
- for (const mpName of marketplaceNames) {
700
- const srcMpDir = safePath.join(marketplacesDir, mpName);
701
- const destMpDir = safePath.join(paths.marketplacesDir, mpName);
702
- // Replace the marketplace directory entirely so skills removed from the
703
- // package do not persist in the user's Claude installation.
704
- logger.info(` Copying marketplace: ${mpName} → ${destMpDir}`);
705
- await rm(destMpDir, { recursive: true, force: true });
706
- await mkdir(destMpDir, { recursive: true });
707
- cpSync(srcMpDir, destMpDir, { recursive: true, force: true });
708
- // Register each plugin and collect installed skill names
709
- for (const pluginName of listSubdirectories(safePath.join(srcMpDir, 'plugins'))) {
710
- const pluginDir = safePath.join(destMpDir, 'plugins', pluginName);
711
- await registerPlugin(mpName, pluginName, pluginDir, version, packageJson.name, paths, logger);
712
- const skillNames = listSubdirectories(safePath.join(srcMpDir, 'plugins', pluginName, 'skills'));
713
- for (const skillName of skillNames) {
714
- installedSkills.push({
715
- name: skillName,
716
- installPath: safePath.join(destMpDir, 'plugins', pluginName, 'skills', skillName),
717
- });
718
- }
719
- }
720
- }
721
- return installedSkills;
722
- }
723
- /**
724
- * Prepare plugins directory and check for conflicts
725
- */
726
- async function prepareInstallation(skillName, options) {
727
- const skillsDir = options.skillsDir ?? getClaudeUserPaths().skillsDir;
728
- // The declared name is author-controlled (SKILL.md `name:`, package.json
729
- // `vat.skills[]`) and `--force` turns this path into an `rm -rf`.
730
- const installPath = safePath.join(skillsDir, assertSkillEntryName(skillName, 'skill name'));
731
- let exists = false;
732
- try {
733
- lstatSync(installPath);
734
- exists = true;
735
- }
736
- catch (error) {
737
- // Does not exist. A refusal is not that: it used to read as "free", and
738
- // the extraction that followed then met the same refusal without a name.
739
- if (!isPathAbsentError(error))
740
- throw error;
741
- }
742
- if (exists && !options.force) {
743
- throw new Error(`Skill already exists at ${installPath}. Use --force to overwrite.`);
744
- }
745
- if (exists && options.force && !options.dryRun) {
746
- await rm(installPath, { recursive: true, force: true });
747
- }
748
- if (!options.dryRun) {
749
- await mkdir(skillsDir, { recursive: true });
750
- }
751
- return { skillsDir, installPath };
752
- }
753
- /**
754
- * Install skill from a path to plugins directory
755
- */
756
- async function installSkillFromPath(skillPath, skillName, options, logger) {
757
- if (!existsSync(skillPath)) {
758
- throw new Error(`Skill path not found: ${skillPath}`);
759
- }
760
- const { installPath } = await prepareInstallation(skillName, options);
761
- if (!options.dryRun) {
762
- logger.info(` Installing ${skillName}...`);
763
- cpSync(skillPath, installPath, { recursive: true, force: true });
764
- }
765
- }
766
- /**
767
- * Output success YAML for dev install
768
- */
769
- function outputDevSuccess(installed, packageName, duration, logger, dryRun) {
770
- writeYamlHeader(dryRun);
771
- process.stdout.write(`sourceType: dev\n`);
772
- process.stdout.write(`package: "${packageName}"\n`);
773
- process.stdout.write(`skillsInstalled: ${installed.length}\n`);
774
- process.stdout.write(`symlink: true\n`);
775
- process.stdout.write(`skills:\n`);
776
- for (const skill of installed) {
777
- process.stdout.write(` - name: ${skill.name}\n`);
778
- process.stdout.write(` installPath: ${skill.installPath}\n`);
779
- process.stdout.write(` sourcePath: ${skill.sourcePath}\n`);
780
- }
781
- process.stdout.write(`duration: ${duration}ms\n`);
782
- if (dryRun) {
783
- logger.info(`\n✅ Dry-run complete: ${installed.length} skill(s) would be symlinked`);
784
- }
785
- else {
786
- logger.info(`\n✅ Dev-installed ${installed.length} skill(s) via symlink`);
787
- logger.info(` After rebuilding, run /reload-plugins in Claude Code`);
788
- }
789
- }
790
- /**
791
- * Output success YAML for install
792
- */
793
- function outputInstallSuccess(skills, source, sourceType, duration, logger, dryRun) {
794
- writeYamlHeader(dryRun);
795
- process.stdout.write(`source: ${source}\n`);
796
- process.stdout.write(`sourceType: ${sourceType}\n`);
797
- process.stdout.write(`skillsInstalled: ${skills.length}\n`);
798
- process.stdout.write(`skills:\n`);
799
- for (const skill of skills) {
800
- process.stdout.write(` - name: ${skill.name}\n`);
801
- process.stdout.write(` installPath: ${skill.installPath}\n`);
802
- }
803
- process.stdout.write(`duration: ${duration}ms\n`);
804
- if (dryRun) {
805
- logger.info(`\n✅ Dry-run complete: ${skills.length} skill(s) would be installed`);
806
- }
807
- else {
808
- logger.info(`\n✅ Installed ${skills.length} skill(s)`);
809
- logger.info(`\n💡 Run 'vat claude plugin list' to verify installation`);
810
- logger.info(` Restart Claude Code or run /reload-plugins to use the new skill`);
811
- }
850
+ await install(run, { kind: 'skills', side: 'source', rootDir: cwd, skillNames: skills }, 'copy');
812
851
  }
852
+ /** Test-facing seam: the pure decisions of this module, reached by its unit tests. */
853
+ export const __internal = {
854
+ assertInstallTarget,
855
+ assertPackagePluginNames,
856
+ boundaryOf,
857
+ executeInstall,
858
+ leftover,
859
+ logInstallOutcome,
860
+ newInstallRun,
861
+ notPlugins,
862
+ notPluginSkills,
863
+ notSkills,
864
+ outcomeOf,
865
+ planInstall,
866
+ record,
867
+ replacedOutcome,
868
+ selectSkills,
869
+ setSource,
870
+ skillNameToFsPath,
871
+ stagedIn,
872
+ };
813
873
  //# sourceMappingURL=install.js.map