@vibe-agent-toolkit/cli 0.2.0-rc.6 → 0.2.0-rc.8

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 (422) hide show
  1. package/README.md +4 -5
  2. package/dist/bin/vat +34 -19
  3. package/dist/bin/vat.js +34 -19
  4. package/dist/bin/vat.js.map +1 -1
  5. package/dist/bin.js +44 -158
  6. package/dist/bin.js.map +1 -1
  7. package/dist/command-loaders.d.ts +2 -2
  8. package/dist/command-loaders.d.ts.map +1 -1
  9. package/dist/command-loaders.js +4 -2
  10. package/dist/command-loaders.js.map +1 -1
  11. package/dist/commands/agent/build.d.ts.map +1 -1
  12. package/dist/commands/agent/build.js +2 -1
  13. package/dist/commands/agent/build.js.map +1 -1
  14. package/dist/commands/agent/import.d.ts.map +1 -1
  15. package/dist/commands/agent/import.js +3 -2
  16. package/dist/commands/agent/import.js.map +1 -1
  17. package/dist/commands/agent/install-path.d.ts +36 -0
  18. package/dist/commands/agent/install-path.d.ts.map +1 -0
  19. package/dist/commands/agent/install-path.js +44 -0
  20. package/dist/commands/agent/install-path.js.map +1 -0
  21. package/dist/commands/agent/install.d.ts.map +1 -1
  22. package/dist/commands/agent/install.js +22 -10
  23. package/dist/commands/agent/install.js.map +1 -1
  24. package/dist/commands/agent/installed.d.ts.map +1 -1
  25. package/dist/commands/agent/installed.js +22 -12
  26. package/dist/commands/agent/installed.js.map +1 -1
  27. package/dist/commands/agent/list.d.ts.map +1 -1
  28. package/dist/commands/agent/list.js +2 -1
  29. package/dist/commands/agent/list.js.map +1 -1
  30. package/dist/commands/agent/run.d.ts.map +1 -1
  31. package/dist/commands/agent/run.js +2 -1
  32. package/dist/commands/agent/run.js.map +1 -1
  33. package/dist/commands/agent/uninstall.d.ts.map +1 -1
  34. package/dist/commands/agent/uninstall.js +13 -6
  35. package/dist/commands/agent/uninstall.js.map +1 -1
  36. package/dist/commands/agent/validate.d.ts.map +1 -1
  37. package/dist/commands/agent/validate.js +3 -2
  38. package/dist/commands/agent/validate.js.map +1 -1
  39. package/dist/commands/ard/emit.d.ts +153 -0
  40. package/dist/commands/ard/emit.d.ts.map +1 -0
  41. package/dist/commands/ard/emit.js +276 -0
  42. package/dist/commands/ard/emit.js.map +1 -0
  43. package/dist/commands/ard/index.d.ts +12 -0
  44. package/dist/commands/ard/index.d.ts.map +1 -0
  45. package/dist/commands/ard/index.js +80 -0
  46. package/dist/commands/ard/index.js.map +1 -0
  47. package/dist/commands/ard/surfaces.d.ts +56 -0
  48. package/dist/commands/ard/surfaces.d.ts.map +1 -0
  49. package/dist/commands/ard/surfaces.js +150 -0
  50. package/dist/commands/ard/surfaces.js.map +1 -0
  51. package/dist/commands/audit/distributed-tree.d.ts.map +1 -1
  52. package/dist/commands/audit/distributed-tree.js +9 -6
  53. package/dist/commands/audit/distributed-tree.js.map +1 -1
  54. package/dist/commands/audit/scan-population.d.ts +158 -0
  55. package/dist/commands/audit/scan-population.d.ts.map +1 -0
  56. package/dist/commands/audit/scan-population.js +292 -0
  57. package/dist/commands/audit/scan-population.js.map +1 -0
  58. package/dist/commands/audit-settings.d.ts.map +1 -1
  59. package/dist/commands/audit-settings.js +6 -5
  60. package/dist/commands/audit-settings.js.map +1 -1
  61. package/dist/commands/audit.d.ts +78 -9
  62. package/dist/commands/audit.d.ts.map +1 -1
  63. package/dist/commands/audit.js +853 -473
  64. package/dist/commands/audit.js.map +1 -1
  65. package/dist/commands/build.d.ts.map +1 -1
  66. package/dist/commands/build.js +24 -24
  67. package/dist/commands/build.js.map +1 -1
  68. package/dist/commands/cache/clear.d.ts.map +1 -1
  69. package/dist/commands/cache/clear.js +17 -10
  70. package/dist/commands/cache/clear.js.map +1 -1
  71. package/dist/commands/claude/budget.d.ts +136 -24
  72. package/dist/commands/claude/budget.d.ts.map +1 -1
  73. package/dist/commands/claude/budget.js +152 -57
  74. package/dist/commands/claude/budget.js.map +1 -1
  75. package/dist/commands/claude/context.d.ts.map +1 -1
  76. package/dist/commands/claude/context.js +4 -3
  77. package/dist/commands/claude/context.js.map +1 -1
  78. package/dist/commands/claude/marketplace/changelog-utils.d.ts +1 -1
  79. package/dist/commands/claude/marketplace/changelog-utils.d.ts.map +1 -1
  80. package/dist/commands/claude/marketplace/changelog-utils.js +1 -2
  81. package/dist/commands/claude/marketplace/changelog-utils.js.map +1 -1
  82. package/dist/commands/claude/marketplace/git-publish.d.ts.map +1 -1
  83. package/dist/commands/claude/marketplace/git-publish.js +3 -2
  84. package/dist/commands/claude/marketplace/git-publish.js.map +1 -1
  85. package/dist/commands/claude/marketplace/license-utils.d.ts.map +1 -1
  86. package/dist/commands/claude/marketplace/license-utils.js +0 -1
  87. package/dist/commands/claude/marketplace/license-utils.js.map +1 -1
  88. package/dist/commands/claude/marketplace/publish-tree.d.ts.map +1 -1
  89. package/dist/commands/claude/marketplace/publish-tree.js +0 -6
  90. package/dist/commands/claude/marketplace/publish-tree.js.map +1 -1
  91. package/dist/commands/claude/marketplace/publish.d.ts.map +1 -1
  92. package/dist/commands/claude/marketplace/publish.js +2 -1
  93. package/dist/commands/claude/marketplace/publish.js.map +1 -1
  94. package/dist/commands/claude/marketplace/validate.d.ts +55 -22
  95. package/dist/commands/claude/marketplace/validate.d.ts.map +1 -1
  96. package/dist/commands/claude/marketplace/validate.js +392 -77
  97. package/dist/commands/claude/marketplace/validate.js.map +1 -1
  98. package/dist/commands/claude/org/api-keys.d.ts +0 -3
  99. package/dist/commands/claude/org/api-keys.d.ts.map +1 -1
  100. package/dist/commands/claude/org/api-keys.js +2 -1
  101. package/dist/commands/claude/org/api-keys.js.map +1 -1
  102. package/dist/commands/claude/org/helpers.d.ts +17 -22
  103. package/dist/commands/claude/org/helpers.d.ts.map +1 -1
  104. package/dist/commands/claude/org/helpers.js +6 -22
  105. package/dist/commands/claude/org/helpers.js.map +1 -1
  106. package/dist/commands/claude/org/invites.d.ts +0 -3
  107. package/dist/commands/claude/org/invites.d.ts.map +1 -1
  108. package/dist/commands/claude/org/invites.js +3 -2
  109. package/dist/commands/claude/org/invites.js.map +1 -1
  110. package/dist/commands/claude/org/skills.d.ts +103 -4
  111. package/dist/commands/claude/org/skills.d.ts.map +1 -1
  112. package/dist/commands/claude/org/skills.js +331 -123
  113. package/dist/commands/claude/org/skills.js.map +1 -1
  114. package/dist/commands/claude/org/users.d.ts +0 -3
  115. package/dist/commands/claude/org/users.d.ts.map +1 -1
  116. package/dist/commands/claude/org/users.js +3 -2
  117. package/dist/commands/claude/org/users.js.map +1 -1
  118. package/dist/commands/claude/org/workspaces.d.ts +0 -3
  119. package/dist/commands/claude/org/workspaces.d.ts.map +1 -1
  120. package/dist/commands/claude/org/workspaces.js +6 -5
  121. package/dist/commands/claude/org/workspaces.js.map +1 -1
  122. package/dist/commands/claude/plugin/build.d.ts +8 -0
  123. package/dist/commands/claude/plugin/build.d.ts.map +1 -1
  124. package/dist/commands/claude/plugin/build.js +28 -33
  125. package/dist/commands/claude/plugin/build.js.map +1 -1
  126. package/dist/commands/claude/plugin/helpers.d.ts.map +1 -1
  127. package/dist/commands/claude/plugin/helpers.js +0 -6
  128. package/dist/commands/claude/plugin/helpers.js.map +1 -1
  129. package/dist/commands/claude/plugin/install.d.ts +12 -0
  130. package/dist/commands/claude/plugin/install.d.ts.map +1 -1
  131. package/dist/commands/claude/plugin/install.js +71 -60
  132. package/dist/commands/claude/plugin/install.js.map +1 -1
  133. package/dist/commands/claude/plugin/list.d.ts.map +1 -1
  134. package/dist/commands/claude/plugin/list.js +3 -2
  135. package/dist/commands/claude/plugin/list.js.map +1 -1
  136. package/dist/commands/claude/plugin/plugin-changelog.d.ts.map +1 -1
  137. package/dist/commands/claude/plugin/plugin-changelog.js +0 -2
  138. package/dist/commands/claude/plugin/plugin-changelog.js.map +1 -1
  139. package/dist/commands/claude/plugin/plugin-files.d.ts.map +1 -1
  140. package/dist/commands/claude/plugin/plugin-files.js +0 -3
  141. package/dist/commands/claude/plugin/plugin-files.js.map +1 -1
  142. package/dist/commands/claude/plugin/plugin-validators.d.ts.map +1 -1
  143. package/dist/commands/claude/plugin/plugin-validators.js +3 -6
  144. package/dist/commands/claude/plugin/plugin-validators.js.map +1 -1
  145. package/dist/commands/claude/plugin/tree-copy.d.ts +73 -0
  146. package/dist/commands/claude/plugin/tree-copy.d.ts.map +1 -1
  147. package/dist/commands/claude/plugin/tree-copy.js +221 -15
  148. package/dist/commands/claude/plugin/tree-copy.js.map +1 -1
  149. package/dist/commands/claude/plugin/uninstall.d.ts.map +1 -1
  150. package/dist/commands/claude/plugin/uninstall.js +4 -4
  151. package/dist/commands/claude/plugin/uninstall.js.map +1 -1
  152. package/dist/commands/consistency-check.d.ts +9 -13
  153. package/dist/commands/consistency-check.d.ts.map +1 -1
  154. package/dist/commands/consistency-check.js +91 -81
  155. package/dist/commands/consistency-check.js.map +1 -1
  156. package/dist/commands/corpus/index.d.ts.map +1 -1
  157. package/dist/commands/corpus/index.js +3 -0
  158. package/dist/commands/corpus/index.js.map +1 -1
  159. package/dist/commands/corpus/report.d.ts +0 -1
  160. package/dist/commands/corpus/report.d.ts.map +1 -1
  161. package/dist/commands/corpus/report.js +1 -3
  162. package/dist/commands/corpus/report.js.map +1 -1
  163. package/dist/commands/corpus/runner.d.ts +58 -1
  164. package/dist/commands/corpus/runner.d.ts.map +1 -1
  165. package/dist/commands/corpus/runner.js +92 -21
  166. package/dist/commands/corpus/runner.js.map +1 -1
  167. package/dist/commands/corpus/scan.d.ts.map +1 -1
  168. package/dist/commands/corpus/scan.js +2 -4
  169. package/dist/commands/corpus/scan.js.map +1 -1
  170. package/dist/commands/corpus/seed.d.ts +4 -4
  171. package/dist/commands/corpus/seed.d.ts.map +1 -1
  172. package/dist/commands/corpus/seed.js +1 -3
  173. package/dist/commands/corpus/seed.js.map +1 -1
  174. package/dist/commands/doctor.d.ts +14 -0
  175. package/dist/commands/doctor.d.ts.map +1 -1
  176. package/dist/commands/doctor.js +147 -51
  177. package/dist/commands/doctor.js.map +1 -1
  178. package/dist/commands/inventory.d.ts.map +1 -1
  179. package/dist/commands/inventory.js +6 -7
  180. package/dist/commands/inventory.js.map +1 -1
  181. package/dist/commands/mcp/list-collections.d.ts.map +1 -1
  182. package/dist/commands/mcp/list-collections.js +2 -1
  183. package/dist/commands/mcp/list-collections.js.map +1 -1
  184. package/dist/commands/mcp/serve.d.ts.map +1 -1
  185. package/dist/commands/mcp/serve.js +3 -2
  186. package/dist/commands/mcp/serve.js.map +1 -1
  187. package/dist/commands/okf/index.d.ts +11 -0
  188. package/dist/commands/okf/index.d.ts.map +1 -0
  189. package/dist/commands/okf/index.js +114 -0
  190. package/dist/commands/okf/index.js.map +1 -0
  191. package/dist/commands/okf/validate.d.ts +191 -0
  192. package/dist/commands/okf/validate.d.ts.map +1 -0
  193. package/dist/commands/okf/validate.js +190 -0
  194. package/dist/commands/okf/validate.js.map +1 -0
  195. package/dist/commands/phase-utils.d.ts +30 -13
  196. package/dist/commands/phase-utils.d.ts.map +1 -1
  197. package/dist/commands/phase-utils.js +56 -19
  198. package/dist/commands/phase-utils.js.map +1 -1
  199. package/dist/commands/positional-args.d.ts.map +1 -1
  200. package/dist/commands/positional-args.js +2 -1
  201. package/dist/commands/positional-args.js.map +1 -1
  202. package/dist/commands/rag/clear-command.d.ts.map +1 -1
  203. package/dist/commands/rag/clear-command.js +2 -1
  204. package/dist/commands/rag/clear-command.js.map +1 -1
  205. package/dist/commands/rag/index-command.d.ts +27 -2
  206. package/dist/commands/rag/index-command.d.ts.map +1 -1
  207. package/dist/commands/rag/index-command.js +48 -8
  208. package/dist/commands/rag/index-command.js.map +1 -1
  209. package/dist/commands/rag/index.d.ts.map +1 -1
  210. package/dist/commands/rag/index.js +2 -0
  211. package/dist/commands/rag/index.js.map +1 -1
  212. package/dist/commands/rag/query-command.d.ts.map +1 -1
  213. package/dist/commands/rag/query-command.js +2 -1
  214. package/dist/commands/rag/query-command.js.map +1 -1
  215. package/dist/commands/rag/stats-command.d.ts.map +1 -1
  216. package/dist/commands/rag/stats-command.js +2 -1
  217. package/dist/commands/rag/stats-command.js.map +1 -1
  218. package/dist/commands/resources/check-progress.d.ts +3 -0
  219. package/dist/commands/resources/check-progress.d.ts.map +1 -1
  220. package/dist/commands/resources/check-progress.js +11 -2
  221. package/dist/commands/resources/check-progress.js.map +1 -1
  222. package/dist/commands/resources/check-supervisor.d.ts.map +1 -1
  223. package/dist/commands/resources/check-supervisor.js +14 -7
  224. package/dist/commands/resources/check-supervisor.js.map +1 -1
  225. package/dist/commands/resources/check.d.ts +168 -8
  226. package/dist/commands/resources/check.d.ts.map +1 -1
  227. package/dist/commands/resources/check.js +222 -118
  228. package/dist/commands/resources/check.js.map +1 -1
  229. package/dist/commands/resources/index.d.ts.map +1 -1
  230. package/dist/commands/resources/index.js +64 -29
  231. package/dist/commands/resources/index.js.map +1 -1
  232. package/dist/commands/resources/query.d.ts +18 -1
  233. package/dist/commands/resources/query.d.ts.map +1 -1
  234. package/dist/commands/resources/query.js +33 -12
  235. package/dist/commands/resources/query.js.map +1 -1
  236. package/dist/commands/resources/scan.d.ts.map +1 -1
  237. package/dist/commands/resources/scan.js +4 -8
  238. package/dist/commands/resources/scan.js.map +1 -1
  239. package/dist/commands/resources/validate.d.ts +30 -1
  240. package/dist/commands/resources/validate.d.ts.map +1 -1
  241. package/dist/commands/resources/validate.js +116 -14
  242. package/dist/commands/resources/validate.js.map +1 -1
  243. package/dist/commands/skill/review-checklist.d.ts +3 -2
  244. package/dist/commands/skill/review-checklist.d.ts.map +1 -1
  245. package/dist/commands/skill/review-checklist.js +4 -2
  246. package/dist/commands/skill/review-checklist.js.map +1 -1
  247. package/dist/commands/skill/review.d.ts +247 -0
  248. package/dist/commands/skill/review.d.ts.map +1 -1
  249. package/dist/commands/skill/review.js +98 -61
  250. package/dist/commands/skill/review.js.map +1 -1
  251. package/dist/commands/skill/test/configure.d.ts.map +1 -1
  252. package/dist/commands/skill/test/configure.js +0 -1
  253. package/dist/commands/skill/test/configure.js.map +1 -1
  254. package/dist/commands/skill/test/run.d.ts +12 -6
  255. package/dist/commands/skill/test/run.d.ts.map +1 -1
  256. package/dist/commands/skill/test/run.js +137 -67
  257. package/dist/commands/skill/test/run.js.map +1 -1
  258. package/dist/commands/skills/build.d.ts +69 -2
  259. package/dist/commands/skills/build.d.ts.map +1 -1
  260. package/dist/commands/skills/build.js +128 -42
  261. package/dist/commands/skills/build.js.map +1 -1
  262. package/dist/commands/skills/install.d.ts +2 -1
  263. package/dist/commands/skills/install.d.ts.map +1 -1
  264. package/dist/commands/skills/install.js +20 -30
  265. package/dist/commands/skills/install.js.map +1 -1
  266. package/dist/commands/skills/list.d.ts +2 -1
  267. package/dist/commands/skills/list.d.ts.map +1 -1
  268. package/dist/commands/skills/list.js +44 -26
  269. package/dist/commands/skills/list.js.map +1 -1
  270. package/dist/commands/skills/package.d.ts +1 -1
  271. package/dist/commands/skills/package.d.ts.map +1 -1
  272. package/dist/commands/skills/package.js +6 -8
  273. package/dist/commands/skills/package.js.map +1 -1
  274. package/dist/commands/skills/scope-guard.d.ts.map +1 -1
  275. package/dist/commands/skills/scope-guard.js +2 -4
  276. package/dist/commands/skills/scope-guard.js.map +1 -1
  277. package/dist/commands/skills/shared.d.ts.map +1 -1
  278. package/dist/commands/skills/shared.js +2 -4
  279. package/dist/commands/skills/shared.js.map +1 -1
  280. package/dist/commands/skills/skill-discovery.d.ts +40 -1
  281. package/dist/commands/skills/skill-discovery.d.ts.map +1 -1
  282. package/dist/commands/skills/skill-discovery.js +32 -4
  283. package/dist/commands/skills/skill-discovery.js.map +1 -1
  284. package/dist/commands/skills/source-resolvers.d.ts +15 -0
  285. package/dist/commands/skills/source-resolvers.d.ts.map +1 -1
  286. package/dist/commands/skills/source-resolvers.js +25 -4
  287. package/dist/commands/skills/source-resolvers.js.map +1 -1
  288. package/dist/commands/skills/validate-command.d.ts.map +1 -1
  289. package/dist/commands/skills/validate-command.js +11 -3
  290. package/dist/commands/skills/validate-command.js.map +1 -1
  291. package/dist/commands/skills/validate.d.ts +66 -7
  292. package/dist/commands/skills/validate.d.ts.map +1 -1
  293. package/dist/commands/skills/validate.js +133 -29
  294. package/dist/commands/skills/validate.js.map +1 -1
  295. package/dist/commands/validate.d.ts +1 -1
  296. package/dist/commands/validate.js +8 -6
  297. package/dist/commands/validate.js.map +1 -1
  298. package/dist/commands/verify.d.ts +102 -5
  299. package/dist/commands/verify.d.ts.map +1 -1
  300. package/dist/commands/verify.js +246 -69
  301. package/dist/commands/verify.js.map +1 -1
  302. package/dist/index.d.ts +0 -1
  303. package/dist/index.d.ts.map +1 -1
  304. package/dist/index.js +5 -1
  305. package/dist/index.js.map +1 -1
  306. package/dist/report-schemas.d.ts +45 -0
  307. package/dist/report-schemas.d.ts.map +1 -0
  308. package/dist/report-schemas.js +102 -0
  309. package/dist/report-schemas.js.map +1 -0
  310. package/dist/skill-resolution/packaging-config.d.ts +49 -5
  311. package/dist/skill-resolution/packaging-config.d.ts.map +1 -1
  312. package/dist/skill-resolution/packaging-config.js +59 -19
  313. package/dist/skill-resolution/packaging-config.js.map +1 -1
  314. package/dist/skill-resolution/resolve-skill-reference.d.ts.map +1 -1
  315. package/dist/skill-resolution/resolve-skill-reference.js +8 -6
  316. package/dist/skill-resolution/resolve-skill-reference.js.map +1 -1
  317. package/dist/utils/agent-discovery.d.ts.map +1 -1
  318. package/dist/utils/agent-discovery.js +22 -11
  319. package/dist/utils/agent-discovery.js.map +1 -1
  320. package/dist/utils/agent-runner.d.ts.map +1 -1
  321. package/dist/utils/agent-runner.js +0 -1
  322. package/dist/utils/agent-runner.js.map +1 -1
  323. package/dist/utils/command-error.d.ts +87 -55
  324. package/dist/utils/command-error.d.ts.map +1 -1
  325. package/dist/utils/command-error.js +113 -64
  326. package/dist/utils/command-error.js.map +1 -1
  327. package/dist/utils/config-loader.d.ts +2 -1
  328. package/dist/utils/config-loader.d.ts.map +1 -1
  329. package/dist/utils/config-loader.js +3 -6
  330. package/dist/utils/config-loader.js.map +1 -1
  331. package/dist/utils/context-budget-issues.d.ts +1 -1
  332. package/dist/utils/context-budget-issues.js +1 -1
  333. package/dist/utils/corpus-target.d.ts +5 -27
  334. package/dist/utils/corpus-target.d.ts.map +1 -1
  335. package/dist/utils/corpus-target.js +7 -33
  336. package/dist/utils/corpus-target.js.map +1 -1
  337. package/dist/utils/edge-lens-evaluation.d.ts +83 -0
  338. package/dist/utils/edge-lens-evaluation.d.ts.map +1 -0
  339. package/dist/utils/edge-lens-evaluation.js +110 -0
  340. package/dist/utils/edge-lens-evaluation.js.map +1 -0
  341. package/dist/utils/help-loader.d.ts.map +1 -1
  342. package/dist/utils/help-loader.js +0 -1
  343. package/dist/utils/help-loader.js.map +1 -1
  344. package/dist/utils/issue-rendering.d.ts +2 -2
  345. package/dist/utils/issue-rendering.js +2 -2
  346. package/dist/utils/optional-backend.d.ts +5 -4
  347. package/dist/utils/optional-backend.d.ts.map +1 -1
  348. package/dist/utils/optional-backend.js +7 -5
  349. package/dist/utils/optional-backend.js.map +1 -1
  350. package/dist/utils/output.d.ts +9 -0
  351. package/dist/utils/output.d.ts.map +1 -1
  352. package/dist/utils/output.js +16 -0
  353. package/dist/utils/output.js.map +1 -1
  354. package/dist/utils/package-json.d.ts +24 -0
  355. package/dist/utils/package-json.d.ts.map +1 -0
  356. package/dist/utils/package-json.js +49 -0
  357. package/dist/utils/package-json.js.map +1 -0
  358. package/dist/utils/project-root-policy.d.ts +20 -0
  359. package/dist/utils/project-root-policy.d.ts.map +1 -1
  360. package/dist/utils/project-root-policy.js +30 -0
  361. package/dist/utils/project-root-policy.js.map +1 -1
  362. package/dist/utils/projection-query.d.ts +41 -4
  363. package/dist/utils/projection-query.d.ts.map +1 -1
  364. package/dist/utils/projection-query.js +44 -10
  365. package/dist/utils/projection-query.js.map +1 -1
  366. package/dist/utils/projection-store.d.ts +41 -15
  367. package/dist/utils/projection-store.d.ts.map +1 -1
  368. package/dist/utils/projection-store.js +65 -18
  369. package/dist/utils/projection-store.js.map +1 -1
  370. package/dist/utils/repeatable-option.d.ts +37 -0
  371. package/dist/utils/repeatable-option.d.ts.map +1 -0
  372. package/dist/utils/repeatable-option.js +39 -0
  373. package/dist/utils/repeatable-option.js.map +1 -0
  374. package/dist/utils/resource-loader.d.ts +4 -3
  375. package/dist/utils/resource-loader.d.ts.map +1 -1
  376. package/dist/utils/resource-loader.js +63 -32
  377. package/dist/utils/resource-loader.js.map +1 -1
  378. package/dist/utils/root-argv.d.ts +53 -0
  379. package/dist/utils/root-argv.d.ts.map +1 -0
  380. package/dist/utils/root-argv.js +215 -0
  381. package/dist/utils/root-argv.js.map +1 -0
  382. package/dist/utils/run-integrity.d.ts +95 -0
  383. package/dist/utils/run-integrity.d.ts.map +1 -0
  384. package/dist/utils/run-integrity.js +102 -0
  385. package/dist/utils/run-integrity.js.map +1 -0
  386. package/dist/utils/skill-packaging-config.d.ts +27 -0
  387. package/dist/utils/skill-packaging-config.d.ts.map +1 -1
  388. package/dist/utils/skill-packaging-config.js +30 -0
  389. package/dist/utils/skill-packaging-config.js.map +1 -1
  390. package/dist/utils/sqlite-experimental-warning.d.ts +69 -0
  391. package/dist/utils/sqlite-experimental-warning.d.ts.map +1 -0
  392. package/dist/utils/sqlite-experimental-warning.js +144 -0
  393. package/dist/utils/sqlite-experimental-warning.js.map +1 -0
  394. package/dist/utils/url-redact.d.ts.map +1 -1
  395. package/dist/utils/url-redact.js +4 -6
  396. package/dist/utils/url-redact.js.map +1 -1
  397. package/dist/utils/user-context-scanner.d.ts +8 -0
  398. package/dist/utils/user-context-scanner.d.ts.map +1 -1
  399. package/dist/utils/user-context-scanner.js +4 -2
  400. package/dist/utils/user-context-scanner.js.map +1 -1
  401. package/dist/utils/validate-help-files.d.ts.map +1 -1
  402. package/dist/utils/validate-help-files.js +12 -6
  403. package/dist/utils/validate-help-files.js.map +1 -1
  404. package/dist/utils/vat-bin-path.d.ts.map +1 -1
  405. package/dist/utils/vat-bin-path.js +0 -1
  406. package/dist/utils/vat-bin-path.js.map +1 -1
  407. package/dist/version.d.ts.map +1 -1
  408. package/dist/version.js +1 -3
  409. package/dist/version.js.map +1 -1
  410. package/docs/audit.md +141 -37
  411. package/docs/doctor.md +7 -6
  412. package/docs/index.md +19 -11
  413. package/docs/rag.md +3 -2
  414. package/docs/resources.md +5 -2
  415. package/docs/skill-test.md +10 -9
  416. package/docs/skills.md +53 -69
  417. package/package.json +24 -29
  418. package/schemas/ard-emit.json +149 -0
  419. package/schemas/claude-budget.json +197 -0
  420. package/schemas/okf-validate.json +170 -0
  421. package/schemas/resources-check.json +186 -0
  422. package/schemas/skill-review.json +230 -0
