@design-ai/cli 4.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (529) hide show
  1. package/.claude-plugin/plugin.json +164 -0
  2. package/AGENTS.ko.md +126 -0
  3. package/AGENTS.md +182 -0
  4. package/CHANGELOG.md +6883 -0
  5. package/CLAUDE.md +39 -0
  6. package/LICENSE +21 -0
  7. package/README.ko.md +245 -0
  8. package/README.md +277 -0
  9. package/agents/README.md +14 -0
  10. package/agents/a11y-reviewer.md +121 -0
  11. package/agents/component-architect.md +57 -0
  12. package/agents/design-critic.md +46 -0
  13. package/agents/token-extractor.md +63 -0
  14. package/cli/bin/design-ai-mcp.mjs +6 -0
  15. package/cli/bin/design-ai.mjs +40 -0
  16. package/cli/commands/audit.mjs +111 -0
  17. package/cli/commands/check.mjs +226 -0
  18. package/cli/commands/doctor.mjs +141 -0
  19. package/cli/commands/examples.mjs +58 -0
  20. package/cli/commands/help.mjs +271 -0
  21. package/cli/commands/install.mjs +161 -0
  22. package/cli/commands/learn.mjs +1871 -0
  23. package/cli/commands/list.mjs +143 -0
  24. package/cli/commands/mcp.mjs +27 -0
  25. package/cli/commands/pack.mjs +211 -0
  26. package/cli/commands/prompt.mjs +199 -0
  27. package/cli/commands/route.mjs +247 -0
  28. package/cli/commands/search.mjs +60 -0
  29. package/cli/commands/show.mjs +52 -0
  30. package/cli/commands/site.mjs +558 -0
  31. package/cli/commands/status.mjs +161 -0
  32. package/cli/commands/uninstall.mjs +119 -0
  33. package/cli/commands/update.mjs +184 -0
  34. package/cli/commands/version.mjs +94 -0
  35. package/cli/commands/workspace.mjs +176 -0
  36. package/cli/lib/brief.mjs +52 -0
  37. package/cli/lib/check.mjs +642 -0
  38. package/cli/lib/dispatch.mjs +109 -0
  39. package/cli/lib/doctor.mjs +505 -0
  40. package/cli/lib/examples.mjs +258 -0
  41. package/cli/lib/exec.mjs +63 -0
  42. package/cli/lib/help-flags.mjs +5 -0
  43. package/cli/lib/learn.mjs +3694 -0
  44. package/cli/lib/log.mjs +47 -0
  45. package/cli/lib/mcp-server.mjs +426 -0
  46. package/cli/lib/output.mjs +101 -0
  47. package/cli/lib/pack.mjs +599 -0
  48. package/cli/lib/paths.mjs +63 -0
  49. package/cli/lib/prompt.mjs +603 -0
  50. package/cli/lib/route.mjs +740 -0
  51. package/cli/lib/search.mjs +158 -0
  52. package/cli/lib/show.mjs +161 -0
  53. package/cli/lib/signals.mjs +2337 -0
  54. package/cli/lib/site.mjs +6617 -0
  55. package/cli/lib/skill-proposals.mjs +2046 -0
  56. package/cli/lib/suggest.mjs +66 -0
  57. package/cli/lib/workspace.mjs +1056 -0
  58. package/commands/README.md +25 -0
  59. package/commands/component-spec.md +34 -0
  60. package/commands/conversational.md +44 -0
  61. package/commands/design-from-brief.md +141 -0
  62. package/commands/design-review.md +62 -0
  63. package/commands/document-from-brief.md +47 -0
  64. package/commands/extract-tokens.md +38 -0
  65. package/commands/game-ui.md +40 -0
  66. package/commands/illustration.md +37 -0
  67. package/commands/iterate.md +167 -0
  68. package/commands/motion-design.md +54 -0
  69. package/commands/palette-from-brand.md +50 -0
  70. package/commands/print.md +49 -0
  71. package/commands/slide-deck.md +46 -0
  72. package/commands/spatial.md +47 -0
  73. package/commands/stability-review.md +74 -0
  74. package/commands/video.md +42 -0
  75. package/commands/website-improvement.md +77 -0
  76. package/docs/AGENT-DEVELOPMENT.md +658 -0
  77. package/docs/AI-LEARNING.md +429 -0
  78. package/docs/AIDER-INTEGRATION.md +224 -0
  79. package/docs/ARCHITECTURE.ko.md +183 -0
  80. package/docs/ARCHITECTURE.md +105 -0
  81. package/docs/CODEX-INTEGRATION.md +311 -0
  82. package/docs/COMPANY-WEBSITE-DOGFOOD.ko.md +144 -0
  83. package/docs/COMPANY-WEBSITE-DOGFOOD.md +150 -0
  84. package/docs/COMPANY-WEBSITE-INTAKE-TEMPLATE.ko.md +153 -0
  85. package/docs/COMPANY-WEBSITE-INTAKE-TEMPLATE.md +154 -0
  86. package/docs/CONTRIBUTING.ko.md +203 -0
  87. package/docs/CONTRIBUTING.md +242 -0
  88. package/docs/CURSOR-INTEGRATION.md +220 -0
  89. package/docs/DISTRIBUTION.ko.md +248 -0
  90. package/docs/DISTRIBUTION.md +253 -0
  91. package/docs/DOGFOOD-FINDINGS.md +180 -0
  92. package/docs/DOGFOOD-V4-FINDINGS.md +139 -0
  93. package/docs/DOGFOOD-V4-MKDOCS-FINDINGS.md +233 -0
  94. package/docs/DOGFOOD-V4-NPM-FINDINGS.md +157 -0
  95. package/docs/DOGFOOD-V4-VSCODE-FINDINGS.md +135 -0
  96. package/docs/FIGMA-INTEGRATION.md +222 -0
  97. package/docs/MCP-INTEGRATION.md +243 -0
  98. package/docs/MIGRATION-v4.md +121 -0
  99. package/docs/PLUGIN-PACKAGING.md +240 -0
  100. package/docs/PRODUCT-READINESS.md +66 -0
  101. package/docs/QUICKSTART.ko.md +178 -0
  102. package/docs/QUICKSTART.md +179 -0
  103. package/docs/RELEASE-CHECKLIST.md +292 -0
  104. package/docs/ROADMAP.md +19495 -0
  105. package/docs/SESSION-LOG.md +940 -0
  106. package/docs/TOKEN-SYNC.md +237 -0
  107. package/docs/USING.ko.md +206 -0
  108. package/docs/USING.md +218 -0
  109. package/docs/WEBSITE-IMPROVEMENT.ko.md +75 -0
  110. package/docs/WEBSITE-IMPROVEMENT.md +89 -0
  111. package/docs/announcements/README.md +56 -0
  112. package/docs/announcements/dev-to-korea.md +206 -0
  113. package/docs/announcements/hashnode-post.ko.md +232 -0
  114. package/docs/announcements/okky-post.ko.md +158 -0
  115. package/docs/announcements/press-kit.md +101 -0
  116. package/docs/announcements/reddit-r-korea.md +196 -0
  117. package/docs/announcements/show-hn.md +114 -0
  118. package/docs/announcements/twitter-thread.md +255 -0
  119. package/docs/external-status.md +34 -0
  120. package/docs/integrations/aider-walkthrough.ko.md +275 -0
  121. package/docs/integrations/aider-walkthrough.md +274 -0
  122. package/docs/integrations/codex-walkthrough.ko.md +297 -0
  123. package/docs/integrations/codex-walkthrough.md +297 -0
  124. package/docs/integrations/cursor-walkthrough.ko.md +281 -0
  125. package/docs/integrations/cursor-walkthrough.md +284 -0
  126. package/docs/integrations/design-ai-mcp-server.md +169 -0
  127. package/docs/integrations/figma-mcp.md +149 -0
  128. package/docs/integrations/github-mcp.md +178 -0
  129. package/docs/integrations/linear-mcp.md +215 -0
  130. package/docs/integrations/notion-mcp.md +174 -0
  131. package/docs/integrations/sdk-walkthrough.ko.md +375 -0
  132. package/docs/integrations/sdk-walkthrough.md +375 -0
  133. package/docs/integrations/slack-mcp.md +208 -0
  134. package/docs/integrations/vscode-walkthrough.ko.md +202 -0
  135. package/docs/integrations/vscode-walkthrough.md +205 -0
  136. package/docs/requirements.txt +16 -0
  137. package/docs/site-overrides/extra.css +83 -0
  138. package/docs/site-overrides/favicon.svg +4 -0
  139. package/docs/site-overrides/logo.svg +10 -0
  140. package/docs/site-overrides/main.html +18 -0
  141. package/docs/website-console/app.js +2540 -0
  142. package/docs/website-console/index.html +24 -0
  143. package/docs/website-console/styles.css +975 -0
  144. package/examples/README.md +252 -0
  145. package/examples/cases/dogfood-v4-korean-hr-onboarding.md +356 -0
  146. package/examples/component-accordion-actions.md +153 -0
  147. package/examples/component-accordion-details.md +147 -0
  148. package/examples/component-accordion-summary.md +180 -0
  149. package/examples/component-accordion.md +229 -0
  150. package/examples/component-account-card.md +284 -0
  151. package/examples/component-address-input.md +260 -0
  152. package/examples/component-affix.md +190 -0
  153. package/examples/component-alert-dialog.md +210 -0
  154. package/examples/component-alert-title.md +77 -0
  155. package/examples/component-alert.md +192 -0
  156. package/examples/component-amount-input.md +278 -0
  157. package/examples/component-anchor.md +198 -0
  158. package/examples/component-app-bar.md +249 -0
  159. package/examples/component-aspect-ratio.md +156 -0
  160. package/examples/component-auto-complete.md +199 -0
  161. package/examples/component-avatar-group.md +170 -0
  162. package/examples/component-avatar.md +191 -0
  163. package/examples/component-back-top.md +123 -0
  164. package/examples/component-backdrop.md +148 -0
  165. package/examples/component-badge.md +181 -0
  166. package/examples/component-banner.md +237 -0
  167. package/examples/component-biometric-gate.md +281 -0
  168. package/examples/component-blockquote.md +153 -0
  169. package/examples/component-border-beam.md +207 -0
  170. package/examples/component-bottom-navigation.md +226 -0
  171. package/examples/component-box.md +112 -0
  172. package/examples/component-breadcrumb.md +222 -0
  173. package/examples/component-button-base.md +190 -0
  174. package/examples/component-button-group.md +117 -0
  175. package/examples/component-button.md +213 -0
  176. package/examples/component-calendar.md +280 -0
  177. package/examples/component-callout.md +131 -0
  178. package/examples/component-card-actions.md +101 -0
  179. package/examples/component-card-content.md +90 -0
  180. package/examples/component-card-header.md +122 -0
  181. package/examples/component-card-media.md +79 -0
  182. package/examples/component-card.md +233 -0
  183. package/examples/component-carousel.md +266 -0
  184. package/examples/component-cascader.md +135 -0
  185. package/examples/component-category-picker.md +271 -0
  186. package/examples/component-chart.md +237 -0
  187. package/examples/component-chat-interface.md +399 -0
  188. package/examples/component-checkbox.md +149 -0
  189. package/examples/component-click-away-listener.md +87 -0
  190. package/examples/component-code.md +213 -0
  191. package/examples/component-collapsible.md +219 -0
  192. package/examples/component-color-picker.md +110 -0
  193. package/examples/component-combobox.md +267 -0
  194. package/examples/component-command.md +299 -0
  195. package/examples/component-config-provider.md +190 -0
  196. package/examples/component-context-menu.md +179 -0
  197. package/examples/component-css-baseline.md +148 -0
  198. package/examples/component-date-picker.md +249 -0
  199. package/examples/component-descriptions.md +242 -0
  200. package/examples/component-dialog-actions.md +111 -0
  201. package/examples/component-dialog-content-text.md +76 -0
  202. package/examples/component-dialog-content.md +105 -0
  203. package/examples/component-dialog-title.md +104 -0
  204. package/examples/component-dialog.md +176 -0
  205. package/examples/component-divider.md +182 -0
  206. package/examples/component-doc-page.md +217 -0
  207. package/examples/component-drawer.md +257 -0
  208. package/examples/component-dropdown.md +220 -0
  209. package/examples/component-email-layout.md +253 -0
  210. package/examples/component-empty-state.md +269 -0
  211. package/examples/component-empty.md +172 -0
  212. package/examples/component-fade.md +87 -0
  213. package/examples/component-feature-grid.md +201 -0
  214. package/examples/component-field.md +266 -0
  215. package/examples/component-filled-input.md +114 -0
  216. package/examples/component-flex.md +117 -0
  217. package/examples/component-float-button.md +214 -0
  218. package/examples/component-form-control-label.md +134 -0
  219. package/examples/component-form-control.md +113 -0
  220. package/examples/component-form-controls.md +251 -0
  221. package/examples/component-form-group.md +86 -0
  222. package/examples/component-form-helper-text.md +95 -0
  223. package/examples/component-form-label.md +98 -0
  224. package/examples/component-form.md +291 -0
  225. package/examples/component-game-hud.md +362 -0
  226. package/examples/component-game-menu.md +327 -0
  227. package/examples/component-grid.md +140 -0
  228. package/examples/component-grow.md +79 -0
  229. package/examples/component-hero-block.md +252 -0
  230. package/examples/component-hover-card.md +211 -0
  231. package/examples/component-icon-button.md +142 -0
  232. package/examples/component-icon.md +102 -0
  233. package/examples/component-illustration.md +280 -0
  234. package/examples/component-image-list.md +107 -0
  235. package/examples/component-image.md +282 -0
  236. package/examples/component-input-adornment.md +101 -0
  237. package/examples/component-input-base.md +134 -0
  238. package/examples/component-input-number.md +141 -0
  239. package/examples/component-input-otp.md +271 -0
  240. package/examples/component-input.md +213 -0
  241. package/examples/component-item.md +243 -0
  242. package/examples/component-kbd.md +197 -0
  243. package/examples/component-krw-amount.md +219 -0
  244. package/examples/component-label.md +118 -0
  245. package/examples/component-layout.md +217 -0
  246. package/examples/component-link.md +223 -0
  247. package/examples/component-list-item-avatar.md +72 -0
  248. package/examples/component-list-item-button.md +139 -0
  249. package/examples/component-list-item-icon.md +82 -0
  250. package/examples/component-list-item-text.md +98 -0
  251. package/examples/component-list-item.md +116 -0
  252. package/examples/component-list-subheader.md +70 -0
  253. package/examples/component-list.md +198 -0
  254. package/examples/component-loading-button.md +125 -0
  255. package/examples/component-loading-sequence.md +254 -0
  256. package/examples/component-lottie-player.md +279 -0
  257. package/examples/component-masonry.md +191 -0
  258. package/examples/component-mentions.md +189 -0
  259. package/examples/component-menu-item.md +102 -0
  260. package/examples/component-menu-list.md +98 -0
  261. package/examples/component-menu.md +139 -0
  262. package/examples/component-menubar.md +259 -0
  263. package/examples/component-message.md +162 -0
  264. package/examples/component-mobile-stepper.md +155 -0
  265. package/examples/component-modal.md +220 -0
  266. package/examples/component-navigation-menu.md +295 -0
  267. package/examples/component-notification.md +166 -0
  268. package/examples/component-otp-countdown.md +245 -0
  269. package/examples/component-outlined-input.md +145 -0
  270. package/examples/component-page-transition.md +281 -0
  271. package/examples/component-pagination.md +226 -0
  272. package/examples/component-paper.md +199 -0
  273. package/examples/component-pass-auth.md +265 -0
  274. package/examples/component-payment-brand-button.md +253 -0
  275. package/examples/component-payment-method-selector.md +272 -0
  276. package/examples/component-payment-receipt.md +277 -0
  277. package/examples/component-popconfirm.md +144 -0
  278. package/examples/component-popover.md +239 -0
  279. package/examples/component-popper.md +129 -0
  280. package/examples/component-pricing-cards.md +247 -0
  281. package/examples/component-progress.md +195 -0
  282. package/examples/component-qr-code.md +201 -0
  283. package/examples/component-radio.md +167 -0
  284. package/examples/component-rate.md +202 -0
  285. package/examples/component-resizable.md +163 -0
  286. package/examples/component-result.md +270 -0
  287. package/examples/component-scroll-area.md +199 -0
  288. package/examples/component-scroll-reveal.md +320 -0
  289. package/examples/component-segmented.md +178 -0
  290. package/examples/component-select.md +258 -0
  291. package/examples/component-separator.md +177 -0
  292. package/examples/component-sheet.md +277 -0
  293. package/examples/component-sidebar.md +257 -0
  294. package/examples/component-skeleton.md +178 -0
  295. package/examples/component-slide.md +89 -0
  296. package/examples/component-slider.md +208 -0
  297. package/examples/component-snackbar-content.md +92 -0
  298. package/examples/component-snackbar.md +121 -0
  299. package/examples/component-sonner.md +165 -0
  300. package/examples/component-space.md +125 -0
  301. package/examples/component-spatial-locomotion.md +373 -0
  302. package/examples/component-spatial-panel.md +330 -0
  303. package/examples/component-speed-dial-action.md +78 -0
  304. package/examples/component-speed-dial.md +177 -0
  305. package/examples/component-spin.md +173 -0
  306. package/examples/component-spinner.md +204 -0
  307. package/examples/component-splitter.md +210 -0
  308. package/examples/component-stack.md +130 -0
  309. package/examples/component-statistic.md +282 -0
  310. package/examples/component-step-button.md +157 -0
  311. package/examples/component-step-connector.md +157 -0
  312. package/examples/component-step-content.md +121 -0
  313. package/examples/component-step-icon.md +115 -0
  314. package/examples/component-step-label.md +101 -0
  315. package/examples/component-step.md +84 -0
  316. package/examples/component-steps.md +264 -0
  317. package/examples/component-stock-chart.md +251 -0
  318. package/examples/component-swipeable-drawer.md +149 -0
  319. package/examples/component-switch.md +99 -0
  320. package/examples/component-tab-scroll-button.md +180 -0
  321. package/examples/component-tab.md +120 -0
  322. package/examples/component-table-body.md +82 -0
  323. package/examples/component-table-cell.md +118 -0
  324. package/examples/component-table-container.md +75 -0
  325. package/examples/component-table-footer.md +72 -0
  326. package/examples/component-table-head.md +78 -0
  327. package/examples/component-table-pagination.md +137 -0
  328. package/examples/component-table-row.md +113 -0
  329. package/examples/component-table-sort-label.md +109 -0
  330. package/examples/component-table.md +202 -0
  331. package/examples/component-tabs.md +184 -0
  332. package/examples/component-tag-badge.md +309 -0
  333. package/examples/component-tag.md +102 -0
  334. package/examples/component-testimonial-carousel.md +218 -0
  335. package/examples/component-textarea-autosize.md +114 -0
  336. package/examples/component-textarea.md +173 -0
  337. package/examples/component-time-picker.md +205 -0
  338. package/examples/component-timeline.md +170 -0
  339. package/examples/component-toast.md +224 -0
  340. package/examples/component-toggle-button.md +129 -0
  341. package/examples/component-toggle.md +220 -0
  342. package/examples/component-toolbar.md +104 -0
  343. package/examples/component-tooltip.md +178 -0
  344. package/examples/component-tour.md +176 -0
  345. package/examples/component-transaction-list-item.md +243 -0
  346. package/examples/component-transfer.md +156 -0
  347. package/examples/component-tree-select.md +202 -0
  348. package/examples/component-tree.md +297 -0
  349. package/examples/component-typography.md +215 -0
  350. package/examples/component-upload.md +295 -0
  351. package/examples/component-video-hero.md +409 -0
  352. package/examples/component-video-player.md +340 -0
  353. package/examples/component-voice-input.md +408 -0
  354. package/examples/component-watermark.md +217 -0
  355. package/examples/component-zoom.md +92 -0
  356. package/examples/doc-explanation-example.md +196 -0
  357. package/examples/doc-how-to-example.md +205 -0
  358. package/examples/doc-tutorial-example.md +162 -0
  359. package/examples/dogfood-korean-fintech-system.md +433 -0
  360. package/examples/email-transactional-example.md +259 -0
  361. package/examples/palette-saas-violet.md +235 -0
  362. package/examples/print-business-card-spec.md +205 -0
  363. package/examples/print-packaging-spec.md +285 -0
  364. package/examples/report-example.md +228 -0
  365. package/examples/slide-deck-example.md +237 -0
  366. package/examples/website-improvement-report.md +128 -0
  367. package/examples/website-improvement-workspace.json +126 -0
  368. package/install.sh +189 -0
  369. package/knowledge/COVERAGE.md +740 -0
  370. package/knowledge/PRINCIPLES.md +108 -0
  371. package/knowledge/a11y/contrast.md +70 -0
  372. package/knowledge/a11y/keyboard-and-focus.md +108 -0
  373. package/knowledge/colors/color-theory.md +90 -0
  374. package/knowledge/colors/mui-palette-structure.md +105 -0
  375. package/knowledge/colors/palettes-by-product-type.md +876 -0
  376. package/knowledge/components/INDEX.md +231 -0
  377. package/knowledge/components/index.json +1470 -0
  378. package/knowledge/components/shadcn-registry.md +159 -0
  379. package/knowledge/conversational/ai-chat-interfaces.md +323 -0
  380. package/knowledge/conversational/chatbot-design.md +312 -0
  381. package/knowledge/conversational/conversational-ui-fundamentals.md +299 -0
  382. package/knowledge/conversational/korean-voice-conventions.md +281 -0
  383. package/knowledge/conversational/voice-ui-patterns.md +297 -0
  384. package/knowledge/design-tokens/ant-design.md +124 -0
  385. package/knowledge/design-tokens/material-3.md +235 -0
  386. package/knowledge/design-tokens/polaris-and-carbon.md +231 -0
  387. package/knowledge/design-tokens/tailwind-v4.md +284 -0
  388. package/knowledge/game-ui/game-accessibility.md +317 -0
  389. package/knowledge/game-ui/game-ui-fundamentals.md +281 -0
  390. package/knowledge/game-ui/hud-design.md +324 -0
  391. package/knowledge/game-ui/korean-gaming-conventions.md +312 -0
  392. package/knowledge/game-ui/menu-systems.md +388 -0
  393. package/knowledge/i18n/korean-app-store-visual.md +223 -0
  394. package/knowledge/i18n/korean-document-style.md +301 -0
  395. package/knowledge/i18n/korean-payments.md +211 -0
  396. package/knowledge/i18n/korean-product-conventions.md +96 -0
  397. package/knowledge/i18n/korean-publishing.md +139 -0
  398. package/knowledge/i18n/korean-typography.md +123 -0
  399. package/knowledge/icons/curated-sets.md +225 -0
  400. package/knowledge/illustration/hero-illustrations.md +234 -0
  401. package/knowledge/illustration/illustration-systems.md +221 -0
  402. package/knowledge/illustration/mascot-design.md +208 -0
  403. package/knowledge/illustration/spot-illustrations.md +246 -0
  404. package/knowledge/illustration/svg-optimization.md +308 -0
  405. package/knowledge/layout/spacing-and-grid.md +119 -0
  406. package/knowledge/motion/app-loading-sequences.md +277 -0
  407. package/knowledge/motion/choreography-depth.md +297 -0
  408. package/knowledge/motion/marketing-motion.md +223 -0
  409. package/knowledge/motion/micro-interactions.md +266 -0
  410. package/knowledge/motion/motion-tools.md +281 -0
  411. package/knowledge/motion/principles.md +126 -0
  412. package/knowledge/patterns/auth-flow-design.md +316 -0
  413. package/knowledge/patterns/b2b-onboarding-flows.md +182 -0
  414. package/knowledge/patterns/brand-identity.md +238 -0
  415. package/knowledge/patterns/brand-references.md +97 -0
  416. package/knowledge/patterns/chart-color-encoding.md +269 -0
  417. package/knowledge/patterns/chart-types.md +344 -0
  418. package/knowledge/patterns/dashboard-composition.md +299 -0
  419. package/knowledge/patterns/design-system-qa.md +356 -0
  420. package/knowledge/patterns/document-typography.md +248 -0
  421. package/knowledge/patterns/email-design.md +338 -0
  422. package/knowledge/patterns/empty-states.md +263 -0
  423. package/knowledge/patterns/error-states.md +318 -0
  424. package/knowledge/patterns/form-design.md +237 -0
  425. package/knowledge/patterns/information-architecture.md +341 -0
  426. package/knowledge/patterns/landing-hero-design.md +283 -0
  427. package/knowledge/patterns/landing-page-patterns.md +681 -0
  428. package/knowledge/patterns/list-and-feed.md +371 -0
  429. package/knowledge/patterns/mobile-navigation.md +278 -0
  430. package/knowledge/patterns/money-and-amount.md +301 -0
  431. package/knowledge/patterns/onboarding.md +300 -0
  432. package/knowledge/patterns/pricing-page-design.md +261 -0
  433. package/knowledge/patterns/realtime-data.md +280 -0
  434. package/knowledge/patterns/report-design.md +330 -0
  435. package/knowledge/patterns/search-ux.md +373 -0
  436. package/knowledge/patterns/settings-page.md +325 -0
  437. package/knowledge/patterns/slide-deck-design.md +316 -0
  438. package/knowledge/patterns/styles-catalog.md +2599 -0
  439. package/knowledge/patterns/technical-writing.md +306 -0
  440. package/knowledge/patterns/ui-reasoning.md +1853 -0
  441. package/knowledge/patterns/ux-guidelines.md +1454 -0
  442. package/knowledge/platforms/react-native.md +395 -0
  443. package/knowledge/print/brochures-and-flyers.md +276 -0
  444. package/knowledge/print/korean-print-conventions.md +284 -0
  445. package/knowledge/print/packaging.md +321 -0
  446. package/knowledge/print/print-fundamentals.md +238 -0
  447. package/knowledge/print/signage-and-posters.md +235 -0
  448. package/knowledge/print/stationery.md +262 -0
  449. package/knowledge/spatial/ar-patterns.md +253 -0
  450. package/knowledge/spatial/comfort-and-accessibility.md +245 -0
  451. package/knowledge/spatial/spatial-design-fundamentals.md +346 -0
  452. package/knowledge/spatial/spatial-ui-elements.md +343 -0
  453. package/knowledge/spatial/vr-patterns.md +302 -0
  454. package/knowledge/typography/font-pairings.md +332 -0
  455. package/knowledge/typography/mui-type-scale.md +57 -0
  456. package/knowledge/typography/type-scale-fundamentals.md +105 -0
  457. package/knowledge/video/in-product-video.md +277 -0
  458. package/knowledge/video/korean-video-conventions.md +247 -0
  459. package/knowledge/video/marketing-video.md +258 -0
  460. package/knowledge/video/social-and-short-form.md +257 -0
  461. package/knowledge/video/video-fundamentals.md +296 -0
  462. package/package.json +106 -0
  463. package/skills/README.md +39 -0
  464. package/skills/color-palette/PLAYBOOK.md +175 -0
  465. package/skills/color-palette/SKILL.md +6 -0
  466. package/skills/color-palette/TEMPLATE.md +103 -0
  467. package/skills/component-spec-writer/PLAYBOOK.md +169 -0
  468. package/skills/component-spec-writer/SKILL.md +6 -0
  469. package/skills/component-spec-writer/TEMPLATE.md +115 -0
  470. package/skills/conversational-ui-designer/PLAYBOOK.md +231 -0
  471. package/skills/conversational-ui-designer/SKILL.md +6 -0
  472. package/skills/design-broadcast/PLAYBOOK.md +146 -0
  473. package/skills/design-broadcast/SKILL.md +6 -0
  474. package/skills/design-critique/PLAYBOOK.md +137 -0
  475. package/skills/design-critique/SKILL.md +6 -0
  476. package/skills/design-pr-review/PLAYBOOK.md +171 -0
  477. package/skills/design-pr-review/SKILL.md +6 -0
  478. package/skills/design-system-builder/PLAYBOOK.md +218 -0
  479. package/skills/design-system-builder/SKILL.md +6 -0
  480. package/skills/design-system-qa/PLAYBOOK.md +163 -0
  481. package/skills/design-system-qa/SKILL.md +6 -0
  482. package/skills/document-author/PLAYBOOK.md +167 -0
  483. package/skills/document-author/SKILL.md +6 -0
  484. package/skills/figma-token-sync/PLAYBOOK.md +149 -0
  485. package/skills/figma-token-sync/SKILL.md +6 -0
  486. package/skills/game-ui-designer/PLAYBOOK.md +209 -0
  487. package/skills/game-ui-designer/SKILL.md +6 -0
  488. package/skills/handoff-spec/PLAYBOOK.md +170 -0
  489. package/skills/handoff-spec/SKILL.md +6 -0
  490. package/skills/illustration-designer/PLAYBOOK.md +166 -0
  491. package/skills/illustration-designer/SKILL.md +6 -0
  492. package/skills/motion-designer/PLAYBOOK.md +223 -0
  493. package/skills/motion-designer/SKILL.md +6 -0
  494. package/skills/print-designer/PLAYBOOK.md +209 -0
  495. package/skills/print-designer/SKILL.md +6 -0
  496. package/skills/slide-deck-author/PLAYBOOK.md +198 -0
  497. package/skills/slide-deck-author/SKILL.md +6 -0
  498. package/skills/spatial-designer/PLAYBOOK.md +238 -0
  499. package/skills/spatial-designer/SKILL.md +6 -0
  500. package/skills/ux-audit/PLAYBOOK.md +142 -0
  501. package/skills/ux-audit/SKILL.md +6 -0
  502. package/skills/video-designer/PLAYBOOK.md +240 -0
  503. package/skills/video-designer/SKILL.md +6 -0
  504. package/skills/website-improvement/PLAYBOOK.md +118 -0
  505. package/skills/website-improvement/SKILL.md +6 -0
  506. package/skills/website-improvement/TEMPLATE.md +103 -0
  507. package/tools/audit/changelog-generate.py +176 -0
  508. package/tools/audit/check-coverage.py +483 -0
  509. package/tools/audit/doctor_assertions.py +256 -0
  510. package/tools/audit/example-qa.py +108 -0
  511. package/tools/audit/frontmatter-check.py +199 -0
  512. package/tools/audit/integration-check.py +95 -0
  513. package/tools/audit/korean-copy-check.py +161 -0
  514. package/tools/audit/link-check.py +146 -0
  515. package/tools/audit/local-ci.py +558 -0
  516. package/tools/audit/package-contents.py +723 -0
  517. package/tools/audit/package-smoke.py +22443 -0
  518. package/tools/audit/raw-hex-check.py +252 -0
  519. package/tools/audit/registry-smoke.py +8188 -0
  520. package/tools/audit/release-metadata.py +9832 -0
  521. package/tools/audit/run-all.py +351 -0
  522. package/tools/audit/smoke_assertions.py +12742 -0
  523. package/tools/audit/stability-review.py +332 -0
  524. package/tools/audit/stale-check.py +156 -0
  525. package/tools/migrations/add-version-frontmatter.py +132 -0
  526. package/tools/migrations/bump-last-updated.py +131 -0
  527. package/tools/migrations/promote-stability.py +172 -0
  528. package/tools/preview/README.md +58 -0
  529. package/tools/preview/render-tokens.py +454 -0
