@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,256 @@
1
+ """Shared assertions for design-ai doctor JSON reports used by smoke tests."""
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+
6
+ EXPECTED_DOCTOR_PASS_LABELS = {
7
+ "Source layout",
8
+ "Version alignment",
9
+ "Manifest paths",
10
+ "Node runtime",
11
+ "Python runtime",
12
+ "Audit runner",
13
+ "Audit scripts",
14
+ "Doctor assertions helper",
15
+ "Smoke assertions helper",
16
+ "Example QA audit",
17
+ "Package contents check",
18
+ "Package smoke check",
19
+ "Registry smoke check",
20
+ "Installed skills",
21
+ "Installed agents",
22
+ "Installed slash commands",
23
+ }
24
+
25
+
26
+ def _assert_keys(value: dict, expected: list[str], *, label: str, context: str) -> None:
27
+ actual = list(value)
28
+ if actual != expected:
29
+ raise SystemExit(
30
+ f"doctor JSON {label} keys changed after {context}: "
31
+ f"expected {expected}, got {actual}"
32
+ )
33
+
34
+
35
+ def _assert_non_empty_string(value: object, *, label: str, context: str) -> None:
36
+ if not isinstance(value, str) or not value:
37
+ raise SystemExit(f"doctor JSON {label} after {context} is missing or not a string")
38
+
39
+
40
+ def _assert_non_negative_int(value: object, *, label: str, context: str) -> None:
41
+ if type(value) is not int or value < 0:
42
+ raise SystemExit(f"doctor JSON {label} after {context} is missing or not a non-negative integer")
43
+
44
+
45
+ def assert_doctor_report_clean(report: dict, *, context: str) -> None:
46
+ if not isinstance(report, dict):
47
+ raise SystemExit(f"doctor JSON after {context} is not an object")
48
+
49
+ _assert_keys(report, ["context", "checks", "summary", "fix"], label="top-level", context=context)
50
+
51
+ report_context = report["context"]
52
+ if not isinstance(report_context, dict):
53
+ raise SystemExit(f"doctor JSON context after {context} is not an object")
54
+ _assert_keys(
55
+ report_context,
56
+ ["sourceRoot", "claudeHome", "prefix", "expected"],
57
+ label="context",
58
+ context=context,
59
+ )
60
+ _assert_non_empty_string(report_context.get("sourceRoot"), label="context.sourceRoot", context=context)
61
+ _assert_non_empty_string(report_context.get("claudeHome"), label="context.claudeHome", context=context)
62
+ _assert_non_empty_string(report_context.get("prefix"), label="context.prefix", context=context)
63
+
64
+ expected = report_context["expected"]
65
+ if not isinstance(expected, dict):
66
+ raise SystemExit(f"doctor JSON context.expected after {context} is not an object")
67
+ _assert_keys(expected, ["skills", "agents", "commands"], label="context.expected", context=context)
68
+ for key in ("skills", "agents", "commands"):
69
+ _assert_non_negative_int(expected.get(key), label=f"context.expected.{key}", context=context)
70
+
71
+ checks = report["checks"]
72
+ if not isinstance(checks, list) or not checks:
73
+ raise SystemExit(f"doctor JSON checks after {context} is missing or empty")
74
+ for index, check in enumerate(checks):
75
+ if not isinstance(check, dict):
76
+ raise SystemExit(f"doctor JSON checks[{index}] after {context} is not an object")
77
+ _assert_keys(check, ["status", "label", "detail", "action"], label=f"checks[{index}]", context=context)
78
+ if check.get("status") not in {"PASS", "WARN", "FAIL"}:
79
+ raise SystemExit(f"doctor JSON checks[{index}].status after {context} is invalid")
80
+ _assert_non_empty_string(check.get("label"), label=f"checks[{index}].label", context=context)
81
+ _assert_non_empty_string(check.get("detail"), label=f"checks[{index}].detail", context=context)
82
+ if not isinstance(check.get("action"), str):
83
+ raise SystemExit(f"doctor JSON checks[{index}].action after {context} is not a string")
84
+
85
+ summary = report.get("summary", {})
86
+ if not isinstance(summary, dict):
87
+ raise SystemExit(f"doctor JSON summary after {context} is not an object")
88
+ _assert_keys(summary, ["pass", "warn", "fail"], label="summary", context=context)
89
+ for key in ("pass", "warn", "fail"):
90
+ _assert_non_negative_int(summary.get(key), label=f"summary.{key}", context=context)
91
+
92
+ actual_summary = {
93
+ "pass": sum(1 for check in checks if check.get("status") == "PASS"),
94
+ "warn": sum(1 for check in checks if check.get("status") == "WARN"),
95
+ "fail": sum(1 for check in checks if check.get("status") == "FAIL"),
96
+ }
97
+ if summary != actual_summary:
98
+ raise SystemExit(
99
+ f"doctor JSON summary after {context} does not match check statuses: "
100
+ f"expected {actual_summary}, got {summary}"
101
+ )
102
+
103
+ fix = report["fix"]
104
+ if not isinstance(fix, dict):
105
+ raise SystemExit(f"doctor JSON fix after {context} is not an object")
106
+ _assert_keys(fix, ["attempted", "applied", "reason"], label="fix", context=context)
107
+ if not isinstance(fix.get("attempted"), bool) or not isinstance(fix.get("applied"), bool):
108
+ raise SystemExit(f"doctor JSON fix booleans after {context} are invalid")
109
+ if not isinstance(fix.get("reason"), str):
110
+ raise SystemExit(f"doctor JSON fix.reason after {context} is not a string")
111
+
112
+ check_statuses = {
113
+ check.get("label"): check.get("status")
114
+ for check in report.get("checks", [])
115
+ if isinstance(check, dict)
116
+ }
117
+ missing_or_failed = sorted(
118
+ f"{label}={check_statuses.get(label, 'missing')}"
119
+ for label in EXPECTED_DOCTOR_PASS_LABELS
120
+ if check_statuses.get(label) != "PASS"
121
+ )
122
+ if missing_or_failed:
123
+ raise SystemExit(
124
+ f"doctor did not report every required {context} check as PASS: "
125
+ + ", ".join(missing_or_failed)
126
+ )
127
+
128
+ if summary.get("fail", 0) != 0 or summary.get("warn", 0) != 0:
129
+ raise SystemExit(f"doctor reported warning(s) or failure(s) after {context}")
130
+
131
+
132
+ def _passing_report() -> dict:
133
+ return {
134
+ "context": {
135
+ "sourceRoot": "/tmp/design-ai",
136
+ "claudeHome": "/tmp/claude",
137
+ "prefix": "design-",
138
+ "expected": {
139
+ "skills": 19,
140
+ "agents": 4,
141
+ "commands": 16,
142
+ },
143
+ },
144
+ "checks": [
145
+ {
146
+ "status": "PASS",
147
+ "label": label,
148
+ "detail": f"{label} fixture passed",
149
+ "action": "",
150
+ }
151
+ for label in sorted(EXPECTED_DOCTOR_PASS_LABELS)
152
+ ],
153
+ "summary": {"pass": len(EXPECTED_DOCTOR_PASS_LABELS), "warn": 0, "fail": 0},
154
+ "fix": {
155
+ "attempted": False,
156
+ "applied": False,
157
+ "reason": "",
158
+ },
159
+ }
160
+
161
+
162
+ def _expect_assertion_failure(report: dict, *, context: str, expected: str) -> None:
163
+ try:
164
+ assert_doctor_report_clean(report, context=context)
165
+ except SystemExit as error:
166
+ if expected not in str(error):
167
+ raise
168
+ else:
169
+ raise SystemExit(f"self-test failed: expected doctor assertion failure containing {expected!r}")
170
+
171
+
172
+ def run_self_test(*, context: str = "doctor assertion self-test", quiet: bool = False) -> None:
173
+ assert_doctor_report_clean(_passing_report(), context=context)
174
+
175
+ missing_report = _passing_report()
176
+ missing_report["summary"]["pass"] -= 1
177
+ missing_report["checks"] = [
178
+ check
179
+ for check in missing_report["checks"]
180
+ if check["label"] != "Registry smoke check"
181
+ ]
182
+ _expect_assertion_failure(
183
+ missing_report,
184
+ context=context,
185
+ expected="Registry smoke check=missing",
186
+ )
187
+
188
+ warning_report = _passing_report()
189
+ warning_report["summary"]["warn"] = 1
190
+ warning_report["summary"]["pass"] -= 1
191
+ warning_report["checks"][0]["status"] = "WARN"
192
+ _expect_assertion_failure(
193
+ warning_report,
194
+ context=context,
195
+ expected="WARN",
196
+ )
197
+
198
+ failed_check_report = _passing_report()
199
+ for check in failed_check_report["checks"]:
200
+ if check["label"] == "Node runtime":
201
+ check["status"] = "FAIL"
202
+ break
203
+ failed_check_report["summary"]["pass"] -= 1
204
+ failed_check_report["summary"]["fail"] = 1
205
+ _expect_assertion_failure(
206
+ failed_check_report,
207
+ context=context,
208
+ expected="Node runtime=FAIL",
209
+ )
210
+
211
+ missing_fix_report = _passing_report()
212
+ missing_fix_report.pop("fix")
213
+ _expect_assertion_failure(
214
+ missing_fix_report,
215
+ context=context,
216
+ expected="top-level keys changed",
217
+ )
218
+
219
+ mismatched_summary_report = _passing_report()
220
+ mismatched_summary_report["summary"]["pass"] -= 1
221
+ _expect_assertion_failure(
222
+ mismatched_summary_report,
223
+ context=context,
224
+ expected="does not match check statuses",
225
+ )
226
+
227
+ changed_check_shape_report = _passing_report()
228
+ changed_check_shape_report["checks"][0].pop("action")
229
+ _expect_assertion_failure(
230
+ changed_check_shape_report,
231
+ context=context,
232
+ expected="checks[0] keys changed",
233
+ )
234
+
235
+ if not quiet:
236
+ print("Doctor assertions self-test passed")
237
+
238
+
239
+ def main() -> None:
240
+ parser = argparse.ArgumentParser(description=__doc__)
241
+ parser.add_argument(
242
+ "--self-test",
243
+ action="store_true",
244
+ help="Run local doctor JSON assertion fixtures",
245
+ )
246
+ args = parser.parse_args()
247
+
248
+ if args.self_test:
249
+ run_self_test()
250
+ return
251
+
252
+ run_self_test()
253
+
254
+
255
+ if __name__ == "__main__":
256
+ main()
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env python3
2
+ """Audit the top worked example for every routed design workflow.
3
+
4
+ This keeps the route/example recommendation surface honest. If routing starts
5
+ pointing a workflow at a weak or incomplete example, this audit fails before the
6
+ package ships.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import subprocess
12
+ import sys
13
+ from pathlib import Path
14
+
15
+ ROOT = Path(__file__).resolve().parents[2]
16
+ CLI = ROOT / "cli" / "bin" / "design-ai.mjs"
17
+
18
+
19
+ def run_example_check() -> subprocess.CompletedProcess[str]:
20
+ return subprocess.run(
21
+ [
22
+ "node",
23
+ str(CLI),
24
+ "check",
25
+ "--examples",
26
+ "--all-routes",
27
+ "--limit",
28
+ "1",
29
+ "--json",
30
+ ],
31
+ cwd=ROOT,
32
+ capture_output=True,
33
+ text=True,
34
+ )
35
+
36
+
37
+ def issue_lines(report: dict) -> list[str]:
38
+ lines: list[str] = []
39
+ for route in report.get("routes", []):
40
+ if route.get("status") == "pass":
41
+ continue
42
+ route_id = route.get("routeId", "(unknown route)")
43
+ lines.append(f"- {route_id}: {route.get('status', 'unknown')}")
44
+ for example_report in route.get("examples", []):
45
+ example = example_report.get("example", {})
46
+ artifact = example_report.get("report", {})
47
+ if artifact.get("status") == "pass":
48
+ continue
49
+ rel_path = example.get("relPath", "(unknown example)")
50
+ lines.append(f" - {rel_path}: {artifact.get('status', 'unknown')}")
51
+ for result in artifact.get("results", []):
52
+ if result.get("level") == "pass":
53
+ continue
54
+ evidence = result.get("evidence")
55
+ suffix = f" — {evidence}" if evidence else ""
56
+ lines.append(f" - {result.get('id', 'unknown')}: {result.get('message', '')}{suffix}")
57
+ return lines
58
+
59
+
60
+ def main() -> int:
61
+ if not CLI.exists():
62
+ print(f"ERROR: design-ai CLI not found at {CLI}", file=sys.stderr)
63
+ return 1
64
+
65
+ completed = run_example_check()
66
+ if completed.returncode != 0:
67
+ print("ERROR: example QA command failed", file=sys.stderr)
68
+ if completed.stdout.strip():
69
+ print(completed.stdout.rstrip())
70
+ if completed.stderr.strip():
71
+ print(completed.stderr.rstrip(), file=sys.stderr)
72
+ return 1
73
+
74
+ try:
75
+ report = json.loads(completed.stdout)
76
+ except json.JSONDecodeError as exc:
77
+ print(f"ERROR: could not parse example QA JSON: {exc}", file=sys.stderr)
78
+ if completed.stdout.strip():
79
+ print(completed.stdout.rstrip())
80
+ return 1
81
+
82
+ status = report.get("status")
83
+ total_routes = report.get("totalRoutes", 0)
84
+ passed_routes = report.get("passedRoutes", 0)
85
+ warned_routes = report.get("warnedRoutes", 0)
86
+ failed_routes = report.get("failedRoutes", 0)
87
+ total_examples = report.get("totalExamples", 0)
88
+ passed_examples = report.get("passedExamples", 0)
89
+
90
+ if status != "pass":
91
+ print(
92
+ "Example route QA failed: "
93
+ f"{passed_routes}/{total_routes} routes pass, "
94
+ f"{warned_routes} warn, {failed_routes} fail"
95
+ )
96
+ for line in issue_lines(report):
97
+ print(line)
98
+ return 1
99
+
100
+ print(
101
+ "Example route QA passed: "
102
+ f"{passed_routes}/{total_routes} routes and {passed_examples}/{total_examples} examples pass"
103
+ )
104
+ return 0
105
+
106
+
107
+ if __name__ == "__main__":
108
+ sys.exit(main())
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Validate YAML frontmatter on every markdown file in knowledge/ and examples/.
4
+
5
+ Checks:
6
+ - Frontmatter present (`---\n...\n---`)
7
+ - Required keys: title, applies_to (or `source` for generated, or `purpose` for PRINCIPLES.md)
8
+ - For generated files: source and extracted_at present
9
+ - applies_to is a list (YAML array)
10
+
11
+ Outputs a report to console; exits 1 if any errors.
12
+
13
+ Usage:
14
+ python3 tools/audit/frontmatter-check.py [--strict]
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import argparse
19
+ import re
20
+ import sys
21
+ from pathlib import Path
22
+
23
+ ROOT = Path(__file__).resolve().parents[2]
24
+
25
+
26
+ REQUIRED_KEYS = {"title"}
27
+ RECOMMENDED_KEYS = {"applies_to"}
28
+ GENERATED_KEYS = {"source", "extracted_at"}
29
+ # Optional versioning keys (added in v3.11). Validated only if present —
30
+ # missing them is fine for backward compatibility, but if present they
31
+ # should be well-formed.
32
+ VERSIONING_KEYS = {"version", "last_updated", "stability"}
33
+ STABILITY_VALUES = {"stable", "beta", "experimental", "deprecated"}
34
+
35
+
36
+ def is_hand_written(text: str) -> bool:
37
+ return "<!-- hand-written -->" in text
38
+
39
+
40
+ def parse_frontmatter(text: str) -> dict[str, object] | None:
41
+ """Lightweight YAML parser — handles flat keys + simple lists.
42
+ Returns None if no frontmatter found.
43
+ Skips a leading HTML comment (e.g., `<!-- hand-written -->`) before frontmatter.
44
+ """
45
+ lines = text.splitlines()
46
+
47
+ # Skip leading HTML comments (e.g., <!-- hand-written -->) and blank lines
48
+ cursor = 0
49
+ while cursor < len(lines):
50
+ stripped = lines[cursor].strip()
51
+ if stripped.startswith("<!--") and stripped.endswith("-->"):
52
+ cursor += 1
53
+ continue
54
+ if stripped == "":
55
+ cursor += 1
56
+ continue
57
+ break
58
+
59
+ if cursor >= len(lines) or not lines[cursor].strip().startswith("---"):
60
+ return None
61
+
62
+ fm_lines: list[str] = []
63
+ for line in lines[cursor + 1:]:
64
+ if line.strip().startswith("---"):
65
+ break
66
+ fm_lines.append(line)
67
+ if not fm_lines:
68
+ return None
69
+
70
+ fm: dict[str, object] = {}
71
+ current_list_key: str | None = None
72
+ for line in fm_lines:
73
+ stripped = line.rstrip()
74
+ if not stripped:
75
+ current_list_key = None
76
+ continue
77
+
78
+ # List continuation: " - item"
79
+ if current_list_key and stripped.lstrip().startswith("-"):
80
+ item = stripped.lstrip()[1:].strip()
81
+ if item:
82
+ fm[current_list_key] = list(fm.get(current_list_key, [])) + [item]
83
+ continue
84
+
85
+ # New key
86
+ match = re.match(r"^([a-zA-Z_]+):\s*(.*)$", stripped)
87
+ if not match:
88
+ continue
89
+ key, value = match.group(1), match.group(2).strip()
90
+ if value == "":
91
+ current_list_key = key
92
+ fm[key] = []
93
+ elif value.startswith("[") and value.endswith("]"):
94
+ # Inline list
95
+ inner = value[1:-1]
96
+ items = [item.strip().strip("\"'") for item in inner.split(",") if item.strip()]
97
+ fm[key] = items
98
+ current_list_key = None
99
+ else:
100
+ fm[key] = value.strip().strip("\"'")
101
+ current_list_key = None
102
+ return fm
103
+
104
+
105
+ def validate(path: Path, strict: bool) -> list[str]:
106
+ text = path.read_text(encoding="utf-8")
107
+ rel = path.relative_to(ROOT)
108
+ errors: list[str] = []
109
+ fm = parse_frontmatter(text)
110
+
111
+ if fm is None:
112
+ errors.append(f"{rel}: no frontmatter found")
113
+ return errors
114
+
115
+ # Required keys
116
+ missing = REQUIRED_KEYS - set(fm.keys())
117
+ if missing:
118
+ errors.append(f"{rel}: missing required keys {missing}")
119
+
120
+ # applies_to should be a list
121
+ if "applies_to" in fm and not isinstance(fm["applies_to"], list):
122
+ errors.append(f"{rel}: 'applies_to' should be a list, got {type(fm['applies_to']).__name__}")
123
+
124
+ # Generated files need source + extracted_at
125
+ if not is_hand_written(text):
126
+ # Heuristic: files in knowledge/ are generated unless marked
127
+ if "knowledge/" in str(rel) and "design-tokens/" not in str(rel) and "/INDEX.md" not in str(rel) and rel.parent.name != "knowledge":
128
+ # Most other knowledge files are generated; check
129
+ missing_gen = GENERATED_KEYS - set(fm.keys())
130
+ if missing_gen and strict:
131
+ errors.append(f"{rel}: generated file missing {missing_gen}")
132
+
133
+ # Recommended keys (warn only in strict)
134
+ if strict:
135
+ missing_rec = RECOMMENDED_KEYS - set(fm.keys())
136
+ if missing_rec:
137
+ errors.append(f"{rel}: (warn) recommended keys missing {missing_rec}")
138
+
139
+ # Versioning keys: validate format if present (added v3.11)
140
+ version = fm.get("version")
141
+ if version is not None and not _is_valid_version(version):
142
+ errors.append(f"{rel}: invalid version '{version}' — expected semver (e.g., 1.0.0)")
143
+
144
+ last_updated = fm.get("last_updated")
145
+ if last_updated is not None and not _is_valid_date_or_yearmonth(last_updated):
146
+ errors.append(f"{rel}: invalid last_updated '{last_updated}' — expected YYYY-MM or YYYY-MM-DD")
147
+
148
+ stability = fm.get("stability")
149
+ if stability is not None and str(stability) not in STABILITY_VALUES:
150
+ errors.append(
151
+ f"{rel}: invalid stability '{stability}' — expected one of {sorted(STABILITY_VALUES)}"
152
+ )
153
+
154
+ return errors
155
+
156
+
157
+ def _is_valid_version(value: object) -> bool:
158
+ """Match semver-like X.Y.Z (with optional -prerelease)."""
159
+ if not isinstance(value, str):
160
+ return False
161
+ return bool(re.match(r"^\d+\.\d+\.\d+(-[A-Za-z0-9.-]+)?$", value))
162
+
163
+
164
+ def _is_valid_date_or_yearmonth(value: object) -> bool:
165
+ if not isinstance(value, str):
166
+ return False
167
+ return bool(re.match(r"^\d{4}-\d{2}(-\d{2})?$", value))
168
+
169
+
170
+ def main() -> None:
171
+ parser = argparse.ArgumentParser(description=__doc__)
172
+ parser.add_argument("--strict", action="store_true", help="Treat warnings as errors")
173
+ args = parser.parse_args()
174
+
175
+ # Only check knowledge/ — examples/ are prose component specs without
176
+ # required frontmatter contract.
177
+ targets: list[Path] = list(ROOT.glob("knowledge/**/*.md"))
178
+
179
+ all_errors: list[str] = []
180
+ files_checked = 0
181
+ for path in targets:
182
+ if path.name == "README.md" or path.name == "COVERAGE.md":
183
+ continue
184
+ files_checked += 1
185
+ errors = validate(path, args.strict)
186
+ all_errors.extend(errors)
187
+
188
+ print(f"Checked {files_checked} files")
189
+ if all_errors:
190
+ print(f"\n{len(all_errors)} issues:\n")
191
+ for err in all_errors:
192
+ print(f" {err}")
193
+ sys.exit(1)
194
+ else:
195
+ print("All frontmatter valid ✓")
196
+
197
+
198
+ if __name__ == "__main__":
199
+ main()
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env python3
2
+ """Verify that integration walkthrough docs have the expected sections.
3
+
4
+ Each walkthrough should include: Prerequisites, Setup, at least 3
5
+ "Walkthrough" sections, Tips, and Next/cross-reference. This catches
6
+ walkthroughs that drift from the standard structure over time.
7
+
8
+ Usage:
9
+ python3 tools/audit/integration-check.py
10
+ python3 tools/audit/integration-check.py --strict
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import argparse
15
+ import sys
16
+ from pathlib import Path
17
+
18
+ ROOT = Path(__file__).resolve().parents[2]
19
+ INTEGRATIONS_DIR = ROOT / "docs" / "integrations"
20
+
21
+ # Walkthrough files (not the MCP-specific ones, which have a different shape).
22
+ WALKTHROUGH_FILES = [
23
+ "codex-walkthrough.md",
24
+ "cursor-walkthrough.md",
25
+ "aider-walkthrough.md",
26
+ "sdk-walkthrough.md",
27
+ "vscode-walkthrough.md",
28
+ ]
29
+
30
+ # Required headings (substring match, case-insensitive).
31
+ REQUIRED_SECTIONS = [
32
+ "prerequisites",
33
+ "setup",
34
+ "walkthrough", # at least one
35
+ "next",
36
+ ]
37
+
38
+ MIN_WALKTHROUGH_COUNT = 3 # at least 3 numbered "Walkthrough N:" sections
39
+
40
+
41
+ def check_file(path: Path) -> list[str]:
42
+ """Return list of issues for this file."""
43
+ if not path.exists():
44
+ return [f"missing: {path.relative_to(ROOT)}"]
45
+
46
+ content = path.read_text(encoding="utf-8").lower()
47
+ issues: list[str] = []
48
+
49
+ for section in REQUIRED_SECTIONS:
50
+ if section not in content:
51
+ issues.append(f"missing section: '{section}'")
52
+
53
+ walkthrough_count = content.count("## walkthrough ")
54
+ if walkthrough_count < MIN_WALKTHROUGH_COUNT:
55
+ issues.append(
56
+ f"only {walkthrough_count} 'Walkthrough N:' sections "
57
+ f"(min {MIN_WALKTHROUGH_COUNT})"
58
+ )
59
+
60
+ return issues
61
+
62
+
63
+ def main() -> None:
64
+ parser = argparse.ArgumentParser(description=__doc__)
65
+ parser.add_argument("--strict", action="store_true", help="Exit 1 on any issue")
66
+ args = parser.parse_args()
67
+
68
+ if not INTEGRATIONS_DIR.exists():
69
+ print(f"::error::Missing dir: {INTEGRATIONS_DIR.relative_to(ROOT)}")
70
+ sys.exit(1)
71
+
72
+ total_issues = 0
73
+ for filename in WALKTHROUGH_FILES:
74
+ path = INTEGRATIONS_DIR / filename
75
+ issues = check_file(path)
76
+ if issues:
77
+ print(f"\n{path.relative_to(ROOT)}:")
78
+ for issue in issues:
79
+ print(f" - {issue}")
80
+ total_issues += len(issues)
81
+ else:
82
+ print(f"✓ {path.relative_to(ROOT)}")
83
+
84
+ print()
85
+ if total_issues == 0:
86
+ print("All integration walkthroughs valid ✓")
87
+ sys.exit(0)
88
+
89
+ print(f"Found {total_issues} issue(s) across walkthroughs.")
90
+ if args.strict:
91
+ sys.exit(1)
92
+
93
+
94
+ if __name__ == "__main__":
95
+ main()