@@ -6,7 +6,7 @@ import { basename } from 'node:path';
6
6
  import { API_SKILL_MAX_UPLOAD_BYTES, collectNonPortableAssetReferenceIssues, collectNonPortableCommandIssues, collectUnqualifiedMcpToolIssues, declaredSkillNameIn, describeOversizeBundle, evalSuiteUnitPath, formatBytes, NEVER_UPLOADED_DIR_NAMES, readDeclaredSkillName, } from '@vibe-agent-toolkit/agent-skills';
7
7
  import { ApiRequestError, ApiTransportError, buildMultipartFormData, skillVersionsPath, } from '@vibe-agent-toolkit/claude-marketplace';
8
8
  import { createAllowUsageLedger, runValidationFramework } from '@vibe-agent-toolkit/schema';
9
- import { isAbsoluteAnyPlatform, normalizedTmpdir, safePath, toForwardSlash, } from '@vibe-agent-toolkit/utils';
9
+ import { direntKindFollowingSync, isAbsoluteAnyPlatform, isFilesystemAccessError, normalizedTmpdir, safePath, toForwardSlash, } from '@vibe-agent-toolkit/utils';
10
10
  import { Command } from 'commander';
11
11
  import { resolveSkillPackagingConfig } from '../../../skill-resolution/packaging-config.js';
