@litfamily/lithermes 1.0.8 → 1.0.9

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 (652) hide show
  1. package/README.md +299 -13
  2. package/README_Ko-KR.md +300 -14
  3. package/assets/lithermes-plugin/README.md +17 -6
  4. package/assets/lithermes-plugin/__init__.py +96 -21
  5. package/assets/lithermes-plugin/core.py +15 -6
  6. package/assets/lithermes-plugin/core_commands.py +140 -28
  7. package/assets/lithermes-plugin/core_contexts.py +88 -15
  8. package/assets/lithermes-plugin/core_contract.py +118 -9
  9. package/assets/lithermes-plugin/core_routing.py +197 -8
  10. package/assets/lithermes-plugin/deliverable_hedge_guard.py +378 -357
  11. package/assets/lithermes-plugin/diagnostics.py +53 -2
  12. package/assets/lithermes-plugin/humanizer_detector.py +348 -0
  13. package/assets/lithermes-plugin/humanizer_ko_metrics.py +108 -0
  14. package/assets/lithermes-plugin/humanizer_office.py +18 -0
  15. package/assets/lithermes-plugin/lit_mark.py +1 -1
  16. package/assets/lithermes-plugin/motion_receipts.py +80 -0
  17. package/assets/lithermes-plugin/payload-version.json +2240 -64
  18. package/assets/lithermes-plugin/plugin.yaml +1 -1
  19. package/assets/lithermes-plugin/rules/bundled-rules/lit-humanizer.md +14 -0
  20. package/assets/lithermes-plugin/skills/autoconference/assets/report_template.md +2 -1
  21. package/assets/lithermes-plugin/skills/autoconference/modes/ship/SKILL.md +11 -8
  22. package/assets/lithermes-plugin/skills/autoresearch/modes/ship/type-checklists.md +2 -2
  23. package/assets/lithermes-plugin/skills/browser-drive/ORIGIN.json +17 -0
  24. package/assets/lithermes-plugin/skills/browser-drive/SKILL.md +41 -16
  25. package/assets/lithermes-plugin/skills/browser-drive/scripts/capability_probe.py +122 -10
  26. package/assets/lithermes-plugin/skills/frontend-ui-ux/SKILL.md +45 -13
  27. package/assets/lithermes-plugin/skills/frontend-ui-ux/examples/phase-3-readme-ab/README.md +51 -0
  28. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/README.md +44 -0
  29. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/adaptive-layout.md +34 -62
  30. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/brand-and-imagery.md +22 -70
  31. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/complete-contract.md +18 -4
  32. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/composition.md +27 -78
  33. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/craft-floor.md +74 -0
  34. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/creative-directions.md +25 -69
  35. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/evidence-review.md +18 -97
  36. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/implementation-platforms.md +23 -83
  37. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/inclusive-interface.md +22 -70
  38. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/interaction-motion.md +34 -68
  39. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/motion-guide.md +62 -0
  40. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/operating-lanes.md +14 -78
  41. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/performance-delivery.md +22 -71
  42. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/product-direction.md +17 -70
  43. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/production.md +19 -7
  44. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/redesign-playbook.md +23 -76
  45. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/slop-register.md +69 -0
  46. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/system-foundations.md +22 -67
  47. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/visual-language.md +16 -72
  48. package/assets/lithermes-plugin/skills/frontend-ui-ux/references/visual-reconstruction.md +16 -81
  49. package/assets/lithermes-plugin/skills/frontend-ui-ux/resources/THIRD-PARTY-NOTICE.txt +15 -0
  50. package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/probe-page.js +311 -0
  51. package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/probe-rules.json +46 -0
  52. package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/probe.mjs +231 -0
  53. package/assets/lithermes-plugin/skills/lit-comprehend/SKILL.md +36 -25
  54. package/assets/lithermes-plugin/skills/lit-comprehend/references/artifact-template.md +45 -87
  55. package/assets/lithermes-plugin/skills/lit-comprehend/scripts/verify-explainer.py +3 -27
  56. package/assets/lithermes-plugin/skills/lit-diagram-drawer/NOTICE +33 -0
  57. package/assets/lithermes-plugin/skills/lit-diagram-drawer/SKILL.md +308 -0
  58. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-architecture-dark.html +1 -0
  59. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-architecture-full.html +1 -0
  60. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-architecture-light.html +1 -0
  61. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-bar-dark.html +1 -0
  62. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-bar-full.html +1 -0
  63. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-bar-light.html +1 -0
  64. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-beeswarm-dark.html +1 -0
  65. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-beeswarm-full.html +1 -0
  66. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-beeswarm-light.html +1 -0
  67. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-bubble-dark.html +1 -0
  68. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-bubble-full.html +1 -0
  69. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-bubble-light.html +1 -0
  70. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-bump-dark.html +1 -0
  71. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-bump-full.html +1 -0
  72. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-bump-light.html +1 -0
  73. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-data-flow-dark.html +1 -0
  74. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-data-flow-full.html +1 -0
  75. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-data-flow-light.html +1 -0
  76. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-datalake-dark.html +1 -0
  77. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-datalake-full.html +1 -0
  78. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-datalake-light.html +1 -0
  79. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-db-schema-dark.html +1 -0
  80. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-db-schema-full.html +1 -0
  81. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-db-schema-light.html +1 -0
  82. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-dependency-dark.html +1 -0
  83. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-dependency-full.html +1 -0
  84. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-dependency-light.html +1 -0
  85. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-deployment-dark.html +1 -0
  86. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-deployment-full.html +1 -0
  87. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-deployment-light.html +1 -0
  88. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-dp-integration-dark.html +1 -0
  89. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-dp-integration-full.html +1 -0
  90. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-dp-integration-light.html +1 -0
  91. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-dp-security-matrix-dark.html +1 -0
  92. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-dp-security-matrix-full.html +1 -0
  93. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-dp-security-matrix-light.html +1 -0
  94. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-er-dark.html +1 -0
  95. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-er-full.html +1 -0
  96. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-er-light.html +1 -0
  97. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-fishbone-dark.html +1 -0
  98. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-fishbone-full.html +1 -0
  99. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-fishbone-light.html +1 -0
  100. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-flowchart-dark.html +1 -0
  101. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-flowchart-full.html +1 -0
  102. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-flowchart-light.html +1 -0
  103. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-gantt-dark.html +1 -0
  104. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-gantt-full.html +1 -0
  105. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-gantt-light.html +1 -0
  106. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-heatmap-dark.html +1 -0
  107. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-heatmap-full.html +1 -0
  108. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-heatmap-light.html +1 -0
  109. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-high-level-dark.html +1 -0
  110. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-high-level-full.html +1 -0
  111. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-high-level-light.html +1 -0
  112. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-high-level-vertical-dark.html +1 -0
  113. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-high-level-vertical-full.html +1 -0
  114. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-high-level-vertical-light.html +1 -0
  115. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-import-drawio-dark.html +1 -0
  116. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-import-drawio-full.html +1 -0
  117. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-import-drawio-light.html +1 -0
  118. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-import-excalidraw-dark.html +1 -0
  119. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-import-excalidraw-full.html +1 -0
  120. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-import-excalidraw-light.html +1 -0
  121. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-import-mermaid-dark.html +1 -0
  122. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-import-mermaid-full.html +1 -0
  123. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-import-mermaid-light.html +1 -0
  124. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-it-state-dark.html +1 -0
  125. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-it-state-full.html +1 -0
  126. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-it-state-light.html +1 -0
  127. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-journey-dark.html +1 -0
  128. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-journey-full.html +1 -0
  129. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-journey-light.html +1 -0
  130. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-kanban-dark.html +1 -0
  131. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-kanban-full.html +1 -0
  132. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-kanban-light.html +1 -0
  133. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-layers-dark.html +1 -0
  134. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-layers-full.html +1 -0
  135. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-layers-light.html +1 -0
  136. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-line-dark.html +1 -0
  137. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-line-full.html +1 -0
  138. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-line-light.html +1 -0
  139. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-loop-dark.html +1 -0
  140. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-loop-full.html +1 -0
  141. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-loop-light.html +1 -0
  142. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-loop-terminal-dark.html +1 -0
  143. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-loop-terminal-full.html +1 -0
  144. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-loop-terminal-light.html +1 -0
  145. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-marimekko-dark.html +1 -0
  146. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-marimekko-full.html +1 -0
  147. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-marimekko-light.html +1 -0
  148. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-medallion-dark.html +1 -0
  149. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-medallion-full.html +1 -0
  150. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-medallion-light.html +1 -0
  151. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-nested-dark.html +1 -0
  152. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-nested-full.html +1 -0
  153. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-nested-light.html +1 -0
  154. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-org-chart-dark.html +1 -0
  155. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-org-chart-full.html +1 -0
  156. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-org-chart-light.html +1 -0
  157. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-paved-road-animated-dark.html +1 -0
  158. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-paved-road-animated-full.html +1 -0
  159. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-paved-road-animated-light.html +1 -0
  160. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-polar-dark.html +1 -0
  161. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-polar-full.html +1 -0
  162. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-polar-light.html +1 -0
  163. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-policy-trace-animated-dark.html +1 -0
  164. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-policy-trace-animated-full.html +1 -0
  165. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-policy-trace-animated-light.html +1 -0
  166. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-process-dark.html +1 -0
  167. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-process-full.html +1 -0
  168. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-process-light.html +1 -0
  169. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-pyramid-dark.html +1 -0
  170. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-pyramid-full.html +1 -0
  171. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-pyramid-light.html +1 -0
  172. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-quadrant-consultant-dark.html +1 -0
  173. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-quadrant-consultant-full.html +1 -0
  174. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-quadrant-consultant-light.html +1 -0
  175. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-quadrant-dark.html +1 -0
  176. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-quadrant-full.html +1 -0
  177. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-quadrant-light.html +1 -0
  178. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-queue-animated-dark.html +1 -0
  179. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-queue-animated-full.html +1 -0
  180. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-queue-animated-light.html +1 -0
  181. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-radar-dark.html +1 -0
  182. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-radar-full.html +1 -0
  183. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-radar-light.html +1 -0
  184. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-ridgeline-dark.html +1 -0
  185. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-ridgeline-full.html +1 -0
  186. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-ridgeline-light.html +1 -0
  187. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-sankey-dark.html +1 -0
  188. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-sankey-full.html +1 -0
  189. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-sankey-light.html +1 -0
  190. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-scatter-dark.html +1 -0
  191. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-scatter-full.html +1 -0
  192. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-scatter-light.html +1 -0
  193. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-sequence-dark.html +1 -0
  194. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-sequence-full.html +1 -0
  195. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-sequence-light.html +1 -0
  196. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-sequence-oauth-dark.html +1 -0
  197. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-sequence-oauth-full.html +1 -0
  198. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-sequence-oauth-light.html +1 -0
  199. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-slopegraph-dark.html +1 -0
  200. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-slopegraph-full.html +1 -0
  201. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-slopegraph-light.html +1 -0
  202. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-state-dark.html +1 -0
  203. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-state-full.html +1 -0
  204. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-state-lifecycle-dark.html +1 -0
  205. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-state-lifecycle-full.html +1 -0
  206. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-state-lifecycle-light.html +1 -0
  207. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-state-light.html +1 -0
  208. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-story-map-dark.html +1 -0
  209. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-story-map-full.html +1 -0
  210. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-story-map-light.html +1 -0
  211. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-streamgraph-dark.html +1 -0
  212. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-streamgraph-full.html +1 -0
  213. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-streamgraph-light.html +1 -0
  214. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-swimlane-dark.html +1 -0
  215. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-swimlane-full.html +1 -0
  216. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-swimlane-light.html +1 -0
  217. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-timeline-dark.html +1 -0
  218. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-timeline-full.html +1 -0
  219. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-timeline-light.html +1 -0
  220. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-tree-block-decomposition-dark.html +1 -0
  221. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-tree-block-decomposition-full.html +1 -0
  222. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-tree-block-decomposition-light.html +1 -0
  223. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-tree-dark.html +1 -0
  224. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-tree-full.html +1 -0
  225. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-tree-light.html +1 -0
  226. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-treemap-dark.html +1 -0
  227. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-treemap-full.html +1 -0
  228. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-treemap-light.html +1 -0
  229. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-uml-class-dark.html +1 -0
  230. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-uml-class-full.html +1 -0
  231. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-uml-class-light.html +1 -0
  232. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-venn-dark.html +1 -0
  233. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-venn-full.html +1 -0
  234. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-venn-light.html +1 -0
  235. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-wardley-dark.html +1 -0
  236. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-wardley-full.html +1 -0
  237. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-wardley-light.html +1 -0
  238. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-waterfall-dark.html +1 -0
  239. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-waterfall-full.html +1 -0
  240. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-waterfall-light.html +1 -0
  241. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/fonts/OFL.txt +94 -0
  242. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/fonts/PretendardVariable.woff2 +0 -0
  243. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/fonts/provenance.json +22 -0
  244. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/icons.html +231 -0
  245. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/index.html +1 -0
  246. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/licenses/devicon-MIT.txt +20 -0
  247. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/licenses/diagram-design-MIT.txt +21 -0
  248. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/licenses/log-z-MIT.txt +21 -0
  249. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/licenses/simple-icons-CC0.md +30 -0
  250. package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/licenses/tabler-MIT.txt +21 -0
  251. package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/01-service-architecture/after.html +1 -0
  252. package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/01-service-architecture/brief.md +33 -0
  253. package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/02-release-decision/after.html +1 -0
  254. package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/02-release-decision/brief.md +18 -0
  255. package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/03-data-retention/after.html +1 -0
  256. package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/03-data-retention/brief.md +19 -0
  257. package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/04-oauth-sequence/after.html +1 -0
  258. package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/04-oauth-sequence/brief.md +17 -0
  259. package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/05-review-lanes/after.html +1 -0
  260. package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/05-review-lanes/brief.md +17 -0
  261. package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/06-rollout-gates/after.html +1 -0
  262. package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/06-rollout-gates/brief.md +17 -0
  263. package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/07-deployment-boundary/after.html +1 -0
  264. package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/07-deployment-boundary/brief.md +19 -0
  265. package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/08-policy-traces/after.html +1 -0
  266. package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/08-policy-traces/brief.md +20 -0
  267. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/accessibility.md +37 -0
  268. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/export.md +29 -0
  269. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/import-drawio.md +21 -0
  270. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/import-excalidraw.md +14 -0
  271. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/import-mermaid.md +18 -0
  272. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/import-schema.md +28 -0
  273. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/korean-typography.md +34 -0
  274. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/motion.md +29 -0
  275. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/office-pptx-docx.md +40 -0
  276. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/output-spec.md +48 -0
  277. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/primitive-annotation.md +12 -0
  278. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/primitive-icons.md +21 -0
  279. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/primitive-sketchy.md +11 -0
  280. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/primitive-terminal.md +11 -0
  281. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/profiles.md +24 -0
  282. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/semantic-patterns.md +27 -0
  283. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/style-guide.md +67 -0
  284. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-architecture.md +51 -0
  285. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-bar.md +51 -0
  286. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-beeswarm.md +49 -0
  287. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-bubble.md +49 -0
  288. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-bump.md +49 -0
  289. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-catalog.json +1433 -0
  290. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-data-flow.md +51 -0
  291. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-datalake.md +49 -0
  292. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-db-schema.md +49 -0
  293. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-dependency.md +49 -0
  294. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-deployment.md +49 -0
  295. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-dp-integration.md +49 -0
  296. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-dp-security-matrix.md +49 -0
  297. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-er.md +49 -0
  298. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-fishbone.md +49 -0
  299. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-flowchart.md +49 -0
  300. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-gantt.md +49 -0
  301. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-heatmap.md +53 -0
  302. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-high-level-vertical.md +53 -0
  303. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-high-level.md +53 -0
  304. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-import-drawio.md +53 -0
  305. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-import-excalidraw.md +53 -0
  306. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-import-mermaid.md +53 -0
  307. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-it-state.md +54 -0
  308. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-journey.md +54 -0
  309. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-kanban.md +54 -0
  310. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-layers.md +54 -0
  311. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-line.md +54 -0
  312. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-loop-terminal.md +53 -0
  313. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-loop.md +54 -0
  314. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-marimekko.md +54 -0
  315. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-medallion.md +54 -0
  316. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-nested.md +59 -0
  317. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-org-chart.md +58 -0
  318. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-paved-road-animated.md +72 -0
  319. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-polar.md +67 -0
  320. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-policy-trace-animated.md +69 -0
  321. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-process.md +58 -0
  322. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-pyramid.md +59 -0
  323. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-quadrant-consultant.md +60 -0
  324. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-quadrant.md +57 -0
  325. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-queue-animated.md +63 -0
  326. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-radar.md +57 -0
  327. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-ridgeline.md +63 -0
  328. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-sankey.md +68 -0
  329. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-scatter.md +60 -0
  330. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-sequence-oauth.md +66 -0
  331. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-sequence.md +63 -0
  332. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-slopegraph.md +65 -0
  333. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-state-lifecycle.md +53 -0
  334. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-state.md +59 -0
  335. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-story-map.md +52 -0
  336. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-streamgraph.md +53 -0
  337. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-swimlane.md +52 -0
  338. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-timeline.md +52 -0
  339. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-tree-block-decomposition.md +57 -0
  340. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-tree.md +54 -0
  341. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-treemap.md +54 -0
  342. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-uml-class.md +52 -0
  343. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-venn.md +51 -0
  344. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-wardley.md +52 -0
  345. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-waterfall.md +54 -0
  346. package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/verifier-guide.md +31 -0
  347. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/block-registry.mjs +43 -0
  348. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/browser-version.mjs +12 -0
  349. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/check-visible-text.py +12 -0
  350. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_brief.py +192 -0
  351. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_catalog.py +135 -0
  352. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_checks.py +123 -0
  353. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_cli.py +94 -0
  354. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_geometry.py +130 -0
  355. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_metrics.py +166 -0
  356. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_quality.py +293 -0
  357. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_quality_geometry.py +200 -0
  358. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_records.py +205 -0
  359. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_sequence.py +55 -0
  360. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/doctor.mjs +24 -0
  361. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/drawio_extract.py +209 -0
  362. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/drawio_source.py +188 -0
  363. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/excalidraw_extract.py +233 -0
  364. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/excalidraw_support.py +64 -0
  365. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/export.mjs +125 -0
  366. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/mermaid_extract.py +240 -0
  367. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/mermaid_source.py +39 -0
  368. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/office-safety.mjs +54 -0
  369. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/verify-all.py +12 -0
  370. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/verify-brief.py +12 -0
  371. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/verify-diagram.py +12 -0
  372. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/verify-motion.py +12 -0
  373. package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/verify-type.py +12 -0
  374. package/assets/lithermes-plugin/skills/lit-docx/NOTICE +1 -0
  375. package/assets/lithermes-plugin/skills/lit-docx/SKILL.md +97 -0
  376. package/assets/lithermes-plugin/skills/lit-docx/references/frontmatter_schema.md +42 -0
  377. package/assets/lithermes-plugin/skills/lit-docx/references/journal_style_spec.md +17 -0
  378. package/assets/lithermes-plugin/skills/lit-docx/references/markdown_quality_checklist.md +37 -0
  379. package/assets/lithermes-plugin/skills/lit-docx/references/slop_phrase_list.yaml +58 -0
  380. package/assets/lithermes-plugin/skills/lit-docx/references/slop_rules.md +35 -0
  381. package/assets/lithermes-plugin/skills/lit-docx/scripts/clean_markdown.py +95 -0
  382. package/assets/lithermes-plugin/skills/lit-docx/scripts/convert_md_to_docx.py +1041 -0
  383. package/assets/lithermes-plugin/skills/lit-docx/scripts/convert_md_to_pdf.py +281 -0
  384. package/assets/lithermes-plugin/skills/lit-docx/scripts/convert_pdf.py +130 -0
  385. package/assets/lithermes-plugin/skills/lit-docx/scripts/edit_docx.py +297 -0
  386. package/assets/lithermes-plugin/skills/lit-docx/scripts/embed_images.py +78 -0
  387. package/assets/lithermes-plugin/skills/lit-docx/scripts/generate_docx_templates.py +491 -0
  388. package/assets/lithermes-plugin/skills/lit-docx/scripts/slop_lint.py +536 -0
  389. package/assets/lithermes-plugin/skills/lit-docx/scripts/visual_audit.py +41 -0
  390. package/assets/lithermes-plugin/skills/lit-docx/templates/docx/acs.docx +0 -0
  391. package/assets/lithermes-plugin/skills/lit-docx/templates/docx/elsevier.docx +0 -0
  392. package/assets/lithermes-plugin/skills/lit-docx/templates/docx/ieee.docx +0 -0
  393. package/assets/lithermes-plugin/skills/lit-docx/templates/docx/korean-generic.docx +0 -0
  394. package/assets/lithermes-plugin/skills/lit-docx/templates/docx/nature.docx +0 -0
  395. package/assets/lithermes-plugin/skills/lit-docx/templates/latex/acs/README.md +5 -0
  396. package/assets/lithermes-plugin/skills/lit-docx/templates/latex/acs/template.tex +37 -0
  397. package/assets/lithermes-plugin/skills/lit-docx/templates/latex/elsevier/README.md +5 -0
  398. package/assets/lithermes-plugin/skills/lit-docx/templates/latex/elsevier/template.tex +38 -0
  399. package/assets/lithermes-plugin/skills/lit-docx/templates/latex/ieee/README.md +5 -0
  400. package/assets/lithermes-plugin/skills/lit-docx/templates/latex/ieee/template.tex +37 -0
  401. package/assets/lithermes-plugin/skills/lit-docx/templates/latex/korean-generic/README.md +5 -0
  402. package/assets/lithermes-plugin/skills/lit-docx/templates/latex/korean-generic/template.tex +37 -0
  403. package/assets/lithermes-plugin/skills/lit-docx/templates/latex/nature/README.md +5 -0
  404. package/assets/lithermes-plugin/skills/lit-docx/templates/latex/nature/template.tex +37 -0
  405. package/assets/lithermes-plugin/skills/lit-docx/templates/registry.yaml +1366 -0
  406. package/assets/lithermes-plugin/skills/lit-humanizer/NOTICE +170 -0
  407. package/assets/lithermes-plugin/skills/lit-humanizer/SKILL.md +125 -0
  408. package/assets/lithermes-plugin/skills/lit-humanizer/assets/report-skeleton.md +26 -0
  409. package/assets/lithermes-plugin/skills/lit-humanizer/assets/slide-text-skeleton.md +30 -0
  410. package/assets/lithermes-plugin/skills/lit-humanizer/examples/accessibility-note/after.md +7 -0
  411. package/assets/lithermes-plugin/skills/lit-humanizer/examples/accessibility-note/before.md +7 -0
  412. package/assets/lithermes-plugin/skills/lit-humanizer/examples/api-reference/after.md +9 -0
  413. package/assets/lithermes-plugin/skills/lit-humanizer/examples/api-reference/before.md +15 -0
  414. package/assets/lithermes-plugin/skills/lit-humanizer/examples/changelog/after.md +5 -0
  415. package/assets/lithermes-plugin/skills/lit-humanizer/examples/changelog/before.md +12 -0
  416. package/assets/lithermes-plugin/skills/lit-humanizer/examples/code-comments/after.md +10 -0
  417. package/assets/lithermes-plugin/skills/lit-humanizer/examples/code-comments/before.md +19 -0
  418. package/assets/lithermes-plugin/skills/lit-humanizer/examples/commit/after.md +5 -0
  419. package/assets/lithermes-plugin/skills/lit-humanizer/examples/commit/before.md +7 -0
  420. package/assets/lithermes-plugin/skills/lit-humanizer/examples/data-release/after.md +7 -0
  421. package/assets/lithermes-plugin/skills/lit-humanizer/examples/data-release/before.md +9 -0
  422. package/assets/lithermes-plugin/skills/lit-humanizer/examples/detector-validation/ko-metrics-golden.json +49 -0
  423. package/assets/lithermes-plugin/skills/lit-humanizer/examples/detector-validation/pos-real.txt +26 -0
  424. package/assets/lithermes-plugin/skills/lit-humanizer/examples/detector-validation/rule-cases.json +306 -0
  425. package/assets/lithermes-plugin/skills/lit-humanizer/examples/english-essay/after.md +5 -0
  426. package/assets/lithermes-plugin/skills/lit-humanizer/examples/english-essay/before.md +5 -0
  427. package/assets/lithermes-plugin/skills/lit-humanizer/examples/incident-update/after.md +7 -0
  428. package/assets/lithermes-plugin/skills/lit-humanizer/examples/incident-update/before.md +7 -0
  429. package/assets/lithermes-plugin/skills/lit-humanizer/examples/ko-essay/after.md +7 -0
  430. package/assets/lithermes-plugin/skills/lit-humanizer/examples/ko-essay/before.md +9 -0
  431. package/assets/lithermes-plugin/skills/lit-humanizer/examples/ko-report/after.md +9 -0
  432. package/assets/lithermes-plugin/skills/lit-humanizer/examples/ko-report/before.md +13 -0
  433. package/assets/lithermes-plugin/skills/lit-humanizer/examples/ko-slide/after.md +19 -0
  434. package/assets/lithermes-plugin/skills/lit-humanizer/examples/ko-slide/before.md +21 -0
  435. package/assets/lithermes-plugin/skills/lit-humanizer/examples/meeting-brief/after.md +9 -0
  436. package/assets/lithermes-plugin/skills/lit-humanizer/examples/meeting-brief/before.md +11 -0
  437. package/assets/lithermes-plugin/skills/lit-humanizer/examples/policy-faq/after.md +9 -0
  438. package/assets/lithermes-plugin/skills/lit-humanizer/examples/policy-faq/before.md +9 -0
  439. package/assets/lithermes-plugin/skills/lit-humanizer/examples/pr/after.md +15 -0
  440. package/assets/lithermes-plugin/skills/lit-humanizer/examples/pr/before.md +17 -0
  441. package/assets/lithermes-plugin/skills/lit-humanizer/examples/project-update/after.md +7 -0
  442. package/assets/lithermes-plugin/skills/lit-humanizer/examples/project-update/before.md +9 -0
  443. package/assets/lithermes-plugin/skills/lit-humanizer/examples/readme/after.md +12 -0
  444. package/assets/lithermes-plugin/skills/lit-humanizer/examples/readme/before.md +14 -0
  445. package/assets/lithermes-plugin/skills/lit-humanizer/examples/report/after.md +13 -0
  446. package/assets/lithermes-plugin/skills/lit-humanizer/examples/report/before.md +11 -0
  447. package/assets/lithermes-plugin/skills/lit-humanizer/examples/research-abstract/after.md +5 -0
  448. package/assets/lithermes-plugin/skills/lit-humanizer/examples/research-abstract/before.md +5 -0
  449. package/assets/lithermes-plugin/skills/lit-humanizer/examples/slides/after.md +19 -0
  450. package/assets/lithermes-plugin/skills/lit-humanizer/examples/slides/before.md +19 -0
  451. package/assets/lithermes-plugin/skills/lit-humanizer/examples/troubleshooting/after.md +11 -0
  452. package/assets/lithermes-plugin/skills/lit-humanizer/examples/troubleshooting/before.md +11 -0
  453. package/assets/lithermes-plugin/skills/lit-humanizer/references/README.md +16 -0
  454. package/assets/lithermes-plugin/skills/lit-humanizer/references/always-on-rule.md +9 -0
  455. package/assets/lithermes-plugin/skills/lit-humanizer/references/code-patterns.md +468 -0
  456. package/assets/lithermes-plugin/skills/lit-humanizer/references/deliverable-channels.md +111 -0
  457. package/assets/lithermes-plugin/skills/lit-humanizer/references/en-patterns-checklist.md +43 -0
  458. package/assets/lithermes-plugin/skills/lit-humanizer/references/en-patterns-content.md +243 -0
  459. package/assets/lithermes-plugin/skills/lit-humanizer/references/en-patterns-structure.md +375 -0
  460. package/assets/lithermes-plugin/skills/lit-humanizer/references/en-patterns.md +57 -0
  461. package/assets/lithermes-plugin/skills/lit-humanizer/references/ko-metrics.md +33 -0
  462. package/assets/lithermes-plugin/skills/lit-humanizer/references/ko-patterns-a-d.md +659 -0
  463. package/assets/lithermes-plugin/skills/lit-humanizer/references/ko-patterns-e-j.md +387 -0
  464. package/assets/lithermes-plugin/skills/lit-humanizer/references/ko-patterns.md +31 -0
  465. package/assets/lithermes-plugin/skills/lit-humanizer/references/rewrite-playbook.md +54 -0
  466. package/assets/lithermes-plugin/skills/lit-humanizer/references/taxonomy.md +153 -0
  467. package/assets/lithermes-plugin/skills/lit-humanizer/rules.json +884 -0
  468. package/assets/lithermes-plugin/skills/lit-humanizer/scripts/detect.py +17 -0
  469. package/assets/lithermes-plugin/skills/lit-humanizer/scripts/extract_office_text.py +114 -0
  470. package/assets/lithermes-plugin/skills/lit-humanizer/scripts/ko_metrics.py +35 -0
  471. package/assets/lithermes-plugin/skills/lit-pptx/NOTICE +1 -0
  472. package/assets/lithermes-plugin/skills/lit-pptx/SKILL.md +109 -0
  473. package/assets/lithermes-plugin/skills/lit-pptx/agents/analyst.md +49 -0
  474. package/assets/lithermes-plugin/skills/lit-pptx/agents/antithesis.md +65 -0
  475. package/assets/lithermes-plugin/skills/lit-pptx/agents/architect.md +44 -0
  476. package/assets/lithermes-plugin/skills/lit-pptx/agents/critic.md +42 -0
  477. package/assets/lithermes-plugin/skills/lit-pptx/agents/synthesizer.md +47 -0
  478. package/assets/lithermes-plugin/skills/lit-pptx/agents/thesis.md +33 -0
  479. package/assets/lithermes-plugin/skills/lit-pptx/assets/azure/circle-lg.png +0 -0
  480. package/assets/lithermes-plugin/skills/lit-pptx/assets/azure/circle-md.png +0 -0
  481. package/assets/lithermes-plugin/skills/lit-pptx/assets/azure/circle-sm.png +0 -0
  482. package/assets/lithermes-plugin/skills/lit-pptx/bin/office.mjs +77 -0
  483. package/assets/lithermes-plugin/skills/lit-pptx/fonts/PRETENDARD-OFL.txt +94 -0
  484. package/assets/lithermes-plugin/skills/lit-pptx/fonts/a2z-font/A2Z-Black.otf +0 -0
  485. package/assets/lithermes-plugin/skills/lit-pptx/fonts/a2z-font/A2Z-Bold.otf +0 -0
  486. package/assets/lithermes-plugin/skills/lit-pptx/fonts/a2z-font/A2Z-ExtraBold.otf +0 -0
  487. package/assets/lithermes-plugin/skills/lit-pptx/fonts/a2z-font/A2Z-ExtraLight.otf +0 -0
  488. package/assets/lithermes-plugin/skills/lit-pptx/fonts/a2z-font/A2Z-Light.otf +0 -0
  489. package/assets/lithermes-plugin/skills/lit-pptx/fonts/a2z-font/A2Z-Medium.otf +0 -0
  490. package/assets/lithermes-plugin/skills/lit-pptx/fonts/a2z-font/A2Z-Regular.otf +0 -0
  491. package/assets/lithermes-plugin/skills/lit-pptx/fonts/a2z-font/A2Z-SemiBold.otf +0 -0
  492. package/assets/lithermes-plugin/skills/lit-pptx/fonts/a2z-font/A2Z-Thin.otf +0 -0
  493. package/assets/lithermes-plugin/skills/lit-pptx/fonts/a2z-font/OFL.txt +94 -0
  494. package/assets/lithermes-plugin/skills/lit-pptx/fonts/a2z-font/SOURCE.md +29 -0
  495. package/assets/lithermes-plugin/skills/lit-pptx/pretendard-font/public/static/PretendardGOV-Bold.otf +0 -0
  496. package/assets/lithermes-plugin/skills/lit-pptx/pretendard-font/public/static/PretendardGOV-Regular.otf +0 -0
  497. package/assets/lithermes-plugin/skills/lit-pptx/references/anti-slop-checklist.md +71 -0
  498. package/assets/lithermes-plugin/skills/lit-pptx/references/authoring-guide.md +139 -0
  499. package/assets/lithermes-plugin/skills/lit-pptx/references/consensus-protocol.md +125 -0
  500. package/assets/lithermes-plugin/skills/lit-pptx/references/data-display-cookbook.md +45 -0
  501. package/assets/lithermes-plugin/skills/lit-pptx/references/design-system-azure.md +58 -0
  502. package/assets/lithermes-plugin/skills/lit-pptx/runtime/package-lock.json +750 -0
  503. package/assets/lithermes-plugin/skills/lit-pptx/runtime/package.json +15 -0
  504. package/assets/lithermes-plugin/skills/lit-pptx/runtime/requirements.in +7 -0
  505. package/assets/lithermes-plugin/skills/lit-pptx/runtime/requirements.lock +391 -0
  506. package/assets/lithermes-plugin/skills/lit-pptx/scripts/compile-deck.js +447 -0
  507. package/assets/lithermes-plugin/skills/lit-pptx/scripts/craft_extras.py +215 -0
  508. package/assets/lithermes-plugin/skills/lit-pptx/scripts/embed_fonts.py +199 -0
  509. package/assets/lithermes-plugin/skills/lit-pptx/scripts/integrity.py +60 -0
  510. package/assets/lithermes-plugin/skills/lit-pptx/scripts/inventory.py +64 -0
  511. package/assets/lithermes-plugin/skills/lit-pptx/scripts/learn_template.py +433 -0
  512. package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/derive-palette.js +176 -0
  513. package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/directive-parser.js +244 -0
  514. package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/font-map.js +79 -0
  515. package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/gen-bg.js +85 -0
  516. package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/gen-gradient-assets.js +36 -0
  517. package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/layout-resolver.js +736 -0
  518. package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/markdown-loader.js +136 -0
  519. package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/render-adapter-html.js +357 -0
  520. package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/render-adapter-pptx.js +1864 -0
  521. package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/slide-ast.js +539 -0
  522. package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/spec-validator.js +94 -0
  523. package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/template-registry.js +355 -0
  524. package/assets/lithermes-plugin/skills/lit-pptx/scripts/qa_deck.py +791 -0
  525. package/assets/lithermes-plugin/skills/lit-pptx/scripts/validate_pptx.py +206 -0
  526. package/assets/lithermes-plugin/skills/lit-pptx/slide-formats/markdown-slide-spec-v1.md +251 -0
  527. package/assets/lithermes-plugin/skills/lit-pptx/slide-formats/markdown-slide-spec-v2.md +221 -0
  528. package/assets/lithermes-plugin/skills/lit-pptx/slide-formats/slide-ast-v1.schema.json +229 -0
  529. package/assets/lithermes-plugin/skills/lit-pptx/slide-formats/slide-ast-v2.schema.json +144 -0
  530. package/assets/lithermes-plugin/skills/lit-pptx/slide-formats/template-enrollment-contract.md +228 -0
  531. package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/AZURE-A2Z/capabilities.yaml +146 -0
  532. package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/AZURE-A2Z/layout-mapping.yaml +384 -0
  533. package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/AZURE-A2Z/template.yaml +37 -0
  534. package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/AZURE-PRO/capabilities.yaml +154 -0
  535. package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/AZURE-PRO/layout-mapping.yaml +405 -0
  536. package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/AZURE-PRO/template.yaml +37 -0
  537. package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/BOILERPLATE-A2Z/capabilities.yaml +179 -0
  538. package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/BOILERPLATE-A2Z/layout-mapping.yaml +299 -0
  539. package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/BOILERPLATE-A2Z/template.yaml +45 -0
  540. package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/BOILERPLATE-PRETENDARD/capabilities.yaml +181 -0
  541. package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/BOILERPLATE-PRETENDARD/layout-mapping.yaml +299 -0
  542. package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/BOILERPLATE-PRETENDARD/template.yaml +44 -0
  543. package/assets/lithermes-plugin/skills/lit-scientific-visualization/SKILL.md +2 -0
  544. package/assets/lithermes-plugin/skills/lit-typographic-motion/SKILL.md +71 -0
  545. package/assets/lithermes-plugin/skills/lit-typographic-motion/bin/audio.py +45 -0
  546. package/assets/lithermes-plugin/skills/lit-typographic-motion/bin/motion.mjs +690 -0
  547. package/assets/lithermes-plugin/skills/lit-typographic-motion/bin/runtime.mjs +299 -0
  548. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/NOTICE +23 -0
  549. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/THIRD_PARTY_NOTICES +55 -0
  550. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/browser.mjs +169 -0
  551. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/completion.mjs +52 -0
  552. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/constants.mjs +162 -0
  553. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/director.mjs +33 -0
  554. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/encode.mjs +191 -0
  555. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/flash.mjs +163 -0
  556. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/frame.mjs +428 -0
  557. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/gate.mjs +483 -0
  558. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/look.mjs +223 -0
  559. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/page.js +622 -0
  560. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/presets.mjs +170 -0
  561. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/scenes.mjs +242 -0
  562. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/sound.mjs +382 -0
  563. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stage-gate.mjs +97 -0
  564. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stage-init.js +186 -0
  565. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stage-kit.js +389 -0
  566. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stage-qa.mjs +358 -0
  567. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stage-worker.mjs +33 -0
  568. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stage.mjs +691 -0
  569. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stills.mjs +71 -0
  570. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stroke.mjs +231 -0
  571. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/timeline.mjs +177 -0
  572. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/treatment.mjs +244 -0
  573. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/type.mjs +183 -0
  574. package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/util.mjs +152 -0
  575. package/assets/lithermes-plugin/skills/lit-typographic-motion/references/craft-loop.md +175 -0
  576. package/assets/lithermes-plugin/skills/lit-typographic-motion/references/engine-contract.md +166 -0
  577. package/assets/lithermes-plugin/skills/lit-typographic-motion/references/runtime.md +122 -0
  578. package/assets/lithermes-plugin/skills/lit-typographic-motion/references/stage.md +261 -0
  579. package/assets/lithermes-plugin/skills/lit-typographic-motion/references/style-bibles.md +122 -0
  580. package/assets/lithermes-plugin/skills/lit-typographic-motion/references/treatment.md +164 -0
  581. package/assets/lithermes-plugin/skills/lit-typographic-motion/references/type-craft.md +92 -0
  582. package/assets/lithermes-plugin/skills/lit-typographic-motion/runtime/fonts.json +154 -0
  583. package/assets/lithermes-plugin/skills/lit-typographic-motion/runtime/licenses/CREDITS +20 -0
  584. package/assets/lithermes-plugin/skills/lit-typographic-motion/runtime/licenses/EMS-OFL.txt +91 -0
  585. package/assets/lithermes-plugin/skills/lit-typographic-motion/runtime/package-lock.json +88 -0
  586. package/assets/lithermes-plugin/skills/lit-typographic-motion/runtime/package.json +12 -0
  587. package/assets/lithermes-plugin/skills/lit-typographic-motion/runtime/requirements.in +1 -0
  588. package/assets/lithermes-plugin/skills/lit-typographic-motion/runtime/requirements.lock +727 -0
  589. package/assets/lithermes-plugin/skills/lit-typographic-motion/runtime/word-timing.json +9 -0
  590. package/assets/lithermes-plugin/skills/litresearch/SKILL.md +19 -18
  591. package/assets/lithermes-plugin/skills/litwork/SKILL.md +28 -0
  592. package/assets/lithermes-plugin/skills/visual-qa/references/complete-contract.md +3 -3
  593. package/package.json +1 -1
  594. package/readme-assets/ab-simple/s11-ui-baseline-desktop.webp +0 -0
  595. package/readme-assets/ab-simple/s11-ui-lithermes-desktop.webp +0 -0
  596. package/readme-assets/ab-simple/s3-ui-baseline-desktop.webp +0 -0
  597. package/readme-assets/ab-simple/s3-ui-lithermes-desktop.webp +0 -0
  598. package/readme-assets/ab-simple/s4-ui-baseline-desktop.webp +0 -0
  599. package/readme-assets/ab-simple/s4-ui-lithermes-desktop.webp +0 -0
  600. package/readme-assets/ab-simple/s5-office-baseline-slides.webp +0 -0
  601. package/readme-assets/ab-simple/s5-office-lithermes-slides.webp +0 -0
  602. package/readme-assets/ab-simple/s7-lithermes-diagram.webp +0 -0
  603. package/readme-assets/ab-simple/s8-office-baseline-slides.webp +0 -0
  604. package/readme-assets/ab-simple/s8-office-lithermes-slides.webp +0 -0
  605. package/readme-assets/ab-simple/s9-office-lithermes-pages.webp +0 -0
  606. package/readme-assets/badge-version.svg +1 -1
  607. package/readme-assets/cover-motion-still.webp +0 -0
  608. package/readme-assets/cover-motion.webp +0 -0
  609. package/readme-assets/skills/autoconference.webp +0 -0
  610. package/readme-assets/skills/autoresearch.webp +0 -0
  611. package/readme-assets/skills/browser-drive.webp +0 -0
  612. package/readme-assets/skills/comment-checker.webp +0 -0
  613. package/readme-assets/skills/debugging.webp +0 -0
  614. package/readme-assets/skills/deep-interview.webp +0 -0
  615. package/readme-assets/skills/frontend-ui-ux.webp +0 -0
  616. package/readme-assets/skills/lit-burnoff-file.webp +0 -0
  617. package/readme-assets/skills/lit-burnoff.webp +0 -0
  618. package/readme-assets/skills/lit-code.webp +0 -0
  619. package/readme-assets/skills/lit-commit.webp +0 -0
  620. package/readme-assets/skills/lit-comprehend.webp +0 -0
  621. package/readme-assets/skills/lit-crucible.webp +0 -0
  622. package/readme-assets/skills/lit-diagram-drawer.webp +0 -0
  623. package/readme-assets/skills/lit-docx.webp +0 -0
  624. package/readme-assets/skills/lit-handoff.webp +0 -0
  625. package/readme-assets/skills/lit-humanizer.webp +0 -0
  626. package/readme-assets/skills/lit-init.webp +0 -0
  627. package/readme-assets/skills/lit-plan.webp +0 -0
  628. package/readme-assets/skills/lit-pptx.webp +0 -0
  629. package/readme-assets/skills/lit-recap.webp +0 -0
  630. package/readme-assets/skills/lit-scientific-visualization.webp +0 -0
  631. package/readme-assets/skills/lit-typographic-motion.webp +0 -0
  632. package/readme-assets/skills/litgoal.webp +0 -0
  633. package/readme-assets/skills/litresearch.webp +0 -0
  634. package/readme-assets/skills/litwork.webp +0 -0
  635. package/readme-assets/skills/lsp-setup.webp +0 -0
  636. package/readme-assets/skills/lsp.webp +0 -0
  637. package/readme-assets/skills/readme-studio.webp +0 -0
  638. package/readme-assets/skills/refactor.webp +0 -0
  639. package/readme-assets/skills/review-work.webp +0 -0
  640. package/readme-assets/skills/rules.webp +0 -0
  641. package/readme-assets/skills/start-work.webp +0 -0
  642. package/readme-assets/skills/structural-search.webp +0 -0
  643. package/readme-assets/skills/visual-qa.webp +0 -0
  644. package/readme-assets/skills/wikify.webp +0 -0
  645. package/src/cli.js +40 -1
  646. package/src/lib/check.js +21 -0
  647. package/src/lib/install.js +262 -7
  648. package/src/lib/skillPayload.js +3 -3
  649. package/assets/lithermes-plugin/skills/lit-korean/SKILL.md +0 -237
  650. package/assets/lithermes-plugin/skills/lit-korean/references/quick-rules.md +0 -24
  651. package/assets/lithermes-plugin/skills/lit-korean/references/safety-checklist.md +0 -23
  652. package/readme-assets/cover.webp +0 -0