@@ -0,0 +1,723 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Verify the npm package dry-run contents before publishing.
4
+
5
+ This turns the human "scan npm pack output" release step into a deterministic
6
+ gate. It checks that required runtime files, every plugin manifest path, and
7
+ skill sidecar docs are included, while test/build/cache-only files stay out of
8
+ the tarball.
9
+
10
+ Usage:
11
+ python3 tools/audit/package-contents.py
12
+ python3 tools/audit/package-contents.py --json
13
+ python3 tools/audit/package-contents.py --self-test
14
+ npm pack --dry-run --json | python3 tools/audit/package-contents.py --pack-json -
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import argparse
19
+ import json
20
+ import re
21
+ import subprocess
22
+ import sys
23
+ from pathlib import Path
24
+
25
+ ROOT = Path(__file__).resolve().parents[2]
26
+ PACKAGE_JSON = ROOT / "package.json"
27
+ PLUGIN_JSON = ROOT / ".claude-plugin" / "plugin.json"
28
+
29
+ MAX_PACKAGE_SIZE_MB = 15
30
+ MAX_PACKAGE_SIZE_BYTES = MAX_PACKAGE_SIZE_MB * 1024 * 1024
31
+ CATALOG_COUNT_LABELS = {
32
+ "skills": "skill",
33
+ "commands": "command",
34
+ "agents": "agent",
35
+ }
36
+ CATALOG_COUNT_SECTIONS = {
37
+ "skill": "skills",
38
+ "skills": "skills",
39
+ "command": "commands",
40
+ "commands": "commands",
41
+ "agent": "agents",
42
+ "agents": "agents",
43
+ }
44
+ CATALOG_COUNT_RE = re.compile(
45
+ r"\b(?P<count>\d+)\s+(?:(?:review|slash)\s+)?"
46
+ r"(?P<section>skills?|commands?|agents?)\b"
47
+ )
48
+ MARKDOWN_LINK_RE = re.compile(r"\[([^\]]*)\]\(([^)\s]+?)(?:#[^)]+)?\)")
49
+ INLINE_CODE_RE = re.compile(r"`[^`]*`")
50
+ EXTERNAL_LINK_PREFIXES = ("http://", "https://", "mailto:", "ftp://", "tel:")
51
+
52
+ REQUIRED_PATHS = {
53
+ "package.json",
54
+ "README.md",
55
+ "README.ko.md",
56
+ "LICENSE",
57
+ "AGENTS.md",
58
+ "AGENTS.ko.md",
59
+ "CLAUDE.md",
60
+ "CHANGELOG.md",
61
+ "install.sh",
62
+ ".claude-plugin/plugin.json",
63
+ "cli/bin/design-ai.mjs",
64
+ "cli/bin/design-ai-mcp.mjs",
65
+ "cli/lib/dispatch.mjs",
66
+ "cli/lib/mcp-server.mjs",
67
+ "knowledge/PRINCIPLES.md",
68
+ "examples/README.md",
69
+ "skills/component-spec-writer/PLAYBOOK.md",
70
+ "commands/design-from-brief.md",
71
+ "docs/DISTRIBUTION.md",
72
+ "docs/DISTRIBUTION.ko.md",
73
+ "docs/QUICKSTART.md",
74
+ "docs/QUICKSTART.ko.md",
75
+ "docs/USING.md",
76
+ "docs/USING.ko.md",
77
+ "tools/audit/run-all.py",
78
+ "tools/audit/frontmatter-check.py",
79
+ "tools/audit/link-check.py",
80
+ "tools/audit/korean-copy-check.py",
81
+ "tools/audit/raw-hex-check.py",
82
+ "tools/audit/integration-check.py",
83
+ "tools/audit/stale-check.py",
84
+ "tools/audit/check-coverage.py",
85
+ "tools/audit/doctor_assertions.py",
86
+ "tools/audit/smoke_assertions.py",
87
+ "tools/audit/example-qa.py",
88
+ "tools/audit/local-ci.py",
89
+ "tools/audit/package-contents.py",
90
+ "tools/audit/package-smoke.py",
91
+ "tools/audit/registry-smoke.py",
92
+ "tools/audit/release-metadata.py",
93
+ }
94
+
95
+ FORBIDDEN_PREFIXES = (
96
+ "refs/",
97
+ ".git/",
98
+ ".github/",
99
+ "node_modules/",
100
+ "tools/extractors/",
101
+ )
102
+
103
+ FORBIDDEN_SUFFIXES = (
104
+ ".pyc",
105
+ ".tgz",
106
+ ".test.mjs",
107
+ ".test.js",
108
+ ".spec.mjs",
109
+ ".spec.js",
110
+ )
111
+
112
+ FORBIDDEN_SEGMENTS = (
113
+ "/__pycache__/",
114
+ "/node_modules/",
115
+ )
116
+
117
+
118
+ def parse_pack_payload(raw: str, *, source: str) -> dict:
119
+ try:
120
+ payload = json.loads(raw)
121
+ except json.JSONDecodeError as error:
122
+ print(raw, file=sys.stderr)
123
+ raise SystemExit(f"failed to parse npm pack JSON from {source}: {error}") from error
124
+
125
+ if isinstance(payload, list):
126
+ if len(payload) != 1:
127
+ raise SystemExit(f"expected exactly one npm pack result from {source}, got {len(payload)}")
128
+ return payload[0]
129
+
130
+ if isinstance(payload, dict):
131
+ return payload
132
+
133
+ raise SystemExit(f"expected npm pack JSON object or single-item array from {source}")
134
+
135
+
136
+ def run_npm_pack_dry_run() -> dict:
137
+ result = subprocess.run(
138
+ ["npm", "pack", "--dry-run", "--json"],
139
+ cwd=ROOT,
140
+ text=True,
141
+ capture_output=True,
142
+ )
143
+
144
+ if result.returncode != 0:
145
+ if result.stdout:
146
+ print(result.stdout, end="")
147
+ if result.stderr:
148
+ print(result.stderr, end="", file=sys.stderr)
149
+ raise SystemExit(f"npm pack --dry-run --json failed with exit code {result.returncode}")
150
+
151
+ return parse_pack_payload(result.stdout, source="npm pack --dry-run --json")
152
+
153
+
154
+ def load_pack_json(pack_json: str) -> dict:
155
+ if pack_json == "-":
156
+ return parse_pack_payload(sys.stdin.read(), source="stdin")
157
+
158
+ pack_json_path = Path(pack_json).resolve()
159
+ try:
160
+ raw = pack_json_path.read_text(encoding="utf-8")
161
+ except OSError as error:
162
+ raise SystemExit(f"failed to read npm pack JSON fixture: {pack_json_path}") from error
163
+
164
+ return parse_pack_payload(raw, source=str(pack_json_path))
165
+
166
+
167
+ def load_manifest_paths() -> set[str]:
168
+ manifest = load_plugin_json()
169
+ required: set[str] = set()
170
+
171
+ for section in ("skills", "commands", "agents"):
172
+ entries = manifest.get(section, [])
173
+ if not isinstance(entries, list):
174
+ raise SystemExit(f"plugin manifest section is not a list: {section}")
175
+ for entry in entries:
176
+ path = entry.get("path") if isinstance(entry, dict) else None
177
+ if not path:
178
+ raise SystemExit(f"plugin manifest entry in {section} is missing path")
179
+ required.add(path)
180
+
181
+ return required
182
+
183
+
184
+ def load_skill_sidecar_paths() -> set[str]:
185
+ required: set[str] = set()
186
+ skills_root = ROOT / "skills"
187
+ for file_path in skills_root.glob("*/*.md"):
188
+ if file_path.name in {"SKILL.md", "PLAYBOOK.md", "TEMPLATE.md"}:
189
+ required.add(file_path.relative_to(ROOT).as_posix())
190
+ return required
191
+
192
+
193
+ def load_required_paths() -> set[str]:
194
+ return REQUIRED_PATHS | load_manifest_paths() | load_skill_sidecar_paths()
195
+
196
+
197
+ def load_run_all_audit_scripts() -> list[str]:
198
+ text = (ROOT / "tools/audit/run-all.py").read_text(encoding="utf-8")
199
+ match = re.search(
200
+ r"AUDITS: tuple\[AuditSpec, \.\.\.\] = \(\n(?P<body>.*?)\n\)\n\n\n@dataclass",
201
+ text,
202
+ re.DOTALL,
203
+ )
204
+ if not match:
205
+ raise SystemExit("failed to locate AUDITS tuple in tools/audit/run-all.py")
206
+ return re.findall(r'script="([^"]+\.py)"', match.group("body"))
207
+
208
+
209
+ def load_package_json() -> dict:
210
+ return json.loads(PACKAGE_JSON.read_text(encoding="utf-8"))
211
+
212
+
213
+ def load_plugin_json() -> dict:
214
+ return json.loads(PLUGIN_JSON.read_text(encoding="utf-8"))
215
+
216
+
217
+ def matches_package_files_exclusion(path: str, pattern: str) -> bool:
218
+ normalized_pattern = pattern.rstrip("/")
219
+ if path == normalized_pattern or path.startswith(f"{normalized_pattern}/"):
220
+ return True
221
+ if pattern == "**/__pycache__/**":
222
+ return "/__pycache__/" in f"/{path}"
223
+ if pattern == "**/*.pyc":
224
+ return path.endswith(".pyc")
225
+ if pattern.startswith("cli/**/*."):
226
+ return path.startswith("cli/") and path.endswith(pattern.removeprefix("cli/**/*"))
227
+ return False
228
+
229
+
230
+ def load_package_file_fixture_paths(package_json: dict) -> set[str]:
231
+ package_files = package_json.get("files")
232
+ if not isinstance(package_files, list):
233
+ raise SystemExit("package.json files field is not a list")
234
+
235
+ included: set[str] = set()
236
+ exclusions: list[str] = []
237
+ for entry in package_files:
238
+ if not isinstance(entry, str) or not entry:
239
+ raise SystemExit("package.json files field contains a non-string entry")
240
+ if entry.startswith("!"):
241
+ exclusions.append(entry[1:])
242
+ continue
243
+
244
+ path = entry.rstrip("/")
245
+ absolute_path = ROOT / path
246
+ if absolute_path.is_file():
247
+ included.add(path)
248
+ elif absolute_path.is_dir():
249
+ for file_path in absolute_path.rglob("*"):
250
+ if file_path.is_file():
251
+ included.add(file_path.relative_to(ROOT).as_posix())
252
+
253
+ for exclusion in exclusions:
254
+ included = {
255
+ path
256
+ for path in included
257
+ if not matches_package_files_exclusion(path, exclusion)
258
+ }
259
+
260
+ included.add("package.json")
261
+ return included
262
+
263
+
264
+ def format_catalog_count(count: int, singular: str) -> str:
265
+ return f"{count} {singular}{'' if count == 1 else 's'}"
266
+
267
+
268
+ def catalog_counts_from_manifest(plugin_json: dict) -> tuple[dict[str, int], list[str]]:
269
+ counts: dict[str, int] = {}
270
+ errors: list[str] = []
271
+ for section in CATALOG_COUNT_LABELS:
272
+ entries = plugin_json.get(section)
273
+ if not isinstance(entries, list):
274
+ errors.append(f"plugin manifest section is not a list: {section}")
275
+ continue
276
+ counts[section] = len(entries)
277
+ return counts, errors
278
+
279
+
280
+ def catalog_count_mentions(description: str) -> dict[str, list[tuple[int, str]]]:
281
+ mentions: dict[str, list[tuple[int, str]]] = {}
282
+ for match in CATALOG_COUNT_RE.finditer(description):
283
+ section = CATALOG_COUNT_SECTIONS[match.group("section")]
284
+ mentions.setdefault(section, []).append((int(match.group("count")), match.group(0)))
285
+ return mentions
286
+
287
+
288
+ def replace_first_catalog_count(description: str, section: str, count: int) -> str:
289
+ replaced = False
290
+
291
+ def replace(match: re.Match[str]) -> str:
292
+ nonlocal replaced
293
+ if replaced or CATALOG_COUNT_SECTIONS[match.group("section")] != section:
294
+ return match.group(0)
295
+
296
+ replaced = True
297
+ return match.group(0).replace(match.group("count"), str(count), 1)
298
+
299
+ updated = CATALOG_COUNT_RE.sub(replace, description)
300
+ if not replaced:
301
+ raise SystemExit(f"self-test failed: fixture description is missing {section} count")
302
+ return updated
303
+
304
+
305
+ def metadata_inventory_errors(package_json: dict, plugin_json: dict) -> list[str]:
306
+ expected_counts, errors = catalog_counts_from_manifest(plugin_json)
307
+ if errors:
308
+ return errors
309
+
310
+ descriptions = {
311
+ "package.json description": package_json.get("description"),
312
+ ".claude-plugin/plugin.json description": plugin_json.get("description"),
313
+ }
314
+ for label, description in descriptions.items():
315
+ if not isinstance(description, str) or not description.strip():
316
+ errors.append(f"{label} is missing")
317
+ continue
318
+
319
+ mentions = catalog_count_mentions(description)
320
+ for section, expected_count in expected_counts.items():
321
+ expected_label = format_catalog_count(expected_count, CATALOG_COUNT_LABELS[section])
322
+ section_mentions = mentions.get(section, [])
323
+ if not section_mentions:
324
+ errors.append(f"{label} missing catalog count: {expected_label}")
325
+ continue
326
+
327
+ for count, phrase in section_mentions:
328
+ if count != expected_count:
329
+ errors.append(
330
+ f"{label} catalog count mismatch: {phrase} != {expected_label}"
331
+ )
332
+
333
+ return errors
334
+
335
+
336
+ def is_forbidden(path: str) -> bool:
337
+ if path.startswith(FORBIDDEN_PREFIXES):
338
+ return True
339
+ if path.endswith(FORBIDDEN_SUFFIXES):
340
+ return True
341
+ normalized = f"/{path}"
342
+ return any(segment in normalized for segment in FORBIDDEN_SEGMENTS)
343
+
344
+
345
+ def is_external_link(url: str) -> bool:
346
+ return url.startswith(EXTERNAL_LINK_PREFIXES)
347
+
348
+
349
+ def is_anchor_only_link(url: str) -> bool:
350
+ return url.startswith("#")
351
+
352
+
353
+ def is_refs_link(url: str) -> bool:
354
+ """refs/ links are intentionally excluded from package/runtime closure checks."""
355
+ return "refs/" in url
356
+
357
+
358
+ def normalize_package_link_target(source_path: Path, link: str) -> tuple[str | None, str | None]:
359
+ """Resolve a markdown link target to a repo-relative package path.
360
+
361
+ Returns (target_path, error). Links that are intentionally skipped return
362
+ (None, None). Only concrete files are checked against the package file list;
363
+ directory links remain valid because npm pack reports files, not directories.
364
+ """
365
+ if is_external_link(link) or is_anchor_only_link(link) or is_refs_link(link):
366
+ return None, None
367
+
368
+ normalized_link = link.split("?", 1)[0].split("#", 1)[0]
369
+ if not normalized_link:
370
+ return None, None
371
+
372
+ resolved = (source_path.parent / normalized_link).resolve()
373
+ try:
374
+ relative = resolved.relative_to(ROOT)
375
+ except ValueError:
376
+ return None, (
377
+ f"packaged markdown link points outside repository: "
378
+ f"{source_path.relative_to(ROOT).as_posix()} -> {link}"
379
+ )
380
+
381
+ if not resolved.exists() or not resolved.is_file():
382
+ return None, None
383
+
384
+ return relative.as_posix(), None
385
+
386
+
387
+ def packaged_markdown_link_errors(file_paths: set[str]) -> list[str]:
388
+ errors: list[str] = []
389
+ markdown_paths = sorted(path for path in file_paths if path.endswith(".md"))
390
+
391
+ for markdown_path in markdown_paths:
392
+ source_path = ROOT / markdown_path
393
+ if not source_path.is_file():
394
+ continue
395
+
396
+ in_code_block = False
397
+ text = source_path.read_text(encoding="utf-8")
398
+ for line in text.splitlines():
399
+ if line.strip().startswith("```"):
400
+ in_code_block = not in_code_block
401
+ continue
402
+ if in_code_block:
403
+ continue
404
+
405
+ line_without_code = INLINE_CODE_RE.sub("", line)
406
+ for match in MARKDOWN_LINK_RE.finditer(line_without_code):
407
+ _, link = match.groups()
408
+ target_path, target_error = normalize_package_link_target(source_path, link)
409
+ if target_error:
410
+ errors.append(target_error)
411
+ continue
412
+ if target_path is None:
413
+ continue
414
+ if target_path not in file_paths:
415
+ errors.append(
416
+ "packaged markdown link target is not included: "
417
+ f"{markdown_path} -> {link} ({target_path})"
418
+ )
419
+
420
+ return errors
421
+
422
+
423
+ def format_bytes(size: int) -> str:
424
+ if size >= 1024 * 1024:
425
+ return f"{size / 1024 / 1024:.1f} MB"
426
+ if size >= 1024:
427
+ return f"{size / 1024:.1f} KB"
428
+ return f"{size} B"
429
+
430
+
431
+ def verify_package_contents(
432
+ pack: dict,
433
+ *,
434
+ package_json: dict | None = None,
435
+ plugin_json: dict | None = None,
436
+ required_paths: set[str] | None = None,
437
+ ) -> dict:
438
+ files = pack.get("files", [])
439
+ if not isinstance(files, list):
440
+ raise SystemExit("npm pack JSON did not include a files array")
441
+
442
+ file_paths = {entry.get("path") for entry in files if isinstance(entry, dict)}
443
+ file_paths.discard(None)
444
+
445
+ package_json = package_json or load_package_json()
446
+ plugin_json = plugin_json or load_plugin_json()
447
+ required_paths = required_paths or load_required_paths()
448
+
449
+ errors: list[str] = []
450
+ missing = sorted(path for path in required_paths if path not in file_paths)
451
+ forbidden = sorted(path for path in file_paths if is_forbidden(path))
452
+ markdown_link_errors = packaged_markdown_link_errors(file_paths)
453
+
454
+ package_size = int(pack.get("size") or 0)
455
+ if package_size > MAX_PACKAGE_SIZE_BYTES:
456
+ errors.append(
457
+ f"package size {format_bytes(package_size)} exceeds {MAX_PACKAGE_SIZE_MB} MB limit"
458
+ )
459
+
460
+ if pack.get("name") != package_json.get("name"):
461
+ errors.append(f"package name mismatch: {pack.get('name')} != {package_json.get('name')}")
462
+ if pack.get("version") != package_json.get("version"):
463
+ errors.append(
464
+ f"package version mismatch: {pack.get('version')} != {package_json.get('version')}"
465
+ )
466
+ if plugin_json.get("version") != package_json.get("version"):
467
+ errors.append(
468
+ "plugin manifest version mismatch: "
469
+ f"{plugin_json.get('version')} != {package_json.get('version')}"
470
+ )
471
+ errors.extend(metadata_inventory_errors(package_json, plugin_json))
472
+
473
+ if missing:
474
+ errors.append("missing required package path(s): " + ", ".join(missing))
475
+ if forbidden:
476
+ errors.append("forbidden package path(s): " + ", ".join(forbidden))
477
+ errors.extend(markdown_link_errors)
478
+
479
+ return {
480
+ "name": pack.get("name"),
481
+ "version": pack.get("version"),
482
+ "plugin_version": plugin_json.get("version"),
483
+ "filename": pack.get("filename"),
484
+ "file_count": len(file_paths),
485
+ "package_size": package_size,
486
+ "unpacked_size": int(pack.get("unpackedSize") or 0),
487
+ "missing": missing,
488
+ "forbidden": forbidden,
489
+ "markdown_link_errors": markdown_link_errors,
490
+ "errors": errors,
491
+ }
492
+
493
+
494
+ def assert_condition(condition: bool, message: str) -> None:
495
+ if not condition:
496
+ raise SystemExit(f"self-test failed: {message}")
497
+
498
+
499
+ def run_self_test() -> int:
500
+ package_json = load_package_json()
501
+ plugin_json = load_plugin_json()
502
+ required_paths = sorted(load_required_paths())
503
+ package_file_paths = sorted(load_package_file_fixture_paths(package_json))
504
+ assert_condition(
505
+ any(path.endswith("/PLAYBOOK.md") for path in required_paths),
506
+ "skill playbook paths should be part of required package contents",
507
+ )
508
+ assert_condition(
509
+ any(path.endswith("/TEMPLATE.md") for path in required_paths),
510
+ "skill template paths should be part of required package contents when present",
511
+ )
512
+ assert_condition(
513
+ "tools/audit/smoke_assertions.py" in required_paths,
514
+ "shared smoke assertion helper should be required package contents",
515
+ )
516
+ assert_condition(
517
+ "tools/audit/check-coverage.py" in required_paths,
518
+ "coverage audit script should be required package contents",
519
+ )
520
+ for localized_entrypoint in (
521
+ "README.ko.md",
522
+ "AGENTS.ko.md",
523
+ "docs/QUICKSTART.ko.md",
524
+ "docs/USING.ko.md",
525
+ "docs/DISTRIBUTION.ko.md",
526
+ ):
527
+ assert_condition(
528
+ localized_entrypoint in required_paths,
529
+ f"{localized_entrypoint} should be required package contents",
530
+ )
531
+ run_all_audit_paths = {
532
+ f"tools/audit/{script}"
533
+ for script in load_run_all_audit_scripts()
534
+ }
535
+ missing_run_all_audit_paths = sorted(
536
+ path for path in run_all_audit_paths if path not in required_paths
537
+ )
538
+ assert_condition(
539
+ len(run_all_audit_paths) == 8,
540
+ "run-all.py should still enumerate eight repository audits",
541
+ )
542
+ assert_condition(
543
+ not missing_run_all_audit_paths,
544
+ "run-all.py audit scripts should be required package contents: "
545
+ + ", ".join(missing_run_all_audit_paths),
546
+ )
547
+ missing_fixture_paths = sorted(
548
+ path for path in required_paths if path not in package_file_paths
549
+ )
550
+ assert_condition(
551
+ not missing_fixture_paths,
552
+ "package.json files fixture should include all required package contents: "
553
+ + ", ".join(missing_fixture_paths),
554
+ )
555
+
556
+ passing_pack = {
557
+ "name": package_json["name"],
558
+ "version": package_json["version"],
559
+ "filename": "fixture.tgz",
560
+ "size": 1024,
561
+ "unpackedSize": 4096,
562
+ "files": [{"path": path} for path in package_file_paths],
563
+ }
564
+ passing_summary = verify_package_contents(
565
+ passing_pack,
566
+ package_json=package_json,
567
+ plugin_json=plugin_json,
568
+ required_paths=set(required_paths),
569
+ )
570
+ assert_condition(
571
+ passing_summary["errors"] == [],
572
+ "complete fixture should pass without errors",
573
+ )
574
+
575
+ missing_markdown_target_paths = set(package_file_paths)
576
+ missing_markdown_target_paths.remove("AGENTS.ko.md")
577
+ missing_markdown_required_paths = set(required_paths)
578
+ missing_markdown_required_paths.remove("AGENTS.ko.md")
579
+ missing_markdown_target_summary = verify_package_contents(
580
+ {
581
+ **passing_pack,
582
+ "files": [{"path": path} for path in sorted(missing_markdown_target_paths)],
583
+ },
584
+ package_json=package_json,
585
+ plugin_json=plugin_json,
586
+ required_paths=missing_markdown_required_paths,
587
+ )
588
+ assert_condition(
589
+ "docs/USING.ko.md -> ../AGENTS.ko.md"
590
+ in "\n".join(missing_markdown_target_summary["markdown_link_errors"]),
591
+ "packaged markdown links should fail when their existing target is not packaged",
592
+ )
593
+
594
+ failing_pack = {
595
+ "name": package_json["name"],
596
+ "version": "0.0.0",
597
+ "filename": "bad-fixture.tgz",
598
+ "size": MAX_PACKAGE_SIZE_BYTES + 1,
599
+ "unpackedSize": 0,
600
+ "files": [
601
+ {"path": "package.json"},
602
+ {"path": "refs/source.md"},
603
+ {"path": "cli/lib/check.test.mjs"},
604
+ ],
605
+ }
606
+ mismatched_plugin_json = {**plugin_json, "version": "0.0.0"}
607
+ failing_summary = verify_package_contents(
608
+ failing_pack,
609
+ package_json=package_json,
610
+ plugin_json=mismatched_plugin_json,
611
+ required_paths=set(required_paths),
612
+ )
613
+ joined_errors = "\n".join(failing_summary["errors"])
614
+
615
+ assert_condition(
616
+ any(path.endswith("plugin.json") for path in failing_summary["missing"]),
617
+ "incomplete fixture should report missing manifest/runtime paths",
618
+ )
619
+ assert_condition(
620
+ "refs/source.md" in failing_summary["forbidden"],
621
+ "forbidden refs/ path should be detected",
622
+ )
623
+ assert_condition(
624
+ "cli/lib/check.test.mjs" in failing_summary["forbidden"],
625
+ "forbidden CLI test file should be detected",
626
+ )
627
+ assert_condition(
628
+ "package size" in joined_errors,
629
+ "oversized package should be reported",
630
+ )
631
+ assert_condition(
632
+ "package version mismatch" in joined_errors,
633
+ "version mismatch should be reported",
634
+ )
635
+ assert_condition(
636
+ "plugin manifest version mismatch" in joined_errors,
637
+ "plugin manifest version mismatch should be reported",
638
+ )
639
+
640
+ stale_package_json = {
641
+ **package_json,
642
+ "description": replace_first_catalog_count(
643
+ package_json["description"],
644
+ "skills",
645
+ len(plugin_json["skills"]) - 1,
646
+ ),
647
+ }
648
+ stale_package_summary = verify_package_contents(
649
+ passing_pack,
650
+ package_json=stale_package_json,
651
+ plugin_json=plugin_json,
652
+ required_paths=set(required_paths),
653
+ )
654
+ assert_condition(
655
+ "package.json description catalog count mismatch"
656
+ in "\n".join(stale_package_summary["errors"]),
657
+ "stale package.json inventory count should be reported",
658
+ )
659
+
660
+ stale_plugin_json = {
661
+ **plugin_json,
662
+ "description": replace_first_catalog_count(
663
+ plugin_json["description"],
664
+ "agents",
665
+ len(plugin_json["agents"]) - 1,
666
+ ),
667
+ }
668
+ stale_plugin_summary = verify_package_contents(
669
+ passing_pack,
670
+ package_json=package_json,
671
+ plugin_json=stale_plugin_json,
672
+ required_paths=set(required_paths),
673
+ )
674
+ assert_condition(
675
+ ".claude-plugin/plugin.json description catalog count mismatch"
676
+ in "\n".join(stale_plugin_summary["errors"]),
677
+ "stale plugin manifest inventory count should be reported",
678
+ )
679
+
680
+ print("Package contents self-test passed")
681
+ return 0
682
+
683
+
684
+ def main() -> int:
685
+ parser = argparse.ArgumentParser(description=__doc__)
686
+ parser.add_argument("--json", action="store_true", help="Print machine-readable summary")
687
+ parser.add_argument(
688
+ "--self-test",
689
+ action="store_true",
690
+ help="Run built-in success and failure fixture checks without invoking npm pack",
691
+ )
692
+ parser.add_argument(
693
+ "--pack-json",
694
+ metavar="FILE",
695
+ help="Read npm pack --json output from a fixture file instead of running npm pack; use - for stdin",
696
+ )
697
+ args = parser.parse_args()
698
+
699
+ if args.self_test:
700
+ return run_self_test()
701
+
702
+ pack = load_pack_json(args.pack_json) if args.pack_json else run_npm_pack_dry_run()
703
+ summary = verify_package_contents(pack)
704
+ if args.json:
705
+ print(json.dumps(summary, ensure_ascii=False, indent=2))
706
+ elif summary["errors"]:
707
+ print("Package contents check failed:")
708
+ for error in summary["errors"]:
709
+ print(f"- {error}")
710
+ else:
711
+ print(
712
+ "Package contents check passed: "
713
+ f"{summary['name']}@{summary['version']}, "
714
+ f"{summary['file_count']} files, "
715
+ f"packed {format_bytes(summary['package_size'])}, "
716
+ f"unpacked {format_bytes(summary['unpacked_size'])}"
717
+ )
718
+
719
+ return 1 if summary["errors"] else 0
720
+
721
+
722
+ if __name__ == "__main__":
723
+ raise SystemExit(main())