12
12
  import { downloadNpmPackage } from '../plugin/helpers.js';
@@ -459,19 +459,23 @@ export function resolveSourceArgument(source) {
459
459
  : safePath.resolve(process.cwd(), source);
460
460
  }
461
461
  /**
462
- * The largest SKILL.md this will decompress to read a `name` out of.
462
+ * The largest single markdown member this will decompress to read.
463
463
  *
464
464
  * Not a limit on what may be PUBLISHED — the API's own ceiling decides that, and
465
- * a bigger document simply goes unread rather than refused. It bounds the one
466
- * allocation this module performs on attacker-supplied metadata: `getData()`
465
+ * a bigger document simply goes unread rather than refused. It bounds the
466
+ * allocations this module performs on attacker-supplied metadata: `getData()`
467
467
  * inflates whatever `header.size` claims, and a header can claim anything. A
468
- * megabyte is two orders of magnitude above any real SKILL.md.
468
+ * megabyte is two orders of magnitude above any real skill document.
469
+ *
470
+ * ⚠️ It is a PER-ENTRY bound. The total is bounded separately, and more
471
+ * tightly: nothing is inflated at all until the archive's declared uncompressed
472
+ * total is known to be within {@link API_SKILL_MAX_UPLOAD_BYTES}.
469
473
  */
