@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,2540 @@
1
+ (function () {
2
+ "use strict";
3
+
4
+ var STORAGE_KEY = "design-ai.website-console.v1";
5
+ var ACTIVE_TAB_KEY = "design-ai.website-console.active-tab";
6
+ var SELECTED_TEMPLATE_KEY = "design-ai.website-console.prompt-template";
7
+
8
+ var auditCategories = [
9
+ {
10
+ id: "visual-design",
11
+ label: "Visual Design",
12
+ description: "Layout, type, color, spacing, and hierarchy.",
13
+ defaultVerification: ["Compare spacing rhythm across target pages", "Check contrast ratios for key text pairs"],
14
+ },
15
+ {
16
+ id: "ux-flow",
17
+ label: "UX Flow",
18
+ description: "Navigation, CTA, forms, conversion path, and confusion points.",
19
+ defaultVerification: ["Complete the primary user flow on desktop and mobile", "Confirm one dominant CTA per decision point"],
20
+ },
21
+ {
22
+ id: "responsive",
23
+ label: "Responsive QA",
24
+ description: "Desktop, tablet, and mobile layout behavior.",
25
+ defaultVerification: ["Verify desktop, tablet, and mobile viewports", "Check text wrapping and touch targets"],
26
+ },
27
+ {
28
+ id: "accessibility",
29
+ label: "Accessibility",
30
+ description: "Keyboard, focus, contrast, semantic HTML, ARIA.",
31
+ defaultVerification: ["Tab through all interactive controls", "Confirm visible focus and accessible names"],
32
+ },
33
+ {
34
+ id: "performance",
35
+ label: "Performance",
36
+ description: "Core Web Vitals, images, bundles, rendering bottlenecks.",
37
+ defaultVerification: ["Run Lighthouse or deployment analytics when available", "Confirm image dimensions and lazy-loading"],
38
+ },
39
+ {
40
+ id: "seo",
41
+ label: "SEO",
42
+ description: "Title, description, headings, canonical, OG, sitemap.",
43
+ defaultVerification: ["Inspect metadata for each priority page", "Validate heading order and canonical links"],
44
+ },
45
+ {
46
+ id: "technical-quality",
47
+ label: "Technical Quality",
48
+ description: "Components, style duplication, dead code, dependency risk.",
49
+ defaultVerification: ["Inspect component ownership before editing", "Run target repo lint/typecheck/build"],
50
+ },
51
+ {
52
+ id: "runtime-issues",
53
+ label: "Runtime Issues",
54
+ description: "Console, network, hydration, broken asset failures.",
55
+ defaultVerification: ["Open the site in Browser or Chrome DevTools", "Confirm console and network panels are clean"],
56
+ },
57
+ {
58
+ id: "content-quality",
59
+ label: "Content Quality",
60
+ description: "Copy clarity, IA, proof, trust, and CTA language.",
61
+ defaultVerification: ["Read the page as a first-time visitor", "Check whether claims have concrete proof"],
62
+ },
63
+ ];
64
+
65
+ var mcpItems = [
66
+ ["github", "GitHub", "Repo, issues, PRs, code review"],
67
+ ["figma", "Figma", "Design files, tokens, component reference"],
68
+ ["browser", "Browser/Playwright", "Page interaction and visual verification"],
69
+ ["chromeDevtools", "Chrome DevTools", "Console, network, performance debugging"],
70
+ ["deploy", "Deploy", "Vercel, Netlify, Cloudflare previews and logs"],
71
+ ["sentry", "Sentry", "Production error and performance traces"],
72
+ ["database", "Database", "Supabase, Neon, Postgres schema and data dependencies"],
73
+ ["cms", "CMS", "Sanity, Contentful, WordPress, Shopify content"],
74
+ ["collaboration", "Collaboration", "Notion, Slack, Linear, Jira feedback flow"],
75
+ ["research", "Research", "Firecrawl, Tavily, Apify competitor research"],
76
+ ];
77
+
78
+ var deployOptions = ["vercel", "netlify", "cloudflare", "other", "none"];
79
+ var cmsOptions = ["sanity", "contentful", "wordpress", "shopify", "none", "other"];
80
+ var databaseOptions = ["supabase", "neon", "postgres", "none", "other"];
81
+ var viewportOptions = ["desktop", "tablet", "mobile"];
82
+ var statusOptions = ["todo", "in-progress", "done", "blocked"];
83
+ var mcpStatusOptions = ["required", "optional", "unused", "unavailable"];
84
+ var priorityOptions = ["p0", "p1", "p2", "p3"];
85
+ var impactOptions = ["high", "medium", "low"];
86
+ var effortOptions = ["high", "medium", "low"];
87
+
88
+ var tabs = [
89
+ ["profile", "Site Profile"],
90
+ ["audit", "Audit Checklist"],
91
+ ["mcp", "MCP Matrix"],
92
+ ["graph", "Workflow Graph"],
93
+ ["tasks", "Refactor Plan"],
94
+ ["prompts", "Prompt Generator"],
95
+ ["report", "Handoff Report"],
96
+ ];
97
+
98
+ var templates = [
99
+ ["codex-repo-intake", "Codex repo intake"],
100
+ ["codex-implementation", "Codex implementation"],
101
+ ["codex-visual-qa", "Codex visual QA"],
102
+ ["codex-deployment", "Codex deployment verification"],
103
+ ["claude-design-review", "Claude design review"],
104
+ ["claude-competitor", "Claude competitor research"],
105
+ ["claude-copy-ux", "Claude copy/UX critique"],
106
+ ["handoff-report", "Final handoff report"],
107
+ ];
108
+
109
+ var appState = {
110
+ workspace: loadWorkspace(),
111
+ activeTab: localStorage.getItem(ACTIVE_TAB_KEY) || "profile",
112
+ selectedTemplate: localStorage.getItem(SELECTED_TEMPLATE_KEY) || "codex-repo-intake",
113
+ runbookActionFilter: "all",
114
+ runbookEvidenceFilter: "all",
115
+ message: "",
116
+ };
117
+
118
+ function createDefaultChecklist() {
119
+ return auditCategories.reduce(function (acc, item) {
120
+ acc[item.id] = {
121
+ status: "todo",
122
+ notes: "",
123
+ findings: [],
124
+ };
125
+ return acc;
126
+ }, {});
127
+ }
128
+
129
+ function createDefaultWorkspace() {
130
+ return {
131
+ version: 1,
132
+ updatedAt: new Date().toISOString(),
133
+ siteProfile: {
134
+ id: "sample-korean-saas",
135
+ name: "Korean SaaS marketing site",
136
+ liveUrl: "https://example.com",
137
+ repoUrl: "https://github.com/acme/korean-saas-site",
138
+ localPath: "/Users/you/dev/korean-saas-site",
139
+ figmaUrl: "https://figma.com/file/example",
140
+ brandNotes: "Quiet B2B SaaS tone, Pretendard typography, dense but readable Korean product copy, indigo accent only for action and focus.",
141
+ deployProvider: "vercel",
142
+ sentryProject: "acme/korean-saas-web",
143
+ cms: "sanity",
144
+ database: "none",
145
+ pages: ["/", "/pricing", "/signup", "/docs"],
146
+ userFlows: ["Visitor compares pricing and starts signup", "Existing customer finds feature proof before contacting sales"],
147
+ viewports: ["desktop", "tablet", "mobile"],
148
+ },
149
+ auditChecklist: {
150
+ "visual-design": {
151
+ status: "in-progress",
152
+ notes: "Hero hierarchy and CTA contrast need review before company pilot.",
153
+ findings: ["Primary CTA competes with secondary link on the homepage"],
154
+ },
155
+ "ux-flow": {
156
+ status: "todo",
157
+ notes: "Map visitor path from landing page to pricing and signup.",
158
+ findings: [],
159
+ },
160
+ "responsive": {
161
+ status: "todo",
162
+ notes: "Check 1440, 1024, 390, and 360 width layouts.",
163
+ findings: [],
164
+ },
165
+ "accessibility": {
166
+ status: "todo",
167
+ notes: "Keyboard and focus audit required for nav, pricing toggle, and forms.",
168
+ findings: ["Focus state is not yet documented for the mobile menu"],
169
+ },
170
+ "performance": {
171
+ status: "todo",
172
+ notes: "Run Lighthouse after visual pass.",
173
+ findings: [],
174
+ },
175
+ "seo": {
176
+ status: "todo",
177
+ notes: "Inspect title, description, heading order, canonical, OG.",
178
+ findings: [],
179
+ },
180
+ "technical-quality": {
181
+ status: "todo",
182
+ notes: "Confirm component reuse before editing target repo.",
183
+ findings: [],
184
+ },
185
+ "runtime-issues": {
186
+ status: "todo",
187
+ notes: "Open console/network once preview deploy is available.",
188
+ findings: [],
189
+ },
190
+ "content-quality": {
191
+ status: "in-progress",
192
+ notes: "Copy should lead with proof and reduce generic SaaS phrasing.",
193
+ findings: ["Pricing page does not explain plan fit in the first viewport"],
194
+ },
195
+ },
196
+ mcpReadiness: {
197
+ github: "required",
198
+ figma: "optional",
199
+ browser: "required",
200
+ chromeDevtools: "optional",
201
+ deploy: "required",
202
+ sentry: "optional",
203
+ database: "unused",
204
+ cms: "optional",
205
+ collaboration: "optional",
206
+ research: "optional",
207
+ },
208
+ refactorTasks: [
209
+ {
210
+ id: "task-homepage-cta",
211
+ title: "Clarify homepage CTA hierarchy",
212
+ category: "visual-design",
213
+ problem: "Primary and secondary actions compete in the hero, which weakens the visitor's first decision.",
214
+ evidence: "Sample finding: Primary CTA competes with secondary link on the homepage.",
215
+ impact: "high",
216
+ effort: "medium",
217
+ priority: "p1",
218
+ pages: ["/"],
219
+ recommendedMcp: ["browser", "figma"],
220
+ codexPrompt: "Inspect the target homepage implementation, preserve existing design system patterns, and revise the hero CTA hierarchy so the primary signup action is visually dominant while the secondary action remains available.",
221
+ verification: ["Run target repo lint/build", "Verify desktop/tablet/mobile hero layout", "Confirm focus indicators and text contrast"],
222
+ risks: ["Could change conversion copy without stakeholder approval"],
223
+ },
224
+ ],
225
+ implementationEvidence: {
226
+ executedWork: [],
227
+ verificationResults: [],
228
+ remainingRisks: [
229
+ "MCP readiness gaps may limit verification depth.",
230
+ "Copy or brand changes may require stakeholder review.",
231
+ "Automated performance/accessibility tooling is outside this MVP unless run in the target repo.",
232
+ ],
233
+ nextActions: [],
234
+ },
235
+ operatorRunbook: null,
236
+ reportNotes: "MVP audit is a planning console. Run the generated prompts inside the target website repo before marking implementation complete.",
237
+ };
238
+ }
239
+
240
+ function normalizeWorkspace(raw) {
241
+ var fallback = createDefaultWorkspace();
242
+ var source = raw && typeof raw === "object" ? raw : {};
243
+ var profile = source.siteProfile && typeof source.siteProfile === "object" ? source.siteProfile : {};
244
+ var workspace = {
245
+ version: 1,
246
+ updatedAt: source.updatedAt || new Date().toISOString(),
247
+ siteProfile: {
248
+ id: String(profile.id || fallback.siteProfile.id),
249
+ name: String(profile.name || fallback.siteProfile.name),
250
+ liveUrl: String(profile.liveUrl || ""),
251
+ repoUrl: String(profile.repoUrl || ""),
252
+ localPath: String(profile.localPath || ""),
253
+ figmaUrl: String(profile.figmaUrl || ""),
254
+ brandNotes: String(profile.brandNotes || ""),
255
+ deployProvider: normalizeEnum(profile.deployProvider, deployOptions, "none"),
256
+ sentryProject: String(profile.sentryProject || ""),
257
+ cms: normalizeEnum(profile.cms, cmsOptions, "none"),
258
+ database: normalizeEnum(profile.database, databaseOptions, "none"),
259
+ pages: normalizeStringArray(profile.pages, fallback.siteProfile.pages),
260
+ userFlows: normalizeStringArray(profile.userFlows, fallback.siteProfile.userFlows),
261
+ viewports: normalizeStringArray(profile.viewports, fallback.siteProfile.viewports).filter(function (item) {
262
+ return viewportOptions.indexOf(item) !== -1;
263
+ }),
264
+ },
265
+ auditChecklist: normalizeChecklist(source.auditChecklist || fallback.auditChecklist),
266
+ mcpReadiness: normalizeMcp(source.mcpReadiness || fallback.mcpReadiness),
267
+ refactorTasks: normalizeTasks(source.refactorTasks || fallback.refactorTasks),
268
+ implementationEvidence: normalizeImplementationEvidence(source.implementationEvidence || fallback.implementationEvidence),
269
+ operatorRunbook: normalizeOperatorRunbook(source.operatorRunbook || (source.bundle && source.bundle.operatorRunbook), source),
270
+ reportNotes: String(source.reportNotes || ""),
271
+ };
272
+ if (workspace.siteProfile.viewports.length === 0) {
273
+ workspace.siteProfile.viewports = ["desktop"];
274
+ }
275
+ return workspace;
276
+ }
277
+
278
+ function normalizeOperatorRunbook(value, container) {
279
+ if (!value || typeof value !== "object") return null;
280
+ var sourceBundle = normalizeRunbookSourceBundle(value.sourceBundle || (container && container.sourceBundle) || (container && container.bundle && container.bundle.sourceBundle));
281
+ var rows = Array.isArray(value.stageHumanLineDisplayRows)
282
+ ? value.stageHumanLineDisplayRows.map(normalizeRunbookRow).filter(function (row) { return row.key; })
283
+ : [];
284
+ var rowByKey = rows.reduce(function (acc, row) {
285
+ acc[row.key] = row;
286
+ return acc;
287
+ }, {});
288
+ var actionStatusKeys = ["ready", "optional", "manual", "blocked"];
289
+ var evidenceProgressStatusKeys = ["blocked", "ready"];
290
+ var actionStatusIndex = fillRunbookKeyIndexFromRows(
291
+ normalizeRunbookKeyIndex(value.stageHumanLineDisplayRowKeysByActionStatus, actionStatusKeys),
292
+ rows,
293
+ "actionStatus",
294
+ actionStatusKeys,
295
+ );
296
+ var evidenceProgressStatusIndex = fillRunbookKeyIndexFromRows(
297
+ normalizeRunbookKeyIndex(value.stageHumanLineDisplayRowKeysByEvidenceProgressStatus, evidenceProgressStatusKeys),
298
+ rows,
299
+ "evidenceProgressStatus",
300
+ evidenceProgressStatusKeys,
301
+ );
302
+ return {
303
+ version: Number(value.version || 1),
304
+ source: String(value.source || "bundle-handoff"),
305
+ stageCount: Number(value.stageCount || rows.length),
306
+ commandStageCount: Number(value.commandStageCount || 0),
307
+ manualStageCount: Number(value.manualStageCount || 0),
308
+ requiredStageCount: Number(value.requiredStageCount || 0),
309
+ optionalStageCount: Number(value.optionalStageCount || 0),
310
+ readOnlyCommandStageCount: Number(value.readOnlyCommandStageCount || 0),
311
+ localOutputCommandStageCount: Number(value.localOutputCommandStageCount || 0),
312
+ externalCallCommandStageCount: Number(value.externalCallCommandStageCount || 0),
313
+ targetRepoMutationCommandStageCount: Number(value.targetRepoMutationCommandStageCount || 0),
314
+ effectiveTaskId: String(value.effectiveTaskId || ""),
315
+ effectiveStrictTaskCommandKey: String(value.effectiveStrictTaskCommandKey || ""),
316
+ sourceBundle: sourceBundle,
317
+ nextStageKey: String(value.nextStageKey || ""),
318
+ nextCommandKey: String(value.nextCommandKey || ""),
319
+ nextStageHumanLine: String(value.nextStageHumanLine || ""),
320
+ nextStageHumanLineDisplayRow: normalizeRunbookRow(value.nextStageHumanLineDisplayRow || rowByKey[value.nextStageKey] || {}),
321
+ stageHumanLineDisplayRows: rows,
322
+ stageHumanLineDisplayRowByKey: rowByKey,
323
+ stageHumanLineDisplayRowSummary: normalizePlainObject(value.stageHumanLineDisplayRowSummary),
324
+ stageHumanLineDisplayRowKeysByActionStatus: actionStatusIndex,
325
+ stageHumanLineDisplayRowKeysByEvidenceProgressStatus: evidenceProgressStatusIndex,
326
+ };
327
+ }
328
+
329
+ function normalizeRunbookSourceBundle(value) {
330
+ if (!value || typeof value !== "object") return null;
331
+ return {
332
+ directory: String(value.directory || ""),
333
+ sourceWorkspace: String(value.sourceWorkspace || ""),
334
+ siteName: String(value.siteName || ""),
335
+ status: String(value.status || "unknown"),
336
+ valid: value.valid === true,
337
+ workspaceStatus: String(value.workspaceStatus || ""),
338
+ mcpStatus: String(value.mcpStatus || ""),
339
+ mcpProbeStatus: String(value.mcpProbeStatus || ""),
340
+ checksumAlgorithm: String(value.checksumAlgorithm || ""),
341
+ checksumBundleDigest: String(value.checksumBundleDigest || ""),
342
+ verifiedChecksumFiles: Number(value.verifiedChecksumFiles || 0),
343
+ expectedChecksumFiles: Number(value.expectedChecksumFiles || 0),
344
+ verifiedGeneratedFiles: Number(value.verifiedGeneratedFiles || 0),
345
+ expectedGeneratedFiles: Number(value.expectedGeneratedFiles || 0),
346
+ issueCount: Number(value.issueCount || 0),
347
+ warningCount: Number(value.warningCount || 0),
348
+ failureCount: Number(value.failureCount || 0),
349
+ strictCheckCommand: String(value.strictCheckCommand || ""),
350
+ strictHandoffCommand: String(value.strictHandoffCommand || ""),
351
+ };
352
+ }
353
+
354
+ function normalizeRunbookRow(value) {
355
+ var row = value && typeof value === "object" ? value : {};
356
+ return {
357
+ step: Number(row.step || 0),
358
+ key: String(row.key || ""),
359
+ label: String(row.label || ""),
360
+ line: String(row.line || ""),
361
+ required: row.required === true,
362
+ manual: row.manual === true,
363
+ commandCount: Number(row.commandCount || 0),
364
+ actionType: String(row.actionType || ""),
365
+ actionLabel: String(row.actionLabel || ""),
366
+ actionStatus: String(row.actionStatus || ""),
367
+ actionStatusLabel: String(row.actionStatusLabel || ""),
368
+ actionStatusTone: String(row.actionStatusTone || ""),
369
+ hasEvidenceProgress: row.hasEvidenceProgress === true,
370
+ evidenceProgressStatus: String(row.evidenceProgressStatus || ""),
371
+ evidenceProgressStatusLabel: String(row.evidenceProgressStatusLabel || ""),
372
+ evidenceProgressStatusTone: String(row.evidenceProgressStatusTone || ""),
373
+ evidenceProgressIconName: String(row.evidenceProgressIconName || ""),
374
+ evidenceProgressLabel: String(row.evidenceProgressLabel || ""),
375
+ evidenceCompletionPercent: Number(row.evidenceCompletionPercent || 0),
376
+ firstUncheckedEvidenceItemLabel: String(row.firstUncheckedEvidenceItemLabel || ""),
377
+ };
378
+ }
379
+
380
+ function normalizePlainObject(value) {
381
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
382
+ }
383
+
384
+ function normalizeRunbookKeyIndex(value, keys) {
385
+ var source = normalizePlainObject(value);
386
+ return keys.reduce(function (acc, key) {
387
+ acc[key] = normalizeStringArray(source[key], []);
388
+ return acc;
389
+ }, {});
390
+ }
391
+
392
+ function fillRunbookKeyIndexFromRows(index, rows, statusField, keys) {
393
+ keys.forEach(function (key) {
394
+ if (index[key] && index[key].length) return;
395
+ index[key] = rows.filter(function (row) {
396
+ return row[statusField] === key;
397
+ }).map(function (row) {
398
+ return row.key;
399
+ });
400
+ });
401
+ return index;
402
+ }
403
+
404
+ function extractOperatorRunbookPayload(value) {
405
+ if (!value || typeof value !== "object") return null;
406
+ if (value.operatorRunbook) return value.operatorRunbook;
407
+ if (value.bundle && value.bundle.operatorRunbook) return value.bundle.operatorRunbook;
408
+ return null;
409
+ }
410
+
411
+ function extractSourceBundleProvenancePayload(value) {
412
+ if (!value || typeof value !== "object") return null;
413
+ if (value.type === "website-improvement-source-bundle-provenance") return value.sourceBundle;
414
+ if (!value.siteProfile && !value.operatorRunbook && !(value.bundle && value.bundle.operatorRunbook) && value.sourceBundle) {
415
+ return value.sourceBundle;
416
+ }
417
+ return null;
418
+ }
419
+
420
+ function extractSourceBundleRevalidationGatePayload(value) {
421
+ if (!value || typeof value !== "object") return null;
422
+ if (value.type !== "website-improvement-source-bundle-revalidation-gate") return null;
423
+ var sourceBundle = value.sourceBundle && typeof value.sourceBundle === "object" ? value.sourceBundle : {};
424
+ var gate = value.revalidationGate && typeof value.revalidationGate === "object" ? value.revalidationGate : {};
425
+ return {
426
+ directory: sourceBundle.directory || "",
427
+ sourceWorkspace: sourceBundle.sourceWorkspace || "",
428
+ siteName: sourceBundle.siteName || "",
429
+ checksumBundleDigest: sourceBundle.checksumBundleDigest || "",
430
+ status: sourceBundle.status || gate.status || "unknown",
431
+ valid: sourceBundle.valid === true || gate.valid === true,
432
+ failureCount: Number(gate.failureCount || 0),
433
+ warningCount: Number(gate.warningCount || 0),
434
+ issueCount: Number(gate.issueCount || 0),
435
+ strictCheckCommand: gate.strictCheckCommand || "",
436
+ };
437
+ }
438
+
439
+ function createSourceBundleOnlyRunbook(sourceBundle, source) {
440
+ return normalizeOperatorRunbook({
441
+ version: 1,
442
+ source: source || "source-bundle-provenance",
443
+ sourceBundle: sourceBundle,
444
+ stageCount: 0,
445
+ stageHumanLineDisplayRows: [],
446
+ });
447
+ }
448
+
449
+ function normalizeImplementationEvidence(value) {
450
+ var fallback = createDefaultWorkspace().implementationEvidence;
451
+ var source = value && typeof value === "object" ? value : {};
452
+ return {
453
+ executedWork: normalizeStringArray(source.executedWork, []),
454
+ verificationResults: normalizeStringArray(source.verificationResults, []),
455
+ remainingRisks: normalizeStringArray(source.remainingRisks, fallback.remainingRisks),
456
+ nextActions: normalizeStringArray(source.nextActions, []),
457
+ };
458
+ }
459
+
460
+ function normalizeChecklist(value) {
461
+ var fallback = createDefaultChecklist();
462
+ return auditCategories.reduce(function (acc, item) {
463
+ var row = value && value[item.id] && typeof value[item.id] === "object" ? value[item.id] : {};
464
+ acc[item.id] = {
465
+ status: normalizeEnum(row.status, statusOptions, fallback[item.id].status),
466
+ notes: String(row.notes || ""),
467
+ findings: normalizeStringArray(row.findings, []),
468
+ };
469
+ return acc;
470
+ }, {});
471
+ }
472
+
473
+ function normalizeMcp(value) {
474
+ return mcpItems.reduce(function (acc, item) {
475
+ var key = item[0];
476
+ acc[key] = normalizeEnum(value && value[key], mcpStatusOptions, "unused");
477
+ return acc;
478
+ }, {});
479
+ }
480
+
481
+ function normalizeTasks(value) {
482
+ if (!Array.isArray(value)) return [];
483
+ return value.map(function (task, index) {
484
+ var item = task && typeof task === "object" ? task : {};
485
+ return {
486
+ id: String(item.id || "task-" + Date.now() + "-" + index),
487
+ title: String(item.title || "Untitled website improvement task"),
488
+ category: normalizeEnum(item.category, auditCategories.map(function (category) { return category.id; }), "ux-flow"),
489
+ problem: String(item.problem || ""),
490
+ evidence: String(item.evidence || ""),
491
+ impact: normalizeEnum(item.impact, impactOptions, "medium"),
492
+ effort: normalizeEnum(item.effort, effortOptions, "medium"),
493
+ priority: normalizeEnum(item.priority, priorityOptions, "p2"),
494
+ pages: normalizeStringArray(item.pages, []),
495
+ recommendedMcp: normalizeStringArray(item.recommendedMcp, []),
496
+ codexPrompt: String(item.codexPrompt || ""),
497
+ verification: normalizeStringArray(item.verification, []),
498
+ risks: normalizeStringArray(item.risks, []),
499
+ };
500
+ });
501
+ }
502
+
503
+ function normalizeEnum(value, allowed, fallback) {
504
+ return allowed.indexOf(value) === -1 ? fallback : value;
505
+ }
506
+
507
+ function normalizeStringArray(value, fallback) {
508
+ var source = Array.isArray(value) ? value : fallback || [];
509
+ return source
510
+ .map(function (item) { return String(item || "").trim(); })
511
+ .filter(Boolean);
512
+ }
513
+
514
+ function loadWorkspace() {
515
+ try {
516
+ var stored = localStorage.getItem(STORAGE_KEY);
517
+ if (!stored) return createDefaultWorkspace();
518
+ return normalizeWorkspace(JSON.parse(stored));
519
+ } catch (error) {
520
+ return createDefaultWorkspace();
521
+ }
522
+ }
523
+
524
+ function saveWorkspace() {
525
+ appState.workspace.updatedAt = new Date().toISOString();
526
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(appState.workspace, null, 2));
527
+ }
528
+
529
+ function setMessage(text) {
530
+ appState.message = text;
531
+ render();
532
+ }
533
+
534
+ function escapeHtml(value) {
535
+ return String(value == null ? "" : value)
536
+ .replace(/&/g, "&")
537
+ .replace(/</g, "&lt;")
538
+ .replace(/>/g, "&gt;")
539
+ .replace(/"/g, "&quot;");
540
+ }
541
+
542
+ function escapeAttr(value) {
543
+ return escapeHtml(value).replace(/'/g, "&#39;");
544
+ }
545
+
546
+ function linesToText(items) {
547
+ return normalizeStringArray(items, []).join("\n");
548
+ }
549
+
550
+ function textToLines(text) {
551
+ return String(text || "")
552
+ .split(/\r?\n/)
553
+ .map(function (line) { return line.trim(); })
554
+ .filter(Boolean);
555
+ }
556
+
557
+ function categoryById(id) {
558
+ return auditCategories.find(function (item) { return item.id === id; }) || auditCategories[0];
559
+ }
560
+
561
+ function optionList(options, selected) {
562
+ return options.map(function (option) {
563
+ return "<option value=\"" + escapeAttr(option) + "\"" + (option === selected ? " selected" : "") + ">" + escapeHtml(labelize(option)) + "</option>";
564
+ }).join("");
565
+ }
566
+
567
+ function labelize(value) {
568
+ return String(value || "").replace(/-/g, " ").replace(/\b\w/g, function (char) {
569
+ return char.toUpperCase();
570
+ });
571
+ }
572
+
573
+ function badge(value) {
574
+ return "<span class=\"badge badge--" + escapeAttr(value) + "\">" + escapeHtml(labelize(value)) + "</span>";
575
+ }
576
+
577
+ function pill(value, prefix) {
578
+ return "<span class=\"pill pill--" + escapeAttr(value) + "\">" + escapeHtml(prefix ? prefix + " " + labelize(value) : labelize(value)) + "</span>";
579
+ }
580
+
581
+ function metricData() {
582
+ var checklist = appState.workspace.auditChecklist;
583
+ var done = auditCategories.filter(function (item) { return checklist[item.id].status === "done"; }).length;
584
+ var blocked = auditCategories.filter(function (item) { return checklist[item.id].status === "blocked"; }).length;
585
+ var requiredMcp = mcpItems.filter(function (item) {
586
+ return appState.workspace.mcpReadiness[item[0]] === "required";
587
+ }).length;
588
+ var evidence = appState.workspace.implementationEvidence;
589
+ var evidenceCount = evidence.executedWork.length + evidence.verificationResults.length;
590
+ var runbook = appState.workspace.operatorRunbook;
591
+ var graph = buildWorkflowGraph();
592
+ return {
593
+ pages: appState.workspace.siteProfile.pages.length,
594
+ done: done,
595
+ blocked: blocked,
596
+ tasks: appState.workspace.refactorTasks.length,
597
+ requiredMcp: requiredMcp,
598
+ evidence: evidenceCount,
599
+ runbookStages: runbook ? runbook.stageCount || runbook.stageHumanLineDisplayRows.length : 0,
600
+ graphNodes: graph.summary.nodeCount,
601
+ graphEdges: graph.summary.edgeCount,
602
+ };
603
+ }
604
+
605
+ function render() {
606
+ var root = document.getElementById("app");
607
+ if (!root) return;
608
+ var metrics = metricData();
609
+ root.dataset.status = "ready";
610
+ root.innerHTML = [
611
+ "<div class=\"workspace\">",
612
+ renderSidebar(metrics),
613
+ "<main id=\"main\" class=\"main\" tabindex=\"-1\">",
614
+ renderTopbar(metrics),
615
+ renderActivePanel(),
616
+ "</main>",
617
+ "</div>",
618
+ "<div class=\"sr-only\" aria-live=\"polite\">" + escapeHtml(appState.message) + "</div>",
619
+ ].join("");
620
+ }
621
+
622
+ function renderSidebar(metrics) {
623
+ return [
624
+ "<aside class=\"sidebar\" aria-label=\"Website console sections\">",
625
+ "<div class=\"brand\">",
626
+ "<span class=\"brand__eyebrow\">design-ai</span>",
627
+ "<h1>Website Console</h1>",
628
+ "<p>Local control tower for site audits, MCP readiness, prompts, and handoff reports.</p>",
629
+ "</div>",
630
+ "<ul class=\"nav-list\">",
631
+ tabs.map(function (tab) {
632
+ var id = tab[0];
633
+ var label = tab[1];
634
+ var count = id === "audit" ? metrics.done + "/" + auditCategories.length
635
+ : id === "tasks" ? String(metrics.tasks)
636
+ : id === "mcp" ? String(metrics.requiredMcp)
637
+ : id === "graph" ? String(metrics.graphNodes)
638
+ : id === "report" && (metrics.evidence || metrics.runbookStages) ? String(metrics.evidence || metrics.runbookStages)
639
+ : "";
640
+ return [
641
+ "<li>",
642
+ "<button type=\"button\" class=\"nav-button\" data-nav=\"" + escapeAttr(id) + "\" aria-current=\"" + (appState.activeTab === id ? "page" : "false") + "\">",
643
+ "<span>" + escapeHtml(label) + "</span>",
644
+ count ? "<span class=\"nav-count\">" + escapeHtml(count) + "</span>" : "",
645
+ "</button>",
646
+ "</li>",
647
+ ].join("");
648
+ }).join(""),
649
+ "</ul>",
650
+ "<div class=\"sidebar-actions\">",
651
+ "<button type=\"button\" class=\"button button--primary\" data-action=\"export-workspace\">Export JSON</button>",
652
+ "<button type=\"button\" class=\"button\" data-action=\"import-click\">Import workspace/runbook JSON</button>",
653
+ "<input class=\"sr-only\" type=\"file\" accept=\"application/json,.json\" id=\"import-file\" data-action=\"import-file\">",
654
+ "<button type=\"button\" class=\"button button--danger\" data-action=\"reset-sample\">Reset sample</button>",
655
+ appState.message ? "<p class=\"field\"><small>" + escapeHtml(appState.message) + "</small></p>" : "",
656
+ "</div>",
657
+ "</aside>",
658
+ ].join("");
659
+ }
660
+
661
+ function renderTopbar(metrics) {
662
+ var profile = appState.workspace.siteProfile;
663
+ return [
664
+ "<section class=\"topbar\" aria-label=\"Workspace summary\">",
665
+ "<div>",
666
+ "<h2>" + escapeHtml(tabs.find(function (tab) { return tab[0] === appState.activeTab; })[1]) + "</h2>",
667
+ "<p>" + escapeHtml(profile.name) + " is tracked locally. Use this app to prepare website improvement work before switching into the target repo.</p>",
668
+ "</div>",
669
+ "<div class=\"topbar__meta\">",
670
+ badge(appState.workspace.version === 1 ? "done" : "blocked"),
671
+ "<span class=\"badge badge--optional\">Updated " + escapeHtml(formatDate(appState.workspace.updatedAt)) + "</span>",
672
+ "</div>",
673
+ "</section>",
674
+ "<section class=\"summary-strip\" aria-label=\"Workspace metrics\">",
675
+ metric("Pages", metrics.pages, "Priority URLs tracked"),
676
+ metric("Audit done", metrics.done + "/" + auditCategories.length, metrics.blocked + " blocked"),
677
+ metric("Tasks", metrics.tasks, "Refactor plan items"),
678
+ metric("Required MCP", metrics.requiredMcp, "Connections to prepare"),
679
+ "</section>",
680
+ ].join("");
681
+ }
682
+
683
+ function metric(label, value, note) {
684
+ return [
685
+ "<div class=\"metric\">",
686
+ "<div class=\"metric__label\">" + escapeHtml(label) + "</div>",
687
+ "<div class=\"metric__value\">" + escapeHtml(value) + "</div>",
688
+ "<div class=\"metric__note\">" + escapeHtml(note) + "</div>",
689
+ "</div>",
690
+ ].join("");
691
+ }
692
+
693
+ function formatDate(value) {
694
+ try {
695
+ return new Date(value).toLocaleString(undefined, {
696
+ month: "short",
697
+ day: "numeric",
698
+ hour: "2-digit",
699
+ minute: "2-digit",
700
+ });
701
+ } catch (error) {
702
+ return "now";
703
+ }
704
+ }
705
+
706
+ function renderActivePanel() {
707
+ if (appState.activeTab === "profile") return renderProfile();
708
+ if (appState.activeTab === "audit") return renderAudit();
709
+ if (appState.activeTab === "mcp") return renderMcp();
710
+ if (appState.activeTab === "graph") return renderGraph();
711
+ if (appState.activeTab === "tasks") return renderTasks();
712
+ if (appState.activeTab === "prompts") return renderPrompts();
713
+ return renderReport();
714
+ }
715
+
716
+ function renderProfile() {
717
+ var profile = appState.workspace.siteProfile;
718
+ return [
719
+ panel("Site Profile", "Baseline inputs used by audits, tasks, prompts, and handoff reports.", [
720
+ "<div class=\"form-grid\">",
721
+ textField("Site name", "siteProfile.name", profile.name),
722
+ textField("Live URL", "siteProfile.liveUrl", profile.liveUrl),
723
+ textField("Repo URL", "siteProfile.repoUrl", profile.repoUrl),
724
+ textField("Local path", "siteProfile.localPath", profile.localPath),
725
+ textField("Figma URL", "siteProfile.figmaUrl", profile.figmaUrl),
726
+ textField("Sentry project", "siteProfile.sentryProject", profile.sentryProject),
727
+ selectField("Deploy platform", "siteProfile.deployProvider", deployOptions, profile.deployProvider),
728
+ selectField("CMS", "siteProfile.cms", cmsOptions, profile.cms),
729
+ selectField("Database", "siteProfile.database", databaseOptions, profile.database),
730
+ textareaField("Design system / brand notes", "siteProfile.brandNotes", profile.brandNotes, "field--wide"),
731
+ textareaField("Pages", "siteProfile.pages", linesToText(profile.pages), "field--wide", "One path or URL per line."),
732
+ textareaField("User flows", "siteProfile.userFlows", linesToText(profile.userFlows), "field--wide", "One flow per line."),
733
+ renderViewportField(profile.viewports),
734
+ "</div>",
735
+ ].join("")),
736
+ panel("MVP Boundary", "This app coordinates work; target repo code changes happen in the target website repository.", [
737
+ "<div class=\"notice\">No crawling, Lighthouse, axe, visual diff, MCP writes, backend sync, embeddings, or fine-tuning run in this MVP.</div>",
738
+ "<div class=\"grid-3\">",
739
+ boundaryItem("Local first", "State stays in browser localStorage and can be exported as JSON."),
740
+ boundaryItem("Prompt led", "Codex and Claude prompts are generated from the current profile, findings, and tasks."),
741
+ boundaryItem("Repo safe", "This design-ai repo does not include or mutate the target website source code."),
742
+ "</div>",
743
+ ].join("")),
744
+ ].join("");
745
+ }
746
+
747
+ function boundaryItem(title, body) {
748
+ return "<div><h4>" + escapeHtml(title) + "</h4><p>" + escapeHtml(body) + "</p></div>";
749
+ }
750
+
751
+ function textField(label, path, value, extraClass) {
752
+ return [
753
+ "<div class=\"field " + escapeAttr(extraClass || "") + "\">",
754
+ "<label for=\"" + escapeAttr(path) + "\">" + escapeHtml(label) + "</label>",
755
+ "<input id=\"" + escapeAttr(path) + "\" type=\"text\" value=\"" + escapeAttr(value) + "\" data-field=\"" + escapeAttr(path) + "\">",
756
+ "</div>",
757
+ ].join("");
758
+ }
759
+
760
+ function selectField(label, path, options, value) {
761
+ return [
762
+ "<div class=\"field\">",
763
+ "<label for=\"" + escapeAttr(path) + "\">" + escapeHtml(label) + "</label>",
764
+ "<select id=\"" + escapeAttr(path) + "\" data-field=\"" + escapeAttr(path) + "\">",
765
+ optionList(options, value),
766
+ "</select>",
767
+ "</div>",
768
+ ].join("");
769
+ }
770
+
771
+ function textareaField(label, path, value, extraClass, help) {
772
+ return [
773
+ "<div class=\"field " + escapeAttr(extraClass || "") + "\">",
774
+ "<label for=\"" + escapeAttr(path) + "\">" + escapeHtml(label) + "</label>",
775
+ "<textarea id=\"" + escapeAttr(path) + "\" data-field=\"" + escapeAttr(path) + "\">" + escapeHtml(value) + "</textarea>",
776
+ help ? "<small>" + escapeHtml(help) + "</small>" : "",
777
+ "</div>",
778
+ ].join("");
779
+ }
780
+
781
+ function renderViewportField(selected) {
782
+ return [
783
+ "<fieldset class=\"field field--wide checkbox-row\">",
784
+ "<legend>Viewport coverage</legend>",
785
+ viewportOptions.map(function (viewport) {
786
+ return [
787
+ "<label class=\"checkbox-pill\">",
788
+ "<input type=\"checkbox\" value=\"" + escapeAttr(viewport) + "\" data-viewport=\"" + escapeAttr(viewport) + "\"" + (selected.indexOf(viewport) !== -1 ? " checked" : "") + ">",
789
+ escapeHtml(labelize(viewport)),
790
+ "</label>",
791
+ ].join("");
792
+ }).join(""),
793
+ "</fieldset>",
794
+ ].join("");
795
+ }
796
+
797
+ function renderAudit() {
798
+ var checklist = appState.workspace.auditChecklist;
799
+ return panel("Audit Pipeline", "Track professional site diagnostics before translating findings into implementation tasks.", [
800
+ "<div class=\"table-wrap\">",
801
+ "<table>",
802
+ "<thead><tr><th>Category</th><th>Status</th><th>Notes</th><th>Findings</th></tr></thead>",
803
+ "<tbody>",
804
+ auditCategories.map(function (category) {
805
+ var row = checklist[category.id];
806
+ return [
807
+ "<tr>",
808
+ "<td><strong>" + escapeHtml(category.label) + "</strong><br><small>" + escapeHtml(category.description) + "</small></td>",
809
+ "<td><select data-audit-status=\"" + escapeAttr(category.id) + "\">" + optionList(statusOptions, row.status) + "</select><br><br>" + badge(row.status) + "</td>",
810
+ "<td><textarea data-audit-notes=\"" + escapeAttr(category.id) + "\">" + escapeHtml(row.notes) + "</textarea></td>",
811
+ "<td><textarea data-audit-findings=\"" + escapeAttr(category.id) + "\">" + escapeHtml(linesToText(row.findings)) + "</textarea><small>One finding per line.</small></td>",
812
+ "</tr>",
813
+ ].join("");
814
+ }).join(""),
815
+ "</tbody>",
816
+ "</table>",
817
+ "</div>",
818
+ "<div class=\"button-row\" style=\"margin-top: 16px;\">",
819
+ "<button type=\"button\" class=\"button button--primary\" data-action=\"generate-tasks\">Generate starter tasks</button>",
820
+ "<button type=\"button\" class=\"button\" data-action=\"mark-audit-todo\">Mark all todo</button>",
821
+ "</div>",
822
+ ].join(""));
823
+ }
824
+
825
+ function renderMcp() {
826
+ var readiness = appState.workspace.mcpReadiness;
827
+ return panel("MCP Readiness Matrix", "Classify what the website improvement workflow needs before implementation begins.", [
828
+ "<div class=\"table-wrap\">",
829
+ "<table>",
830
+ "<thead><tr><th>MCP</th><th>Use case</th><th>Status</th><th>Operational note</th></tr></thead>",
831
+ "<tbody>",
832
+ mcpItems.map(function (item) {
833
+ var key = item[0];
834
+ var label = item[1];
835
+ var description = item[2];
836
+ var status = readiness[key];
837
+ return [
838
+ "<tr>",
839
+ "<td><strong>" + escapeHtml(label) + "</strong></td>",
840
+ "<td>" + escapeHtml(description) + "</td>",
841
+ "<td><select data-mcp=\"" + escapeAttr(key) + "\">" + optionList(mcpStatusOptions, status) + "</select><br><br>" + badge(status) + "</td>",
842
+ "<td>" + escapeHtml(mcpAdvice(key, status)) + "</td>",
843
+ "</tr>",
844
+ ].join("");
845
+ }).join(""),
846
+ "</tbody>",
847
+ "</table>",
848
+ "</div>",
849
+ ].join(""));
850
+ }
851
+
852
+ function mcpAdvice(key, status) {
853
+ if (status === "required") return "Prepare auth and verify access before assigning implementation work.";
854
+ if (status === "optional") return "Use when available; prompt should include a manual fallback.";
855
+ if (status === "unavailable") return "Keep the task manual and mention the missing integration as a risk.";
856
+ return key === "browser" ? "Manual screenshot review only; no runtime interaction expected." : "Not needed for the current site scope.";
857
+ }
858
+
859
+ function renderTasks() {
860
+ var tasks = appState.workspace.refactorTasks;
861
+ return panel("Refactor Plan", "Starter work items generated from audit findings and refined before sending to Codex.", [
862
+ "<div class=\"button-row\" style=\"margin-bottom: 12px;\">",
863
+ "<button type=\"button\" class=\"button button--primary\" data-action=\"generate-tasks\">Generate from findings</button>",
864
+ "<button type=\"button\" class=\"button\" data-action=\"add-task\">Add manual task</button>",
865
+ "</div>",
866
+ tasks.length === 0 ? "<div class=\"empty-state\">No refactor tasks yet. Add findings in Audit Checklist and generate starter tasks.</div>" : "",
867
+ "<div class=\"task-list\">",
868
+ tasks.map(renderTaskRow).join(""),
869
+ "</div>",
870
+ ].join(""));
871
+ }
872
+
873
+ function renderGraph() {
874
+ var graph = buildWorkflowGraph();
875
+ return [
876
+ panel("Workflow Graph", "Render the local Website Improvement workflow before exporting it for CLI or target-repo handoff.", [
877
+ "<div class=\"button-row\" style=\"margin-bottom: 12px;\">",
878
+ "<button type=\"button\" class=\"button button--primary\" data-action=\"copy-graph-json\">Copy graph JSON</button>",
879
+ "<button type=\"button\" class=\"button\" data-action=\"download-graph-json\">Export graph JSON</button>",
880
+ "</div>",
881
+ "<div class=\"graph-summary\" aria-label=\"Workflow graph summary\">",
882
+ metric("Nodes", graph.summary.nodeCount, "Workspace graph items"),
883
+ metric("Edges", graph.summary.edgeCount, "Workflow dependencies"),
884
+ metric("Tasks", graph.summary.taskCount, graph.summary.generatedTaskCount + " generated candidate(s)"),
885
+ metric("Required MCP", graph.summary.requiredMcpCount, graph.mcpStatus + " readiness"),
886
+ "</div>",
887
+ "<div class=\"graph-boundaries\" aria-label=\"Workflow boundaries\">",
888
+ graph.boundaries.map(function (item) {
889
+ return "<span class=\"pill\">" + escapeHtml(item) + "</span>";
890
+ }).join(""),
891
+ "</div>",
892
+ renderGraphLanes(graph),
893
+ ].join("")),
894
+ panel("Workflow Edges", "Deterministic edge list used by the portable graph export.", [
895
+ "<div class=\"table-wrap graph-edge-table\">",
896
+ "<table>",
897
+ "<thead><tr><th>From</th><th>To</th><th>Type</th><th>Label</th></tr></thead>",
898
+ "<tbody>",
899
+ graph.edges.map(function (edge) {
900
+ return [
901
+ "<tr>",
902
+ "<td><code>" + escapeHtml(edge.from) + "</code></td>",
903
+ "<td><code>" + escapeHtml(edge.to) + "</code></td>",
904
+ "<td>" + escapeHtml(edge.type) + "</td>",
905
+ "<td>" + escapeHtml(edge.label) + "</td>",
906
+ "</tr>",
907
+ ].join("");
908
+ }).join(""),
909
+ "</tbody>",
910
+ "</table>",
911
+ "</div>",
912
+ ].join("")),
913
+ ].join("");
914
+ }
915
+
916
+ function renderGraphLanes(graph) {
917
+ var lanes = [
918
+ ["intake", "Intake", ["workspace", "site-profile"]],
919
+ ["audit", "Audit", ["audit-category"]],
920
+ ["mcp", "MCP", ["mcp-readiness"]],
921
+ ["tasks", "Tasks", ["refactor-task"]],
922
+ ["prompts", "Prompts", ["prompt-template"]],
923
+ ["handoff", "Handoff", ["handoff-report", "handoff-bundle", "target-repo"]],
924
+ ];
925
+ return [
926
+ "<div class=\"graph-lanes\" aria-label=\"Workflow graph lanes\">",
927
+ lanes.map(function (lane) {
928
+ var nodes = graph.nodes.filter(function (node) {
929
+ return lane[2].indexOf(node.type) !== -1;
930
+ });
931
+ return [
932
+ "<section class=\"graph-lane\" aria-label=\"" + escapeAttr(lane[1]) + " nodes\">",
933
+ "<div class=\"graph-lane__header\">",
934
+ "<strong>" + escapeHtml(lane[1]) + "</strong>",
935
+ "<span>" + nodes.length + "</span>",
936
+ "</div>",
937
+ "<ol>",
938
+ nodes.map(renderGraphNode).join(""),
939
+ "</ol>",
940
+ "</section>",
941
+ ].join("");
942
+ }).join(""),
943
+ "</div>",
944
+ ].join("");
945
+ }
946
+
947
+ function renderGraphNode(node) {
948
+ return [
949
+ "<li class=\"graph-node graph-node--" + escapeAttr(node.type) + "\">",
950
+ "<div class=\"graph-node__top\">",
951
+ "<span class=\"graph-node__id\">" + escapeHtml(node.id) + "</span>",
952
+ badge(node.status),
953
+ "</div>",
954
+ "<strong>" + escapeHtml(node.label) + "</strong>",
955
+ renderGraphNodeMeta(node),
956
+ "</li>",
957
+ ].join("");
958
+ }
959
+
960
+ function renderGraphNodeMeta(node) {
961
+ if (node.type === "audit-category") {
962
+ return "<small>" + escapeHtml(node.data.findingCount + " finding(s)") + "</small>";
963
+ }
964
+ if (node.type === "mcp-readiness") {
965
+ return "<small>" + escapeHtml(node.data.requestedStatus + " / " + node.data.state) + "</small>";
966
+ }
967
+ if (node.type === "refactor-task") {
968
+ return "<small>" + escapeHtml(node.data.priority + " · " + node.data.category) + "</small>";
969
+ }
970
+ if (node.type === "prompt-template") {
971
+ return "<small>" + escapeHtml(node.data.agent + " · " + node.data.output) + "</small>";
972
+ }
973
+ if (node.type === "target-repo") {
974
+ return "<small>" + escapeHtml(node.data.repoUrl || node.data.localPath || "external repo boundary") + "</small>";
975
+ }
976
+ return "<small>" + escapeHtml(node.type) + "</small>";
977
+ }
978
+
979
+ function renderTaskRow(task) {
980
+ var category = categoryById(task.category);
981
+ return [
982
+ "<article class=\"task-row\">",
983
+ "<div>",
984
+ "<h4>" + escapeHtml(task.title) + "</h4>",
985
+ "<p>" + escapeHtml(task.problem) + "</p>",
986
+ "<div class=\"task-meta\">",
987
+ pill(task.priority, ""),
988
+ pill(task.impact, "Impact"),
989
+ pill(task.effort, "Effort"),
990
+ "<span class=\"pill\">" + escapeHtml(category.label) + "</span>",
991
+ task.pages.length ? "<span class=\"pill\">" + escapeHtml(task.pages.join(", ")) + "</span>" : "",
992
+ "</div>",
993
+ task.evidence ? "<p style=\"margin-top: 8px;\"><strong>Evidence:</strong> " + escapeHtml(task.evidence) + "</p>" : "",
994
+ "<details style=\"margin-top: 8px;\"><summary>Codex prompt and verification</summary><p>" + escapeHtml(task.codexPrompt) + "</p><ul>" + task.verification.map(function (item) { return "<li>" + escapeHtml(item) + "</li>"; }).join("") + "</ul></details>",
995
+ "</div>",
996
+ "<div class=\"task-actions\">",
997
+ "<button type=\"button\" class=\"button\" data-action=\"copy-task\" data-task=\"" + escapeAttr(task.id) + "\">Copy prompt</button>",
998
+ "<button type=\"button\" class=\"button button--danger\" data-action=\"remove-task\" data-task=\"" + escapeAttr(task.id) + "\">Remove</button>",
999
+ "</div>",
1000
+ "</article>",
1001
+ ].join("");
1002
+ }
1003
+
1004
+ function renderPrompts() {
1005
+ var promptText = buildPrompt(appState.selectedTemplate);
1006
+ return panel("Prompt Generator", "Generate execution-ready prompts for Codex and Claude from the current workspace.", [
1007
+ "<div class=\"template-layout\">",
1008
+ "<div class=\"template-list\" role=\"group\" aria-label=\"Prompt templates\">",
1009
+ templates.map(function (template) {
1010
+ var id = template[0];
1011
+ var label = template[1];
1012
+ return "<button type=\"button\" class=\"template-button\" data-template=\"" + escapeAttr(id) + "\" aria-pressed=\"" + (id === appState.selectedTemplate ? "true" : "false") + "\">" + escapeHtml(label) + "</button>";
1013
+ }).join(""),
1014
+ "</div>",
1015
+ "<div>",
1016
+ "<div class=\"button-row\" style=\"margin-bottom: 8px;\">",
1017
+ "<button type=\"button\" class=\"button button--primary\" data-action=\"copy-prompt\">Copy prompt</button>",
1018
+ "<button type=\"button\" class=\"button\" data-action=\"download-prompt\">Export .md</button>",
1019
+ "</div>",
1020
+ "<textarea class=\"output-box\" readonly data-output=\"prompt\">" + escapeHtml(promptText) + "</textarea>",
1021
+ "</div>",
1022
+ "</div>",
1023
+ ].join(""));
1024
+ }
1025
+
1026
+ function renderReport() {
1027
+ var report = buildHandoffReport();
1028
+ var evidence = appState.workspace.implementationEvidence;
1029
+ return [
1030
+ renderOperatorRunbook(),
1031
+ panel("Handoff Report", "Draft the before/after and verification report after target repo implementation work.", [
1032
+ "<div class=\"evidence-summary\" aria-label=\"Implementation evidence summary\">",
1033
+ metric("Executed", evidence.executedWork.length, "Target repo changes"),
1034
+ metric("Verified", evidence.verificationResults.length, "Checks recorded"),
1035
+ metric("Risks", evidence.remainingRisks.length, "Open items"),
1036
+ metric("Next", evidence.nextActions.length, "Follow-up steps"),
1037
+ "</div>",
1038
+ "<div class=\"form-grid evidence-grid\">",
1039
+ textareaField("Executed work", "implementationEvidence.executedWork", linesToText(evidence.executedWork), "", "One completed target-repo change per line."),
1040
+ textareaField("Verification results", "implementationEvidence.verificationResults", linesToText(evidence.verificationResults), "", "One command, browser check, deployment check, or manual QA result per line."),
1041
+ textareaField("Remaining risks", "implementationEvidence.remainingRisks", linesToText(evidence.remainingRisks), "", "One unresolved risk or dependency per line."),
1042
+ textareaField("Next actions", "implementationEvidence.nextActions", linesToText(evidence.nextActions), "", "One follow-up task per line."),
1043
+ "</div>",
1044
+ "<div class=\"field field--wide\" style=\"margin-bottom: 12px;\">",
1045
+ "<label for=\"reportNotes\">Report notes</label>",
1046
+ "<textarea id=\"reportNotes\" data-field=\"reportNotes\">" + escapeHtml(appState.workspace.reportNotes) + "</textarea>",
1047
+ "</div>",
1048
+ "<div class=\"button-row\" style=\"margin-bottom: 8px;\">",
1049
+ "<button type=\"button\" class=\"button button--primary\" data-action=\"copy-report\">Copy report</button>",
1050
+ "<button type=\"button\" class=\"button\" data-action=\"download-report\">Export .md</button>",
1051
+ "</div>",
1052
+ "<pre class=\"report-preview\" data-output=\"report\">" + escapeHtml(report) + "</pre>",
1053
+ ].join("")),
1054
+ ].join("");
1055
+ }
1056
+
1057
+ function renderOperatorRunbook() {
1058
+ var runbook = appState.workspace.operatorRunbook;
1059
+ if (!runbook) {
1060
+ return panel("Operator Runbook", "Import `design-ai site <bundle-dir> --bundle-handoff --json` to inspect the target-repo handoff stages in this console.", [
1061
+ "<div class=\"empty-state\">No operator runbook imported. Use the sidebar Import JSON action with a bundle handoff JSON output.</div>",
1062
+ ].join(""));
1063
+ }
1064
+ var summary = runbook.stageHumanLineDisplayRowSummary || {};
1065
+ var rows = runbook.stageHumanLineDisplayRows || [];
1066
+ var filteredRows = filterRunbookRows(runbook);
1067
+ var rowActionsDisabled = rows.length ? "" : " disabled aria-disabled=\"true\"";
1068
+ var nextLineDisabled = runbook.nextStageHumanLine ? "" : " disabled aria-disabled=\"true\"";
1069
+ return panel("Operator Runbook", "Review the verified bundle handoff stages before switching into the target website repo.", [
1070
+ "<div class=\"evidence-summary\" aria-label=\"Operator runbook summary\">",
1071
+ metric("Stages", runbook.stageCount || rows.length, (runbook.requiredStageCount || 0) + " required"),
1072
+ metric("Manual", summary.manualCount || 0, "Target-repo or evidence steps"),
1073
+ metric("Blocked evidence", summary.blockedEvidenceProgressCount || 0, "Rows needing evidence"),
1074
+ metric("Next", runbook.nextStageKey || "none", runbook.nextCommandKey || "No command"),
1075
+ "</div>",
1076
+ renderRunbookMetadata(runbook),
1077
+ renderRunbookSourceBundleDetails(runbook),
1078
+ renderRunbookSourceBundleWarning(runbook),
1079
+ renderRunbookProvenanceOnlyNotice(runbook, rows),
1080
+ "<div class=\"button-row\" style=\"margin-bottom: 12px;\">",
1081
+ "<button type=\"button\" class=\"button button--primary\" data-action=\"copy-runbook\">Copy runbook</button>",
1082
+ "<button type=\"button\" class=\"button\" data-action=\"download-runbook\">Export runbook .md</button>",
1083
+ "<button type=\"button\" class=\"button\" data-action=\"copy-filtered-runbook\"" + rowActionsDisabled + ">Copy filtered rows</button>",
1084
+ "<button type=\"button\" class=\"button\" data-action=\"download-filtered-runbook\"" + rowActionsDisabled + ">Export filtered .md</button>",
1085
+ "<button type=\"button\" class=\"button\" data-action=\"copy-next-runbook-line\"" + nextLineDisabled + ">Copy next line</button>",
1086
+ "<button type=\"button\" class=\"button button--danger\" data-action=\"clear-runbook\">Clear runbook</button>",
1087
+ "</div>",
1088
+ rows.length ? renderRunbookStatusIndex(runbook, filteredRows.length, rows.length) : "",
1089
+ renderRunbookRows(filteredRows, rows.length),
1090
+ ].filter(Boolean).join(""));
1091
+ }
1092
+
1093
+ function renderRunbookMetadata(runbook) {
1094
+ var sourceBundle = runbook.sourceBundle || {};
1095
+ var gateRequired = runbook.sourceBundle && sourceBundleNeedsRevalidation(runbook.sourceBundle);
1096
+ return [
1097
+ "<div class=\"graph-boundaries\" aria-label=\"Operator runbook metadata\">",
1098
+ "<span class=\"pill\">Source: " + escapeHtml(runbook.source || "bundle-handoff") + "</span>",
1099
+ "<span class=\"pill\">Task: " + escapeHtml(runbook.effectiveTaskId || "not specified") + "</span>",
1100
+ "<span class=\"pill\">Strict command: " + escapeHtml(runbook.effectiveStrictTaskCommandKey || "not specified") + "</span>",
1101
+ "<span class=\"pill\">Command stages: " + escapeHtml(String(runbook.commandStageCount || 0)) + "</span>",
1102
+ "<span class=\"pill\">Manual stages: " + escapeHtml(String(runbook.manualStageCount || 0)) + "</span>",
1103
+ "<span class=\"pill\">Read-only: " + escapeHtml(String(runbook.readOnlyCommandStageCount || 0)) + "</span>",
1104
+ "<span class=\"pill\">Local output: " + escapeHtml(String(runbook.localOutputCommandStageCount || 0)) + "</span>",
1105
+ sourceBundle.status ? "<span class=\"pill\">Bundle: " + escapeHtml(sourceBundle.status + "/" + (sourceBundle.valid ? "valid" : "invalid")) + "</span>" : "",
1106
+ runbook.sourceBundle ? "<span class=\"badge badge--" + escapeAttr(gateRequired ? "warn" : "pass") + "\">Gate: " + escapeHtml(gateRequired ? "required" : "not required") + "</span>" : "",
1107
+ sourceBundle.checksumBundleDigest ? "<span class=\"pill\">Digest: " + escapeHtml(shortDisplay(sourceBundle.checksumBundleDigest, 12)) + "</span>" : "",
1108
+ sourceBundle.expectedGeneratedFiles ? "<span class=\"pill\">Generated: " + escapeHtml(String(sourceBundle.verifiedGeneratedFiles || 0) + "/" + String(sourceBundle.expectedGeneratedFiles)) + "</span>" : "",
1109
+ "</div>",
1110
+ ].filter(Boolean).join("");
1111
+ }
1112
+
1113
+ function renderRunbookSourceBundleDetails(runbook) {
1114
+ var sourceBundle = runbook.sourceBundle;
1115
+ if (!sourceBundle) return "";
1116
+ var checkCommand = sourceBundle.strictCheckCommand || "";
1117
+ var handoffCommand = sourceBundle.strictHandoffCommand || "";
1118
+ return [
1119
+ "<div class=\"runbook-source-bundle\" aria-label=\"Source bundle provenance\">",
1120
+ "<div class=\"runbook-source-bundle__header\"><div><strong>Source Bundle</strong><span>" + escapeHtml(sourceBundle.directory || "No source directory recorded") + "</span></div><div class=\"runbook-line-actions\"><button type=\"button\" class=\"button row-copy-button\" data-action=\"copy-runbook-source-bundle\">Copy Markdown</button><button type=\"button\" class=\"button row-copy-button\" data-action=\"download-runbook-source-bundle\">Export Markdown</button><button type=\"button\" class=\"button row-copy-button\" data-action=\"copy-runbook-source-bundle-json\">Copy provenance JSON</button><button type=\"button\" class=\"button row-copy-button\" data-action=\"download-runbook-source-bundle-json\">Export provenance JSON</button></div></div>",
1121
+ "<div class=\"table-wrap\">",
1122
+ "<table>",
1123
+ "<caption class=\"sr-only\">Source bundle provenance details</caption>",
1124
+ "<tbody>",
1125
+ sourceBundleCopyRow("Source", runbook.source || "source-bundle-provenance", "copy-runbook-source-marker"),
1126
+ sourceBundleRow("Status", (sourceBundle.status || "unknown") + "/" + (sourceBundle.valid ? "valid" : "invalid")),
1127
+ sourceBundleRow("Workspace", sourceBundle.workspaceStatus || "not recorded"),
1128
+ sourceBundleRow("MCP", [sourceBundle.mcpStatus, sourceBundle.mcpProbeStatus].filter(Boolean).join(" / ") || "not recorded"),
1129
+ sourceBundleRow("Checksum", sourceBundle.checksumBundleDigest || "not recorded"),
1130
+ sourceBundleRow("Checksum files", String(sourceBundle.verifiedChecksumFiles || 0) + "/" + String(sourceBundle.expectedChecksumFiles || 0)),
1131
+ sourceBundleRow("Generated files", String(sourceBundle.verifiedGeneratedFiles || 0) + "/" + String(sourceBundle.expectedGeneratedFiles || 0)),
1132
+ sourceBundleRow("Diagnostics", String(sourceBundle.failureCount || 0) + " failures, " + String(sourceBundle.warningCount || 0) + " warnings, " + String(sourceBundle.issueCount || 0) + " issues"),
1133
+ sourceBundleRevalidationRow(sourceBundle),
1134
+ sourceBundleCommandRow("Strict check command", checkCommand, "copy-runbook-source-check-command"),
1135
+ sourceBundleCommandRow("Strict handoff command", handoffCommand, "copy-runbook-source-handoff-command"),
1136
+ "</tbody>",
1137
+ "</table>",
1138
+ "</div>",
1139
+ "</div>",
1140
+ ].join("");
1141
+ }
1142
+
1143
+ function sourceBundleRow(label, value) {
1144
+ return [
1145
+ "<tr>",
1146
+ "<th scope=\"row\">" + escapeHtml(label) + "</th>",
1147
+ "<td>" + escapeHtml(value || "not recorded") + "</td>",
1148
+ "</tr>",
1149
+ ].join("");
1150
+ }
1151
+
1152
+ function sourceBundleCopyRow(label, value, action) {
1153
+ return [
1154
+ "<tr>",
1155
+ "<th scope=\"row\">" + escapeHtml(label) + "</th>",
1156
+ "<td class=\"runbook-line-cell\">",
1157
+ "<code>" + escapeHtml(value || "not recorded") + "</code>",
1158
+ "<div class=\"runbook-line-actions\"><button type=\"button\" class=\"button row-copy-button\" data-action=\"" + escapeAttr(action) + "\">Copy source</button></div>",
1159
+ "</td>",
1160
+ "</tr>",
1161
+ ].join("");
1162
+ }
1163
+
1164
+ function sourceBundleCommandRow(label, command, action) {
1165
+ return [
1166
+ "<tr>",
1167
+ "<th scope=\"row\">" + escapeHtml(label) + "</th>",
1168
+ "<td class=\"runbook-line-cell\">",
1169
+ command ? "<code>" + escapeHtml(command) + "</code><div class=\"runbook-line-actions\"><button type=\"button\" class=\"button row-copy-button\" data-action=\"" + escapeAttr(action) + "\">Copy command</button></div>" : "<span class=\"muted\">not recorded</span>",
1170
+ "</td>",
1171
+ "</tr>",
1172
+ ].join("");
1173
+ }
1174
+
1175
+ function sourceBundleRevalidationRow(sourceBundle) {
1176
+ var required = sourceBundleNeedsRevalidation(sourceBundle);
1177
+ var label = required ? "required" : "not required";
1178
+ var tone = required ? "warn" : "pass";
1179
+ return [
1180
+ "<tr>",
1181
+ "<th scope=\"row\">Revalidation gate</th>",
1182
+ "<td class=\"runbook-line-cell\">",
1183
+ "<span class=\"badge badge--" + escapeAttr(tone) + "\">" + escapeHtml(label) + "</span>",
1184
+ "<span class=\"runbook-revalidation-detail\">" + escapeHtml(formatSourceBundleRevalidationSummary(sourceBundle)) + "</span>",
1185
+ "<div class=\"runbook-line-actions\"><button type=\"button\" class=\"button row-copy-button\" data-action=\"copy-runbook-source-revalidation-gate\">Copy gate JSON</button><button type=\"button\" class=\"button row-copy-button\" data-action=\"download-runbook-source-revalidation-gate\">Export gate JSON</button></div>",
1186
+ "</td>",
1187
+ "</tr>",
1188
+ ].join("");
1189
+ }
1190
+
1191
+ function renderRunbookSourceBundleWarning(runbook) {
1192
+ var sourceBundle = runbook.sourceBundle;
1193
+ if (!sourceBundle) return "";
1194
+ var failureCount = Number(sourceBundle.failureCount || 0);
1195
+ if (!sourceBundleNeedsRevalidation(sourceBundle)) return "";
1196
+ return [
1197
+ "<div class=\"runbook-source-bundle-warning\" role=\"alert\">",
1198
+ "<strong>Source bundle needs revalidation</strong>",
1199
+ "<span>Status is " + escapeHtml((sourceBundle.status || "unknown") + "/" + (sourceBundle.valid ? "valid" : "invalid")) + " with " + escapeHtml(String(failureCount)) + " failures. Run the strict bundle check before target-repo execution.</span>",
1200
+ sourceBundle.strictCheckCommand ? "<code>" + escapeHtml(sourceBundle.strictCheckCommand) + "</code>" : "",
1201
+ "</div>",
1202
+ ].filter(Boolean).join("");
1203
+ }
1204
+
1205
+ function renderRunbookProvenanceOnlyNotice(runbook, rows) {
1206
+ if (rows.length || !runbook.sourceBundle) return "";
1207
+ var message = runbook.source === "source-bundle-revalidation-gate"
1208
+ ? "This gate-only import contains source-bundle identity, diagnostics, and guard commands only. Import a full bundle handoff JSON when you need stage rows for target-repo execution."
1209
+ : "This import contains source-bundle identity, diagnostics, and guard commands only. Import a full bundle handoff JSON when you need stage rows for target-repo execution.";
1210
+ return [
1211
+ "<div class=\"runbook-provenance-only\" role=\"status\">",
1212
+ "<strong>Provenance-only review</strong>",
1213
+ "<span>Source: <code>" + escapeHtml(runbook.source || "source-bundle-provenance") + "</code></span>",
1214
+ "<span>" + escapeHtml(message) + "</span>",
1215
+ "</div>",
1216
+ ].join("");
1217
+ }
1218
+
1219
+ function renderRunbookStatusIndex(runbook, visibleCount, totalCount) {
1220
+ var actionIndex = runbook.stageHumanLineDisplayRowKeysByActionStatus || {};
1221
+ var evidenceIndex = runbook.stageHumanLineDisplayRowKeysByEvidenceProgressStatus || {};
1222
+ var actionOptions = [["all", totalCount], ["ready", (actionIndex.ready || []).length], ["optional", (actionIndex.optional || []).length], ["manual", (actionIndex.manual || []).length], ["blocked", (actionIndex.blocked || []).length]];
1223
+ var evidenceOptions = [["all", totalCount], ["blocked", (evidenceIndex.blocked || []).length], ["ready", (evidenceIndex.ready || []).length]];
1224
+ var filtersActive = appState.runbookActionFilter !== "all" || appState.runbookEvidenceFilter !== "all";
1225
+ return [
1226
+ "<div class=\"runbook-filter\" aria-label=\"Operator runbook row filters\">",
1227
+ "<div class=\"runbook-filter__summary\"><span><strong>" + escapeHtml(String(visibleCount)) + "</strong> of " + escapeHtml(String(totalCount)) + " rows shown</span><button type=\"button\" class=\"button reset-filter-button\" data-action=\"reset-runbook-filters\"" + (filtersActive ? "" : " disabled aria-disabled=\"true\"") + ">Reset filters</button></div>",
1228
+ "<div class=\"runbook-filter__group\" role=\"group\" aria-label=\"Filter by action status\">",
1229
+ "<span class=\"runbook-filter__label\">Action</span>",
1230
+ actionOptions.map(function (option) {
1231
+ return renderRunbookFilterButton("action", option[0], option[1], appState.runbookActionFilter === option[0]);
1232
+ }).join(""),
1233
+ "</div>",
1234
+ "<div class=\"runbook-filter__group\" role=\"group\" aria-label=\"Filter by evidence progress\">",
1235
+ "<span class=\"runbook-filter__label\">Evidence</span>",
1236
+ evidenceOptions.map(function (option) {
1237
+ return renderRunbookFilterButton("evidence", option[0], option[1], appState.runbookEvidenceFilter === option[0]);
1238
+ }).join(""),
1239
+ "</div>",
1240
+ "</div>",
1241
+ ].join("");
1242
+ }
1243
+
1244
+ function renderRunbookFilterButton(type, value, count, selected) {
1245
+ var label = value === "all" ? "All" : labelize(value);
1246
+ return [
1247
+ "<button type=\"button\" class=\"filter-chip\" data-runbook-filter-type=\"" + escapeAttr(type) + "\" data-runbook-filter-value=\"" + escapeAttr(value) + "\" aria-pressed=\"" + (selected ? "true" : "false") + "\">",
1248
+ "<span>" + escapeHtml(label) + "</span>",
1249
+ "<strong>" + escapeHtml(String(count)) + "</strong>",
1250
+ "</button>",
1251
+ ].join("");
1252
+ }
1253
+
1254
+ function filterRunbookRows(runbook) {
1255
+ var rows = runbook.stageHumanLineDisplayRows || [];
1256
+ var actionFilter = appState.runbookActionFilter || "all";
1257
+ var evidenceFilter = appState.runbookEvidenceFilter || "all";
1258
+ var actionKeys = actionFilter === "all" ? null : (runbook.stageHumanLineDisplayRowKeysByActionStatus || {})[actionFilter] || [];
1259
+ var evidenceKeys = evidenceFilter === "all" ? null : (runbook.stageHumanLineDisplayRowKeysByEvidenceProgressStatus || {})[evidenceFilter] || [];
1260
+ return rows.filter(function (row) {
1261
+ return (!actionKeys || actionKeys.indexOf(row.key) !== -1) && (!evidenceKeys || evidenceKeys.indexOf(row.key) !== -1);
1262
+ });
1263
+ }
1264
+
1265
+ function renderRunbookRows(rows, totalRows) {
1266
+ if (!rows.length) {
1267
+ return totalRows
1268
+ ? "<div class=\"empty-state\">No operator runbook rows match the selected filters.</div>"
1269
+ : "<div class=\"empty-state\">The imported runbook did not include display-ready rows.</div>";
1270
+ }
1271
+ return [
1272
+ "<div class=\"table-wrap\">",
1273
+ "<table>",
1274
+ "<caption class=\"sr-only\">Filtered operator runbook stages</caption>",
1275
+ "<thead><tr><th>Stage</th><th>Action</th><th>Evidence</th><th>Copy-ready line</th></tr></thead>",
1276
+ "<tbody>",
1277
+ rows.map(function (row) {
1278
+ return [
1279
+ "<tr>",
1280
+ "<td><strong>" + escapeHtml(row.step + ". " + row.label) + "</strong><br><code>" + escapeHtml(row.key) + "</code></td>",
1281
+ "<td>" + badge(row.actionStatus || "planned") + "<br><small>" + escapeHtml(row.actionLabel || row.actionType) + "</small></td>",
1282
+ "<td>" + badge(row.evidenceProgressStatus || "planned") + "<br><small>" + escapeHtml(row.evidenceProgressLabel || "No progress") + "</small></td>",
1283
+ "<td class=\"runbook-line-cell\"><small>" + escapeHtml(row.line) + "</small><div class=\"runbook-line-actions\"><button type=\"button\" class=\"button row-copy-button\" data-action=\"copy-runbook-row-markdown\" data-runbook-row-key=\"" + escapeAttr(row.key) + "\">Copy row</button><button type=\"button\" class=\"button row-copy-button\" data-action=\"download-runbook-row-markdown\" data-runbook-row-key=\"" + escapeAttr(row.key) + "\">Export row</button><button type=\"button\" class=\"button row-copy-button\" data-action=\"copy-runbook-row-line\" data-runbook-row-key=\"" + escapeAttr(row.key) + "\">Copy line</button></div></td>",
1284
+ "</tr>",
1285
+ ].join("");
1286
+ }).join(""),
1287
+ "</tbody>",
1288
+ "</table>",
1289
+ "</div>",
1290
+ ].join("");
1291
+ }
1292
+
1293
+ function syncReportPreview() {
1294
+ var output = document.querySelector("[data-output='report']");
1295
+ if (output) {
1296
+ output.textContent = buildHandoffReport();
1297
+ }
1298
+ }
1299
+
1300
+ function panel(title, subtitle, body) {
1301
+ return [
1302
+ "<section class=\"panel\">",
1303
+ "<div class=\"panel__header\"><div><h3>" + escapeHtml(title) + "</h3><p>" + escapeHtml(subtitle) + "</p></div></div>",
1304
+ "<div class=\"panel__body\">",
1305
+ body,
1306
+ "</div>",
1307
+ "</section>",
1308
+ ].join("");
1309
+ }
1310
+
1311
+ function setByPath(root, path, value) {
1312
+ var parts = path.split(".");
1313
+ var target = root;
1314
+ for (var i = 0; i < parts.length - 1; i += 1) {
1315
+ target = target[parts[i]];
1316
+ }
1317
+ var key = parts[parts.length - 1];
1318
+ if (
1319
+ path === "siteProfile.pages" ||
1320
+ path === "siteProfile.userFlows" ||
1321
+ path === "implementationEvidence.executedWork" ||
1322
+ path === "implementationEvidence.verificationResults" ||
1323
+ path === "implementationEvidence.remainingRisks" ||
1324
+ path === "implementationEvidence.nextActions"
1325
+ ) {
1326
+ target[key] = textToLines(value);
1327
+ } else {
1328
+ target[key] = value;
1329
+ }
1330
+ }
1331
+
1332
+ function generateTasksFromFindings() {
1333
+ var workspace = appState.workspace;
1334
+ var existingIds = new Set(workspace.refactorTasks.map(function (task) { return task.id; }));
1335
+ var existingCategories = new Set(workspace.refactorTasks.map(function (task) { return task.category; }));
1336
+ var created = [];
1337
+ auditCategories.forEach(function (category) {
1338
+ if (existingCategories.has(category.id)) return;
1339
+ var row = workspace.auditChecklist[category.id];
1340
+ var findings = row.findings;
1341
+ if (findings.length === 0) return;
1342
+ var id = "task-" + category.id;
1343
+ if (existingIds.has(id)) return;
1344
+ var task = taskFromCategory(category, findings[0]);
1345
+ created.push(task);
1346
+ existingIds.add(id);
1347
+ existingCategories.add(category.id);
1348
+ });
1349
+ workspace.refactorTasks = workspace.refactorTasks.concat(created);
1350
+ saveWorkspace();
1351
+ setMessage(created.length + " starter task(s) generated.");
1352
+ }
1353
+
1354
+ function taskFromCategory(category, finding) {
1355
+ var profile = appState.workspace.siteProfile;
1356
+ var priority = category.id === "accessibility" || category.id === "runtime-issues" ? "p0" : "p1";
1357
+ var impact = priority === "p0" ? "high" : "medium";
1358
+ var recommendedMcp = recommendedMcpForCategory(category.id);
1359
+ return {
1360
+ id: "task-" + category.id,
1361
+ title: "Resolve " + category.label + " finding",
1362
+ category: category.id,
1363
+ problem: finding,
1364
+ evidence: "Audit finding captured in the Website Improvement Console.",
1365
+ impact: impact,
1366
+ effort: "medium",
1367
+ priority: priority,
1368
+ pages: profile.pages.slice(0, 3),
1369
+ recommendedMcp: recommendedMcp,
1370
+ codexPrompt: buildCodexTaskPrompt(category.id, finding),
1371
+ verification: category.defaultVerification.concat(["Run target repo lint/typecheck/build when available"]),
1372
+ risks: ["Target repo architecture may constrain the fix", "Manual stakeholder review may be needed before changing copy or brand language"],
1373
+ };
1374
+ }
1375
+
1376
+ function recommendedMcpForCategory(categoryId) {
1377
+ var map = {
1378
+ "visual-design": ["browser", "figma"],
1379
+ "ux-flow": ["browser", "github"],
1380
+ responsive: ["browser", "chromeDevtools"],
1381
+ accessibility: ["browser", "chromeDevtools"],
1382
+ performance: ["chromeDevtools", "deploy"],
1383
+ seo: ["browser", "deploy"],
1384
+ "technical-quality": ["github"],
1385
+ "runtime-issues": ["browser", "chromeDevtools", "sentry"],
1386
+ "content-quality": ["figma", "research", "cms"],
1387
+ };
1388
+ return map[categoryId] || ["github"];
1389
+ }
1390
+
1391
+ function buildCodexTaskPrompt(categoryId, finding) {
1392
+ var profile = appState.workspace.siteProfile;
1393
+ return [
1394
+ "You are working in the target website repo, not in design-ai.",
1395
+ "Site: " + profile.name,
1396
+ "Live URL: " + profile.liveUrl,
1397
+ "Category: " + categoryById(categoryId).label,
1398
+ "Problem: " + finding,
1399
+ "",
1400
+ "Inspect the target repo first. Reuse existing architecture, UI components, state patterns, styling conventions, and design tokens. Do not add dependencies unless the existing codebase clearly requires them.",
1401
+ "",
1402
+ "Implement the smallest safe improvement, then verify desktop/tablet/mobile behavior, keyboard focus, screen-reader semantics where relevant, and the target repo's lint/typecheck/build commands.",
1403
+ ].join("\n");
1404
+ }
1405
+
1406
+ function addManualTask() {
1407
+ var category = "ux-flow";
1408
+ var task = {
1409
+ id: "task-manual-" + Date.now(),
1410
+ title: "Manual website improvement task",
1411
+ category: category,
1412
+ problem: "Describe the website issue before sending this task to Codex.",
1413
+ evidence: "",
1414
+ impact: "medium",
1415
+ effort: "medium",
1416
+ priority: "p2",
1417
+ pages: appState.workspace.siteProfile.pages.slice(0, 1),
1418
+ recommendedMcp: ["github", "browser"],
1419
+ codexPrompt: buildCodexTaskPrompt(category, "Describe the website issue before sending this task to Codex."),
1420
+ verification: ["Run target repo lint/typecheck/build", "Verify desktop/tablet/mobile behavior"],
1421
+ risks: ["Task needs more evidence before implementation"],
1422
+ };
1423
+ appState.workspace.refactorTasks.push(task);
1424
+ saveWorkspace();
1425
+ setMessage("Manual task added.");
1426
+ }
1427
+
1428
+ function removeTask(taskId) {
1429
+ appState.workspace.refactorTasks = appState.workspace.refactorTasks.filter(function (task) {
1430
+ return task.id !== taskId;
1431
+ });
1432
+ saveWorkspace();
1433
+ setMessage("Task removed.");
1434
+ }
1435
+
1436
+ function markdownList(items, fallback) {
1437
+ var normalized = normalizeStringArray(items, []);
1438
+ if (normalized.length === 0) return "- " + fallback;
1439
+ return normalized.map(function (item) { return "- " + item; }).join("\n");
1440
+ }
1441
+
1442
+ function profileBlock() {
1443
+ var profile = appState.workspace.siteProfile;
1444
+ return [
1445
+ "Site profile:",
1446
+ "- Name: " + profile.name,
1447
+ "- Live URL: " + (profile.liveUrl || "not provided"),
1448
+ "- Repo URL: " + (profile.repoUrl || "not provided"),
1449
+ "- Local path: " + (profile.localPath || "not provided"),
1450
+ "- Figma URL: " + (profile.figmaUrl || "not provided"),
1451
+ "- Deploy: " + profile.deployProvider,
1452
+ "- Sentry: " + (profile.sentryProject || "not provided"),
1453
+ "- CMS: " + profile.cms,
1454
+ "- Database: " + profile.database,
1455
+ "- Viewports: " + profile.viewports.join(", "),
1456
+ "",
1457
+ "Priority pages:",
1458
+ markdownList(profile.pages, "No pages listed"),
1459
+ "",
1460
+ "User flows:",
1461
+ markdownList(profile.userFlows, "No user flows listed"),
1462
+ "",
1463
+ "Brand/design notes:",
1464
+ profile.brandNotes || "No brand notes provided.",
1465
+ ].join("\n");
1466
+ }
1467
+
1468
+ function auditBlock() {
1469
+ return auditCategories.map(function (category) {
1470
+ var row = appState.workspace.auditChecklist[category.id];
1471
+ return [
1472
+ "- " + category.label + " [" + row.status + "]",
1473
+ " Notes: " + (row.notes || "none"),
1474
+ " Findings: " + (row.findings.length ? row.findings.join("; ") : "none"),
1475
+ ].join("\n");
1476
+ }).join("\n");
1477
+ }
1478
+
1479
+ function mcpBlock() {
1480
+ return mcpItems.map(function (item) {
1481
+ return "- " + item[1] + ": " + appState.workspace.mcpReadiness[item[0]];
1482
+ }).join("\n");
1483
+ }
1484
+
1485
+ function taskBlock(task) {
1486
+ if (!task) return "No refactor task selected. Use the Refactor Plan section first.";
1487
+ return [
1488
+ "Selected task:",
1489
+ "- Title: " + task.title,
1490
+ "- Category: " + categoryById(task.category).label,
1491
+ "- Problem: " + task.problem,
1492
+ "- Evidence: " + (task.evidence || "not provided"),
1493
+ "- Impact: " + task.impact,
1494
+ "- Effort: " + task.effort,
1495
+ "- Priority: " + task.priority,
1496
+ "- Pages: " + (task.pages.join(", ") || "not specified"),
1497
+ "- Recommended MCP: " + (task.recommendedMcp.join(", ") || "none"),
1498
+ "",
1499
+ "Verification:",
1500
+ markdownList(task.verification, "Run target repo verification"),
1501
+ "",
1502
+ "Risks:",
1503
+ markdownList(task.risks, "No risks listed"),
1504
+ ].join("\n");
1505
+ }
1506
+
1507
+ function primaryTask() {
1508
+ var tasks = appState.workspace.refactorTasks.slice();
1509
+ var rank = { p0: 0, p1: 1, p2: 2, p3: 3 };
1510
+ tasks.sort(function (a, b) {
1511
+ return rank[a.priority] - rank[b.priority];
1512
+ });
1513
+ return tasks[0] || null;
1514
+ }
1515
+
1516
+ function orderedTasks(workspace) {
1517
+ var rank = { p0: 0, p1: 1, p2: 2, p3: 3 };
1518
+ return workspace.refactorTasks.slice().sort(function (a, b) {
1519
+ var priority = rank[a.priority] - rank[b.priority];
1520
+ if (priority !== 0) return priority;
1521
+ return a.title.localeCompare(b.title);
1522
+ });
1523
+ }
1524
+
1525
+ function cloneWorkspace(workspace) {
1526
+ return normalizeWorkspace(JSON.parse(JSON.stringify(workspace)));
1527
+ }
1528
+
1529
+ function workflowTasks(workspace) {
1530
+ var graphWorkspace = cloneWorkspace(workspace);
1531
+ var existingIds = new Set(graphWorkspace.refactorTasks.map(function (task) { return task.id; }));
1532
+ var existingCategories = new Set(graphWorkspace.refactorTasks.map(function (task) { return task.category; }));
1533
+ var created = [];
1534
+ auditCategories.forEach(function (category) {
1535
+ if (existingCategories.has(category.id)) return;
1536
+ var row = graphWorkspace.auditChecklist[category.id];
1537
+ if (!row || row.findings.length === 0) return;
1538
+ var id = "task-" + category.id;
1539
+ if (existingIds.has(id)) return;
1540
+ var task = taskFromCategoryForWorkspace(graphWorkspace, category, row.findings[0]);
1541
+ graphWorkspace.refactorTasks.push(task);
1542
+ created.push(task);
1543
+ existingIds.add(id);
1544
+ existingCategories.add(category.id);
1545
+ });
1546
+ return {
1547
+ workspace: graphWorkspace,
1548
+ tasks: orderedTasks(graphWorkspace),
1549
+ created: created,
1550
+ };
1551
+ }
1552
+
1553
+ function taskFromCategoryForWorkspace(workspace, category, finding) {
1554
+ var priority = category.id === "accessibility" || category.id === "runtime-issues" ? "p0" : "p1";
1555
+ var impact = priority === "p0" ? "high" : "medium";
1556
+ return {
1557
+ id: "task-" + category.id,
1558
+ title: "Resolve " + category.label + " finding",
1559
+ category: category.id,
1560
+ problem: finding,
1561
+ evidence: "Audit finding captured in the Website Improvement Console.",
1562
+ impact: impact,
1563
+ effort: "medium",
1564
+ priority: priority,
1565
+ pages: workspace.siteProfile.pages.slice(0, 3),
1566
+ recommendedMcp: recommendedMcpForCategory(category.id),
1567
+ codexPrompt: buildCodexTaskPromptForWorkspace(workspace, category.id, finding),
1568
+ verification: category.defaultVerification.concat(["Run target repo lint/typecheck/build when available"]),
1569
+ risks: ["Target repo architecture may constrain the fix", "Manual stakeholder review may be needed before changing copy or brand language"],
1570
+ };
1571
+ }
1572
+
1573
+ function buildCodexTaskPromptForWorkspace(workspace, categoryId, finding) {
1574
+ return [
1575
+ "You are working in the target website repo, not in design-ai.",
1576
+ "Site: " + workspace.siteProfile.name,
1577
+ "Live URL: " + workspace.siteProfile.liveUrl,
1578
+ "Category: " + categoryById(categoryId).label,
1579
+ "Problem: " + finding,
1580
+ "",
1581
+ "Inspect the target repo first. Reuse existing architecture, UI components, state patterns, styling conventions, and design tokens. Do not add dependencies unless the existing codebase clearly requires them.",
1582
+ "",
1583
+ "Implement the smallest safe improvement, then verify desktop/tablet/mobile behavior, keyboard focus, screen-reader semantics where relevant, and the target repo's lint/typecheck/build commands.",
1584
+ ].join("\n");
1585
+ }
1586
+
1587
+ function combineStatus(left, right) {
1588
+ if (left === "fail" || right === "fail") return "fail";
1589
+ if (left === "warn" || right === "warn") return "warn";
1590
+ return "pass";
1591
+ }
1592
+
1593
+ function workflowNode(id, type, label, status, data) {
1594
+ return {
1595
+ id: id,
1596
+ type: type,
1597
+ label: label,
1598
+ status: status,
1599
+ data: data || {},
1600
+ };
1601
+ }
1602
+
1603
+ function workflowEdge(from, to, type, label) {
1604
+ return {
1605
+ id: from + "->" + to + ":" + type,
1606
+ from: from,
1607
+ to: to,
1608
+ type: type,
1609
+ label: label,
1610
+ };
1611
+ }
1612
+
1613
+ function profileNodeId(profile) {
1614
+ return "profile:" + (profile.id || "site");
1615
+ }
1616
+
1617
+ function workspaceStatus(workspace) {
1618
+ var hasProfile = Boolean(workspace.siteProfile.name && workspace.siteProfile.liveUrl);
1619
+ var blocked = auditCategories.some(function (category) {
1620
+ return workspace.auditChecklist[category.id].status === "blocked";
1621
+ });
1622
+ if (!hasProfile) return "fail";
1623
+ return blocked ? "warn" : "pass";
1624
+ }
1625
+
1626
+ function mcpState(key, status, profile) {
1627
+ if (status === "unused") return "not-needed";
1628
+ if (status === "unavailable") return "missing";
1629
+ if (key === "github") return profile.repoUrl || profile.localPath ? "ready" : "needs-evidence";
1630
+ if (key === "figma") return profile.figmaUrl ? "ready" : "needs-evidence";
1631
+ if (key === "deploy") return profile.deployProvider !== "none" ? "ready" : "needs-evidence";
1632
+ if (key === "sentry") return profile.sentryProject ? "ready" : "needs-evidence";
1633
+ return "declared";
1634
+ }
1635
+
1636
+ function mcpLevel(requestedStatus, state) {
1637
+ if (requestedStatus === "unavailable") return "fail";
1638
+ if (requestedStatus === "required" && state === "needs-evidence") return "warn";
1639
+ return "pass";
1640
+ }
1641
+
1642
+ function buildWorkflowGraph() {
1643
+ var taskResult = workflowTasks(appState.workspace);
1644
+ var workspace = taskResult.workspace;
1645
+ var profile = workspace.siteProfile;
1646
+ var workspaceLevel = workspaceStatus(workspace);
1647
+ var nodes = [];
1648
+ var edges = [];
1649
+ var profileId = profileNodeId(profile);
1650
+ var mcpNodes = [];
1651
+
1652
+ nodes.push(workflowNode("workspace:intake", "workspace", "Workspace intake", workspaceLevel, {
1653
+ version: workspace.version,
1654
+ updatedAt: workspace.updatedAt,
1655
+ source: "localStorage",
1656
+ workspaceStatus: workspaceLevel,
1657
+ }));
1658
+ nodes.push(workflowNode(profileId, "site-profile", profile.name, workspaceLevel, {
1659
+ id: profile.id,
1660
+ liveUrl: profile.liveUrl,
1661
+ repoUrl: profile.repoUrl,
1662
+ localPath: profile.localPath,
1663
+ figmaUrl: profile.figmaUrl,
1664
+ deployProvider: profile.deployProvider,
1665
+ cms: profile.cms,
1666
+ database: profile.database,
1667
+ pages: profile.pages,
1668
+ userFlows: profile.userFlows,
1669
+ viewports: profile.viewports,
1670
+ brandNotes: profile.brandNotes,
1671
+ }));
1672
+ edges.push(workflowEdge("workspace:intake", profileId, "profile", "Workspace defines the target site profile"));
1673
+
1674
+ auditCategories.forEach(function (category) {
1675
+ var row = workspace.auditChecklist[category.id];
1676
+ var nodeId = "audit:" + category.id;
1677
+ nodes.push(workflowNode(nodeId, "audit-category", category.label, row.status, {
1678
+ category: category.id,
1679
+ notes: row.notes,
1680
+ findings: row.findings,
1681
+ findingCount: row.findings.length,
1682
+ defaultVerification: category.defaultVerification,
1683
+ }));
1684
+ edges.push(workflowEdge(profileId, nodeId, "audit-input", "Site context drives this audit category"));
1685
+ });
1686
+
1687
+ mcpItems.forEach(function (item) {
1688
+ var key = item[0];
1689
+ var status = workspace.mcpReadiness[key];
1690
+ var state = mcpState(key, status, profile);
1691
+ var level = mcpLevel(status, state);
1692
+ var node = workflowNode("mcp:" + key, "mcp-readiness", item[1], level, {
1693
+ key: key,
1694
+ requestedStatus: status,
1695
+ state: state,
1696
+ evidence: item[2],
1697
+ actions: mcpAdvice(key, status),
1698
+ });
1699
+ mcpNodes.push(node);
1700
+ nodes.push(node);
1701
+ edges.push(workflowEdge(profileId, node.id, "readiness-input", "Site profile provides MCP readiness evidence"));
1702
+ });
1703
+
1704
+ taskResult.tasks.forEach(function (task) {
1705
+ var taskNodeId = "task:" + task.id;
1706
+ nodes.push(workflowNode(taskNodeId, "refactor-task", task.title, "planned", {
1707
+ id: task.id,
1708
+ category: task.category,
1709
+ problem: task.problem,
1710
+ evidence: task.evidence,
1711
+ impact: task.impact,
1712
+ effort: task.effort,
1713
+ priority: task.priority,
1714
+ pages: task.pages,
1715
+ recommendedMcp: task.recommendedMcp,
1716
+ codexPrompt: task.codexPrompt,
1717
+ verification: task.verification,
1718
+ risks: task.risks,
1719
+ }));
1720
+ edges.push(workflowEdge("audit:" + task.category, taskNodeId, "finding-to-task", "Audit finding informs this refactor task"));
1721
+ edges.push(workflowEdge(profileId, taskNodeId, "site-context", "Site profile scopes this refactor task"));
1722
+ task.recommendedMcp.forEach(function (key) {
1723
+ if (workspace.mcpReadiness[key]) {
1724
+ edges.push(workflowEdge("mcp:" + key, taskNodeId, "mcp-support", "MCP readiness supports task execution"));
1725
+ }
1726
+ });
1727
+ });
1728
+
1729
+ templates.forEach(function (template) {
1730
+ var promptId = "prompt:" + template[0];
1731
+ nodes.push(workflowNode(promptId, "prompt-template", template[1], "ready", {
1732
+ id: template[0],
1733
+ agent: template[0].indexOf("claude") === 0 ? "Claude" : "Codex",
1734
+ output: template[0] === "handoff-report" ? "report" : "prompt",
1735
+ description: template[1],
1736
+ taskSelectable: template[0] === "codex-implementation",
1737
+ }));
1738
+ edges.push(workflowEdge(profileId, promptId, "profile-context", "Prompt template receives site profile context"));
1739
+ });
1740
+
1741
+ taskResult.tasks.forEach(function (task) {
1742
+ edges.push(workflowEdge("task:" + task.id, "prompt:codex-implementation", "implementation-prompt", "Task can be exported as a Codex implementation prompt"));
1743
+ });
1744
+
1745
+ nodes.push(workflowNode("handoff:report", "handoff-report", "Handoff report", "ready", {
1746
+ output: "website-handoff.md",
1747
+ purpose: "Summarize site state, audit findings, priority improvements, verification, and remaining risk",
1748
+ }));
1749
+ nodes.push(workflowNode("handoff:bundle", "handoff-bundle", "Local handoff bundle", "ready", {
1750
+ output: "website-handoff-bundle",
1751
+ purpose: "Package the local Website Improvement plan without mutating the target repo",
1752
+ }));
1753
+ nodes.push(workflowNode("handoff:target-repo", "target-repo", "Target website repo", "external", {
1754
+ repoUrl: profile.repoUrl,
1755
+ localPath: profile.localPath,
1756
+ boundary: "Implementation happens outside the design-ai repository",
1757
+ }));
1758
+ edges.push(workflowEdge(profileId, "handoff:report", "handoff-input", "Site profile anchors the handoff report"));
1759
+ taskResult.tasks.forEach(function (task) {
1760
+ edges.push(workflowEdge("task:" + task.id, "handoff:report", "handoff-input", "Refactor task is summarized in the handoff report"));
1761
+ });
1762
+ mcpNodes.filter(function (node) {
1763
+ return node.data.requestedStatus !== "unused";
1764
+ }).forEach(function (node) {
1765
+ edges.push(workflowEdge(node.id, "handoff:report", "readiness-input", "MCP readiness is summarized in the handoff report"));
1766
+ });
1767
+ templates.forEach(function (template) {
1768
+ edges.push(workflowEdge("prompt:" + template[0], "handoff:target-repo", "agent-prompt", "Prompt can be used in the target website workflow"));
1769
+ });
1770
+ edges.push(workflowEdge("handoff:report", "handoff:bundle", "bundle-input", "Handoff report can be packaged into a local bundle"));
1771
+ edges.push(workflowEdge("handoff:bundle", "handoff:target-repo", "handoff", "Verified bundle can become target-repo implementation context"));
1772
+
1773
+ var mcpStatus = mcpNodes.some(function (node) { return node.status === "fail"; }) ? "fail"
1774
+ : mcpNodes.some(function (node) { return node.status === "warn"; }) ? "warn"
1775
+ : "pass";
1776
+ var status = combineStatus(workspaceLevel, mcpStatus);
1777
+ return {
1778
+ version: 1,
1779
+ kind: "website-improvement-workflow-graph",
1780
+ generatedAt: workspace.updatedAt,
1781
+ filePath: "localStorage",
1782
+ status: status,
1783
+ workspaceStatus: workspaceLevel,
1784
+ mcpStatus: mcpStatus,
1785
+ externalCalls: false,
1786
+ site: {
1787
+ id: profile.id,
1788
+ name: profile.name,
1789
+ liveUrl: profile.liveUrl,
1790
+ repoUrl: profile.repoUrl,
1791
+ localPath: profile.localPath,
1792
+ },
1793
+ summary: {
1794
+ status: status,
1795
+ workspaceStatus: workspaceLevel,
1796
+ mcpStatus: mcpStatus,
1797
+ nodeCount: nodes.length,
1798
+ edgeCount: edges.length,
1799
+ auditCategoryCount: auditCategories.length,
1800
+ taskCount: taskResult.tasks.length,
1801
+ generatedTaskCount: taskResult.created.length,
1802
+ requiredMcpCount: mcpItems.filter(function (item) { return workspace.mcpReadiness[item[0]] === "required"; }).length,
1803
+ promptTemplateCount: templates.length,
1804
+ },
1805
+ nodes: nodes,
1806
+ edges: edges,
1807
+ boundaries: [
1808
+ "deterministic-local",
1809
+ "no-external-mcp-calls",
1810
+ "no-target-repo-mutation",
1811
+ "no-new-dependencies",
1812
+ ],
1813
+ };
1814
+ }
1815
+
1816
+ function buildPrompt(templateId) {
1817
+ var profile = profileBlock();
1818
+ var audit = auditBlock();
1819
+ var mcp = mcpBlock();
1820
+ var task = taskBlock(primaryTask());
1821
+ var commonRules = [
1822
+ "Rules:",
1823
+ "- Work in the target website repository, not in this design-ai repository.",
1824
+ "- Inspect existing architecture, components, state, styling, and design tokens before editing.",
1825
+ "- Keep changes scoped and avoid new dependencies unless clearly justified.",
1826
+ "- Preserve accessibility: keyboard reachability, visible focus, semantic HTML, screen-reader labels, and WCAG 2.1 AA contrast.",
1827
+ "- Verify desktop, tablet, and mobile layouts.",
1828
+ ].join("\n");
1829
+
1830
+ var map = {
1831
+ "codex-repo-intake": [
1832
+ "# Codex repo intake prompt",
1833
+ profile,
1834
+ "",
1835
+ "Goal: inspect the target website repo and produce a concise implementation plan for website improvement work.",
1836
+ "",
1837
+ commonRules,
1838
+ "",
1839
+ "Read first:",
1840
+ "- package/dependency manifest",
1841
+ "- app/router entrypoints",
1842
+ "- layout and design system primitives",
1843
+ "- styling/token setup",
1844
+ "- test/build scripts",
1845
+ "",
1846
+ "Return: repo structure, routing, reusable components, state/data model, likely touch points, risks, and exact verification commands.",
1847
+ ],
1848
+ "codex-implementation": [
1849
+ "# Codex implementation prompt",
1850
+ profile,
1851
+ "",
1852
+ task,
1853
+ "",
1854
+ commonRules,
1855
+ "",
1856
+ "Implement the smallest safe fix. After editing, run the target repo's most relevant lint/typecheck/build/test command and summarize changed files plus verification.",
1857
+ ],
1858
+ "codex-visual-qa": [
1859
+ "# Codex visual QA prompt",
1860
+ profile,
1861
+ "",
1862
+ "Audit checklist state:",
1863
+ audit,
1864
+ "",
1865
+ "Use Browser/Playwright if available. Verify the priority pages across " + appState.workspace.siteProfile.viewports.join(", ") + ". Check layout, typography, CTA hierarchy, forms, focus indicators, console errors, and broken assets.",
1866
+ ],
1867
+ "codex-deployment": [
1868
+ "# Codex deployment verification prompt",
1869
+ profile,
1870
+ "",
1871
+ "MCP readiness:",
1872
+ mcp,
1873
+ "",
1874
+ "Verify the deployment or preview URL, runtime logs, environment assumptions, SEO metadata, and major user flows. Report pass/fail evidence and remaining launch risks.",
1875
+ ],
1876
+ "claude-design-review": [
1877
+ "# Claude design review prompt",
1878
+ profile,
1879
+ "",
1880
+ "Review the live site or screenshots as a senior product designer. Focus on visual hierarchy, layout rhythm, typography, color, spacing, CTA clarity, responsive behavior, and accessibility concerns. Provide one best path and cite concrete evidence.",
1881
+ ],
1882
+ "claude-competitor": [
1883
+ "# Claude competitor research prompt",
1884
+ profile,
1885
+ "",
1886
+ "Research 3-5 relevant competitors or peer websites. Compare homepage structure, conversion path, proof points, pricing presentation, visual tone, content clarity, and SEO positioning. Return a concise opportunity map, not a generic benchmark.",
1887
+ ],
1888
+ "claude-copy-ux": [
1889
+ "# Claude copy/UX critique prompt",
1890
+ profile,
1891
+ "",
1892
+ "Critique the site's copy, information architecture, trust signals, CTA language, and conversion flow. Rewrite only the highest-impact sections and explain why the edits reduce user uncertainty.",
1893
+ ],
1894
+ "handoff-report": [
1895
+ "# Final handoff report prompt",
1896
+ profile,
1897
+ "",
1898
+ "Refactor plan:",
1899
+ appState.workspace.refactorTasks.map(function (item) {
1900
+ return "- [" + item.priority + "] " + item.title + ": " + item.problem;
1901
+ }).join("\n") || "- No tasks listed",
1902
+ "",
1903
+ "Create a final handoff report with target site info, audit summary, priority recommendations, executed work, verification results, remaining risks, and next actions.",
1904
+ ],
1905
+ };
1906
+
1907
+ return (map[templateId] || map["codex-repo-intake"]).join("\n");
1908
+ }
1909
+
1910
+ function buildHandoffReport() {
1911
+ var profile = appState.workspace.siteProfile;
1912
+ var tasks = appState.workspace.refactorTasks;
1913
+ var evidence = appState.workspace.implementationEvidence;
1914
+ return [
1915
+ "# Website improvement handoff: " + profile.name,
1916
+ "",
1917
+ "> Generated by design-ai Website Improvement Console.",
1918
+ "",
1919
+ "## Target site",
1920
+ "",
1921
+ "- Live URL: " + (profile.liveUrl || "not provided"),
1922
+ "- Repo URL: " + (profile.repoUrl || "not provided"),
1923
+ "- Local path: " + (profile.localPath || "not provided"),
1924
+ "- Figma URL: " + (profile.figmaUrl || "not provided"),
1925
+ "- Deploy provider: " + profile.deployProvider,
1926
+ "- CMS: " + profile.cms,
1927
+ "- Database: " + profile.database,
1928
+ "- Viewports: " + profile.viewports.join(", "),
1929
+ "",
1930
+ "## Diagnostic summary",
1931
+ "",
1932
+ auditBlock(),
1933
+ "",
1934
+ "## MCP Readiness",
1935
+ "",
1936
+ mcpBlock(),
1937
+ "",
1938
+ "## Priority improvement plan",
1939
+ "",
1940
+ tasks.length ? tasks.map(function (task) {
1941
+ return [
1942
+ "### [" + task.priority.toUpperCase() + "] " + task.title,
1943
+ "",
1944
+ "- Category: " + categoryById(task.category).label,
1945
+ "- Impact: " + task.impact,
1946
+ "- Effort: " + task.effort,
1947
+ "- Pages: " + (task.pages.join(", ") || "not specified"),
1948
+ "- MCP: " + (task.recommendedMcp.join(", ") || "none"),
1949
+ "- Problem: " + task.problem,
1950
+ "- Evidence: " + (task.evidence || "not provided"),
1951
+ "",
1952
+ "Verification:",
1953
+ markdownList(task.verification, "Run target repo verification"),
1954
+ "",
1955
+ "Risks:",
1956
+ markdownList(task.risks, "No risks listed"),
1957
+ ].join("\n");
1958
+ }).join("\n\n") : "No refactor tasks generated yet.",
1959
+ "",
1960
+ "## Executed work",
1961
+ "",
1962
+ markdownList(evidence.executedWork, "Not recorded yet. Add implementation notes after running Codex in the target repo."),
1963
+ "",
1964
+ "## Verification results",
1965
+ "",
1966
+ markdownList(evidence.verificationResults, "Not recorded yet. Paste target repo lint/typecheck/build, Browser QA, and deployment checks here."),
1967
+ "",
1968
+ "## Remaining risks",
1969
+ "",
1970
+ markdownList(evidence.remainingRisks, "No remaining risks recorded."),
1971
+ "",
1972
+ "## Next actions",
1973
+ "",
1974
+ markdownList(evidence.nextActions, "No next actions recorded."),
1975
+ "",
1976
+ "## Notes",
1977
+ "",
1978
+ appState.workspace.reportNotes || "No notes recorded.",
1979
+ ].join("\n");
1980
+ }
1981
+
1982
+ function buildOperatorRunbookMarkdown(options) {
1983
+ var runbook = appState.workspace.operatorRunbook;
1984
+ if (!runbook) return "No operator runbook imported.";
1985
+ var settings = options && typeof options === "object" ? options : {};
1986
+ var allRows = runbook.stageHumanLineDisplayRows || [];
1987
+ var rows = settings.filtered ? filterRunbookRows(runbook) : allRows;
1988
+ var actionFilter = appState.runbookActionFilter || "all";
1989
+ var evidenceFilter = appState.runbookEvidenceFilter || "all";
1990
+ var provenanceOnly = allRows.length === 0 && !!runbook.sourceBundle;
1991
+ return [
1992
+ settings.filtered ? "# Website improvement operator runbook - filtered rows" : "# Website improvement operator runbook",
1993
+ "",
1994
+ "- Source: " + runbook.source,
1995
+ "- Provenance-only: " + (provenanceOnly ? "yes" : "no"),
1996
+ "- Stages: " + (runbook.stageCount || allRows.length),
1997
+ "- Rows included: " + rows.length + " of " + allRows.length,
1998
+ "- Effective task: " + (runbook.effectiveTaskId || "not specified"),
1999
+ "- Strict task command key: " + (runbook.effectiveStrictTaskCommandKey || "not specified"),
2000
+ "- Source bundle status: " + formatSourceBundleMarkdownStatus(runbook.sourceBundle),
2001
+ "- Strict bundle check command: " + formatSourceBundleMarkdownCommand(runbook.sourceBundle, "strictCheckCommand"),
2002
+ "- Strict bundle handoff command: " + formatSourceBundleMarkdownCommand(runbook.sourceBundle, "strictHandoffCommand"),
2003
+ "- Source bundle revalidation: " + formatSourceBundleRevalidationMarkdown(runbook.sourceBundle),
2004
+ "- Action filter: " + (settings.filtered ? actionFilter : "all"),
2005
+ "- Evidence filter: " + (settings.filtered ? evidenceFilter : "all"),
2006
+ "- Next stage: " + (runbook.nextStageKey || "none"),
2007
+ "- Next command: " + (runbook.nextCommandKey || "none"),
2008
+ "",
2009
+ "## Stages",
2010
+ "",
2011
+ rows.length ? rows.map(buildOperatorRunbookRowMarkdown).join("\n\n") : formatEmptyRunbookRowsMessage(settings, provenanceOnly, runbook.source),
2012
+ ].join("\n");
2013
+ }
2014
+
2015
+ function formatEmptyRunbookRowsMessage(settings, provenanceOnly, source) {
2016
+ if (settings.filtered) return "No operator runbook rows match the selected filters.";
2017
+ if (provenanceOnly) return "This provenance-only artifact (source: " + (source || "source-bundle-provenance") + ") contains source-bundle identity, diagnostics, and guard commands only. Import a full bundle handoff JSON when target-repo execution stage rows are required.";
2018
+ return "No display-ready rows included.";
2019
+ }
2020
+
2021
+ function buildSourceBundleMarkdown(sourceBundle, source) {
2022
+ if (!sourceBundle) return "No source bundle provenance recorded.";
2023
+ return [
2024
+ "# Website improvement source bundle provenance",
2025
+ "",
2026
+ sourceBundleMarkdownRow("Source", source || "source-bundle-provenance"),
2027
+ sourceBundleMarkdownRow("Directory", sourceBundle.directory),
2028
+ sourceBundleMarkdownRow("Source workspace", sourceBundle.sourceWorkspace),
2029
+ sourceBundleMarkdownRow("Site name", sourceBundle.siteName),
2030
+ sourceBundleMarkdownRow("Status", (sourceBundle.status || "unknown") + "/" + (sourceBundle.valid ? "valid" : "invalid")),
2031
+ sourceBundleMarkdownRow("Workspace status", sourceBundle.workspaceStatus),
2032
+ sourceBundleMarkdownRow("MCP status", sourceBundle.mcpStatus),
2033
+ sourceBundleMarkdownRow("MCP probe status", sourceBundle.mcpProbeStatus),
2034
+ sourceBundleMarkdownRow("Checksum algorithm", sourceBundle.checksumAlgorithm),
2035
+ sourceBundleMarkdownRow("Checksum bundle digest", sourceBundle.checksumBundleDigest),
2036
+ sourceBundleMarkdownRow("Checksum files", String(sourceBundle.verifiedChecksumFiles || 0) + "/" + String(sourceBundle.expectedChecksumFiles || 0)),
2037
+ sourceBundleMarkdownRow("Generated files", String(sourceBundle.verifiedGeneratedFiles || 0) + "/" + String(sourceBundle.expectedGeneratedFiles || 0)),
2038
+ sourceBundleMarkdownRow("Diagnostics", String(sourceBundle.failureCount || 0) + " failures, " + String(sourceBundle.warningCount || 0) + " warnings, " + String(sourceBundle.issueCount || 0) + " issues"),
2039
+ sourceBundleMarkdownRow("Strict bundle check command", sourceBundle.strictCheckCommand),
2040
+ sourceBundleMarkdownRow("Strict bundle handoff command", sourceBundle.strictHandoffCommand),
2041
+ sourceBundleMarkdownRow("Revalidation gate", formatSourceBundleRevalidationMarkdown(sourceBundle)),
2042
+ ].join("\n");
2043
+ }
2044
+
2045
+ function buildSourceBundleJson(sourceBundle) {
2046
+ return JSON.stringify({
2047
+ type: "website-improvement-source-bundle-provenance",
2048
+ version: 1,
2049
+ source: "source-bundle-provenance",
2050
+ sourceBundle: sourceBundle || null,
2051
+ revalidationGate: buildSourceBundleRevalidationGate(sourceBundle),
2052
+ }, null, 2);
2053
+ }
2054
+
2055
+ function buildSourceBundleRevalidationGateJson(sourceBundle) {
2056
+ return JSON.stringify({
2057
+ type: "website-improvement-source-bundle-revalidation-gate",
2058
+ version: 1,
2059
+ source: "source-bundle-revalidation-gate",
2060
+ sourceBundle: sourceBundle ? {
2061
+ directory: sourceBundle.directory || "",
2062
+ sourceWorkspace: sourceBundle.sourceWorkspace || "",
2063
+ siteName: sourceBundle.siteName || "",
2064
+ checksumBundleDigest: sourceBundle.checksumBundleDigest || "",
2065
+ status: sourceBundle.status || "unknown",
2066
+ valid: sourceBundle.valid === true,
2067
+ } : null,
2068
+ revalidationGate: buildSourceBundleRevalidationGate(sourceBundle),
2069
+ }, null, 2);
2070
+ }
2071
+
2072
+ function sourceBundleMarkdownRow(label, value) {
2073
+ return "- " + label + ": " + (value || "not recorded");
2074
+ }
2075
+
2076
+ function buildOperatorRunbookRowMarkdown(row) {
2077
+ return [
2078
+ "### " + row.step + ". " + row.label,
2079
+ "",
2080
+ "- Key: `" + row.key + "`",
2081
+ "- Action: " + (row.actionStatusLabel || row.actionStatus || "unknown") + " / " + (row.actionLabel || row.actionType || "unknown"),
2082
+ "- Evidence: " + (row.evidenceProgressStatusLabel || row.evidenceProgressStatus || "unknown") + " / " + (row.evidenceProgressLabel || "No progress"),
2083
+ row.firstUncheckedEvidenceItemLabel ? "- Next evidence item: " + row.firstUncheckedEvidenceItemLabel : "",
2084
+ "",
2085
+ row.line,
2086
+ ].filter(Boolean).join("\n");
2087
+ }
2088
+
2089
+ function formatSourceBundleMarkdownStatus(sourceBundle) {
2090
+ if (!sourceBundle) return "not provided";
2091
+ var status = (sourceBundle.status || "unknown") + "/" + (sourceBundle.valid ? "valid" : "invalid");
2092
+ var digest = sourceBundle.checksumBundleDigest ? "; digest " + sourceBundle.checksumBundleDigest : "";
2093
+ var directory = sourceBundle.directory ? "; directory " + sourceBundle.directory : "";
2094
+ return status + digest + directory;
2095
+ }
2096
+
2097
+ function formatSourceBundleMarkdownCommand(sourceBundle, key) {
2098
+ if (!sourceBundle || !sourceBundle[key]) return "not provided";
2099
+ return sourceBundle[key];
2100
+ }
2101
+
2102
+ function sourceBundleNeedsRevalidation(sourceBundle) {
2103
+ if (!sourceBundle) return false;
2104
+ return sourceBundle.valid !== true || Number(sourceBundle.failureCount || 0) > 0;
2105
+ }
2106
+
2107
+ function formatSourceBundleRevalidationMarkdown(sourceBundle) {
2108
+ if (!sourceBundle) return "not provided";
2109
+ if (!sourceBundleNeedsRevalidation(sourceBundle)) return "not required";
2110
+ var failureCount = Number(sourceBundle.failureCount || 0);
2111
+ var status = (sourceBundle.status || "unknown") + "/" + (sourceBundle.valid ? "valid" : "invalid");
2112
+ var command = sourceBundle.strictCheckCommand ? "; run " + sourceBundle.strictCheckCommand : "";
2113
+ return "required; status " + status + "; failures " + String(failureCount) + command;
2114
+ }
2115
+
2116
+ function formatSourceBundleRevalidationSummary(sourceBundle) {
2117
+ if (!sourceBundle) return "not provided";
2118
+ if (!sourceBundleNeedsRevalidation(sourceBundle)) return "not required";
2119
+ return sourceBundle.strictCheckCommand
2120
+ ? "required - run strict check before target-repo execution"
2121
+ : "required - strict check command not recorded";
2122
+ }
2123
+
2124
+ function buildSourceBundleRevalidationGate(sourceBundle) {
2125
+ if (!sourceBundle) {
2126
+ return {
2127
+ required: false,
2128
+ status: "not-provided",
2129
+ valid: false,
2130
+ failureCount: 0,
2131
+ warningCount: 0,
2132
+ issueCount: 0,
2133
+ strictCheckCommand: "",
2134
+ strictCheckCommandAvailable: false,
2135
+ reason: "source-bundle-not-provided",
2136
+ message: "No source bundle provenance recorded.",
2137
+ };
2138
+ }
2139
+ var failureCount = Number(sourceBundle.failureCount || 0);
2140
+ var warningCount = Number(sourceBundle.warningCount || 0);
2141
+ var issueCount = Number(sourceBundle.issueCount || 0);
2142
+ var required = sourceBundleNeedsRevalidation(sourceBundle);
2143
+ var status = (sourceBundle.status || "unknown") + "/" + (sourceBundle.valid ? "valid" : "invalid");
2144
+ var strictCheckCommandAvailable = Boolean(sourceBundle.strictCheckCommand);
2145
+ return {
2146
+ required: required,
2147
+ status: status,
2148
+ valid: sourceBundle.valid === true,
2149
+ failureCount: failureCount,
2150
+ warningCount: warningCount,
2151
+ issueCount: issueCount,
2152
+ strictCheckCommand: String(sourceBundle.strictCheckCommand || ""),
2153
+ strictCheckCommandAvailable: strictCheckCommandAvailable,
2154
+ reason: required
2155
+ ? strictCheckCommandAvailable ? "revalidation-required" : "revalidation-required-command-missing"
2156
+ : "revalidation-not-required",
2157
+ message: required
2158
+ ? "Run the strict bundle check before target-repo execution."
2159
+ : "Source bundle revalidation is not required.",
2160
+ };
2161
+ }
2162
+
2163
+ function shortDisplay(value, maxLength) {
2164
+ var text = String(value || "");
2165
+ var limit = Number(maxLength || 12);
2166
+ if (text.length <= limit) return text;
2167
+ return text.slice(0, limit) + "...";
2168
+ }
2169
+
2170
+ function copyText(text, successMessage) {
2171
+ if (navigator.clipboard && window.isSecureContext) {
2172
+ navigator.clipboard.writeText(text).then(function () {
2173
+ setMessage(successMessage);
2174
+ }).catch(function () {
2175
+ fallbackCopy(text, successMessage);
2176
+ });
2177
+ return;
2178
+ }
2179
+ fallbackCopy(text, successMessage);
2180
+ }
2181
+
2182
+ function fallbackCopy(text, successMessage) {
2183
+ var temp = document.createElement("textarea");
2184
+ temp.value = text;
2185
+ temp.setAttribute("readonly", "readonly");
2186
+ temp.style.position = "fixed";
2187
+ temp.style.left = "-9999px";
2188
+ document.body.appendChild(temp);
2189
+ temp.select();
2190
+ try {
2191
+ document.execCommand("copy");
2192
+ setMessage(successMessage);
2193
+ } catch (error) {
2194
+ setMessage("Copy failed. Select the text manually.");
2195
+ }
2196
+ document.body.removeChild(temp);
2197
+ }
2198
+
2199
+ function downloadFile(name, content, type) {
2200
+ var blob = new Blob([content], { type: type || "text/plain" });
2201
+ var url = URL.createObjectURL(blob);
2202
+ var link = document.createElement("a");
2203
+ link.href = url;
2204
+ link.download = name;
2205
+ document.body.appendChild(link);
2206
+ link.click();
2207
+ document.body.removeChild(link);
2208
+ URL.revokeObjectURL(url);
2209
+ }
2210
+
2211
+ function safeFileSegment(value) {
2212
+ return String(value || "row")
2213
+ .trim()
2214
+ .toLowerCase()
2215
+ .replace(/[^a-z0-9._-]+/g, "-")
2216
+ .replace(/^-+|-+$/g, "")
2217
+ .slice(0, 80) || "row";
2218
+ }
2219
+
2220
+ function handleInput(event) {
2221
+ var target = event.target;
2222
+ if (!target) return;
2223
+
2224
+ if (target.matches("[data-field]")) {
2225
+ setByPath(appState.workspace, target.dataset.field, target.value);
2226
+ saveWorkspace();
2227
+ syncReportPreview();
2228
+ return;
2229
+ }
2230
+
2231
+ if (target.matches("[data-viewport]")) {
2232
+ var viewport = target.dataset.viewport;
2233
+ var viewports = appState.workspace.siteProfile.viewports;
2234
+ if (target.checked && viewports.indexOf(viewport) === -1) {
2235
+ viewports.push(viewport);
2236
+ } else if (!target.checked) {
2237
+ appState.workspace.siteProfile.viewports = viewports.filter(function (item) {
2238
+ return item !== viewport;
2239
+ });
2240
+ }
2241
+ if (appState.workspace.siteProfile.viewports.length === 0) {
2242
+ appState.workspace.siteProfile.viewports = ["desktop"];
2243
+ }
2244
+ saveWorkspace();
2245
+ render();
2246
+ return;
2247
+ }
2248
+
2249
+ if (target.matches("[data-audit-status]")) {
2250
+ appState.workspace.auditChecklist[target.dataset.auditStatus].status = target.value;
2251
+ saveWorkspace();
2252
+ render();
2253
+ return;
2254
+ }
2255
+
2256
+ if (target.matches("[data-audit-notes]")) {
2257
+ appState.workspace.auditChecklist[target.dataset.auditNotes].notes = target.value;
2258
+ saveWorkspace();
2259
+ return;
2260
+ }
2261
+
2262
+ if (target.matches("[data-audit-findings]")) {
2263
+ appState.workspace.auditChecklist[target.dataset.auditFindings].findings = textToLines(target.value);
2264
+ saveWorkspace();
2265
+ return;
2266
+ }
2267
+
2268
+ if (target.matches("[data-mcp]")) {
2269
+ appState.workspace.mcpReadiness[target.dataset.mcp] = target.value;
2270
+ saveWorkspace();
2271
+ render();
2272
+ }
2273
+ }
2274
+
2275
+ function handleClick(event) {
2276
+ var button = event.target.closest("button");
2277
+ if (!button) return;
2278
+
2279
+ if (button.dataset.nav) {
2280
+ appState.activeTab = button.dataset.nav;
2281
+ localStorage.setItem(ACTIVE_TAB_KEY, appState.activeTab);
2282
+ render();
2283
+ return;
2284
+ }
2285
+
2286
+ if (button.dataset.template) {
2287
+ appState.selectedTemplate = button.dataset.template;
2288
+ localStorage.setItem(SELECTED_TEMPLATE_KEY, appState.selectedTemplate);
2289
+ render();
2290
+ return;
2291
+ }
2292
+
2293
+ if (button.dataset.runbookFilterType) {
2294
+ if (button.dataset.runbookFilterType === "action") {
2295
+ appState.runbookActionFilter = button.dataset.runbookFilterValue || "all";
2296
+ } else if (button.dataset.runbookFilterType === "evidence") {
2297
+ appState.runbookEvidenceFilter = button.dataset.runbookFilterValue || "all";
2298
+ }
2299
+ render();
2300
+ return;
2301
+ }
2302
+
2303
+ var action = button.dataset.action;
2304
+ if (!action) return;
2305
+ if (action === "export-workspace") {
2306
+ downloadFile("design-ai-website-workspace.json", JSON.stringify(appState.workspace, null, 2), "application/json");
2307
+ setMessage("Workspace JSON exported.");
2308
+ } else if (action === "import-click") {
2309
+ document.getElementById("import-file").click();
2310
+ } else if (action === "reset-sample") {
2311
+ appState.workspace = createDefaultWorkspace();
2312
+ saveWorkspace();
2313
+ setMessage("Sample workspace restored.");
2314
+ } else if (action === "generate-tasks") {
2315
+ generateTasksFromFindings();
2316
+ } else if (action === "mark-audit-todo") {
2317
+ auditCategories.forEach(function (category) {
2318
+ appState.workspace.auditChecklist[category.id].status = "todo";
2319
+ });
2320
+ saveWorkspace();
2321
+ setMessage("Audit statuses reset to todo.");
2322
+ } else if (action === "add-task") {
2323
+ addManualTask();
2324
+ } else if (action === "remove-task") {
2325
+ removeTask(button.dataset.task);
2326
+ } else if (action === "copy-task") {
2327
+ var task = appState.workspace.refactorTasks.find(function (item) { return item.id === button.dataset.task; });
2328
+ copyText(task ? task.codexPrompt : "", "Task prompt copied.");
2329
+ } else if (action === "copy-prompt") {
2330
+ copyText(buildPrompt(appState.selectedTemplate), "Prompt copied.");
2331
+ } else if (action === "download-prompt") {
2332
+ downloadFile(appState.selectedTemplate + ".md", buildPrompt(appState.selectedTemplate), "text/markdown");
2333
+ setMessage("Prompt exported.");
2334
+ } else if (action === "copy-report") {
2335
+ copyText(buildHandoffReport(), "Handoff report copied.");
2336
+ } else if (action === "download-report") {
2337
+ downloadFile("website-improvement-handoff.md", buildHandoffReport(), "text/markdown");
2338
+ setMessage("Handoff report exported.");
2339
+ } else if (action === "copy-runbook") {
2340
+ copyText(buildOperatorRunbookMarkdown(), "Operator runbook copied.");
2341
+ } else if (action === "download-runbook") {
2342
+ downloadFile("website-operator-runbook.md", buildOperatorRunbookMarkdown(), "text/markdown");
2343
+ setMessage("Operator runbook exported.");
2344
+ } else if (action === "copy-filtered-runbook") {
2345
+ var filteredCopyRunbook = appState.workspace.operatorRunbook;
2346
+ if (filteredCopyRunbook && (filteredCopyRunbook.stageHumanLineDisplayRows || []).length) {
2347
+ copyText(buildOperatorRunbookMarkdown({ filtered: true }), "Filtered operator runbook rows copied.");
2348
+ } else {
2349
+ setMessage("Filtered runbook rows unavailable.");
2350
+ }
2351
+ } else if (action === "download-filtered-runbook") {
2352
+ var filteredExportRunbook = appState.workspace.operatorRunbook;
2353
+ if (filteredExportRunbook && (filteredExportRunbook.stageHumanLineDisplayRows || []).length) {
2354
+ downloadFile("website-operator-runbook.filtered.md", buildOperatorRunbookMarkdown({ filtered: true }), "text/markdown");
2355
+ setMessage("Filtered operator runbook exported.");
2356
+ } else {
2357
+ setMessage("Filtered runbook rows unavailable.");
2358
+ }
2359
+ } else if (action === "copy-runbook-row-markdown") {
2360
+ var markdownRunbook = appState.workspace.operatorRunbook;
2361
+ var markdownRow = markdownRunbook && markdownRunbook.stageHumanLineDisplayRowByKey
2362
+ ? markdownRunbook.stageHumanLineDisplayRowByKey[button.dataset.runbookRowKey]
2363
+ : null;
2364
+ if (markdownRow && markdownRow.line) {
2365
+ copyText(buildOperatorRunbookRowMarkdown(markdownRow), "Runbook row Markdown copied.");
2366
+ } else {
2367
+ setMessage("Runbook row Markdown unavailable.");
2368
+ }
2369
+ } else if (action === "download-runbook-row-markdown") {
2370
+ var exportRunbook = appState.workspace.operatorRunbook;
2371
+ var exportRow = exportRunbook && exportRunbook.stageHumanLineDisplayRowByKey
2372
+ ? exportRunbook.stageHumanLineDisplayRowByKey[button.dataset.runbookRowKey]
2373
+ : null;
2374
+ if (exportRow && exportRow.line) {
2375
+ downloadFile("website-operator-runbook." + safeFileSegment(exportRow.key) + ".md", buildOperatorRunbookRowMarkdown(exportRow), "text/markdown");
2376
+ setMessage("Runbook row Markdown exported.");
2377
+ } else {
2378
+ setMessage("Runbook row Markdown unavailable.");
2379
+ }
2380
+ } else if (action === "copy-runbook-row-line") {
2381
+ var rowRunbook = appState.workspace.operatorRunbook;
2382
+ var row = rowRunbook && rowRunbook.stageHumanLineDisplayRowByKey
2383
+ ? rowRunbook.stageHumanLineDisplayRowByKey[button.dataset.runbookRowKey]
2384
+ : null;
2385
+ if (row && row.line) {
2386
+ copyText(row.line, "Runbook row line copied.");
2387
+ } else {
2388
+ setMessage("Runbook row line unavailable.");
2389
+ }
2390
+ } else if (action === "copy-next-runbook-line") {
2391
+ var runbook = appState.workspace.operatorRunbook;
2392
+ if (runbook && runbook.nextStageHumanLine) {
2393
+ copyText(runbook.nextStageHumanLine, "Next runbook line copied.");
2394
+ } else {
2395
+ setMessage("Next runbook line unavailable.");
2396
+ }
2397
+ } else if (action === "copy-runbook-source-check-command") {
2398
+ var checkRunbook = appState.workspace.operatorRunbook;
2399
+ var checkSourceBundle = checkRunbook && checkRunbook.sourceBundle;
2400
+ if (checkSourceBundle && checkSourceBundle.strictCheckCommand) {
2401
+ copyText(checkSourceBundle.strictCheckCommand, "Strict bundle check command copied.");
2402
+ } else {
2403
+ setMessage("Strict bundle check command unavailable.");
2404
+ }
2405
+ } else if (action === "copy-runbook-source-handoff-command") {
2406
+ var handoffRunbook = appState.workspace.operatorRunbook;
2407
+ var handoffSourceBundle = handoffRunbook && handoffRunbook.sourceBundle;
2408
+ if (handoffSourceBundle && handoffSourceBundle.strictHandoffCommand) {
2409
+ copyText(handoffSourceBundle.strictHandoffCommand, "Strict bundle handoff command copied.");
2410
+ } else {
2411
+ setMessage("Strict bundle handoff command unavailable.");
2412
+ }
2413
+ } else if (action === "copy-runbook-source-marker") {
2414
+ var sourceMarkerRunbook = appState.workspace.operatorRunbook;
2415
+ copyText((sourceMarkerRunbook && sourceMarkerRunbook.source) || "source-bundle-provenance", "Runbook source marker copied.");
2416
+ } else if (action === "copy-runbook-source-bundle") {
2417
+ var sourceBundleRunbook = appState.workspace.operatorRunbook;
2418
+ copyText(buildSourceBundleMarkdown(sourceBundleRunbook && sourceBundleRunbook.sourceBundle, sourceBundleRunbook && sourceBundleRunbook.source), "Source bundle Markdown copied.");
2419
+ } else if (action === "download-runbook-source-bundle") {
2420
+ var exportSourceBundleRunbook = appState.workspace.operatorRunbook;
2421
+ downloadFile("website-source-bundle-provenance.md", buildSourceBundleMarkdown(exportSourceBundleRunbook && exportSourceBundleRunbook.sourceBundle, exportSourceBundleRunbook && exportSourceBundleRunbook.source), "text/markdown");
2422
+ setMessage("Source bundle Markdown exported.");
2423
+ } else if (action === "copy-runbook-source-bundle-json") {
2424
+ var sourceBundleJsonRunbook = appState.workspace.operatorRunbook;
2425
+ copyText(buildSourceBundleJson(sourceBundleJsonRunbook && sourceBundleJsonRunbook.sourceBundle), "Source bundle JSON copied.");
2426
+ } else if (action === "download-runbook-source-bundle-json") {
2427
+ var exportSourceBundleJsonRunbook = appState.workspace.operatorRunbook;
2428
+ downloadFile("website-source-bundle-provenance.json", buildSourceBundleJson(exportSourceBundleJsonRunbook && exportSourceBundleJsonRunbook.sourceBundle), "application/json");
2429
+ setMessage("Source bundle JSON exported.");
2430
+ } else if (action === "copy-runbook-source-revalidation-gate") {
2431
+ var sourceBundleGateRunbook = appState.workspace.operatorRunbook;
2432
+ var sourceBundleGate = sourceBundleGateRunbook && sourceBundleGateRunbook.sourceBundle;
2433
+ if (sourceBundleGate) {
2434
+ copyText(buildSourceBundleRevalidationGateJson(sourceBundleGate), "Source bundle gate JSON copied.");
2435
+ } else {
2436
+ setMessage("Source bundle gate unavailable.");
2437
+ }
2438
+ } else if (action === "download-runbook-source-revalidation-gate") {
2439
+ var exportSourceBundleGateRunbook = appState.workspace.operatorRunbook;
2440
+ var exportSourceBundleGate = exportSourceBundleGateRunbook && exportSourceBundleGateRunbook.sourceBundle;
2441
+ if (exportSourceBundleGate) {
2442
+ downloadFile("website-source-bundle-revalidation-gate.json", buildSourceBundleRevalidationGateJson(exportSourceBundleGate), "application/json");
2443
+ setMessage("Source bundle gate JSON exported.");
2444
+ } else {
2445
+ setMessage("Source bundle gate unavailable.");
2446
+ }
2447
+ } else if (action === "clear-runbook") {
2448
+ appState.workspace.operatorRunbook = null;
2449
+ appState.runbookActionFilter = "all";
2450
+ appState.runbookEvidenceFilter = "all";
2451
+ saveWorkspace();
2452
+ setMessage("Operator runbook cleared.");
2453
+ } else if (action === "reset-runbook-filters") {
2454
+ appState.runbookActionFilter = "all";
2455
+ appState.runbookEvidenceFilter = "all";
2456
+ render();
2457
+ setMessage("Runbook filters reset.");
2458
+ } else if (action === "copy-graph-json") {
2459
+ copyText(JSON.stringify(buildWorkflowGraph(), null, 2), "Workflow graph JSON copied.");
2460
+ } else if (action === "download-graph-json") {
2461
+ downloadFile("website-workflow-graph.json", JSON.stringify(buildWorkflowGraph(), null, 2), "application/json");
2462
+ setMessage("Workflow graph JSON exported.");
2463
+ }
2464
+ }
2465
+
2466
+ function handleImport(event) {
2467
+ var file = event.target.files && event.target.files[0];
2468
+ if (!file) return;
2469
+ var reader = new FileReader();
2470
+ reader.onload = function () {
2471
+ try {
2472
+ var parsed = JSON.parse(String(reader.result || ""));
2473
+ var importedRunbook = normalizeOperatorRunbook(extractOperatorRunbookPayload(parsed), parsed);
2474
+ if (importedRunbook && !parsed.siteProfile) {
2475
+ appState.workspace.operatorRunbook = importedRunbook;
2476
+ appState.runbookActionFilter = "all";
2477
+ appState.runbookEvidenceFilter = "all";
2478
+ appState.activeTab = "report";
2479
+ localStorage.setItem(ACTIVE_TAB_KEY, appState.activeTab);
2480
+ saveWorkspace();
2481
+ setMessage("Bundle handoff operator runbook imported. Report tab opened.");
2482
+ return;
2483
+ }
2484
+ var importedGateSourceBundle = normalizeRunbookSourceBundle(extractSourceBundleRevalidationGatePayload(parsed));
2485
+ if (importedGateSourceBundle && !parsed.siteProfile) {
2486
+ if (appState.workspace.operatorRunbook) {
2487
+ appState.workspace.operatorRunbook.sourceBundle = importedGateSourceBundle;
2488
+ if (!(appState.workspace.operatorRunbook.stageHumanLineDisplayRows || []).length) {
2489
+ appState.workspace.operatorRunbook.source = "source-bundle-revalidation-gate";
2490
+ }
2491
+ } else {
2492
+ appState.workspace.operatorRunbook = createSourceBundleOnlyRunbook(importedGateSourceBundle, "source-bundle-revalidation-gate");
2493
+ }
2494
+ appState.activeTab = "report";
2495
+ localStorage.setItem(ACTIVE_TAB_KEY, appState.activeTab);
2496
+ saveWorkspace();
2497
+ setMessage("Source bundle revalidation gate JSON imported. Report tab opened.");
2498
+ return;
2499
+ }
2500
+ var importedSourceBundle = normalizeRunbookSourceBundle(extractSourceBundleProvenancePayload(parsed));
2501
+ if (importedSourceBundle && !parsed.siteProfile) {
2502
+ if (appState.workspace.operatorRunbook) {
2503
+ appState.workspace.operatorRunbook.sourceBundle = importedSourceBundle;
2504
+ if (!(appState.workspace.operatorRunbook.stageHumanLineDisplayRows || []).length) {
2505
+ appState.workspace.operatorRunbook.source = "source-bundle-provenance";
2506
+ }
2507
+ } else {
2508
+ appState.workspace.operatorRunbook = createSourceBundleOnlyRunbook(importedSourceBundle);
2509
+ }
2510
+ appState.activeTab = "report";
2511
+ localStorage.setItem(ACTIVE_TAB_KEY, appState.activeTab);
2512
+ saveWorkspace();
2513
+ setMessage("Source bundle provenance JSON imported. Report tab opened.");
2514
+ return;
2515
+ }
2516
+ appState.workspace = normalizeWorkspace(parsed);
2517
+ appState.runbookActionFilter = "all";
2518
+ appState.runbookEvidenceFilter = "all";
2519
+ saveWorkspace();
2520
+ setMessage("Workspace JSON imported.");
2521
+ } catch (error) {
2522
+ setMessage("Import failed. Use a valid Website Improvement workspace JSON file.");
2523
+ }
2524
+ };
2525
+ reader.readAsText(file);
2526
+ event.target.value = "";
2527
+ }
2528
+
2529
+ document.addEventListener("input", handleInput);
2530
+ document.addEventListener("change", function (event) {
2531
+ if (event.target && event.target.matches("[data-action='import-file']")) {
2532
+ handleImport(event);
2533
+ } else {
2534
+ handleInput(event);
2535
+ }
2536
+ });
2537
+ document.addEventListener("click", handleClick);
2538
+
2539
+ render();
2540
+ }());