@@ -0,0 +1,130 @@
1
+ """Standard-library parsing and geometry assembly for inline SVG diagrams."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from math import isfinite
7
+ from types import MappingProxyType
8
+ from typing import assert_never
9
+
10
+ from diagram_records import (
11
+ Boundary, Marker, ParsedSvg, Rect, Route, Shape, TextBox,
12
+ _PATH_TOKEN_RE, _clean, _number, _path_segments, attributes, glyph_width,
13
+ )
14
+
15
+ _SVG_RE = re.compile(r"<svg\b([^>]*)>([\s\S]*?)</svg\s*>", re.IGNORECASE)
16
+ _TAG_RE = re.compile(r"<(rect|circle|line|path|text|polygon)\b([^>]*)>([\s\S]*?)</\1\s*>|<(rect|circle|line|path|polygon)\b([^>]*)/?>", re.IGNORECASE)
17
+
18
+ def _parse_shape(kind: str, raw_attrs: str, body: str = "") -> Shape | TextBox | None:
19
+ attrs = attributes(raw_attrs)
20
+ if kind.lower() == "text":
21
+ label = _clean(body)
22
+ if not label:
23
+ return None
24
+ size = _number(attrs.get("font-size"), 16)
25
+ width = glyph_width(label, size)
26
+ anchor = attrs.get("text-anchor", "start")
27
+ x = _number(attrs.get("x")) - (width / 2 if anchor == "middle" else width if anchor == "end" else 0)
28
+ y = _number(attrs.get("y")) - size * 0.82
29
+ return TextBox(
30
+ label=label,
31
+ bounds=Rect(x, y, width, size * 1.22),
32
+ anchor=(_number(attrs.get("x")), _number(attrs.get("y"))),
33
+ font_size=size,
34
+ fill=attrs.get("fill", "#000000"),
35
+ role=attrs.get("data-role", ""),
36
+ edge_for=attrs.get("data-edge-for", ""),
37
+ boundary_for=attrs.get("data-boundary-for", attrs.get("data-group-for", "")),
38
+ )
39
+ kind = kind.lower()
40
+ if kind == "rect":
41
+ rect = Rect(_number(attrs.get("x")), _number(attrs.get("y")), _number(attrs.get("width")), _number(attrs.get("height")))
42
+ elif kind == "circle":
43
+ radius = _number(attrs.get("r"))
44
+ center_x, center_y = _number(attrs.get("cx")), _number(attrs.get("cy"))
45
+ rect = Rect(center_x - radius, center_y - radius, radius * 2, radius * 2)
46
+ else:
47
+ return None
48
+ if rect.w <= 2 or rect.h <= 2:
49
+ return None
50
+ return Shape(kind, rect, attrs.get("fill", ""), attrs)
51
+
52
+
53
+ def _marker_points(body: str) -> tuple[tuple[float, float], ...]:
54
+ points: list[tuple[float, float]] = []
55
+ for match in re.finditer(r"<(path|polygon)\b([^>]*)>", body, re.IGNORECASE):
56
+ attrs = attributes(match.group(2))
57
+ if match.group(1).lower() == "polygon":
58
+ values = [float(value) for value in _PATH_TOKEN_RE.findall(attrs.get("points", "")) if not value.isalpha()]
59
+ points.extend((values[i], values[i + 1]) for i in range(0, len(values) - 1, 2))
60
+ else:
61
+ for segment in _path_segments(attrs.get("d", "")):
62
+ if not points:
63
+ points.append((segment[0], segment[1]))
64
+ points.append((segment[2], segment[3]))
65
+ return tuple(points)
66
+
67
+
68
+ def parse_svg(source: str) -> ParsedSvg:
69
+ """Parse SVG tags and geometric metadata without invoking a renderer."""
70
+ matches = tuple(_SVG_RE.finditer(source))
71
+ svg = "\n".join(match.group(0) for match in matches)
72
+ first_attrs = attributes(matches[0].group(1)) if matches else MappingProxyType({})
73
+ try:
74
+ view_values = [float(value) for value in re.split(r"[\s,]+", first_attrs.get("viewBox", "").strip()) if value]
75
+ except ValueError:
76
+ view_values = []
77
+ view_box = Rect(*view_values) if len(view_values) == 4 else None
78
+ shapes: list[Shape] = []
79
+ texts: list[TextBox] = []
80
+ routes: list[Route] = []
81
+ line_segments: list[Segment] = []
82
+ boundaries: list[Boundary] = []
83
+ for match in _TAG_RE.finditer(svg):
84
+ kind = match.group(1) or match.group(4)
85
+ raw_attrs = match.group(2) if match.group(1) else match.group(5) or ""
86
+ body = match.group(3) or ""
87
+ if kind is None:
88
+ continue
89
+ attrs = attributes(raw_attrs)
90
+ parsed = _parse_shape(kind, raw_attrs, body)
91
+ match parsed:
92
+ case TextBox():
93
+ texts.append(parsed)
94
+ case Shape():
95
+ shapes.append(parsed)
96
+ case None:
97
+ pass
98
+ case unreachable:
99
+ assert_never(unreachable)
100
+ if kind.lower() == "line":
101
+ points = tuple(_number(attrs.get(key), float("nan")) for key in ("x1", "y1", "x2", "y2"))
102
+ if all(isfinite(value) for value in points):
103
+ segment = (points[0], points[1], points[2], points[3])
104
+ if "data-lifeline-for" not in attrs:
105
+ line_segments.append(segment)
106
+ if attrs.get("data-from") and attrs.get("data-to"):
107
+ routes.append(Route(attrs["data-from"], attrs["data-to"], attrs.get("data-label", ""), (segment,), attrs.get("marker-start", ""), attrs.get("marker-end", ""), _number(attrs.get("stroke-width"), 1)))
108
+ if kind.lower() == "rect" and "data-from" in attrs and "data-to" in attrs:
109
+ continue
110
+ if kind.lower() == "path" and attrs.get("data-from") and attrs.get("data-to"):
111
+ segments = _path_segments(attrs.get("d", ""), curve_steps=16)
112
+ if segments:
113
+ routes.append(Route(attrs["data-from"], attrs["data-to"], attrs.get("data-label", ""), segments, attrs.get("marker-start", ""), attrs.get("marker-end", ""), _number(attrs.get("stroke-width"), 1)))
114
+ for shape in shapes:
115
+ attrs = shape.attributes
116
+ marker_keys = ("data-trust-boundary", "data-boundary", "data-boundary-name", "data-group-boundary", "data-group-name", "data-group-label", "data-group-id")
117
+ class_marker = re.search(r"\b(?:boundary|group)(?:[-_][\w-]+)?\b", attrs.get("class", ""), re.IGNORECASE)
118
+ if shape.kind != "rect" or not any(key in attrs for key in marker_keys) and not class_marker:
119
+ continue
120
+ labels = tuple(attrs[key] for key in ("data-trust-boundary", "data-boundary-name", "data-group-boundary", "data-group-name", "data-group-label") if attrs.get(key) and attrs[key] != "true")
121
+ name = attrs.get("data-group-id", attrs.get("id", labels[0] if labels else "outline"))
122
+ boundaries.append(Boundary(name, labels, shape.rect, _number(attrs.get("stroke-width"), 1)))
123
+ markers: dict[str, Marker] = {}
124
+ for match in re.finditer(r"<marker\b([^>]*)>([\s\S]*?)</marker\s*>", svg, re.IGNORECASE):
125
+ attrs = attributes(match.group(1))
126
+ name = attrs.get("id", "")
127
+ if name:
128
+ body = match.group(2)
129
+ markers[name] = Marker(name, _number(attrs.get("refX")), _number(attrs.get("refY")), attrs.get("markerUnits", "strokeWidth"), _marker_points(body), bool(re.search(r"\bz\b|<polygon\b", body, re.IGNORECASE)))
130
+ return ParsedSvg(source, svg, first_attrs, view_box, tuple(shapes), tuple(texts), tuple(routes), tuple(line_segments), tuple(boundaries), MappingProxyType(markers))
@@ -0,0 +1,166 @@
1
+ """Dependency-free SVG text, clipping, contrast, and accessibility metrics."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from html import unescape
6
+ import math
7
+ from pathlib import Path
8
+ import re
9
+ from typing import TypedDict
10
+
11
+ from diagram_geometry import ParsedSvg, Rect, parse_svg
12
+
13
+
14
+ class ContrastFailure(TypedDict):
15
+ text: str
16
+ ratio: float
17
+
18
+
19
+ class DiagramMetrics(TypedDict):
20
+ source: str
21
+ hasSvg: bool
22
+ viewBox: Rect | None
23
+ title: bool
24
+ desc: bool
25
+ roleImage: bool
26
+ ariaLinked: bool
27
+ textCount: int
28
+ textOverlaps: int
29
+ objectOverlaps: int
30
+ offCanvas: int
31
+ offCanvasLabels: list[str]
32
+ arrowCrossings: int
33
+ contrastFailures: list[ContrastFailure]
34
+ colors: int
35
+ largeRadius: int
36
+ hasShadow: bool
37
+ fontFamilyPresent: bool
38
+ localFont: bool
39
+ fontFileValid: bool
40
+ visibleText: str
41
+ type: str
42
+ sourceBytes: int
43
+
44
+
45
+ def _intersects(first: Rect, second: Rect) -> bool:
46
+ return first.x < second.right - 1 and first.right > second.x + 1 and first.y < second.bottom - 1 and first.bottom > second.y + 1
47
+
48
+
49
+ def _contains(first: Rect, second: Rect) -> bool:
50
+ return first.x <= second.x + 1 and first.y <= second.y + 1 and first.right >= second.right - 1 and first.bottom >= second.bottom - 1
51
+
52
+
53
+ def _luminance(color: str) -> float | None:
54
+ if not re.fullmatch(r"#[0-9a-f]{3}(?:[0-9a-f]{3})?", color, re.IGNORECASE):
55
+ return None
56
+ digits = color[1:]
57
+ if len(digits) == 3:
58
+ digits = "".join(character * 2 for character in digits)
59
+ channels = [int(digits[index:index + 2], 16) / 255 for index in (0, 2, 4)]
60
+ linear = [channel / 12.92 if channel <= 0.04045 else ((channel + 0.055) / 1.055) ** 2.4 for channel in channels]
61
+ return 0.2126 * linear[0] + 0.7152 * linear[1] + 0.0722 * linear[2]
62
+
63
+
64
+ def contrast_ratio(foreground: str, background: str) -> float | None:
65
+ """Return WCAG contrast for literal hex colors; unresolved CSS stays unscored."""
66
+ first, second = _luminance(foreground), _luminance(background)
67
+ if first is None or second is None:
68
+ return None
69
+ return (max(first, second) + 0.05) / (min(first, second) + 0.05)
70
+
71
+
72
+ def _visible_text(source: str, parsed: ParsedSvg) -> str:
73
+ from_svg = "\n".join(re.sub(r"\s+", " ", unescape(re.sub(r"<[^>]*>", " ", match.group(1)))).strip() for match in re.finditer(r"<(?:text|title|desc)\b[^>]*>([\s\S]*?)</(?:text|title|desc)\s*>", parsed.svg, re.IGNORECASE))
74
+ document = re.sub(r"<head\b[^>]*>[\s\S]*?</head\s*>|<style\b[^>]*>[\s\S]*?</style\s*>|<script\b[^>]*>[\s\S]*?</script\s*>|<svg\b[^>]*>[\s\S]*?</svg\s*>", " ", source, flags=re.IGNORECASE)
75
+ html_text = re.sub(r"<[^>]*>", " ", unescape(document))
76
+ html_text = re.sub(r"\s+", " ", html_text).strip()
77
+ return "\n".join(value for value in (from_svg, html_text) if value)
78
+
79
+
80
+ def _line_crossings(parsed: ParsedSvg) -> int:
81
+ segments = list(dict.fromkeys([*parsed.line_segments, *(segment for route in parsed.routes for segment in route.segments)]))
82
+ crossings = 0
83
+ for index, first in enumerate(segments):
84
+ for second in segments[index + 1:]:
85
+ a = (first[0], first[1])
86
+ b = (first[2], first[3])
87
+ c = (second[0], second[1])
88
+ d = (second[2], second[3])
89
+ shared = any(math.hypot(point[0] - other[0], point[1] - other[1]) < 2 for point in (a, b) for other in (c, d))
90
+ if not shared and _orientation(a, b, c) * _orientation(a, b, d) < 0 and _orientation(c, d, a) * _orientation(c, d, b) < 0:
91
+ crossings += 1
92
+ return crossings
93
+
94
+
95
+ def _orientation(first: tuple[float, float], second: tuple[float, float], third: tuple[float, float]) -> float:
96
+ return (second[0] - first[0]) * (third[1] - first[1]) - (second[1] - first[1]) * (third[0] - first[0])
97
+
98
+
99
+ def source_metrics(source: str, source_path: str = "<stdin>") -> DiagramMetrics:
100
+ """Measure accessible text and geometric warning conditions in an SVG document."""
101
+ parsed = parse_svg(source)
102
+ view = parsed.view_box
103
+ geometry = [shape.rect for shape in parsed.shapes] + [text.bounds for text in parsed.texts]
104
+ off_canvas: list[str] = []
105
+ if view is not None:
106
+ for index, rect in enumerate(geometry):
107
+ if rect.x < view.x - 1 or rect.y < view.y - 1 or rect.right > view.right + 1 or rect.bottom > view.bottom + 1:
108
+ off_canvas.append(parsed.texts[index - len(parsed.shapes)].label if index >= len(parsed.shapes) else parsed.shapes[index].kind)
109
+ text_overlaps = sum(_intersects(first.bounds, second.bounds) for index, first in enumerate(parsed.texts) for second in parsed.texts[index + 1:])
110
+ visible_shapes = [shape for shape in parsed.shapes if not (view is not None and shape.rect.w >= view.w - 2 and shape.rect.h >= view.h - 2)]
111
+ venn = re.search(r"diagram-venn-(?:light|dark|full)", parsed.attributes.get("id", "")) is not None
112
+ object_overlaps = 0
113
+ for index, first in enumerate(visible_shapes):
114
+ for second in visible_shapes[index + 1:]:
115
+ venn_pair = venn and first.kind == "circle" and second.kind == "circle"
116
+ if not venn_pair and _intersects(first.rect, second.rect) and not _contains(first.rect, second.rect) and not _contains(second.rect, first.rect):
117
+ object_overlaps += 1
118
+ visible_rects = [shape for shape in parsed.shapes if shape.kind == "rect" and shape.fill.lower() not in {"none", "transparent"}]
119
+ paper = "#ffffff"
120
+ if visible_rects:
121
+ visible_rects.sort(key=lambda shape: (bool(view and shape.rect.x <= view.x + 1 and shape.rect.y <= view.y + 1 and shape.rect.w >= view.w - 2 and shape.rect.h >= view.h - 2), shape.rect.w * shape.rect.h), reverse=True)
122
+ paper = visible_rects[0].fill or paper
123
+ failures: list[ContrastFailure] = []
124
+ for label in parsed.texts:
125
+ ratio = contrast_ratio(label.fill, paper)
126
+ if ratio is not None and ratio < 4.5:
127
+ failures.append({"text": label.label, "ratio": round(ratio, 2)})
128
+ colors = {
129
+ match.group(1).lower()
130
+ for match in re.finditer(r"\b(?:fill|stroke)\s*=\s*['\"](#[0-9a-f]{3,8})['\"]", parsed.svg, re.IGNORECASE)
131
+ }
132
+ family_present = bool(re.search(r"Pretendard", parsed.svg, re.IGNORECASE) and re.search(r"Pretendard", source, re.IGNORECASE))
133
+ font_urls = [unescape(match.group(1).strip()) for match in re.finditer(r"@font-face\b[\s\S]{0,250}?url\(\s*['\"]?([^'\")]+)['\"]?\)", source, re.IGNORECASE)]
134
+ local_font = any(url.lower().startswith("data:font/") or not re.match(r"https?://", url, re.IGNORECASE) and re.search(r"\.(?:woff2?|ttf|otf)(?:[?#].*)?$", url, re.IGNORECASE) for url in font_urls)
135
+ font_file_valid = any(
136
+ url.lower().startswith("data:font/")
137
+ or not re.match(r"https?://", url, re.IGNORECASE)
138
+ and source_path != "<stdin>"
139
+ and (Path(source_path).resolve().parent / re.split(r"[?#]", url, maxsplit=1)[0]).is_file()
140
+ for url in font_urls
141
+ )
142
+ return {
143
+ "source": source_path,
144
+ "hasSvg": bool(parsed.svg),
145
+ "viewBox": view,
146
+ "title": bool(re.search(r"<title\b[^>]*>[\s\S]*?</title\s*>", parsed.svg, re.IGNORECASE)),
147
+ "desc": bool(re.search(r"<desc\b[^>]*>[\s\S]*?</desc\s*>", parsed.svg, re.IGNORECASE)),
148
+ "roleImage": bool(re.search(r"\brole\s*=\s*['\"]img['\"]", parsed.svg, re.IGNORECASE)),
149
+ "ariaLinked": bool(parsed.attributes.get("aria-labelledby")),
150
+ "textCount": len(parsed.texts),
151
+ "textOverlaps": text_overlaps,
152
+ "objectOverlaps": object_overlaps,
153
+ "offCanvas": len(off_canvas),
154
+ "offCanvasLabels": off_canvas[:12],
155
+ "arrowCrossings": _line_crossings(parsed),
156
+ "contrastFailures": failures,
157
+ "colors": len(colors),
158
+ "largeRadius": sum(float(shape.attributes.get("rx", "0") or 0) > 8 for shape in parsed.shapes if shape.kind == "rect"),
159
+ "hasShadow": bool(re.search(r"\bbox-shadow\s*:|filter\s*=\s*['\"]url\(#", source, re.IGNORECASE)),
160
+ "fontFamilyPresent": family_present,
161
+ "localFont": local_font,
162
+ "fontFileValid": font_file_valid,
163
+ "visibleText": _visible_text(source, parsed),
164
+ "type": parsed.attributes.get("id", ""),
165
+ "sourceBytes": len(source.encode("utf-8")),
166
+ }
@@ -0,0 +1,293 @@
1
+ """Accessibility, routing, spacing, boundary, and canvas checks for SVG."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ import re
7
+ import colorsys
8
+
9
+ from diagram_geometry import parse_svg
10
+ from diagram_metrics import DiagramMetrics, source_metrics
11
+ from diagram_quality_geometry import (
12
+ distance_to_rect_boundary, marker_length, marker_overlaps_rect, marker_scale,
13
+ marker_tip, rectangle_gap, route_bends, route_crosses_boundary,
14
+ route_crossings, route_distance, route_length, route_manhattan_distance,
15
+ routes_overlap_collinearly, segment_along_boundary, segment_hits_rect,
16
+ segment_hits_text, text_overlaps_node,
17
+ )
18
+ from diagram_records import Boundary, ParsedSvg, Route, Shape, TextBox
19
+ from diagram_sequence import sequence_issues
20
+
21
+ _CONTAMINATION = (
22
+ re.compile(r"\blitfamily\b", re.IGNORECASE),
23
+ re.compile(r"\b(?:generator|generated by|template|prototype)\b", re.IGNORECASE),
24
+ re.compile(r"\bsource\s*:", re.IGNORECASE),
25
+ re.compile(r"\bnotes?\s*:", re.IGNORECASE),
26
+ re.compile(r"\bstatic\s+first\b", re.IGNORECASE),
27
+ re.compile(r"\baccessible\s+labels\b", re.IGNORECASE),
28
+ re.compile(r"\bone\s+accent\b", re.IGNORECASE),
29
+ re.compile(r"\bfocal\b", re.IGNORECASE),
30
+ re.compile(r"\b(?:light|dark|full)\s*[·|/]\s*static\b", re.IGNORECASE),
31
+ re.compile(r"\bdesign\s+rules?\b", re.IGNORECASE),
32
+ re.compile(r"^\s*show\b", re.IGNORECASE),
33
+ )
34
+
35
+ OFFICE_RULES = {
36
+ "group_ratio": 2, "accent_saturation": .12, "accent_light_min": .12,
37
+ "accent_light_max": .93, "accent_hue_family_deg": 20,
38
+ "accent_medium_count": 3, "accent_high_count": 4,
39
+ "latin_share": .80, "title_case_share": .60,
40
+ "label_inset_px": 4,
41
+ }
42
+
43
+
44
+ def _office_craft_issues(parsed: ParsedSvg, nodes: tuple[Shape, ...]) -> list[str]:
45
+ issues: list[str] = []
46
+ if len(parsed.boundaries) >= 2:
47
+ def group(shape: Shape) -> str:
48
+ center = (shape.rect.x + shape.rect.w / 2, shape.rect.y + shape.rect.h / 2)
49
+ for boundary in parsed.boundaries:
50
+ if boundary.rect.x <= center[0] <= boundary.rect.right and boundary.rect.y <= center[1] <= boundary.rect.bottom:
51
+ return boundary.name
52
+ return "unbounded"
53
+
54
+ within, between = [], []
55
+ for index, first in enumerate(nodes):
56
+ for second in nodes[index + 1:]:
57
+ gap = rectangle_gap(first.rect, second.rect)
58
+ (within if group(first) == group(second) else between).append(gap)
59
+ if within and between and min(between) < OFFICE_RULES["group_ratio"] * max(within):
60
+ issues.append(f"OF-201 group ratio {min(between) / max(within):.2f} below {OFFICE_RULES['group_ratio']}")
61
+
62
+ hues: list[float] = []
63
+ for node in nodes:
64
+ if node.attributes.get("display") == "none" or not re.fullmatch(r"#[0-9a-fA-F]{6}", node.fill):
65
+ continue
66
+ rgb = tuple(int(node.fill[index:index + 2], 16) / 255 for index in (1, 3, 5))
67
+ hue, lightness, saturation = colorsys.rgb_to_hls(*rgb)
68
+ hue *= 360
69
+ if saturation < OFFICE_RULES["accent_saturation"] or not OFFICE_RULES["accent_light_min"] <= lightness <= OFFICE_RULES["accent_light_max"]:
70
+ continue
71
+ if not any(min(abs(hue - prior), 360 - abs(hue - prior)) <= OFFICE_RULES["accent_hue_family_deg"] for prior in hues):
72
+ hues.append(hue)
73
+ if len(hues) >= OFFICE_RULES["accent_medium_count"]:
74
+ level = "HIGH" if len(hues) >= OFFICE_RULES["accent_high_count"] else "MEDIUM"
75
+ issues.append(f"OF-202 {level} accent families={len(hues)}")
76
+
77
+ for text in parsed.texts:
78
+ label = text.label.strip()
79
+ latin = sum(character.isascii() and character.isalnum() for character in label)
80
+ if label and latin / len(label) >= OFFICE_RULES["latin_share"] and label != label.upper():
81
+ words = re.findall(r"[A-Za-z]{4,}", label)
82
+ capitals = sum(bool(re.fullmatch(r"[A-Z][a-z]+", word)) for word in words)
83
+ function_word = re.search(r"\b(?:Of|And|The|To|For|In)\b", label)
84
+ if len(words) >= 2 and capitals / len(words) > OFFICE_RULES["title_case_share"] and function_word:
85
+ issues.append(f"OF-203 MEDIUM title-case label={label}")
86
+ if text.edge_for:
87
+ continue # Edge labels have no stable inner box in this parser.
88
+ box = next((boundary.rect for boundary in parsed.boundaries if boundary.name == text.boundary_for), None) if text.boundary_for else None
89
+ if box is None and text.role == "node":
90
+ box = next((node.rect for node in nodes if node.rect.x <= text.anchor[0] <= node.rect.right and
91
+ node.rect.y <= text.anchor[1] <= node.rect.bottom), None)
92
+ if box is not None and text.bounds.w > box.w - OFFICE_RULES["label_inset_px"]:
93
+ issues.append(f"OF-204 label {label} width={text.bounds.w:.1f}px exceeds {box.w - OFFICE_RULES['label_inset_px']:.1f}px")
94
+ return issues
95
+
96
+
97
+ def _node_shapes(parsed: ParsedSvg) -> tuple[Shape, ...]:
98
+ return tuple(shape for shape in parsed.shapes if shape.attributes.get("data-node-id"))
99
+
100
+
101
+ def _node_by_id(nodes: tuple[Shape, ...], name: str) -> Shape | None:
102
+ return next((node for node in nodes if node.attributes.get("data-node-id") == name), None)
103
+
104
+
105
+ def _boundary_label(text: TextBox, outlines: tuple[Boundary, ...]) -> bool:
106
+ return text.role == "boundary" or bool(text.boundary_for) or any(text.label in boundary.labels or text.boundary_for == boundary.name for boundary in outlines)
107
+
108
+
109
+ def _boundary_edges(boundary: Boundary) -> tuple[tuple[float, float, float, float], ...]:
110
+ rect = boundary.rect
111
+ return ((rect.x, rect.y, rect.right, rect.y), (rect.right, rect.y, rect.right, rect.bottom), (rect.right, rect.bottom, rect.x, rect.bottom), (rect.x, rect.bottom, rect.x, rect.y))
112
+
113
+
114
+ def _marker_id(value: str) -> str:
115
+ match = re.search(r"url\(\s*['\"]?#([^\s)'\"`]+)['\"]?\s*\)", value, re.IGNORECASE)
116
+ return match.group(1) if match else ""
117
+
118
+
119
+ def _aligned_route(route: Route, nodes: tuple[Shape, ...]) -> str:
120
+ origin, target = _node_by_id(nodes, route.from_id), _node_by_id(nodes, route.to_id)
121
+ if origin is None or target is None or not route.segments:
122
+ return ""
123
+ same_row = abs(origin.rect.y + origin.rect.h / 2 - target.rect.y - target.rect.h / 2) <= 1
124
+ same_column = abs(origin.rect.x + origin.rect.w / 2 - target.rect.x - target.rect.w / 2) <= 1
125
+ horizontal = all(abs(y1 - y2) <= 1 for _, y1, _, y2 in route.segments)
126
+ vertical = all(abs(x1 - x2) <= 1 for x1, _, x2, _ in route.segments)
127
+ origin_x, target_x = origin.rect.x + origin.rect.w / 2, target.rect.x + target.rect.w / 2
128
+ origin_y, target_y = origin.rect.y + origin.rect.h / 2, target.rect.y + target.rect.h / 2
129
+ row_clear = all(
130
+ node is origin or node is target
131
+ or abs(node.rect.y + node.rect.h / 2 - origin_y) > 1
132
+ or node.rect.x + node.rect.w / 2 <= min(origin_x, target_x)
133
+ or node.rect.x + node.rect.w / 2 >= max(origin_x, target_x)
134
+ for node in nodes
135
+ )
136
+ column_clear = all(
137
+ node is origin or node is target
138
+ or abs(node.rect.x + node.rect.w / 2 - origin_x) > 1
139
+ or node.rect.y + node.rect.h / 2 <= min(origin_y, target_y)
140
+ or node.rect.y + node.rect.h / 2 >= max(origin_y, target_y)
141
+ for node in nodes
142
+ )
143
+ if same_row and row_clear and not horizontal:
144
+ return "same-row"
145
+ if same_column and column_clear and not vertical:
146
+ return "same-column"
147
+ return ""
148
+
149
+
150
+ def _decision_issues(nodes: tuple[Shape, ...], routes: tuple[Route, ...]) -> list[str]:
151
+ issues: list[str] = []
152
+ for node in nodes:
153
+ if node.attributes.get("data-node-type") != "decision":
154
+ continue
155
+ name = node.attributes["data-node-id"]
156
+ outcomes = [value.strip() for value in node.attributes.get("data-outcomes", "").split("|") if value.strip()]
157
+ if len(outcomes) < 2:
158
+ issues.append(f"DECISION_OUTCOMES_MISSING {name} must name at least two outcomes")
159
+ continue
160
+ labels = {route.label for route in routes if route.from_id == name and route.label}
161
+ issues.extend(f"DECISION_OUTCOME_MISSING {name} {outcome}" for outcome in outcomes if outcome not in labels)
162
+ issues.extend(f"DECISION_OUTCOME_UNDECLARED {name} {label}" for label in labels if label not in outcomes)
163
+ return issues
164
+
165
+
166
+ def _route_issues(parsed: ParsedSvg, metrics: DiagramMetrics, nodes: tuple[Shape, ...]) -> list[str]:
167
+ issues: list[str] = []
168
+ sequence = bool(re.search(r"\bdata-type\s*=\s*['\"]sequence(?:-oauth)?['\"]", parsed.source, re.IGNORECASE))
169
+ for text in parsed.texts:
170
+ if text.role != "edge" and not text.edge_for:
171
+ continue
172
+ matches = [route for route in parsed.routes if f"{route.from_id}|{route.to_id}" == text.edge_for and (not route.label or route.label == text.label)]
173
+ if not text.edge_for or len(matches) != 1:
174
+ issues.append(f"EDGE_LABEL_UNBOUND {text.edge_for or text.label}")
175
+ continue
176
+ route = matches[0]
177
+ own = route_distance(text.anchor, route)
178
+ other = min((route_distance(text.anchor, item) for item in parsed.routes if item is not route), default=math.inf)
179
+ if own > 24:
180
+ issues.append(f"EDGE_LABEL_TOO_FAR {text.label} own={own:.1f}px max=24px")
181
+ if math.isfinite(other) and own >= other:
182
+ issues.append(f"EDGE_LABEL_NEAREST_PATH {text.label} own={own:.1f}px other={other:.1f}px")
183
+ for node in nodes:
184
+ if text_overlaps_node(text, node.rect):
185
+ issues.append(f"EDGE_LABEL_NODE_COLLISION {text.label} overlaps {node.attributes['data-node-id']}")
186
+ for route in parsed.routes:
187
+ distance = route_manhattan_distance(route)
188
+ ratio = route_length(route) / distance if distance else 1
189
+ if ratio > 1.4:
190
+ issues.append(f"ROUTE_DETOUR {route.from_id}->{route.to_id} ratio={ratio:.2f} max=1.40")
191
+ aligned = _aligned_route(route, nodes)
192
+ if aligned:
193
+ issues.append(f"ROUTE_ALIGNED_PATH {route.from_id}->{route.to_id} {aligned} neighbors require one straight segment")
194
+ crosses = any(route_crosses_boundary(route, boundary) for boundary in parsed.boundaries)
195
+ limit = 3 if crosses else 2
196
+ bends = route_bends(route)
197
+ if bends > limit:
198
+ issues.append(f"ROUTE_BENDS {route.from_id}->{route.to_id} bends={bends} max={limit}")
199
+ if route.marker_start and route.marker_end:
200
+ issues.append(f"ROUTE_OPPOSED_HEADS {route.from_id}->{route.to_id}")
201
+ for text in parsed.texts:
202
+ if any(segment_hits_text(segment, text) for segment in route.segments):
203
+ issues.append(f"EDGE_TEXT_COLLISION {route.from_id}->{route.to_id}: {text.label}")
204
+ break
205
+ for node in nodes:
206
+ if node.attributes.get("data-node-id") not in {route.from_id, route.to_id} and any(segment_hits_rect(segment, node.rect, 8) for segment in route.segments):
207
+ issues.append(f"ROUTE_THROUGH_NODE {route.from_id}->{route.to_id} via {node.attributes['data-node-id']}")
208
+ if any(segment_along_boundary(segment, boundary) for boundary in parsed.boundaries for segment in route.segments):
209
+ issues.append(f"ROUTE_ALONG_BOUNDARY {route.from_id}->{route.to_id}")
210
+ target = _node_by_id(nodes, route.to_id)
211
+ if route.marker_end:
212
+ marker = parsed.markers.get(_marker_id(route.marker_end))
213
+ minimum = max(5 * route.stroke_width, (metrics["viewBox"].w if metrics["viewBox"] else 1080) * 12 / 1080)
214
+ if marker is None:
215
+ issues.append(f"ARROWHEAD_GEOMETRY_MISSING {route.from_id}->{route.to_id} end")
216
+ elif marker_length(marker) * marker_scale(marker, route) + 0.01 < minimum:
217
+ issues.append(f"ARROWHEAD_SIZE_FLOOR {route.from_id}->{route.to_id} end min={minimum:.1f}px")
218
+ if marker is not None and target is not None and not sequence:
219
+ tip = marker_tip(route, marker, "end")
220
+ if tip is not None and distance_to_rect_boundary(tip, target.rect) > 2:
221
+ issues.append(f"ARROWHEAD_TIP_MISSED_TARGET {route.from_id}->{route.to_id}")
222
+ if marker is not None and target is not None and marker_overlaps_rect(route, target.rect, marker):
223
+ issues.append(f"ARROWHEAD_TARGET_OVERLAP {route.from_id}->{route.to_id} marker intersects target {route.to_id}")
224
+ for index, first in enumerate(parsed.routes):
225
+ for second in parsed.routes[index + 1:]:
226
+ if routes_overlap_collinearly(first, second):
227
+ issues.append(f"ROUTE_COLLINEAR_OVERLAP {first.from_id}->{first.to_id} / {second.from_id}->{second.to_id} shared within 12px")
228
+ for x, y in route_crossings(first, second):
229
+ issues.append(f"ROUTE_CROSSING {first.from_id}->{first.to_id} / {second.from_id}->{second.to_id} at {x:.1f},{y:.1f}")
230
+ return issues
231
+
232
+
233
+ def quality_issues(source: str, *, enforce_canvas: bool = False, scale: float | None = None) -> list[str]:
234
+ """Return stable issue codes for geometry, readability, and accessibility defects."""
235
+ parsed = parse_svg(source)
236
+ metrics = source_metrics(source)
237
+ nodes = _node_shapes(parsed)
238
+ issues: list[str] = []
239
+ contaminated = list(dict.fromkeys(text.label for text in parsed.texts if any(rule.search(text.label) for rule in _CONTAMINATION)))
240
+ if contaminated:
241
+ issues.append("PROCESS_TEXT " + " | ".join(contaminated))
242
+ if not metrics["hasSvg"]:
243
+ issues.append("SVG_MISSING")
244
+ return issues
245
+ for key, code in (("title", "SVG_TITLE_MISSING"), ("desc", "SVG_DESC_MISSING"), ("roleImage", "SVG_ROLE_IMAGE_MISSING"), ("ariaLinked", "SVG_ARIA_LINK_MISSING")):
246
+ if not metrics[key]:
247
+ issues.append(code)
248
+ if metrics["offCanvas"]:
249
+ issues.append("OFF_CANVAS " + " | ".join(metrics["offCanvasLabels"]))
250
+ if metrics["textOverlaps"]:
251
+ issues.append(f"TEXT_OVERLAP count={metrics['textOverlaps']}")
252
+ if metrics["objectOverlaps"]:
253
+ issues.append(f"OBJECT_OVERLAP count={metrics['objectOverlaps']}")
254
+ for failure in metrics["contrastFailures"]:
255
+ issues.append(f"CONTRAST_FLOOR {failure['text']}={failure['ratio']}:1")
256
+ issues.extend(_route_issues(parsed, metrics, nodes))
257
+ issues.extend(_decision_issues(nodes, parsed.routes))
258
+ issues.extend(sequence_issues(source, parsed, nodes))
259
+ for text in parsed.texts:
260
+ for boundary in parsed.boundaries:
261
+ if any(segment_hits_text(edge, text, boundary.stroke_width / 2 + 0.5) for edge in _boundary_edges(boundary)):
262
+ issues.append(f"OUTLINE_TEXT_COLLISION {text.label} crosses {boundary.name}")
263
+ break
264
+ boundary_failures = {failure["text"] for failure in metrics["contrastFailures"]}
265
+ for text in parsed.texts:
266
+ if _boundary_label(text, parsed.boundaries) and text.label in boundary_failures:
267
+ issues.append(f"BOUNDARY_CONTRAST_FLOOR {text.label} below 4.5:1")
268
+ for index, first in enumerate(nodes):
269
+ for second in nodes[index + 1:]:
270
+ if rectangle_gap(first.rect, second.rect) < 12:
271
+ issues.append(f"NODE_GAP {first.attributes['data-node-id']}/{second.attributes['data-node-id']} below 12px")
272
+ issues.extend(_office_craft_issues(parsed, nodes))
273
+ if enforce_canvas:
274
+ ratio = scale if scale is not None else min(metrics["viewBox"].w / 1080, metrics["viewBox"].h / 640) if metrics["viewBox"] else 1
275
+ if not any(text.role == "title" for text in parsed.texts):
276
+ issues.append('TITLE_ROLE_MISSING visible title must use data-role="title"')
277
+ for text in parsed.texts:
278
+ boundary_text = _boundary_label(text, parsed.boundaries)
279
+ minimum = 28 if text.role == "title" else 13 if text.role == "edge" or boundary_text else 15 if text.role == "node" else 0
280
+ if minimum and text.font_size + 0.01 < minimum * ratio:
281
+ detail = f"{text.role or 'boundary'}:{text.label}={text.font_size}px (min {minimum * ratio:.1f}px)"
282
+ issues.append(f"BOUNDARY_FONT_FLOOR {detail}" if boundary_text else "FONT_FLOOR " + detail)
283
+ points = [(node.rect.x, node.rect.y) for node in nodes] + [(node.rect.right, node.rect.bottom) for node in nodes]
284
+ points.extend(point for route in parsed.routes for segment in route.segments for point in ((segment[0], segment[1]), (segment[2], segment[3])))
285
+ if nodes and metrics["viewBox"] and points:
286
+ left, top = min(point[0] for point in points), min(point[1] for point in points)
287
+ right, bottom = max(point[0] for point in points), max(point[1] for point in points)
288
+ width, height = (right - left) / (metrics["viewBox"].w * 0.90), (bottom - top) / (metrics["viewBox"].h * 0.74)
289
+ if width < 0.75 or height < 0.54:
290
+ issues.append(f"CONTENT_FILL safe-area-width={width:.3f} safe-area-height={height:.3f} minimum=.75/.54")
291
+ if not nodes:
292
+ issues.append("CONTENT_NODES_MISSING")
293
+ return list(dict.fromkeys(issues))