470
- const MAX_INSPECTED_SKILL_MD_BYTES = 1024 * 1024;
474
+ const MAX_INSPECTED_DOCUMENT_BYTES = 1024 * 1024;
471
475
  /**
472
476
  * What a ZIP will weigh AFTER the API expands it, read from the archive's own
473
- * central directory — plus the name its inner SKILL.md declares, and any answer
474
- * key it carries.
477
+ * central directory — plus the name its inner SKILL.md declares, any answer key
478
+ * it carries, and the markdown members the portability checks read.
475
479
  *
476
480
  * 🔑 **The API expands a ZIP and applies the ceiling to the UNCOMPRESSED total,
477
481
  * not to the archive.** Measured against the live API by an adopter: a
@@ -503,7 +507,7 @@ const MAX_INSPECTED_SKILL_MD_BYTES = 1024 * 1024;
503
507
  * caller's uncompressed-total gate, so the ceiling this function exists to
504
508
  * enforce could not protect the inflation this function performed. Nothing is
505
509
  * written to disk — that part stands — but the decompression is now ordered
506
- * after the total is known and bounded by {@link MAX_INSPECTED_SKILL_MD_BYTES},
510
+ * after the total is known and bounded by {@link MAX_INSPECTED_DOCUMENT_BYTES},
507
511
  * so an archive declaring a 4 GiB SKILL.md is enumerated and never inflated.
508
512
  */
