@flood-md/agent 0.2.9 → 0.2.10

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 (374) hide show
  1. package/DESIGN.md +26 -4
  2. package/README.md +35 -4
  3. package/SKILL.md +31 -0
  4. package/dist/catalog.json +7136 -0
  5. package/dist/materials/component-accordion.md +42 -0
  6. package/dist/materials/component-alert-dialog.md +46 -0
  7. package/dist/materials/component-alert.md +41 -0
  8. package/dist/materials/component-aspect-ratio.md +36 -0
  9. package/dist/materials/component-avatar-group.md +39 -0
  10. package/dist/materials/component-avatar.md +40 -0
  11. package/dist/materials/component-badge.md +37 -0
  12. package/dist/materials/component-blockquote.md +35 -0
  13. package/dist/materials/component-breadcrumb.md +36 -0
  14. package/dist/materials/component-button-group.md +39 -0
  15. package/dist/materials/component-button.md +43 -0
  16. package/dist/materials/component-calendar.md +59 -0
  17. package/dist/materials/component-card.md +46 -0
  18. package/dist/materials/component-center.md +39 -0
  19. package/dist/materials/component-character-count.md +42 -0
  20. package/dist/materials/component-check-card.md +39 -0
  21. package/dist/materials/component-checkbox-group.md +48 -0
  22. package/dist/materials/component-checkbox.md +37 -0
  23. package/dist/materials/component-code-snippet.md +37 -0
  24. package/dist/materials/component-collapsible.md +40 -0
  25. package/dist/materials/component-color-picker.md +49 -0
  26. package/dist/materials/component-color-swatch.md +35 -0
  27. package/dist/materials/component-combobox.md +50 -0
  28. package/dist/materials/component-command-list.md +48 -0
  29. package/dist/materials/component-container.md +36 -0
  30. package/dist/materials/component-context-menu.md +43 -0
  31. package/dist/materials/component-copy-button.md +42 -0
  32. package/dist/materials/component-data-list-item.md +40 -0
  33. package/dist/materials/component-data-list.md +38 -0
  34. package/dist/materials/component-data-table.md +58 -0
  35. package/dist/materials/component-date-field.md +57 -0
  36. package/dist/materials/component-date-picker.md +66 -0
  37. package/dist/materials/component-date-range-field.md +62 -0
  38. package/dist/materials/component-date-range-picker.md +72 -0
  39. package/dist/materials/component-date-time-picker.md +60 -0
  40. package/dist/materials/component-date-time-range-picker.md +66 -0
  41. package/dist/materials/component-description-list.md +42 -0
  42. package/dist/materials/component-dialog.md +45 -0
  43. package/dist/materials/component-divider.md +37 -0
  44. package/dist/materials/component-drawer.md +50 -0
  45. package/dist/materials/component-dropzone.md +45 -0
  46. package/dist/materials/component-editable.md +57 -0
  47. package/dist/materials/component-empty-state.md +41 -0
  48. package/dist/materials/component-error-summary.md +40 -0
  49. package/dist/materials/component-field.md +46 -0
  50. package/dist/materials/component-fieldset.md +46 -0
  51. package/dist/materials/component-figure.md +36 -0
  52. package/dist/materials/component-file-input.md +45 -0
  53. package/dist/materials/component-file-item.md +48 -0
  54. package/dist/materials/component-file-list.md +39 -0
  55. package/dist/materials/component-file-meta.md +36 -0
  56. package/dist/materials/component-flood-root.md +33 -0
  57. package/dist/materials/component-grid.md +38 -0
  58. package/dist/materials/component-heading.md +38 -0
  59. package/dist/materials/component-hover-card.md +48 -0
  60. package/dist/materials/component-icon-button.md +41 -0
  61. package/dist/materials/component-icon-slot.md +37 -0
  62. package/dist/materials/component-image-preview.md +50 -0
  63. package/dist/materials/component-image.md +37 -0
  64. package/dist/materials/component-inline-code.md +36 -0
  65. package/dist/materials/component-inline.md +39 -0
  66. package/dist/materials/component-input-group.md +43 -0
  67. package/dist/materials/component-input.md +39 -0
  68. package/dist/materials/component-kbd.md +34 -0
  69. package/dist/materials/component-label.md +37 -0
  70. package/dist/materials/component-link.md +35 -0
  71. package/dist/materials/component-list-box.md +57 -0
  72. package/dist/materials/component-list.md +40 -0
  73. package/dist/materials/component-menu.md +54 -0
  74. package/dist/materials/component-meter.md +43 -0
  75. package/dist/materials/component-metric.md +39 -0
  76. package/dist/materials/component-multi-select.md +52 -0
  77. package/dist/materials/component-navigation-list.md +38 -0
  78. package/dist/materials/component-navigation-tree.md +43 -0
  79. package/dist/materials/component-number-input.md +47 -0
  80. package/dist/materials/component-pagination.md +44 -0
  81. package/dist/materials/component-password-input.md +43 -0
  82. package/dist/materials/component-pin-input.md +45 -0
  83. package/dist/materials/component-popover.md +47 -0
  84. package/dist/materials/component-progress.md +42 -0
  85. package/dist/materials/component-radio-card.md +43 -0
  86. package/dist/materials/component-radio-group.md +49 -0
  87. package/dist/materials/component-radio.md +39 -0
  88. package/dist/materials/component-range-calendar.md +58 -0
  89. package/dist/materials/component-rating.md +46 -0
  90. package/dist/materials/component-resizable-panels.md +48 -0
  91. package/dist/materials/component-scroll-area.md +42 -0
  92. package/dist/materials/component-search-input.md +40 -0
  93. package/dist/materials/component-section.md +42 -0
  94. package/dist/materials/component-segmented-control.md +51 -0
  95. package/dist/materials/component-select.md +45 -0
  96. package/dist/materials/component-skeleton.md +42 -0
  97. package/dist/materials/component-skip-link.md +37 -0
  98. package/dist/materials/component-slider.md +51 -0
  99. package/dist/materials/component-spinner.md +37 -0
  100. package/dist/materials/component-split.md +40 -0
  101. package/dist/materials/component-stack.md +37 -0
  102. package/dist/materials/component-status-indicator.md +38 -0
  103. package/dist/materials/component-steps.md +40 -0
  104. package/dist/materials/component-surface.md +37 -0
  105. package/dist/materials/component-switch.md +35 -0
  106. package/dist/materials/component-table.md +46 -0
  107. package/dist/materials/component-tabs.md +43 -0
  108. package/dist/materials/component-tag-input.md +46 -0
  109. package/dist/materials/component-tag.md +40 -0
  110. package/dist/materials/component-text.md +39 -0
  111. package/dist/materials/component-textarea.md +38 -0
  112. package/dist/materials/component-time-field.md +56 -0
  113. package/dist/materials/component-time-picker.md +60 -0
  114. package/dist/materials/component-timeline.md +38 -0
  115. package/dist/materials/component-toast-region.md +42 -0
  116. package/dist/materials/component-toggle-group.md +44 -0
  117. package/dist/materials/component-toggle.md +39 -0
  118. package/dist/materials/component-toolbar.md +42 -0
  119. package/dist/materials/component-tooltip.md +40 -0
  120. package/dist/materials/component-tree-select.md +56 -0
  121. package/dist/materials/component-tree.md +52 -0
  122. package/dist/materials/component-upload-item.md +52 -0
  123. package/dist/materials/component-upload-list.md +40 -0
  124. package/dist/materials/component-visually-hidden.md +37 -0
  125. package/dist/materials/guide-agent-knowledge.md +88 -0
  126. package/dist/materials/guide-agents.md +96 -0
  127. package/dist/materials/guide-faq.md +84 -0
  128. package/dist/materials/guide-foundations.md +232 -0
  129. package/dist/materials/guide-installation.md +79 -0
  130. package/dist/materials/guide-interface-content.md +76 -0
  131. package/dist/materials/guide-introduction.md +40 -0
  132. package/dist/materials/guide-layout.md +193 -0
  133. package/dist/materials/guide-privacy.md +23 -0
  134. package/dist/materials/guide-project-ui.md +127 -0
  135. package/dist/materials/guide-skills.md +108 -0
  136. package/dist/materials/guide-theming.md +73 -0
  137. package/dist/materials/recipe-command-palette.md +66 -0
  138. package/dist/materials/recipe-filter-bar.md +59 -0
  139. package/dist/materials/recipe-form-submission.md +39 -0
  140. package/dist/materials/recipe-grouped-form.md +66 -0
  141. package/dist/materials/recipe-select-all.md +31 -0
  142. package/dist/materials/recipe-split-button.md +26 -0
  143. package/dist/materials/recipe-table-bulk-actions.md +88 -0
  144. package/dist/materials/recipe-workspace-review.md +134 -0
  145. package/dist/materials/token-sys.color.accent.content.json +41 -0
  146. package/dist/materials/token-sys.color.action.danger.content.json +42 -0
  147. package/dist/materials/token-sys.color.action.ghost.content.json +41 -0
  148. package/dist/materials/token-sys.color.action.ghost.disabled-content.json +41 -0
  149. package/dist/materials/token-sys.color.action.ghost.disabled.json +41 -0
  150. package/dist/materials/token-sys.color.action.ghost.hover.json +41 -0
  151. package/dist/materials/token-sys.color.action.ghost.pressed.json +41 -0
  152. package/dist/materials/token-sys.color.action.ghost.rest.json +41 -0
  153. package/dist/materials/token-sys.color.action.on-surface.content.json +39 -0
  154. package/dist/materials/token-sys.color.action.on-surface.disabled-content.json +39 -0
  155. package/dist/materials/token-sys.color.action.on-surface.disabled.json +39 -0
  156. package/dist/materials/token-sys.color.action.on-surface.hover.json +39 -0
  157. package/dist/materials/token-sys.color.action.on-surface.pressed.json +39 -0
  158. package/dist/materials/token-sys.color.action.on-surface.rest.json +39 -0
  159. package/dist/materials/token-sys.color.action.primary.content.json +39 -0
  160. package/dist/materials/token-sys.color.action.primary.disabled-content.json +39 -0
  161. package/dist/materials/token-sys.color.action.primary.disabled.json +39 -0
  162. package/dist/materials/token-sys.color.action.primary.hover.json +39 -0
  163. package/dist/materials/token-sys.color.action.primary.pressed.json +39 -0
  164. package/dist/materials/token-sys.color.action.primary.rest.json +39 -0
  165. package/dist/materials/token-sys.color.action.subtle.content.json +39 -0
  166. package/dist/materials/token-sys.color.action.subtle.disabled-content.json +39 -0
  167. package/dist/materials/token-sys.color.action.subtle.disabled.json +39 -0
  168. package/dist/materials/token-sys.color.action.subtle.hover.json +39 -0
  169. package/dist/materials/token-sys.color.action.subtle.pressed.json +39 -0
  170. package/dist/materials/token-sys.color.action.subtle.rest.json +39 -0
  171. package/dist/materials/token-sys.color.border.decorative.json +42 -0
  172. package/dist/materials/token-sys.color.border.default.json +39 -0
  173. package/dist/materials/token-sys.color.border.floating.json +39 -0
  174. package/dist/materials/token-sys.color.border.strong.json +39 -0
  175. package/dist/materials/token-sys.color.content.default.json +41 -0
  176. package/dist/materials/token-sys.color.content.helper.json +39 -0
  177. package/dist/materials/token-sys.color.content.subtle.json +43 -0
  178. package/dist/materials/token-sys.color.divider.decorative.json +41 -0
  179. package/dist/materials/token-sys.color.feedback.error.json +39 -0
  180. package/dist/materials/token-sys.color.feedback.info.json +39 -0
  181. package/dist/materials/token-sys.color.feedback.success.json +39 -0
  182. package/dist/materials/token-sys.color.feedback.warning.json +39 -0
  183. package/dist/materials/token-sys.color.field.background.json +39 -0
  184. package/dist/materials/token-sys.color.field.border.json +39 -0
  185. package/dist/materials/token-sys.color.field.boundary.json +39 -0
  186. package/dist/materials/token-sys.color.field.content.json +39 -0
  187. package/dist/materials/token-sys.color.field.disabled-content.json +39 -0
  188. package/dist/materials/token-sys.color.field.disabled.json +39 -0
  189. package/dist/materials/token-sys.color.field.placeholder.json +39 -0
  190. package/dist/materials/token-sys.color.focus.ring.json +39 -0
  191. package/dist/materials/token-sys.color.indicator.accent.json +39 -0
  192. package/dist/materials/token-sys.color.indicator.fill.json +39 -0
  193. package/dist/materials/token-sys.color.indicator.track.json +39 -0
  194. package/dist/materials/token-sys.color.loading.placeholder.json +42 -0
  195. package/dist/materials/token-sys.color.scrollbar.thumb-hover.json +39 -0
  196. package/dist/materials/token-sys.color.scrollbar.thumb.json +39 -0
  197. package/dist/materials/token-sys.color.selection.background.json +39 -0
  198. package/dist/materials/token-sys.color.selection.content.json +39 -0
  199. package/dist/materials/token-sys.color.selection.floating.json +39 -0
  200. package/dist/materials/token-sys.color.shadow.floating.json +39 -0
  201. package/dist/materials/token-sys.color.surface.canvas.json +39 -0
  202. package/dist/materials/token-sys.color.surface.default.json +39 -0
  203. package/dist/materials/token-sys.color.surface.floating-translucent.json +39 -0
  204. package/dist/materials/token-sys.color.surface.floating.json +39 -0
  205. package/dist/materials/token-sys.color.surface.overlay.json +39 -0
  206. package/dist/materials/token-sys.color.surface.raised.json +39 -0
  207. package/dist/materials/token-sys.control.2xl.gap.json +34 -0
  208. package/dist/materials/token-sys.control.2xl.height.json +34 -0
  209. package/dist/materials/token-sys.control.2xl.icon-size.json +34 -0
  210. package/dist/materials/token-sys.control.2xl.line-height.json +31 -0
  211. package/dist/materials/token-sys.control.2xl.padding-inline.json +34 -0
  212. package/dist/materials/token-sys.control.2xl.text-size.json +34 -0
  213. package/dist/materials/token-sys.control.2xs.gap.json +34 -0
  214. package/dist/materials/token-sys.control.2xs.height.json +34 -0
  215. package/dist/materials/token-sys.control.2xs.icon-size.json +34 -0
  216. package/dist/materials/token-sys.control.2xs.line-height.json +31 -0
  217. package/dist/materials/token-sys.control.2xs.padding-inline.json +34 -0
  218. package/dist/materials/token-sys.control.2xs.text-size.json +34 -0
  219. package/dist/materials/token-sys.control.lg.gap.json +34 -0
  220. package/dist/materials/token-sys.control.lg.height.json +34 -0
  221. package/dist/materials/token-sys.control.lg.icon-size.json +34 -0
  222. package/dist/materials/token-sys.control.lg.line-height.json +31 -0
  223. package/dist/materials/token-sys.control.lg.padding-inline.json +34 -0
  224. package/dist/materials/token-sys.control.lg.text-size.json +34 -0
  225. package/dist/materials/token-sys.control.md.gap.json +34 -0
  226. package/dist/materials/token-sys.control.md.height.json +34 -0
  227. package/dist/materials/token-sys.control.md.icon-size.json +34 -0
  228. package/dist/materials/token-sys.control.md.line-height.json +31 -0
  229. package/dist/materials/token-sys.control.md.padding-inline.json +34 -0
  230. package/dist/materials/token-sys.control.md.text-size.json +34 -0
  231. package/dist/materials/token-sys.control.sm.gap.json +34 -0
  232. package/dist/materials/token-sys.control.sm.height.json +34 -0
  233. package/dist/materials/token-sys.control.sm.icon-size.json +34 -0
  234. package/dist/materials/token-sys.control.sm.line-height.json +31 -0
  235. package/dist/materials/token-sys.control.sm.padding-inline.json +34 -0
  236. package/dist/materials/token-sys.control.sm.text-size.json +34 -0
  237. package/dist/materials/token-sys.control.xl.gap.json +34 -0
  238. package/dist/materials/token-sys.control.xl.height.json +34 -0
  239. package/dist/materials/token-sys.control.xl.icon-size.json +34 -0
  240. package/dist/materials/token-sys.control.xl.line-height.json +31 -0
  241. package/dist/materials/token-sys.control.xl.padding-inline.json +34 -0
  242. package/dist/materials/token-sys.control.xl.text-size.json +34 -0
  243. package/dist/materials/token-sys.control.xs.gap.json +34 -0
  244. package/dist/materials/token-sys.control.xs.height.json +34 -0
  245. package/dist/materials/token-sys.control.xs.icon-size.json +34 -0
  246. package/dist/materials/token-sys.control.xs.line-height.json +31 -0
  247. package/dist/materials/token-sys.control.xs.padding-inline.json +34 -0
  248. package/dist/materials/token-sys.control.xs.text-size.json +34 -0
  249. package/dist/materials/token-sys.effect.floating.blur.json +34 -0
  250. package/dist/materials/token-sys.effect.floating.saturation.json +31 -0
  251. package/dist/materials/token-sys.effect.floating.shadow-blur.json +34 -0
  252. package/dist/materials/token-sys.effect.floating.shadow-offset.json +34 -0
  253. package/dist/materials/token-sys.focus.offset.json +34 -0
  254. package/dist/materials/token-sys.focus.width.json +34 -0
  255. package/dist/materials/token-sys.font.mono.json +36 -0
  256. package/dist/materials/token-sys.font.sans.json +35 -0
  257. package/dist/materials/token-sys.layer.base.json +31 -0
  258. package/dist/materials/token-sys.layer.modal.json +31 -0
  259. package/dist/materials/token-sys.layer.popover.json +31 -0
  260. package/dist/materials/token-sys.layer.sticky.json +31 -0
  261. package/dist/materials/token-sys.layer.toast.json +31 -0
  262. package/dist/materials/token-sys.motion.duration-fast.json +34 -0
  263. package/dist/materials/token-sys.motion.duration-normal.json +34 -0
  264. package/dist/materials/token-sys.motion.duration-reduced.json +34 -0
  265. package/dist/materials/token-sys.motion.easing.json +36 -0
  266. package/dist/materials/token-sys.radius.action.json +34 -0
  267. package/dist/materials/token-sys.radius.block.json +34 -0
  268. package/dist/materials/token-sys.radius.field.json +34 -0
  269. package/dist/materials/token-sys.radius.indicator.json +34 -0
  270. package/dist/materials/token-sys.radius.surface.json +34 -0
  271. package/dist/materials/token-sys.scrollbar.inset.json +34 -0
  272. package/dist/materials/token-sys.scrollbar.min-thumb.json +34 -0
  273. package/dist/materials/token-sys.scrollbar.size.json +34 -0
  274. package/dist/materials/token-sys.size.choice.json +34 -0
  275. package/dist/materials/token-sys.size.container.lg.json +37 -0
  276. package/dist/materials/token-sys.size.container.md.json +37 -0
  277. package/dist/materials/token-sys.size.container.sm.json +37 -0
  278. package/dist/materials/token-sys.size.grid.min-item.json +37 -0
  279. package/dist/materials/token-sys.size.indicator.lg.json +34 -0
  280. package/dist/materials/token-sys.size.indicator.md.json +34 -0
  281. package/dist/materials/token-sys.size.indicator.sm.json +34 -0
  282. package/dist/materials/token-sys.size.keycap.lg.json +34 -0
  283. package/dist/materials/token-sys.size.keycap.md.json +34 -0
  284. package/dist/materials/token-sys.size.keycap.sm.json +34 -0
  285. package/dist/materials/token-sys.size.switch-height.json +34 -0
  286. package/dist/materials/token-sys.size.switch-thumb.json +34 -0
  287. package/dist/materials/token-sys.size.switch-width.json +34 -0
  288. package/dist/materials/token-sys.size.touch.json +34 -0
  289. package/dist/materials/token-sys.space.0.json +34 -0
  290. package/dist/materials/token-sys.space.1.json +34 -0
  291. package/dist/materials/token-sys.space.12.json +34 -0
  292. package/dist/materials/token-sys.space.2.json +34 -0
  293. package/dist/materials/token-sys.space.3.json +34 -0
  294. package/dist/materials/token-sys.space.4.json +34 -0
  295. package/dist/materials/token-sys.space.6.json +34 -0
  296. package/dist/materials/token-sys.space.8.json +34 -0
  297. package/dist/materials/token-sys.stroke.width.json +34 -0
  298. package/dist/materials/token-sys.type.body.line-height.json +31 -0
  299. package/dist/materials/token-sys.type.body.size.json +34 -0
  300. package/dist/materials/token-sys.type.body.tracking.json +34 -0
  301. package/dist/materials/token-sys.type.body.weight.json +31 -0
  302. package/dist/materials/token-sys.type.compact.line-height.json +31 -0
  303. package/dist/materials/token-sys.type.compact.size.json +34 -0
  304. package/dist/materials/token-sys.type.compact.tracking.json +34 -0
  305. package/dist/materials/token-sys.type.compact.weight.json +31 -0
  306. package/dist/materials/token-sys.type.control.line-height.json +31 -0
  307. package/dist/materials/token-sys.type.control.size.json +34 -0
  308. package/dist/materials/token-sys.type.control.tracking.json +34 -0
  309. package/dist/materials/token-sys.type.control.weight.json +31 -0
  310. package/dist/materials/token-sys.type.dialog-title.line-height.json +33 -0
  311. package/dist/materials/token-sys.type.dialog-title.size.json +36 -0
  312. package/dist/materials/token-sys.type.dialog-title.tracking.json +36 -0
  313. package/dist/materials/token-sys.type.dialog-title.weight.json +33 -0
  314. package/dist/materials/token-sys.type.display.line-height.json +31 -0
  315. package/dist/materials/token-sys.type.display.size.json +34 -0
  316. package/dist/materials/token-sys.type.display.tracking.json +34 -0
  317. package/dist/materials/token-sys.type.display.weight.json +31 -0
  318. package/dist/materials/token-sys.type.field-label.line-height.json +33 -0
  319. package/dist/materials/token-sys.type.field-label.size.json +36 -0
  320. package/dist/materials/token-sys.type.field-label.tracking.json +36 -0
  321. package/dist/materials/token-sys.type.field-label.weight.json +33 -0
  322. package/dist/materials/token-sys.type.group-title.line-height.json +33 -0
  323. package/dist/materials/token-sys.type.group-title.size.json +36 -0
  324. package/dist/materials/token-sys.type.group-title.tracking.json +36 -0
  325. package/dist/materials/token-sys.type.group-title.weight.json +33 -0
  326. package/dist/materials/token-sys.type.helper.line-height.json +31 -0
  327. package/dist/materials/token-sys.type.helper.size.json +34 -0
  328. package/dist/materials/token-sys.type.helper.tracking.json +34 -0
  329. package/dist/materials/token-sys.type.helper.weight.json +31 -0
  330. package/dist/materials/token-sys.type.label.line-height.json +31 -0
  331. package/dist/materials/token-sys.type.label.size.json +34 -0
  332. package/dist/materials/token-sys.type.label.tracking.json +34 -0
  333. package/dist/materials/token-sys.type.label.weight.json +31 -0
  334. package/dist/materials/token-sys.type.large.line-height.json +31 -0
  335. package/dist/materials/token-sys.type.large.size.json +34 -0
  336. package/dist/materials/token-sys.type.large.tracking.json +34 -0
  337. package/dist/materials/token-sys.type.large.weight.json +31 -0
  338. package/dist/materials/token-sys.type.lead.line-height.json +31 -0
  339. package/dist/materials/token-sys.type.lead.size.json +34 -0
  340. package/dist/materials/token-sys.type.lead.tracking.json +34 -0
  341. package/dist/materials/token-sys.type.lead.weight.json +31 -0
  342. package/dist/materials/token-sys.type.page.line-height.json +31 -0
  343. package/dist/materials/token-sys.type.page.size.json +34 -0
  344. package/dist/materials/token-sys.type.page.tracking.json +34 -0
  345. package/dist/materials/token-sys.type.page.weight.json +31 -0
  346. package/dist/materials/token-sys.type.section.line-height.json +31 -0
  347. package/dist/materials/token-sys.type.section.size.json +34 -0
  348. package/dist/materials/token-sys.type.section.tracking.json +34 -0
  349. package/dist/materials/token-sys.type.section.weight.json +31 -0
  350. package/knowledge.json +409 -0
  351. package/lib/index.d.ts +33 -0
  352. package/lib/index.mjs +144 -0
  353. package/lib/inspect.d.ts +12 -0
  354. package/lib/inspect.mjs +187 -0
  355. package/manifest.json +41 -1
  356. package/package.json +7 -1
  357. package/references/flood-composition/SKILL.md +30 -0
  358. package/references/flood-data-display/SKILL.md +32 -0
  359. package/references/flood-feedback/SKILL.md +24 -3
  360. package/references/flood-forms/SKILL.md +51 -2
  361. package/references/flood-foundations/SKILL.md +7 -0
  362. package/references/flood-hierarchy/SKILL.md +87 -0
  363. package/references/flood-interface-content/SKILL.md +142 -0
  364. package/references/flood-overlays/SKILL.md +7 -0
  365. package/references/flood-records/SKILL.md +108 -0
  366. package/references/flood-spacing/SKILL.md +16 -0
  367. package/references/flood-verification/SKILL.md +52 -0
  368. package/references/interface-content-examples.md +47 -0
  369. package/references/knowledge.md +49 -0
  370. package/references/record-decisions.md +25 -0
  371. package/references/rendered-review.md +97 -0
  372. package/references/routing.md +1 -0
  373. package/rules.json +152 -2
  374. package/workflows.json +146 -10
package/lib/index.mjs ADDED
@@ -0,0 +1,144 @@
1
+ import { readFile, lstat, realpath, open } from 'node:fs/promises';
2
+ import { createHash } from 'node:crypto';
3
+ import { dirname, resolve, relative, isAbsolute, sep } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const DEFAULT_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
7
+ const KINDS = new Set(['guide', 'workflow', 'reference', 'component', 'recipe', 'token']);
8
+ const ID = /^[a-z][a-z0-9-]*:[a-z0-9][a-z0-9.-]*$/;
9
+ const MAX_CATALOG_BYTES = 8 * 1024 * 1024;
10
+ const MAX_MATERIAL_BYTES = 1024 * 1024;
11
+ const PAGE_BYTES = 16 * 1024;
12
+ const digest = bytes => createHash('sha256').update(bytes).digest('hex');
13
+ const fail = (code, message) => { const error = new Error(message); error.code = code; throw error; };
14
+ const inside = (root, path) => { const r = relative(root, path); return r === '' || (r !== '..' && !r.startsWith('..' + sep) && !isAbsolute(r)); };
15
+ const safePath = path => typeof path === 'string' && /^[a-zA-Z0-9._/-]+$/.test(path) && !path.startsWith('/') && !path.split('/').some(part => !part || part === '.' || part === '..');
16
+ const text = (value, limit) => typeof value === 'string' && value.trim().length > 0 && value.length <= limit;
17
+
18
+ /** Validate the data contract without loading material bodies or executing instructions. */
19
+ export function validateKnowledgeCatalog(catalog) {
20
+ if (catalog?.schemaVersion !== 1 || !/^\d+\.\d+\.\d+$/.test(catalog.version ?? '') || !Array.isArray(catalog.groups) || !catalog.groups.length || catalog.groups.length > 16 || !Array.isArray(catalog.materials) || !catalog.materials.length || !Array.isArray(catalog.capabilities) || catalog.capabilities.length > 32) fail('INVALID_CATALOG', 'Unsupported or incomplete Flood knowledge catalog.');
21
+ const groups = new Set(), ids = new Set(), files = new Set();
22
+ for (const group of catalog.groups) {
23
+ if (!/^[a-z][a-z0-9-]*$/.test(group.id ?? '') || groups.has(group.id) || !text(group.name, 128) || !text(group.description, 256)) fail('INVALID_CATALOG', 'Invalid or duplicate knowledge group.');
24
+ groups.add(group.id);
25
+ }
26
+ let previous = '';
27
+ for (const item of catalog.materials) {
28
+ if (!ID.test(item.id ?? '') || ids.has(item.id) || item.id <= previous || !KINDS.has(item.kind) || !item.id.startsWith(item.kind + ':') || !text(item.name, 128) || !text(item.description, 1024) || !safePath(item.file) || files.has(item.file) || !/^[a-f0-9]{64}$/.test(item.sha256 ?? '') || !Number.isSafeInteger(item.bytes) || item.bytes < 1 || item.bytes > MAX_MATERIAL_BYTES || !Array.isArray(item.groups) || !item.groups.length || item.groups.length > 8 || new Set(item.groups).size !== item.groups.length || item.groups.some(group => !groups.has(group)) || !Array.isArray(item.links) || !Array.isArray(item.requires) || item.status !== 'available') fail('INVALID_CATALOG', 'Invalid, unsorted or duplicate knowledge material.');
29
+ if (item.installedTarget !== undefined && (!safePath(item.installedTarget) || !(item.installedTarget.startsWith('flood/') || item.installedTarget.startsWith('.agents/skills/flood-ui/')))) fail('INVALID_CATALOG', 'Invalid owned guide target.');
30
+ if (item.requires.length > 16 || item.links.length > 256 || new Set(item.requires).size !== item.requires.length || new Set(item.links).size !== item.links.length) fail('INVALID_CATALOG', 'Knowledge relationships exceed their per-material budget or contain duplicates.');
31
+ ids.add(item.id); files.add(item.file); previous = item.id;
32
+ }
33
+ const byId = new Map(catalog.materials.map(item => [item.id, item]));
34
+ for (const item of catalog.materials) for (const id of [...item.links, ...item.requires]) if (!ids.has(id)) fail('INVALID_CATALOG', `Unresolved knowledge relationship: ${id}`);
35
+ const visiting = new Set(), depths = new Map();
36
+ const visit = id => {
37
+ if (visiting.has(id)) fail('INVALID_CATALOG', 'Knowledge prerequisite cycle.');
38
+ if (depths.has(id)) return depths.get(id);
39
+ if (visiting.size >= 32) fail('INVALID_CATALOG', 'Knowledge prerequisites exceed the supported depth.');
40
+ visiting.add(id);
41
+ let depth = 1;
42
+ for (const dependency of byId.get(id).requires) depth = Math.max(depth, 1 + visit(dependency));
43
+ visiting.delete(id);
44
+ if (depth > 32) fail('INVALID_CATALOG', 'Knowledge prerequisites exceed the supported depth.');
45
+ depths.set(id, depth); return depth;
46
+ };
47
+ for (const id of ids) visit(id);
48
+ const capabilities = new Set();
49
+ for (const item of catalog.capabilities) {
50
+ if (!/^[a-z][a-z0-9-]*$/.test(item.id ?? '') || capabilities.has(item.id) || !['available', 'not-provided'].includes(item.status) || !text(item.description, 256) || !Array.isArray(item.materials) || item.materials.length > 16 || item.materials.some(id => !ids.has(id)) || (item.status === 'available' && !item.materials.length) || (item.status === 'not-provided' && item.materials.length)) fail('INVALID_CATALOG', 'Invalid knowledge capability declaration.');
51
+ capabilities.add(item.id);
52
+ }
53
+ return catalog;
54
+ }
55
+
56
+ async function readRegular(root, path, maxBytes) {
57
+ if (!safePath(path)) fail('INVALID_PATH', 'Knowledge paths must come from the package catalog.');
58
+ const base = await realpath(root);
59
+ let current = base;
60
+ for (const part of path.split('/')) {
61
+ current = resolve(current, part);
62
+ const stat = await lstat(current).catch(() => fail('MISSING_MATERIAL', `Missing knowledge file: ${path}`));
63
+ if (stat.isSymbolicLink()) fail('INVALID_PATH', 'Refusing a linked knowledge file or directory.');
64
+ }
65
+ if (!inside(base, await realpath(current))) fail('INVALID_PATH', 'Knowledge file left its package root.');
66
+ const handle = await open(current, 'r');
67
+ try {
68
+ const stat = await handle.stat();
69
+ if (!stat.isFile() || stat.nlink !== 1 || stat.size > maxBytes) fail('INVALID_MATERIAL', 'Knowledge file is not a bounded regular file.');
70
+ const bytes = await handle.readFile();
71
+ if (bytes.length > maxBytes) fail('INVALID_MATERIAL', 'Knowledge file exceeds its read budget.');
72
+ return bytes;
73
+ } finally { await handle.close(); }
74
+ }
75
+
76
+ const decode = bytes => { try { return new TextDecoder('utf-8', { fatal: true }).decode(bytes); } catch { fail('INVALID_MATERIAL', 'Knowledge must be valid UTF-8.'); } };
77
+ const summary = ({id, kind, name, description, groups, status, links, requires}) => ({id, kind, name, description, groups: [...groups], status, relatedCount: links.length, requires: [...requires]});
78
+
79
+ /** A provider-neutral, read-only reader. A fresh catalog is checked on every operation. */
80
+ export function createKnowledgeReader({root = DEFAULT_ROOT} = {}) {
81
+ async function load() {
82
+ const bytes = await readRegular(root, 'dist/catalog.json', MAX_CATALOG_BYTES);
83
+ let catalog;
84
+ try { catalog = JSON.parse(decode(bytes)); } catch (error) { if (error.code) throw error; fail('INVALID_CATALOG', 'Malformed Flood knowledge catalog.'); }
85
+ validateKnowledgeCatalog(catalog);
86
+ const pkg = JSON.parse(decode(await readRegular(root, 'package.json', 64 * 1024)));
87
+ if (pkg.name !== '@flood-md/agent' || pkg.version !== catalog.version) fail('VERSION_MISMATCH', 'Agent package and knowledge catalog versions differ.');
88
+ return {catalog, fingerprint: digest(bytes)};
89
+ }
90
+ return {
91
+ async info() {
92
+ const {catalog, fingerprint} = await load();
93
+ return {schemaVersion: 1, version: catalog.version, fingerprint, materialCount: catalog.materials.length,
94
+ groups: catalog.groups.map(group => ({...group, count: catalog.materials.filter(item => item.groups.includes(group.id)).length})),
95
+ kinds: [...KINDS].map(kind => ({kind, count: catalog.materials.filter(item => item.kind === kind).length})),
96
+ capabilities: structuredClone(catalog.capabilities)};
97
+ },
98
+ async list({kind, group, search = '', limit = 12, cursor} = {}) {
99
+ if ((kind !== undefined && !KINDS.has(kind)) || !Number.isSafeInteger(limit) || limit < 1 || limit > 50 || typeof search !== 'string' || search.length > 160) fail('INVALID_QUERY', 'Invalid catalog kind, limit or search.');
100
+ const {catalog, fingerprint} = await load();
101
+ if (group !== undefined && !catalog.groups.some(item => item.id === group)) fail('INVALID_QUERY', 'Unknown knowledge group.');
102
+ const query = {kind: kind ?? null, group: group ?? null, search: search.trim().toLowerCase()};
103
+ const queryHash = digest(JSON.stringify(query));
104
+ let offset = 0;
105
+ if (cursor !== undefined) {
106
+ let state;
107
+ try { if (typeof cursor !== 'string' || cursor.length > 1024 || !/^[A-Za-z0-9_-]+$/.test(cursor)) throw Error(); state = JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8')); } catch { fail('INVALID_CURSOR', 'Malformed knowledge cursor.'); }
108
+ if (state.fingerprint !== fingerprint || state.query !== queryHash || !Number.isSafeInteger(state.offset) || state.offset < 1) fail('STALE_CURSOR', 'Catalog or query changed; start a fresh catalog page.');
109
+ offset = state.offset;
110
+ }
111
+ const matches = catalog.materials.filter(item => (!kind || item.kind === kind) && (!group || item.groups.includes(group)) && (!query.search || `${item.id} ${item.name} ${item.description}`.toLowerCase().includes(query.search)));
112
+ if (offset > matches.length) fail('INVALID_CURSOR', 'Knowledge cursor is outside the result set.');
113
+ const page = [];
114
+ const response = () => {
115
+ const next = offset + page.length;
116
+ return {schemaVersion: 1, version: catalog.version, fingerprint, query, total: matches.length, items: page,
117
+ truncated: next < matches.length, nextCursor: next < matches.length ? Buffer.from(JSON.stringify({fingerprint, query: queryHash, offset: next})).toString('base64url') : null};
118
+ };
119
+ for (const item of matches.slice(offset, offset + limit)) {
120
+ page.push(summary(item));
121
+ if (Buffer.byteLength(JSON.stringify(response())) > PAGE_BYTES) { page.pop(); break; }
122
+ }
123
+ if (offset < matches.length && !page.length) fail('INVALID_CATALOG', 'A catalog entry exceeds the discovery budget.');
124
+ return response();
125
+ },
126
+ async read(id, {offset = 0, length = 10000, linksOffset = 0} = {}) {
127
+ if (!ID.test(id ?? '') || !Number.isSafeInteger(offset) || offset < 0 || !Number.isSafeInteger(length) || length < 1 || length > 40000 || !Number.isSafeInteger(linksOffset) || linksOffset < 0) fail('INVALID_QUERY', 'Use a known material ID and a bounded character range.');
128
+ const {catalog, fingerprint} = await load();
129
+ const item = catalog.materials.find(item => item.id === id);
130
+ if (!item) fail('UNKNOWN_MATERIAL', `Unknown installed knowledge material: ${id}`);
131
+ if (linksOffset > item.links.length) fail('INVALID_QUERY', 'Link offset is outside the material.');
132
+ const bytes = await readRegular(root, item.file, MAX_MATERIAL_BYTES);
133
+ if (bytes.length !== item.bytes || digest(bytes) !== item.sha256) fail('INTEGRITY_MISMATCH', 'Knowledge content differs from the versioned catalog.');
134
+ const chars = Array.from(decode(bytes));
135
+ if (offset > chars.length) fail('INVALID_QUERY', 'Read offset is outside the material.');
136
+ const end = Math.min(chars.length, offset + length);
137
+ return {schemaVersion: 1, version: catalog.version, fingerprint, ...summary(item), source: 'package',
138
+ sha256: item.sha256, installedTarget: item.installedTarget ?? null, links: item.links.slice(linksOffset, linksOffset + 32),
139
+ linksOffset, nextLinksOffset: linksOffset + 32 < item.links.length ? linksOffset + 32 : null,
140
+ text: chars.slice(offset, end).join(''), offset, totalCharacters: chars.length,
141
+ truncated: end < chars.length, nextOffset: end < chars.length ? end : null};
142
+ },
143
+ };
144
+ }
@@ -0,0 +1,12 @@
1
+ export interface InspectionTarget {tag:string;id:string|null;className:string;label:string;}
2
+ export interface InspectionIssue {id:'icons.text-glyph'|'icons.unslotted-svg'|'table.mixed-row-centre'|'table.word-fragment'|'copy.action-echo'|'copy.unowned-support'|'forms.track-drift'|'forms.content-track-drift'|'layout.page-overflow';target:InspectionTarget;related?:InspectionTarget;message:string;offset?:number;word?:string;}
3
+ export interface RenderedUIInspection {
4
+ schemaVersion:1;issues:InspectionIssue[];copy:InspectionTarget[];
5
+ fields:(InspectionTarget&{x:number;y:number;width:number;height:number})[];
6
+ truncated:boolean;notChecked:string[];
7
+ }
8
+ /** Invoke inside a browser document, e.g. page.evaluate(inspectRenderedUI). No mutation or network calls. */
9
+ export declare function inspectRenderedUI(options?:{rootSelector?:string;maxElements?:number}):RenderedUIInspection;
10
+ export interface SourceUIIssue {id:'composition.native-dialog'|'composition.native-control'|'composition.layout-owner'|'composition.scaled-minimum';line:number;column:number;excerpt:string;owner:string;message:string;}
11
+ /** Text observations, not an AST or conformance check. No file access, execution or network. */
12
+ export declare function inspectSourceUI(source:string,options?:{format?:'jsx'|'css';maxFindings?:number}):{schemaVersion:1;issues:SourceUIIssue[];truncated:boolean;notChecked:string[]};
@@ -0,0 +1,187 @@
1
+ /** Optional text observations for host-owned JSX/CSS; no parsing or execution. */
2
+ export function inspectSourceUI(source,options={}) {
3
+ if(typeof source!=='string'||source.length>500000)throw new Error('Source must be a string of at most 500000 UTF-16 units');
4
+ const limit=options.maxFindings??50,format=options.format??'jsx';
5
+ if(!Number.isSafeInteger(limit)||limit<1||limit>500)throw new Error('maxFindings must be an integer from 1 to 500');
6
+ if(!['jsx','css'].includes(format))throw new Error('format must be jsx or css');
7
+ const issues=[];let truncated=false;
8
+ const record=(match,id,owner,message)=>{
9
+ if(issues.length>=limit){truncated=true;return;}
10
+ const offset=match.index+match[0].length-match[0].trimStart().length;
11
+ const prefix=source.slice(0,offset),line=prefix.split('\n').length,column=offset-prefix.lastIndexOf('\n');
12
+ issues.push({id,line,column,excerpt:match[0].trimStart().slice(0,180),owner,message});
13
+ };
14
+ if(format==='jsx'){
15
+ for(const match of source.matchAll(/\b(?:window|globalThis)\.(confirm|alert|prompt)\s*\(/g))record(match,'composition.native-dialog',match[1]==='confirm'?'AlertDialog':match[1]==='prompt'?'Dialog':'Alert','Native browser dialog candidate. Review the actual call and use the installed Flood owner unless native UI is explicitly required.');
16
+ for(const match of source.matchAll(/<(select|button)\b|<input\b[^>]*\btype\s*=\s*["'](date|time|checkbox|radio|range)["']/g)){
17
+ const role=match[1]||match[2],owner={select:'Select',button:'Button',date:'DatePicker',time:'TimeField',checkbox:'Checkbox',radio:'Radio',range:'Slider'}[role];
18
+ record(match,'composition.native-control',owner,'Raw control candidate. Review whether an existing Flood component owns this role; native form/table structure and deliberate documented adapters are separate cases.');
19
+ }
20
+ } else {
21
+ for(const match of source.matchAll(/([^{}]+)\{[^{}]*\bdisplay\s*:\s*(grid|flex)\b/g)){
22
+ if(match[1].trim().startsWith('.flood-'))continue;
23
+ record(match,'composition.layout-owner',match[2]==='grid'?'Grid':'Stack / Inline','Custom layout candidate. Shared form tracks and repeated layout roles should use Flood owners. A semantic host shell can legitimately own positioning; record the actual reason.');
24
+ }
25
+ for(const match of source.matchAll(/\bmin-(?:width|inline-size)\s*:\s*(\d*\.?\d+)rem\b/g)){
26
+ if(Number(match[1])>0)record(match,'composition.scaled-minimum','Container / local ScrollArea','Rem-based minimum width candidate. If applied to the whole host, it can force page overflow when text grows. Dense table minima can be legitimate inside a local scroll owner; inspect the actual selector and enlarged desktop result.');
27
+ }
28
+ }
29
+ return {schemaVersion:1,issues,truncated,notChecked:['Text matching is not parsing: comments, strings and examples can match; aliases, dynamic types and indirect calls can be missed. Review source context.','This function reads only the supplied text. It does not execute it, open files or inspect imports, component capability, rendered layout or editorial necessity.']};
30
+ }
31
+
32
+ /** Read-only DOM observations. Pass this self-contained function to page.evaluate. */
33
+ export function inspectRenderedUI(options = {}) {
34
+ if (typeof document === 'undefined') throw new Error('inspectRenderedUI must run in the rendered browser document');
35
+ const root = options.rootSelector ? document.querySelector(options.rootSelector) : document;
36
+ if (!root) throw new Error('Flood inspection root was not found');
37
+ const limit = options.maxElements ?? 500;
38
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > 2000) throw new Error('maxElements must be an integer from 1 to 2000');
39
+ const issues = [], copy = [], fields = [];
40
+ const visible = element => {
41
+ const style = getComputedStyle(element), box = element.getBoundingClientRect();
42
+ return style.display !== 'none' && style.visibility !== 'hidden' && box.width > 0 && box.height > 0;
43
+ };
44
+ const label = element => (element.getAttribute('aria-label') || element.textContent || '').trim().replace(/\s+/g, ' ').slice(0, 180);
45
+ const target = element => ({tag:element.tagName.toLowerCase(), id:element.id || null, className:element.getAttribute('class') || '', label:label(element)});
46
+ let truncated = false;
47
+ const addIssue = finding => { if (issues.length < limit) issues.push(finding); else truncated = true; };
48
+ const select = selector => {
49
+ const matches = [...root.querySelectorAll(selector)].filter(visible);
50
+ if(matches.length > limit) truncated = true;
51
+ return matches.slice(0, limit);
52
+ };
53
+ if(root===document&&document.documentElement.scrollWidth>innerWidth+1)addIssue({id:'layout.page-overflow',target:{tag:'html',id:document.documentElement.id||null,className:document.documentElement.getAttribute('class')||'',label:'Document'},offset:document.documentElement.scrollWidth-innerWidth,message:'The document extends beyond the viewport. Review the actual overflow owner at this text size; dense content can scroll locally. Hiding page overflow does not repair the layout.'});
54
+ for (const button of select('button.flood-button, button.flood-icon-button')) {
55
+ // Text is inspected outside real icon slots, SVGs and visually hidden names.
56
+ const walker = document.createTreeWalker(button, NodeFilter.SHOW_TEXT);
57
+ const parts = []; let node;
58
+ while ((node = walker.nextNode())) {
59
+ if (!node.parentElement?.closest('.flood-icon,svg,.flood-visually-hidden,[aria-hidden="true"]')) parts.push(node.textContent);
60
+ }
61
+ const text = parts.join('').trim();
62
+ if (/^[++×✕✖↗→←✓✔☰](?:\s|$)|(?:\s)[++×✕✖↗→←✓✔☰]$/u.test(text)) {
63
+ addIssue({id:'icons.text-glyph', target:target(button), message:'Possible action icon encoded as font text; review whether IconSlot with explicit artwork is required.'});
64
+ }
65
+ for (const svg of button.querySelectorAll('svg')) {
66
+ if (!svg.closest('.flood-icon,.flood-loading,.flood-spinner')) addIssue({id:'icons.unslotted-svg',target:target(button),message:'Action SVG has no Flood IconSlot owner; inspect sizing and accessible decoration.'});
67
+ }
68
+ }
69
+ for (const row of select('.flood-table tbody tr')) {
70
+ const controls = [...row.querySelectorAll('input:not([type="hidden"]),textarea,select')].filter(visible);
71
+ const actions = [...row.querySelectorAll('button,a.flood-link--button')].filter(visible);
72
+ if (!controls.length && !actions.length) continue;
73
+ const centres = controls.map(control=>{const r=control.getBoundingClientRect();return r.y+r.height/2;});
74
+ if (centres.length && Math.max(...centres)-Math.min(...centres)>3) continue;
75
+ const rowBox=row.getBoundingClientRect();
76
+ const expected = centres.length ? centres.reduce((a,b)=>a+b,0)/centres.length : rowBox.y+rowBox.height/2;
77
+ for (const cell of row.children) {
78
+ if(!cell.matches('td,th') || !visible(cell)) continue;
79
+ // Editable rows compare their passive content to input centres. Record/action
80
+ // rows compare every complete content block, including multiline identities.
81
+ if(centres.length && cell.querySelector('input,textarea,select')) continue;
82
+ const boxes=[];
83
+ const walker=document.createTreeWalker(cell,NodeFilter.SHOW_TEXT);let textNode;
84
+ while((textNode=walker.nextNode())) {
85
+ if(!textNode.textContent?.trim() || !textNode.parentElement || !visible(textNode.parentElement)
86
+ || textNode.parentElement.closest('.flood-visually-hidden,[aria-hidden="true"],script,style')) continue;
87
+ const range=document.createRange();range.selectNodeContents(textNode);
88
+ boxes.push(...[...range.getClientRects()].filter(rect=>rect.width>0&&rect.height>0));
89
+ }
90
+ for(const control of [...cell.querySelectorAll('button,a.flood-link--button')].filter(visible)) boxes.push(control.getBoundingClientRect());
91
+ if(!boxes.length) continue;
92
+ const actual=(Math.min(...boxes.map(rect=>rect.top))+Math.max(...boxes.map(rect=>rect.bottom)))/2;
93
+ if(Math.abs(actual-expected)>3) addIssue({id:'table.mixed-row-centre',target:target(cell),offset:Math.round((actual-expected)*100)/100,message:centres.length
94
+ ? 'Cell content is offset from neighbouring input centres. Review resolved geometry.'
95
+ : 'Cell content is offset from the centre of a record/action row. Compare multiline identity, values and actions; use deliberate top alignment only when justified.'});
96
+ }
97
+ }
98
+ const actionNames=select('button.flood-button,a.flood-link--button').map(button=>(button.textContent||'').trim().replace(/\s+/g,' ').toLowerCase()).filter(name=>name.length>=4);
99
+ let wordsChecked=0;
100
+ const segmenters=new Map();
101
+ const canInspectWords=typeof Intl.Segmenter==='function';
102
+ if(canInspectWords)for(const cell of select('.flood-table th,.flood-table td')){
103
+ if(cell.matches('[data-wrap="anywhere"]'))continue;
104
+ const locale=cell.closest('[lang]')?.getAttribute('lang')||document.documentElement.lang||undefined;
105
+ if(!segmenters.has(locale)){
106
+ try{segmenters.set(locale,new Intl.Segmenter(locale,{granularity:'word'}));}
107
+ catch{segmenters.set(locale,new Intl.Segmenter(undefined,{granularity:'word'}));}
108
+ }
109
+ const walker=document.createTreeWalker(cell,NodeFilter.SHOW_TEXT);let node;
110
+ while((node=walker.nextNode())){
111
+ if(!node.parentElement||!visible(node.parentElement)||node.parentElement.closest('code,pre,.flood-inline-code,.flood-visually-hidden,[aria-hidden="true"]'))continue;
112
+ for(const word of segmenters.get(locale).segment(node.textContent||'')){
113
+ if(!word.isWordLike||word.segment.length<4)continue;
114
+ if(wordsChecked>=limit){truncated=true;break;}wordsChecked++;
115
+ if(word.segment.length>200)continue;
116
+ const range=document.createRange();range.setStart(node,word.index);range.setEnd(node,word.index+word.segment.length);
117
+ const boxes=[...range.getClientRects()].filter(rect=>rect.width>0&&rect.height>0);
118
+ if(boxes.length>1&&Math.max(...boxes.map(rect=>rect.y))-Math.min(...boxes.map(rect=>rect.y))>2){
119
+ addIssue({id:'table.word-fragment',target:target(cell),word:word.segment,message:'A word is split across rendered lines in a table cell. Review intrinsic column width and wrapping; deliberate technical or localized line breaking can be an exception.'});
120
+ }
121
+ }
122
+ if(wordsChecked>=limit)break;
123
+ }
124
+ if(wordsChecked>=limit){truncated=true;break;}
125
+ }
126
+ const grids=select('form [class]').filter(element=>{
127
+ if(element.closest('table') || getComputedStyle(element).display!=='grid')return false;
128
+ return [...element.children].filter(visible).length>=2 && element.querySelectorAll('input,select,textarea,[role="combobox"]').length>=2;
129
+ }).map(element=>{
130
+ const style=getComputedStyle(element),columns=style.gridTemplateColumns.match(/[\d.]+px/g)?.map(parseFloat)??[];
131
+ return {element,columns,gap:parseFloat(style.columnGap)||0,rect:element.getBoundingClientRect()};
132
+ }).filter(grid=>grid.columns.length>=2);
133
+ const contentTracks=grid=>{
134
+ const owners='.flood-field,.flood-date-picker,.flood-date-field,.flood-time-field,.flood-date-range-field,.flood-date-time-picker';
135
+ const nodes=[...grid.element.querySelectorAll(owners)].filter(element=>visible(element)&&!element.closest('table')&&!element.parentElement?.closest(owners));
136
+ if(nodes.length>limit)truncated=true;
137
+ const tracks=[];
138
+ for(const element of nodes.slice(0,limit)){
139
+ const rect=element.getBoundingClientRect();
140
+ const track=tracks.find(item=>Math.abs(item.x-rect.x)<=1);
141
+ if(track){if(Math.abs(track.width-rect.width)>1)return null;}
142
+ else tracks.push({x:rect.x,width:rect.width});
143
+ }
144
+ if(tracks.length!==grid.columns.length)return null;
145
+ tracks.sort((a,b)=>a.x-b.x);
146
+ return {tracks,left:tracks[0].x,right:Math.max(...tracks.map(track=>track.x+track.width))};
147
+ };
148
+ for(const grid of grids)grid.content=contentTracks(grid);
149
+ for(let i=0;i<grids.length;i++)for(let j=i+1;j<grids.length;j++){
150
+ const a=grids[i],b=grids[j];
151
+ if(a.element.closest('form')!==b.element.closest('form'))continue;
152
+ if(a.element.contains(b.element)||b.element.contains(a.element))continue;
153
+ if(!(a.rect.bottom<=b.rect.top+1||b.rect.bottom<=a.rect.top+1))continue;
154
+ if(a.content&&b.content&&a.content.tracks.length===b.content.tracks.length&&Math.abs(a.content.left-b.content.left)<=1&&Math.abs(a.content.right-b.content.right)<=1){
155
+ const offset=Math.max(...a.content.tracks.map((track,index)=>Math.max(Math.abs(track.x-b.content.tracks[index].x),Math.abs(track.width-b.content.tracks[index].width))));
156
+ if(offset>1)addIssue({id:'forms.content-track-drift',target:target(b.element),related:target(a.element),offset:Math.round(offset*100)/100,message:'Stacked form groups share content edges but their actual field starts or widths differ. Review nested surface padding and shared track ownership; matching Grid props or outer boxes is insufficient.'});
157
+ }
158
+ if(Math.abs(a.rect.x-b.rect.x)>1||Math.abs(a.rect.width-b.rect.width)>1)continue;
159
+ const starts=grid=>grid.columns.map((_,index)=>grid.columns.slice(0,index).reduce((sum,width)=>sum+width,0)+index*grid.gap);
160
+ const left=starts(a),right=starts(b);
161
+ const drift=a.columns.length===b.columns.length?Math.max(...left.map((x,index)=>Math.abs(x-right[index]))):null;
162
+ if(drift===null||drift>1)addIssue({id:'forms.track-drift',target:target(b.element),related:target(a.element),...(drift===null?{}:{offset:Math.round(drift*100)/100}),message:'Stacked form grids share an outer frame but use different tracks. Review common gutters/group hierarchy or document why these contexts need different columns.'});
163
+ }
164
+ for(const element of select('h1,h2,h3,h4,h5,h6,.flood-type--helper,.flood-type--compact')) {
165
+ if(label(element)) {
166
+ copy.push(target(element));
167
+ if(!element.closest('button,a,label') && !element.matches('h1,h2,h3,h4,h5,h6')) {
168
+ if(!element.closest('.flood-field,.flood-fieldset,.flood-table,.flood-metric,.flood-status-indicator,.flood-file-meta,.flood-description-list,nav,article,dialog,[role="document"],[role="status"],[role="alert"],[role="alertdialog"],[role="dialog"]')) {
169
+ addIssue({id:'copy.unowned-support',target:target(element),message:'Supporting text sits outside a field, operation state, navigation or structured document. Identify the exact current decision it serves; remove implementation narration. Do not change typography or add a semantic wrapper merely to silence this observation.'});
170
+ }
171
+ const text=(element.textContent||'').trim().replace(/\s+/g,' ').toLowerCase();
172
+ if(actionNames.some(name=>text.includes(name))) addIssue({id:'copy.action-echo',target:target(element),message:'Supporting copy repeats a currently visible action label. Review whether it adds a necessary constraint or only narrates the control.'});
173
+ }
174
+ }
175
+ }
176
+ for(const element of select('.flood-field,fieldset,.flood-section')) {
177
+ const rect=element.getBoundingClientRect();
178
+ fields.push({...target(element),x:rect.x,y:rect.y,width:rect.width,height:rect.height});
179
+ }
180
+ return {schemaVersion:1, issues, copy, fields, truncated, notChecked:[
181
+ 'Editorial necessity and accuracy require contextual review; copy inventory is not an editorial verdict.',
182
+ 'Form grouping and column hierarchy require review of task relationships and visible geometry.',
183
+ 'Only the currently rendered document state is inspected; portals outside a selected root, other pages, iframes and shadow roots are not traversed.',
184
+ 'These observations do not certify accessibility, functional correctness or overall visual quality.',
185
+ ...(!canInspectWords?['Word fragmentation was not inspected because Intl.Segmenter is unavailable.']:[])
186
+ ]};
187
+ }
package/manifest.json CHANGED
@@ -2,6 +2,11 @@
2
2
  "schemaVersion": 1,
3
3
  "entry": "SKILL.md",
4
4
  "files": [
5
+ {
6
+ "source": "dist/catalog.json",
7
+ "target": "flood/catalog.json",
8
+ "kind": "catalog"
9
+ },
5
10
  {
6
11
  "source": "DESIGN.md",
7
12
  "target": "flood/DESIGN.md",
@@ -106,9 +111,44 @@
106
111
  "source": "references/flood-verification/SKILL.md",
107
112
  "target": ".agents/skills/flood-ui/references/flood-verification/SKILL.md",
108
113
  "kind": "skill"
114
+ },
115
+ {
116
+ "source": "references/knowledge.md",
117
+ "target": ".agents/skills/flood-ui/references/knowledge.md",
118
+ "kind": "reference"
119
+ },
120
+ {
121
+ "source": "references/flood-interface-content/SKILL.md",
122
+ "target": ".agents/skills/flood-ui/references/flood-interface-content/SKILL.md",
123
+ "kind": "skill"
124
+ },
125
+ {
126
+ "source": "references/interface-content-examples.md",
127
+ "target": ".agents/skills/flood-ui/references/interface-content-examples.md",
128
+ "kind": "reference"
129
+ },
130
+ {
131
+ "source": "references/rendered-review.md",
132
+ "target": ".agents/skills/flood-ui/references/rendered-review.md",
133
+ "kind": "reference"
134
+ },
135
+ {
136
+ "source": "references/flood-hierarchy/SKILL.md",
137
+ "target": ".agents/skills/flood-ui/references/flood-hierarchy/SKILL.md",
138
+ "kind": "skill"
139
+ },
140
+ {
141
+ "source": "references/flood-records/SKILL.md",
142
+ "target": ".agents/skills/flood-ui/references/flood-records/SKILL.md",
143
+ "kind": "skill"
144
+ },
145
+ {
146
+ "source": "references/record-decisions.md",
147
+ "target": ".agents/skills/flood-ui/references/record-decisions.md",
148
+ "kind": "reference"
109
149
  }
110
150
  ],
111
- "version": "0.2.9",
151
+ "version": "0.2.10",
112
152
  "installedEntry": ".agents/skills/flood-ui/SKILL.md",
113
153
  "pathBase": "source is package-relative; target is relative to the host project root"
114
154
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flood-md/agent",
3
- "version": "0.2.9",
3
+ "version": "0.2.10",
4
4
  "type": "module",
5
5
  "description": "Versioned Flood design instructions for coding agents.",
6
6
  "license": "MIT",
@@ -13,12 +13,18 @@
13
13
  "DESIGN.md",
14
14
  "SKILL.md",
15
15
  "references",
16
+ "lib",
17
+ "dist",
18
+ "knowledge.json",
16
19
  "workflows.json",
17
20
  "rules.json",
18
21
  "manifest.json",
19
22
  "README.md"
20
23
  ],
21
24
  "exports": {
25
+ "./inspect": {"types":"./lib/inspect.d.ts","import":"./lib/inspect.mjs"},
26
+ ".": {"types":"./lib/index.d.ts","import":"./lib/index.mjs"},
27
+ "./dist/*": "./dist/*",
22
28
  "./DESIGN.md": "./DESIGN.md",
23
29
  "./SKILL.md": "./SKILL.md",
24
30
  "./manifest.json": "./manifest.json",
@@ -10,6 +10,11 @@ components. Preserve their anatomy and behavior when improving a composition.
10
10
  Read the governing project instructions, matching declarations and relevant
11
11
  component guide first. This skill grants no additional permissions.
12
12
 
13
+ When introducing visible interface text, apply the
14
+ [interface content skill](../flood-interface-content/SKILL.md) before filling
15
+ optional headings, descriptions or helper slots. Existing user-authored content
16
+ is not permission to rewrite it during an unrelated layout correction.
17
+
13
18
  ## Choose the smallest existing owner
14
19
 
15
20
  Before changing JSX, identify the visible roles and their existing owners:
@@ -18,6 +23,31 @@ metadata, IconButton for an icon action, Dialog for modal behavior, Text/Heading
18
23
  for typography, and the appropriate form, list or table primitive for semantics.
19
24
  Check the actual installed exports and props; do not invent APIs from newer docs.
20
25
 
26
+ Search the installed catalog by the role before settling on a low-level building
27
+ block. Read the selected contract and compare a more specific owner when one
28
+ exists: Metric for a labelled statistic; DatePicker for ordinary date selection
29
+ with typing and a calendar, DateField for an explicitly entry-only interaction;
30
+ StatusIndicator for a named state. Input type="date"
31
+ invokes the browser's picker, not Flood's Calendar. Use it only for an explicit
32
+ native-picker requirement or a documented limitation of the installed controls.
33
+ Heading/Text remain valid for ordinary prose, not a default replacement for an
34
+ available statistic component.
35
+
36
+ Include layout and secondary interactions in that selection: Grid for repeated
37
+ form tracks, Stack/Inline for related layout, AlertDialog for a discard/delete
38
+ confirmation, and Alert/StatusIndicator for the appropriate feedback role.
39
+ window.confirm/alert/prompt are browser UI, not Flood components. A small scope
40
+ does not justify replacing an available owner with native UI or a parallel CSS
41
+ grid. Keep native form, table and semantic document/shell elements where they
42
+ supply structure; styling the host shell is different from reimplementing a
43
+ provided layout or control. Document a genuine missing capability before using
44
+ a project-owned adapter.
45
+
46
+ Record the selected owner and any rejected match before implementing the surface.
47
+ If discovery finds no suitable component, state the missing capability; do not
48
+ claim that the library has no owner without checking. Keep this decision record
49
+ in development notes, never as extra explanatory text in the product.
50
+
21
51
  - Reuse the matching component with its supported props and tokens.
22
52
  - If spacing or alignment is wrong, inspect both the primitive and the parent
23
53
  composition. Fix the owner of the relationship. Keep the primitive intact.
@@ -9,6 +9,15 @@ Use the installed component contracts and
9
9
  [composition workflow](../flood-composition/SKILL.md). Start with the content's
10
10
  relationships, not a wrapper around every piece of text.
11
11
 
12
+ Keep visual/test fixtures separate from the host's working records unless sample
13
+ data is explicitly part of the task. Do not seed a new user's persisted list with
14
+ fictional business records just to fill a screen. Use isolated test state for
15
+ populated screenshots and exercise the real empty state. When samples are required, identify their provenance in the test brief/README.
16
+ A visible distinction is needed where it changes interpretation or action, such
17
+ as sample records mixed with real records. Do not automatically add a permanent
18
+ Demo badge to the shell of an explicitly requested isolated prototype; follow
19
+ the interface-content workflow for contextual placement.
20
+
12
21
  - Table/DataTable preserve caption, headers, cell relationships and documented
13
22
  sorting/selection behavior. A footer represents a summary and should be
14
23
  visually distinguishable without relying on a heavy border everywhere.
@@ -23,6 +32,13 @@ relationships, not a wrapper around every piece of text.
23
32
  - Preserve native image alt/fallback semantics and aspect constraints. Decorative
24
33
  artwork should not duplicate an adjacent accessible identity.
25
34
 
35
+ ## Choose the information, not all available slots
36
+
37
+ For record lists/details, apply the [records skill](../flood-records/SKILL.md)
38
+ before filling metadata and actions. Identity, supporting context, lifecycle state,
39
+ priority and timestamps have different purposes. Use Table/DataTable when users
40
+ need several named comparisons; a trailing Inline of values is not a column model.
41
+
26
42
  ## Exercise data and geometry
27
43
 
28
44
  Test no records, one record, long/wrapping names, absent optional media/actions,
@@ -31,6 +47,22 @@ without inserting a second empty-state design or moving unrelated controls.
31
47
  Align file title/metadata and status/progress by their intended row relationship;
32
48
  do not hide a StatusIndicator dot or replace Progress with a custom track.
33
49
 
50
+ For an editable table, compare the centre of plain amounts and row actions with
51
+ the neighbouring input controls. Table centres record/action and editable rows
52
+ by default; opt into cellVerticalAlign="top" only for deliberate prose alignment.
53
+ Use the shared cellVerticalAlign contract rather than
54
+ moving text with transforms or overriding Input, NumberInput or Button internals.
55
+ Native style on an individual host-owned td/th can express an exceptional cell.
56
+ Do not depend on a low-specificity descendant CSS override. Check the rendered
57
+ centres; equal cell padding alone does not establish alignment.
58
+
59
+ Also inspect readable column widths with long neighbouring records. A centred
60
+ cell can still be wrong when a status or date is fragmented into single letters.
61
+ Table and StatusIndicator preserve word-based intrinsic width by default; keep
62
+ that recipe rather than applying global overflow-wrap:anywhere. Deliberate
63
+ technical identifier cells can opt into data-wrap="anywhere". Otherwise use
64
+ local scrolling or appropriate column budgets, not an empty no-overflow claim.
65
+
34
66
  Use the documented local overflow owner, with a meaningful accessible name. Focus
35
67
  the last action/cell and check that a rail or rounded corner does not cover it.
36
68
  Compare narrow/wide, enlarged text and RTL. Locale-sensitive text in prerendered
@@ -10,6 +10,11 @@ visible role before choosing color: StatusIndicator names a state, Badge marks
10
10
  metadata, Progress describes task progress, Meter describes a bounded measurement,
11
11
  Alert gives contextual feedback and EmptyState explains missing content.
12
12
 
13
+ For several record attributes, use the [records skill](../flood-records/SKILL.md).
14
+ A category called Error is not a failed operation; priority is not lifecycle state.
15
+ Choose the semantic owner before its appearance, and keep essential triage data
16
+ visible even when reducing decorative emphasis.
17
+
13
18
  ## Keep one owner for each message
14
19
 
15
20
  - Status text must distinguish pending, success, failure, cancellation and local
@@ -24,13 +29,29 @@ Alert gives contextual feedback and EmptyState explains missing content.
24
29
  - Spinner retains a meaningful accessible label where standalone. Skeleton is
25
30
  decoration, not another announced task. Preserve the existing reduced-motion
26
31
  behavior without introducing a new animation system.
27
- - EmptyState needs a reason and an appropriate next action when available. Its
28
- icon/illustration and action remain existing primitives with consistent geometry.
32
+ - EmptyState identifies the actual condition. A title can be enough; add a
33
+ description only for new decision-relevant information, not an instruction
34
+ repeating a visible action. Its icon/action use existing primitives.
35
+
36
+ ## Scope feedback to its operation
37
+
38
+ Associate a message with the operation and record that produced it. Preserve
39
+ entered data after refusal and keep a useful error while that operation remains
40
+ unresolved. A successful retry clears/replaces its failure. Explicitly discarding
41
+ the draft ends that draft's save error; it must not follow the user to a list or
42
+ another record. Ignore late results belonging to an abandoned operation.
43
+
44
+ Do not solve this with an automatic timer inside Alert or by clearing every
45
+ message on navigation. A collection-wide read failure can still block editing
46
+ after changing views and must remain until resolved. The host's state model owns
47
+ that distinction; the feedback primitive only presents the supplied state.
29
48
 
30
49
  ## Evidence
31
50
 
32
51
  Trigger the operation and observe each applicable transition, including failure
33
- and retry. Check optional description/action/icon slots, long messages and text
52
+ and retry. Also follow failure → explicit discard → list → reopen a saved record;
53
+ the abandoned draft's failure must be gone and the saved data unchanged. Distinguish
54
+ that from a still-unresolved collection-wide error. Check optional description/action/icon slots, long messages and text
34
55
  enlargement. Keep content visible without stealing focus merely to announce it.
35
56
  Reserve feedback space in a demo when needed for a stable sample, not universally
36
57
  inside every production component. Report audible announcement behavior only