509
513
  export async function inspectZipArchive(zipPath) {
@@ -517,11 +521,18 @@ export async function inspectZipArchive(zipPath) {
517
521
  try {
518
522
  entries = new AdmZip(zipPath).getEntries();
519
523
  }
520
- catch {
521
- // An archive VAT cannot parse is not an archive VAT should block. The API
524
+ catch (error) {
525
+ // An archive VAT cannot PARSE is not an archive VAT should block. The API
522
526
  // is the authority on the upload either way, and refusing here would turn
523
- // "our reader disagrees with your zip tool" into a failed publish.
524
- return undefined;
527
+ // "our reader disagrees with your zip tool" into a failed publish. The
528
+ // caller is told why, so "checks skipped" is never silent.
529
+ //
530
+ // An archive the FILESYSTEM refuses is a different thing: the path was
531
+ // readable a moment ago (the caller holds its bytes), so a refusal here
532
+ // is not a parse disagreement and stays loud.
533
+ if (isFilesystemAccessError(error))
534
+ throw error;
535
+ return { archiveUnreadable: describeZipFailure(error) };
525
536
  }
526
537
  // PASS 1 — headers only. Nothing is decompressed while this runs, so both
527
538
  // refusals the caller can reach are decided on metadata alone.
@@ -530,14 +541,97 @@ export async function inspectZipArchive(zipPath) {
530
541
  const neverUploaded = files
531
542
  .map(entry => toForwardSlash(entry.entryName))
532
543
  .filter(name => name.split('/').some(segment => NEVER_UPLOADED_DIR_NAMES.has(segment)));
533
- // PASS 2 — the ONE entry worth inflating, and only once the total says this
544
+ // PASS 2 — the entries worth inflating, and only once the total says this
534
545
  // archive is not already refused. An over-ceiling archive is about to be
535
546
  // rejected by the caller, so inflating anything out of it would be work spent
536
547
  // on an upload that cannot happen.
537
- const declaredName = uncompressedBytes > API_SKILL_MAX_UPLOAD_BYTES
538
- ? undefined
539
- : declaredNameOf(electShallowestSkillMd(files));
540
- return { uncompressedBytes, declaredName, neverUploaded };
548
+ if (uncompressedBytes > API_SKILL_MAX_UPLOAD_BYTES) {
549
+ return {
550
+ uncompressedBytes, declaredName: undefined, neverUploaded, documents: [], bundleRoot: undefined, unreadable: [],
551
+ };
552
+ }
553
+ const elected = electShallowestSkillMd(files);
554
+ const unreadable = [];
555
+ return {
556
+ uncompressedBytes,
557
+ declaredName: declaredNameOf(elected, unreadable),
558
+ neverUploaded,
559
+ documents: inflateMarkdownMembers(files, unreadable),
560
+ bundleRoot: archiveRootOf(elected?.entryName),
561
+ unreadable,
562
+ };
563
+ }
564
+ /** adm-zip throws plain `Error`s; the message is all it has to say. */
565
+ function describeZipFailure(error) {
566
+ return error instanceof Error ? error.message : String(error);
567
+ }
568
+ /**
569
+ * Every markdown member of the archive, as an upload part.
570
+ *
571
+ * 🔑 This exists so the ZIP lane can run the portability family through the ONE
572
+ * function the directory lane runs it through. Until it did, `install <zip>`
573
+ * performed none of the checks `install --help` promises of "every markdown
574
+ * document in the bundle": the archive went to `sendSkillUpload` as one opaque
575
+ * part, and an operator following the documented contract got silence where they
576
+ * were promised warnings. Routing the parts back through
577
+ * {@link warnUnportableReferences} keeps ONE implementation of the checks — a
578
+ * second copy of the three collector calls is how the two lanes start disagreeing.
579
+ *
580
+ * An entry that will not inflate is SKIPPED, never fatal: this module's standing
581
+ * position is that an archive VAT cannot read is not an archive VAT should block,
582
+ * and the checks it feeds only ever warn.
583
+ */
584
+ function inflateMarkdownMembers(files, unreadable) {
585
+ const documents = [];
586
+ for (const entry of files) {
587
+ const filename = toForwardSlash(entry.entryName);
588
+ if (!filename.toLowerCase().endsWith('.md'))
589
+ continue;
590
+ if (entry.header.size > MAX_INSPECTED_DOCUMENT_BYTES)
591
+ continue;
592
+ const content = inflateOrRecord(entry, unreadable);
593
+ if (content !== undefined)
594
+ documents.push({ fieldName: 'files[]', filename, content });
595
+ }
596
+ return documents;
597
+ }
598
+ /**
599
+ * One member's bytes, or `undefined` with the refusal RECORDED.
600
+ *
601
+ * `getData()` is the only decompression in this module, so it is the only thing
602
+ * that can throw here: adm-zip raises on BAD_CRC, on any compression method but
603
+ * store/deflate, and on an encrypted entry. Those used to escape and abort the
604
+ * publish on exit 2 — contradicting this module's own stance that an archive VAT
605
+ * cannot parse is not an archive VAT should block — and then, once caught, were
606
+ * dropped without a word. The member is skipped AND named.
607
+ */
608
+ function inflateOrRecord(entry, unreadable) {
609
+ try {
610
+ return entry.getData();
611
+ }
612
+ catch (error) {
613
+ // Once per member: the elected SKILL.md is asked for twice (its name, then
614
+ // its text), and one refusal is one fact.
615
+ const name = toForwardSlash(entry.entryName);
616
+ if (!unreadable.some((failure) => failure.entry === name)) {
617
+ unreadable.push({ entry: name, reason: describeZipFailure(error) });
618
+ }
619
+ return undefined;
620
+ }
621
+ }
622
+ /**
623
+ * The directory the elected SKILL.md sits in, which is the archive's bundle root.
624
+ *
625
+ * `my-skill/SKILL.md` roots the bundle at `my-skill`; a SKILL.md at the archive
626
+ * root has no prefix, and neither does an archive with no SKILL.md at all. ZIP
627
+ * fixes `/` as the entry separator, so this reads the format rather than handling
628
+ * a host path.
629
+ */
630
+ function archiveRootOf(entryName) {
631
+ if (entryName === undefined)
632
+ return undefined;
633
+ const cut = toForwardSlash(entryName).lastIndexOf('/');
634
+ return cut === -1 ? undefined : entryName.slice(0, cut);
541
635
  }
542
636
  /**
543
637
  * The SKILL.md nearest the archive root, which is the one whose `name` the API
@@ -597,22 +691,15 @@ function zipEntryDepth(entryName) {
597
691
  * the root of their archive. The election decides; whatever it holds is the
598
692
  * answer, absence included.
599
693
  *
600
- * `getData()` is the only decompression in this module, so it is the only thing
601
- * that can throw here: adm-zip raises on BAD_CRC, on any compression method but
602
- * store/deflate, and on an encrypted entry. Those used to escape and abort the
603
- * publish on exit 2 — contradicting this module's own stance that an archive VAT
604
- * cannot parse is not an archive VAT should block. The size total is already
605
- * computed from headers, so failing here costs only the name.
694
+ * An elected SKILL.md that will not inflate costs only the name (the size total
695
+ * is already computed from headers) — see {@link inflateOrRecord}, which records
696
+ * the refusal so the operator learns why no name was read.
606
697
  */
607
- function declaredNameOf(entry) {
608
- if (entry === undefined || entry.header.size > MAX_INSPECTED_SKILL_MD_BYTES)
609
- return undefined;
610
- try {
611
- return declaredSkillNameIn(entry.getData().toString('utf8'));
612
- }
613
- catch {
698
+ function declaredNameOf(entry, unreadable) {
699
+ if (entry === undefined || entry.header.size > MAX_INSPECTED_DOCUMENT_BYTES)
614
700
  return undefined;
615
- }
701
+ const content = inflateOrRecord(entry, unreadable);
702
+ return content === undefined ? undefined : declaredSkillNameIn(content.toString('utf8'));
616
703
  }
617
704
  async function sendSkillUpload(client, displayTitle, files, bundleRoot) {
618
705
  const multipart = buildUploadBodyOrRefuse({ display_title: displayTitle }, files, bundleRoot);
@@ -650,7 +737,10 @@ function requireDeclaredName(skillMdPath) {
650
737
  * skill dir — nothing inside the tree to withhold.
651
738
  */
652
739
  async function declaredTestInputPaths(skillDir) {
653
- const config = await resolveSkillPackagingConfig(safePath.join(skillDir, 'SKILL.md'));
740
+ // `'refuse'`: an upload that cannot tell which paths are test input might
741
+ // publish an answer key. The throw is reported per skill by the install
742
+ // loop's catch (logged, and counted in its failures) — never swallowed.
743
+ const config = await resolveSkillPackagingConfig(safePath.join(skillDir, 'SKILL.md'), 'refuse');
654
744
  const declared = config?.test?.evals;
655
745
  if (declared === undefined)
656
746
  return new Set();
@@ -675,7 +765,6 @@ function resolvesToDirectory(entry, fullPath, relativePath) {
675
765
  if (!entry.isSymbolicLink())
676
766
  return false;
677
767
  try {
678
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- collected from dir walk
679
768
  return statSync(fullPath).isDirectory();
680
769
  }
681
770
  catch (error) {
@@ -717,7 +806,6 @@ function resolvesToDirectory(entry, fullPath, relativePath) {
717
806
  * would have sent.
718
807
  */
719
808
  function collectFiles(dir, base, testInput, collected) {
720
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- dir from CLI arg
721
809
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
722
810
  const fullPath = safePath.join(dir, entry.name);
723
811
  const relativePath = safePath.relative(base, fullPath);
@@ -767,7 +855,6 @@ export async function collectSkillUploadFiles(skillDir) {
767
855
  */
768
856
  async function prepareSkillUpload(skillDir, titleOverride, logger) {
769
857
  const skillMdPath = safePath.join(skillDir, 'SKILL.md');
770
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- derived from CLI arg
771
858
  if (!existsSync(skillMdPath)) {
772
859
  throw new Error(`SKILL.md not found in ${skillDir}. Is this a built skill directory?`);
773
860
  }
@@ -780,7 +867,6 @@ async function prepareSkillUpload(skillDir, titleOverride, logger) {
780
867
  const collected = await collectSkillUploadFiles(skillDir);
781
868
  const files = [];
782
869
  for (const file of collected.files) {
783
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- collected from dir walk
784
870
  const content = readFileSync(file.absolutePath);
785
871
  files.push({
786
872
  fieldName: 'files[]',
@@ -805,7 +891,7 @@ async function prepareSkillUpload(skillDir, titleOverride, logger) {
805
891
  // Resolved HERE rather than plumbed out of `collectSkillUploadFiles`, which is
806
892
  // exported and deliberately resolves its own so no caller can hand it a config
807
893
  // with the exclusion skipped.
808
- const packaging = await resolveSkillPackagingConfig(skillMdPath);
894
+ const packaging = await resolveSkillPackagingConfig(skillMdPath, 'refuse');
809
895
  warnUnportableReferences(files, dirName, packaging?.validation, logger);
810
896
  return { displayTitle, files, dirName };
811
897
  }
@@ -934,19 +1020,18 @@ async function uploadSkillVersionDir(client, skillId, skillDir, logger) {
934
1020
  * any report said so.
935
1021
  */
936
1022
  export function listNodeModulePackages(nodeModulesDir) {
937
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- constructed from temp dir
938
1023
  if (!existsSync(nodeModulesDir))
939
1024
  return [];
940
1025
  const results = [];
941
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- constructed from temp dir
1026
+ // Followed: a workspace package under node_modules IS a symlink, and a walk
1027
+ // that tested `isDirectory()` on the link skipped every one of them.
942
1028
  for (const entry of readdirSync(nodeModulesDir, { withFileTypes: true })) {
943
- if (!entry.isDirectory())
1029
+ if (direntKindFollowingSync(nodeModulesDir, entry) !== 'directory')
944
1030
  continue;
945
1031
  if (entry.name.startsWith('@')) {
946
1032
  const scopeDir = safePath.join(nodeModulesDir, entry.name);
947
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- constructed from temp dir
948
1033
  for (const scopedEntry of readdirSync(scopeDir, { withFileTypes: true })) {
949
- if (scopedEntry.isDirectory())
1034
+ if (direntKindFollowingSync(scopeDir, scopedEntry) === 'directory')
950
1035
  results.push(safePath.join(scopeDir, scopedEntry.name));
951
1036
  }
952
1037
  }
@@ -962,13 +1047,11 @@ export function listNodeModulePackages(nodeModulesDir) {
962
1047
  */
963
1048
  export function findSkillsDir(packageDir) {
964
1049
  const direct = safePath.join(packageDir, 'dist', 'skills');
965
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- constructed from temp dir
966
1050
  if (existsSync(direct))
967
1051
  return direct;
968
1052
  const candidates = listNodeModulePackages(safePath.join(packageDir, 'node_modules'));
969
1053
  for (const pkgDir of candidates) {
970
1054
  const candidate = safePath.join(pkgDir, 'dist', 'skills');
971
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- constructed from temp dir
972
1055
  if (existsSync(candidate))
973
1056
  return candidate;
974
1057
  }
@@ -1012,9 +1095,9 @@ async function installFromNpm(npmPackage, skillFilter, client, logger) {
1012
1095
  throw new Error(`No dist/skills/ directory found in ${npmPackage}. Was the package built with vat skills build?`);
1013
1096
  }
1014
1097
  logger.info(`Found skills at: ${safePath.relative(packageDir, skillsDir) || 'dist/skills/'}`);
1015
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- constructed from temp dir
1098
+ // Followed: a built skill directory that is a link is still a skill to upload.
1016
1099
  const skillDirs = readdirSync(skillsDir, { withFileTypes: true })
1017
- .filter(e => e.isDirectory())
1100
+ .filter(e => direntKindFollowingSync(skillsDir, e) === 'directory')
1018
1101
  .map(e => e.name);
1019
1102
  if (skillDirs.length === 0) {
1020
1103
  throw new Error(`No skills found in dist/skills/ of ${npmPackage}`);
@@ -1051,11 +1134,9 @@ async function installFromNpm(npmPackage, skillFilter, client, logger) {
1051
1134
  */
1052
1135
  export async function installFromLocal(source, titleOverride, client, logger) {
1053
1136
  const sourcePath = resolveSourceArgument(source);
1054
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- path from CLI arg
1055
1137
  if (!existsSync(sourcePath)) {
1056
1138
  throw new Error(`Source not found: ${sourcePath}`);
1057
1139
  }
1058
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- path from CLI arg
1059
1140
  const stat = statSync(sourcePath);
1060
1141
  // Case-INSENSITIVE. `endsWith('.zip')` refused `MySkill.ZIP` as "not a directory
1061
1142
  // or .zip file" — a file Windows and macOS both consider a zip archive, and one
@@ -1090,7 +1171,6 @@ async function installZipArchive(sourcePath, titleOverride, client, logger) {
1090
1171
  // `basename(path, ext)` matches the extension exactly, so the literal '.zip'
1091
1172
  // leaves `.ZIP` on the title. Slice the length instead.
1092
1173
  const displayTitle = titleOverride ?? basename(sourcePath).slice(0, -'.zip'.length);
1093
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- path from CLI arg
1094
1174
  const zipContent = readFileSync(sourcePath);
1095
1175
  const files = [{
1096
1176
  fieldName: 'files[]',
@@ -1127,50 +1207,162 @@ async function installZipArchive(sourcePath, titleOverride, client, logger) {
1127
1207
  ? 'from the ZIP filename — NOT from the SKILL.md inside it; pass --title to set it'
1128
1208
  : 'from --title'})`);
1129
1209
  const inspected = await inspectZipArchive(sourcePath);
1130
- if (inspected !== undefined) {
1131
- // 🚨 REFUSED, not warned. The directory lane refuses an answer key
1132
- // unconditionally (`NEVER_UPLOADED_DIR_NAMES`, plus whatever the config
1133
- // declares), and until this existed the ZIP lane bypassed `collectFiles`
1134
- // entirely and posted the archive as one opaque part — so
1135
- // `zip -r my-skill.zip my-skill/` over a tree holding
1136
- // `my-skill/evals/evals.json` published the answer key org-wide with a
1137
- // green tick, while `install my-skill/` on the very same tree refused.
1138
- // Two lanes of one command must not disagree about whether a skill's
1139
- // answer key may be published, and a warning is a disagreement.
1140
- //
1141
- // This lane can only see the CONVENTIONAL names: there is no skill
1142
- // directory to walk up from, so a config-declared suite location is
1143
- // unreadable here. That is a narrower check than the directory lane's,
1144
- // not a looser verdict on what it does see.
1145
- if (inspected.neverUploaded.length > 0) {
1146
- throw new Error(`This ZIP contains ${String(inspected.neverUploaded.length)} entr`
1147
- + `${inspected.neverUploaded.length === 1 ? 'y' : 'ies'} that are never published with a `
1148
- + `skill: ${inspected.neverUploaded.slice(0, 10).join(', ')}`
1149
- + `${inspected.neverUploaded.length > 10 ? ', …' : ''}. An eval suite is the skill's ANSWER `
1150
- + `KEY and this would publish it to everyone in the organization. Rebuild the archive from `
1151
- + `the BUILT skill directory (\`vat skills build\`), or upload that directory directly — `
1152
- + `\`install <dir>\` withholds these and reports each one.`);
1153
- }
1154
- if (inspected.uncompressedBytes > API_SKILL_MAX_UPLOAD_BYTES) {
1155
- throw new Error(`ZIP expands to ${formatBytes(inspected.uncompressedBytes)}, over the `
1156
- + `${formatBytes(API_SKILL_MAX_UPLOAD_BYTES)} ceiling. The API expands the archive and `
1157
- + `weighs the UNCOMPRESSED total, so ${formatBytes(zipContent.length)} on the wire does not `
1158
- + `get this under the limit — it will refuse with "Zip file uncompressed size exceeds 30MB". `
1159
- + `Remove files from the bundle; compressing harder cannot help.`);
1210
+ if ('archiveUnreadable' in inspected) {
1211
+ // Degrade, and SAY so. Every check below is skipped, and a skipped check
1212
+ // that is not announced is indistinguishable from one that passed.
1213
+ logger.warn(`Could not read the archive (${inspected.archiveUnreadable}); the answer-key, expanded-size, `
1214
+ + `name and portability checks were not run on it. The API decides the upload.`);
1215
+ }
1216
+ else {
1217
+ refuseOrWarnOnArchive(inspected, displayTitle, zipContent.length, logger);
1218
+ }
1219
+ return sendSkillUpload(client, displayTitle, files);
1220
+ }
1221
+ /**
1222
+ * The archive lane's own decisions, over what {@link inspectZipArchive} read:
1223
+ * two refusals (answer key, expanded size), and the warnings the inspection
1224
+ * earns (title/name divergence, portability, members it could not inflate).
1225
+ */
1226
+ function refuseOrWarnOnArchive(inspected, displayTitle, wireBytes, logger) {
1227
+ // 🚨 REFUSED, not warned. The directory lane refuses an answer key
1228
+ // unconditionally (`NEVER_UPLOADED_DIR_NAMES`, plus whatever the config
1229
+ // declares), and until this existed the ZIP lane bypassed `collectFiles`
1230
+ // entirely and posted the archive as one opaque part — so
1231
+ // `zip -r my-skill.zip my-skill/` over a tree holding
1232
+ // `my-skill/evals/evals.json` published the answer key org-wide with a
1233
+ // green tick, while `install my-skill/` on the very same tree refused.
1234
+ // Two lanes of one command must not disagree about whether a skill's
1235
+ // answer key may be published, and a warning is a disagreement.
1236
+ //
1237
+ // This lane can only see the CONVENTIONAL names: there is no skill
1238
+ // directory to walk up from, so a config-declared suite location is
1239
+ // unreadable here. That is a narrower check than the directory lane's,
1240
+ // not a looser verdict on what it does see.
1241
+ if (inspected.neverUploaded.length > 0) {
1242
+ throw new Error(`This ZIP contains ${String(inspected.neverUploaded.length)} entr`
1243
+ + `${inspected.neverUploaded.length === 1 ? 'y' : 'ies'} that are never published with a `
1244
+ + `skill: ${inspected.neverUploaded.slice(0, 10).join(', ')}`
1245
+ + `${inspected.neverUploaded.length > 10 ? ', …' : ''}. An eval suite is the skill's ANSWER `
1246
+ + `KEY and this would publish it to everyone in the organization. Rebuild the archive from `
1247
+ + `the BUILT skill directory (\`vat skills build\`), or upload that directory directly — `
1248
+ + `\`install <dir>\` withholds these and reports each one.`);
1249
+ }
1250
+ if (inspected.uncompressedBytes > API_SKILL_MAX_UPLOAD_BYTES) {
1251
+ throw new Error(`ZIP expands to ${formatBytes(inspected.uncompressedBytes)}, over the `
1252
+ + `${formatBytes(API_SKILL_MAX_UPLOAD_BYTES)} ceiling. The API expands the archive and `
1253
+ + `weighs the UNCOMPRESSED total, so ${formatBytes(wireBytes)} on the wire does not `
1254
+ + `get this under the limit — it will refuse with "Zip file uncompressed size exceeds 30MB". `
1255
+ + `Remove files from the bundle; compressing harder cannot help.`);
1256
+ }
1257
+ // The title/name divergence this branch discloses above, now stated as a
1258
+ // FACT rather than as a caveat, because the archive can be read after
1259
+ // all. A ZIP whose inner SKILL.md declares a different name publishes a
1260
+ // skill whose title and versions disagree — and that divergence is what
1261
+ // makes a title-keyed lookup unsafe, so it is worth naming at the moment
1262
+ // it is minted rather than diagnosing later.
1263
+ if (inspected.declaredName !== undefined && inspected.declaredName !== displayTitle) {
1264
+ logger.warn(`Title/name divergence: this uploads with display title "${displayTitle}", but the `
1265
+ + `SKILL.md inside declares name "${inspected.declaredName}". Every version will carry `
1266
+ + `the declared name. Pass --title "${inspected.declaredName}" to make them agree.`);
1267
+ }
1268
+ // 🚨 The SAME function the directory lane calls, over the archive's own
1269
+ // markdown. Until this line existed, `install <zip>` ran NONE of the
1270
+ // portability checks its `--help` promises of "every markdown document in
1271
+ // the bundle" — the archive went straight to `sendSkillUpload`, so the
1272
+ // adopter finding that produced the family (10 of 54 skills referencing a
1273
+ // path outside their own tree, published green and unable to run) was
1274
+ // invisible on the lane most likely to carry a hand-built bundle.
1275
+ //
1276
+ // ⚠️ NO validation config, and that is a real narrowing, stated in the
1277
+ // help text. `validation.allow` / `validation.severity` are resolved by
1278
+ // walking up from a skill's SKILL.md to its governing project — and an
1279
+ // archive member has no path on disk to walk up from, the same reason a
1280
+ // config-declared eval-suite location is unreadable here. So a waiver that
1281
+ // silences `vat skills build` does NOT silence this lane. The checks only
1282
+ // warn, so the cost of the narrowing is noise, never a blocked publish;
1283
+ // the cost of skipping them was silence on a documented promise.
1284
+ warnUnportableReferences(inspected.documents, inspected.bundleRoot, undefined, logger);
1285
+ if (inspected.unreadable.length > 0) {
1286
+ const named = inspected.unreadable.map(({ entry, reason }) => `${entry} (${reason})`).join(', ');
1287
+ logger.warn(`${String(inspected.unreadable.length)} archive member(s) could not be inflated and were `
1288
+ + `not checked: ${named}`);
1289
+ }
1290
+ }
1291
+ /**
1292
+ * Delete every version handed in, and keep going when one refuses.
1293
+ *
1294
+ * 🚨 **One refusal is a fact about ONE version.** This loop used to return on the
1295
+ * first one, so every version after it was never ATTEMPTED — not failed, not
1296
+ * reported, unexamined. Deleting a workspace skill requires deleting every
1297
+ * version first, so the operator was left with a skill that could not be deleted,
1298
+ * a report that named one problem out of an unknown number, and no way to tell
1299
+ * from the output which versions were still there. A transient refusal on version
1300
+ * 2 of 30 hid the state of the other 28.
1301
+ *
1302
+ * The sweep therefore ATTEMPTS all of them and reports both lists. The caller
1303
+ * decides what the outcome means: some deleted plus some failed is a run that
1304
+ * happened and went wrong (exit 1, with the record); nothing deleted at all is
1305
+ * the ending the command's help documents as exit 2.
1306
+ *
1307
+ * ⛔ It deliberately does NOT stop early on a repeated failure. A "the API is
1308
+ * clearly down, give up" heuristic is how the abandoned-versions defect reads
1309
+ * from inside: the whole value here is that every version's state is KNOWN when
1310
+ * the command ends.
1311
+ */
1312
+ export async function deleteEveryVersion(client, skillId, versions, logger) {
1313
+ const deleted = [];
1314
+ const failures = [];
1315
+ for (const version of versions) {
1316
+ try {
1317
+ await client.deleteSkillVersion(skillId, version);
1160
1318
  }
1161
- // The title/name divergence this branch discloses above, now stated as a
1162
- // FACT rather than as a caveat, because the archive can be read after
1163
- // all. A ZIP whose inner SKILL.md declares a different name publishes a
1164
- // skill whose title and versions disagree — and that divergence is what
1165
- // makes a title-keyed lookup unsafe, so it is worth naming at the moment
1166
- // it is minted rather than diagnosing later.
1167
- if (inspected.declaredName !== undefined && inspected.declaredName !== displayTitle) {
1168
- logger.warn(`Title/name divergence: this uploads with display title "${displayTitle}", but the `
1169
- + `SKILL.md inside declares name "${inspected.declaredName}". Every version will carry `
1170
- + `the declared name. Pass --title "${inspected.declaredName}" to make them agree.`);
1319
+ catch (error) {
1320
+ const reason = error instanceof Error ? error.message : String(error);
1321
+ failures.push({ version, reason });
1322
+ logger.info(` ⚠ version ${version} was not deleted: ${reason}`);
1323
+ continue;
1171
1324
  }
1325
+ deleted.push(version);
1326
+ logger.info(` Deleted version ${version}`);
1172
1327
  }
1173
- return sendSkillUpload(client, displayTitle, files);
1328
+ return { deleted, failures };
1329
+ }
1330
+ /**
1331
+ * Every version the sweep could not delete, and why — the one sentence both the
1332
+ * published report and the exit-2 throw are built from, so the two endings never
1333
+ * describe the same run differently.
1334
+ */
1335
+ export function describeVersionSweepFailures(failures) {
1336
+ return failures.map(failure => `${failure.version}: ${failure.reason}`).join('; ');
1337
+ }
1338
+ /**
1339
+ * The document a `--all` run publishes when it destroyed something and then did
1340
+ * not finish.
1341
+ *
1342
+ * 🔑 The deleted list is a RECORD, not a progress counter. Version deletion is
1343
+ * irreversible, so a failure part-way through leaves a workspace nobody can
1344
+ * reconstruct — and throwing here would end on `handleCommandError`'s exit 2
1345
+ * ("the run could not happen") and DISCARD the document. It happened, and it
1346
+ * destroyed things the operator now has no other list of. So the lists travel
1347
+ * with the failure and the run ends 1: it happened, and its outcome is wrong.
1348
+ *
1349
+ * `failedVersions` is the other half, and the half a re-run needs: what is still
1350
+ * there. Without it the operator knows what they lost and not what they still
1351
+ * have to deal with.
1352
+ */
1353
+ export function reportHalfDeleted(report) {
1354
+ const { skillId, deletedVersions, failedVersions } = report;
1355
+ return orgCommandFailure({
1356
+ id: skillId,
1357
+ deleted: false,
1358
+ deletedVersions,
1359
+ failedVersions,
1360
+ error: report.error,
1361
+ note: `${String(deletedVersions.length)} version(s) of ${skillId} were deleted and cannot be `
1362
+ + `restored; ${String(failedVersions.length)} could not be deleted and are still there. `
1363
+ + 'The skill itself still exists — the API refuses a skill that still has versions. '
1364
+ + 'Re-run `--all` once the cause is cleared: it will attempt only what is left.',
1365
+ });
1174
1366
  }
1175
1367
  // ── Commands ───────────────────────────────────────────────────────────
1176
1368
  export function createOrgSkillsCommand() {
@@ -1279,9 +1471,16 @@ Description:
1279
1471
  the non-portable reference, non-portable command, and unqualified MCP tool-name
1280
1472
  checks — the ones that catch a skill referencing a path outside its own tree,
1281
1473
  which cannot resolve once the API publishes it alone. These WARN and never
1282
- block, and \`validation.allow\` / \`validation.severity\` from the governing
1283
- config are honoured, so a waiver that makes \`vat skills build\` green makes
1284
- this quiet too. Run \`vat audit <dir>\` for the full report.
1474
+ block. Run \`vat audit <dir>\` for the full report.
1475
+
1476
+ A ZIP faces the same three checks: its markdown members are read out of the
1477
+ archive and run through them, so a hand-built bundle is not the quiet lane.
1478
+ The one difference is the config. On a DIRECTORY, \`validation.allow\` /
1479
+ \`validation.severity\` from the governing vibe-agent-toolkit.config.yaml are
1480
+ honoured, so a waiver that makes \`vat skills build\` green makes this quiet
1481
+ too. On a ZIP there is no directory to resolve that config from — the same
1482
+ reason a config-declared eval-suite location is invisible here — so the checks
1483
+ run UNWAIVED and a warning you have already waived elsewhere will still print.
1285
1484
 
1286
1485
  There are TWO size gates and a ZIP faces both. The REQUEST gate weighs the
1287
1486
  multipart body — file bytes plus about 156 bytes of framing per file — against
@@ -1325,14 +1524,11 @@ Examples:
1325
1524
  // travels with the failure, and the run ends 1: it happened, and its
1326
1525
  // outcome is wrong.
1327
1526
  const deletedVersions = [];
1328
- const halfDeleted = (error) => orgCommandFailure({
1329
- id: skillId,
1330
- deleted: false,
1527
+ const halfDeleted = (error, failedVersions) => reportHalfDeleted({
1528
+ skillId,
1331
1529
  deletedVersions,
1530
+ failedVersions,
1332
1531
  error: error instanceof Error ? error.message : String(error),
1333
- note: `${String(deletedVersions.length)} version(s) of ${skillId} were deleted before this `
1334
- + 'failed and cannot be restored. The skill itself still exists. Re-run '
1335
- + '`--all` once the cause is cleared; the versions listed here are already gone.',
1336
1532
  });
1337
1533
  if (options.all) {
1338
1534
  const versions = await autopaginateSkills(client, skillVersionsPath(skillId));
@@ -1340,21 +1536,25 @@ Examples:
1340
1536
  // "Found …", not "Deleting …": nothing has been deleted at this point
1341
1537
  // and the very next call can be refused. Cf. `installFromLocal`.
1342
1538
  logger.info(`Found ${String(versionData.length)} version(s) of ${skillId} to delete first`);
1343
- for (const ver of versionData) {
1344
- try {
1345
- await client.deleteSkillVersion(skillId, ver.version);
1539
+ // ATTEMPTS every version, whatever any one of them answers. See
1540
+ // `deleteEveryVersion`: returning on the first refusal left the rest
1541
+ // unexamined, and the skill undeletable with no record of what remained.
1542
+ const sweep = await deleteEveryVersion(client, skillId, versionData.map(v => v.version), logger);
1543
+ deletedVersions.push(...sweep.deleted);
1544
+ if (sweep.failures.length > 0) {
1545
+ const detail = describeVersionSweepFailures(sweep.failures);
1546
+ // Nothing was destroyed, so there is no record worth publishing and
1547
+ // "could not happen" (exit 2, via the throw) is the honest ending —
1548
+ // the same rule as before, now decided on the WHOLE sweep rather than
1549
+ // on its first refusal.
1550
+ if (deletedVersions.length === 0) {
1551
+ throw new Error(`No version of ${skillId} could be deleted, so the skill was left alone. `
1552
+ + `${String(sweep.failures.length)} attempt(s) failed — ${detail}`);
1346
1553
  }
1347
- catch (error) {
1348
- // Only after at least one version is gone is there a report worth
1349
- // publishing. Before that nothing happened, and "could not happen"
1350
- // (exit 2, via the throw) is the honest ending.
1351
- if (deletedVersions.length === 0)
1352
- throw error;
1353
- logger.info(` ⚠ version ${ver.version} was not deleted`);
1354
- return halfDeleted(error);
1355
- }
1356
- deletedVersions.push(ver.version);
1357
- logger.info(` Deleted version ${ver.version}`);
1554
+ // The skill delete is not attempted: the API refuses a skill that still
1555
+ // has versions, so it would spend a round trip to be told what is
1556
+ // already known here.
1557
+ return halfDeleted(new Error(detail), sweep.failures.map(failure => failure.version));
1358
1558
  }
1359
1559
  }
1360
1560
  logger.info(`Deleting skill: ${skillId}`);
@@ -1369,7 +1569,9 @@ Examples:
1369
1569
  catch (error) {
1370
1570
  if (deletedVersions.length === 0)
1371
1571
  throw error;
1372
- return halfDeleted(error);
1572
+ // The sweep completed — every version is gone — and the SKILL delete is
1573
+ // what failed, so nothing is left in `failedVersions`.
1574
+ return halfDeleted(error, []);
1373
1575
  }
1374
1576
  const deleted = reportDelete(raw, skillId, SKILL_DELETED_TYPES);
1375
1577
  return deletedVersions.length === 0
@@ -1389,16 +1591,24 @@ Description:
1389
1591
  delete every version and then the skill in one command; without it, delete the
1390
1592
  versions yourself with \`vat claude org skills versions delete\` first.
1391
1593
 
1392
- --all deletes irreversibly and in a loop. If it fails part-way through, the
1393
- versions it already destroyed are listed under deletedVersions in the output
1394
- and the run exits 1 — the run happened, and its outcome is wrong. It exits 2
1395
- only when nothing was deleted at all.
1594
+ --all deletes irreversibly and in a loop, and it ATTEMPTS EVERY VERSION even
1595
+ when one refuses — one refusal is a fact about one version, not about the ones
1596
+ after it. When the sweep ends with anything left, the versions it destroyed are
1597
+ listed under deletedVersions, the ones still there under failedVersions, and
1598
+ the run exits 1 — the run happened, and its outcome is wrong. The skill itself
1599
+ is then left alone, because the API refuses a skill that still has versions.
1600
+ It exits 2 only when nothing was deleted at all.
1601
+
1602
+ A version delete whose response is lost is replayed, and the replay is answered
1603
+ 404 because the first attempt already removed it. That 404 is read as the
1604
+ delete it is, not as a failure — the version appears under deletedVersions.
1396
1605
 
1397
1606
  Exit Codes:
1398
1607
  0 - The skill was deleted
1399
1608
  1 - The run completed and the skill was NOT deleted: the API named a different
1400
- outcome, or --all destroyed some versions and then failed
1401
- 2 - The run could not happen: no API key, or the first request was refused
1609
+ outcome, or --all destroyed some versions and could not destroy the rest
1610
+ 2 - The run could not happen: no API key, the first request was refused, or
1611
+ --all deleted no version at all
1402
1612
 
1403
1613
  Example:
1404
1614
  $ vat claude org skills delete skill_abc123 --all
@@ -1470,10 +1680,8 @@ Example:
1470
1680
  .action(async (skillId, source, options) => {
1471
1681
  await executeOrgCommand('OrgSkillsVersionsAdd', options.debug, async ({ client, logger }) => {
1472
1682
  const resolved = resolveSourceArgument(source);
1473
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- path from CLI arg
1474
1683
  if (!existsSync(resolved))
1475
1684
  throw new Error(`Source not found: ${resolved}`);
1476
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- path from CLI arg
1477
1685
  if (!statSync(resolved).isDirectory()) {
1478
1686
  // Name the asymmetry IN the refusal, not only in --help. `install`
1479
1687
  // accepts a ZIP and this verb does not, so the operator most likely to