@litfamily/lithermes 1.0.7 → 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 +76 -8
  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,791 @@
1
+ #!/usr/bin/env python3
2
+ """Honest, renderer-agnostic QA gate for generated PPTX decks.
3
+
4
+ Brand-neutral: works for any enrolled template. Combines three checks and
5
+ exits non-zero if ANY real defect is found:
6
+
7
+ 1. Layout issues — inventory.py --issues-only (overflow / overlap / off-slide)
8
+ 2. Anti-slop — validate_pptx.py (forbidden/placeholder terms, default fonts)
9
+ 3. WCAG contrast — text colour vs its fill (body < 4.5:1, large < 3:1)
10
+
11
+ No LibreOffice / rendering engine required: everything is read from the OOXML
12
+ via python-pptx. (A renderer is only ever needed for *visual* preview, which is
13
+ separate and must be inspected after the structural gate.)
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import argparse
19
+ import json
20
+ import re
21
+ import subprocess
22
+ import sys
23
+ import tempfile
24
+ from pathlib import Path
25
+ from typing import Any
26
+
27
+ from pptx import Presentation
28
+ from pptx.dml.color import RGBColor
29
+ from pptx.enum.shapes import MSO_SHAPE_TYPE
30
+ from pptx.opc.constants import RELATIONSHIP_TYPE as RT
31
+
32
+ from craft_extras import check as check_office_craft
33
+
34
+ ROOT = Path(__file__).resolve().parents[1]
35
+ VALIDATE = ROOT / "scripts" / "validate_pptx.py"
36
+ INVENTORY = ROOT / "scripts" / "inventory.py"
37
+
38
+ # WCAG 2.x contrast floors.
39
+ BODY_MIN = 4.5
40
+ LARGE_MIN = 3.0
41
+
42
+
43
+ # ── WCAG contrast ──────────────────────────────────────────────────────────
44
+ def _rel_luminance(rgb: tuple[int, int, int]) -> float:
45
+ def chan(c: int) -> float:
46
+ s = c / 255.0
47
+ return s / 12.92 if s <= 0.03928 else ((s + 0.055) / 1.055) ** 2.4
48
+
49
+ r, g, b = (chan(x) for x in rgb)
50
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b
51
+
52
+
53
+ def _ratio(fg: tuple[int, int, int], bg: tuple[int, int, int]) -> float:
54
+ l1, l2 = _rel_luminance(fg), _rel_luminance(bg)
55
+ hi, lo = max(l1, l2), min(l1, l2)
56
+ return (hi + 0.05) / (lo + 0.05)
57
+
58
+
59
+ def _hex_to_rgb(value: Any) -> tuple[int, int, int] | None:
60
+ if isinstance(value, RGBColor):
61
+ value = str(value)
62
+ if not isinstance(value, str):
63
+ return None
64
+ v = value.lstrip("#")
65
+ if len(v) != 6:
66
+ return None
67
+ try:
68
+ return (int(v[0:2], 16), int(v[2:4], 16), int(v[4:6], 16))
69
+ except ValueError:
70
+ return None
71
+
72
+
73
+ def _shape_fill_rgb(shape) -> tuple[int, int, int] | None:
74
+ fill = getattr(shape, "fill", None)
75
+ if fill is None or getattr(fill, "type", None) is None:
76
+ return None
77
+ try:
78
+ rgb = fill.fore_color.rgb
79
+ except (TypeError, AttributeError):
80
+ return None
81
+ return _hex_to_rgb(rgb)
82
+
83
+
84
+ def _is_large(size_pt: float | None, bold: bool) -> bool:
85
+ if size_pt is None:
86
+ return False
87
+ return size_pt >= 18 or (bold and size_pt >= 14)
88
+
89
+
90
+ def _bbox(shape):
91
+ try:
92
+ l, t, w, h = int(shape.left), int(shape.top), int(shape.width), int(shape.height)
93
+ return (l, t, l + w, t + h)
94
+ except (TypeError, ValueError):
95
+ return None
96
+
97
+
98
+ def _resolve_bg(shape, idx, filled):
99
+ """Effective background for a text shape: its own fill, else the topmost
100
+ filled shape drawn *behind* it (lower z-order) that contains its centre,
101
+ else white. Prevents false positives like white text over a charcoal band."""
102
+ own = _shape_fill_rgb(shape)
103
+ if own is not None:
104
+ return own
105
+ box = _bbox(shape)
106
+ if box is not None:
107
+ cx, cy = (box[0] + box[2]) / 2, (box[1] + box[3]) / 2
108
+ best = None
109
+ for f_idx, f_rgb, f_box in filled:
110
+ if f_idx >= idx or f_box is None:
111
+ continue
112
+ if f_box[0] <= cx <= f_box[2] and f_box[1] <= cy <= f_box[3]:
113
+ best = f_rgb # later (higher idx) wins -> topmost behind text
114
+ if best is not None:
115
+ return best
116
+ return (255, 255, 255)
117
+
118
+
119
+ def check_contrast(path: Path) -> dict[str, Any]:
120
+ """Flag explicit-colour text that falls under the WCAG floor on its fill."""
121
+ prs = Presentation(str(path))
122
+ violations: list[dict[str, Any]] = []
123
+ checked = 0
124
+ for s_idx, slide in enumerate(prs.slides, start=1):
125
+ shapes = list(slide.shapes)
126
+ filled = [(i, rgb, _bbox(sh)) for i, sh in enumerate(shapes) if (rgb := _shape_fill_rgb(sh)) is not None]
127
+ for idx, shape in enumerate(shapes):
128
+ if not getattr(shape, "has_text_frame", False):
129
+ continue
130
+ bg = _resolve_bg(shape, idx, filled)
131
+ for para in shape.text_frame.paragraphs:
132
+ for run in para.runs:
133
+ if not run.text.strip():
134
+ continue
135
+ try:
136
+ fg = _hex_to_rgb(run.font.color.rgb)
137
+ except (TypeError, AttributeError):
138
+ fg = None
139
+ if fg is None:
140
+ continue # theme/inherited colour — cannot judge, skip
141
+ checked += 1
142
+ size = float(run.font.size.pt) if run.font.size is not None else None
143
+ bold = bool(run.font.bold)
144
+ ratio = _ratio(fg, bg)
145
+ floor = LARGE_MIN if _is_large(size, bold) else BODY_MIN
146
+ if ratio < floor:
147
+ violations.append({
148
+ "slide": s_idx,
149
+ "text": run.text.strip()[:60],
150
+ "fg": "#%02X%02X%02X" % fg,
151
+ "bg": "#%02X%02X%02X" % bg,
152
+ "ratio": round(ratio, 2),
153
+ "floor": floor,
154
+ })
155
+ return {"pass": not violations, "checked_runs": checked, "violations": violations[:30]}
156
+
157
+
158
+ # ── picture / table overlap ─────────────────────────────────────────────────
159
+ # inventory.py only considers text shapes, so a chart image drawn on top of a
160
+ # data table (or another image / body text) slips through. This check covers
161
+ # PICTURE and TABLE (graphicFrame) shapes against every other content-bearing
162
+ # shape and fails on any real overlap.
163
+ OVERLAP_TOL_IN = 0.06 # inches; ignore hairline touches / rounding
164
+ MIN_TEXT_PT = 6.0
165
+
166
+
167
+ def _shape_kind(shape) -> str | None:
168
+ """Classify a shape for overlap purposes: 'picture', 'table', 'text', or
169
+ None for decorative/empty shapes (backgrounds, rules, blank frames)."""
170
+ if shape.shape_type == MSO_SHAPE_TYPE.PICTURE:
171
+ return "picture"
172
+ if getattr(shape, "has_table", False):
173
+ return "table"
174
+ if getattr(shape, "has_text_frame", False) and shape.text_frame.text.strip():
175
+ return "text"
176
+ return None
177
+
178
+
179
+ def _bbox_in(shape) -> tuple[float, float, float, float] | None:
180
+ try:
181
+ l = shape.left / 914400.0
182
+ t = shape.top / 914400.0
183
+ w = shape.width / 914400.0
184
+ h = shape.height / 914400.0
185
+ except (TypeError, ValueError):
186
+ return None
187
+ return (l, t, l + w, t + h)
188
+
189
+
190
+ def _is_full_bleed(box, page_area: float) -> bool:
191
+ """A shape covering essentially the whole canvas is a ground, not an occluder."""
192
+ return ((box[2] - box[0]) * (box[3] - box[1])) >= 0.92 * page_area
193
+
194
+
195
+ def _overlap_extent(box_a, box_b) -> tuple[float, float]:
196
+ return (min(box_a[2], box_b[2]) - max(box_a[0], box_b[0]),
197
+ min(box_a[3], box_b[3]) - max(box_a[1], box_b[1]))
198
+
199
+
200
+ def check_semantic_overlaps(path: Path) -> dict[str, Any]:
201
+ """Text running over other text — the one occlusion nothing else catches.
202
+
203
+ Free positioning lets an author drop a box anywhere, so two text frames can
204
+ now land on top of each other and produce a slide where neither is readable.
205
+ Nothing in the gate saw that: the inventory arm meant to catch it read a key
206
+ that never existed, so it counted zero for the whole life of the gate.
207
+
208
+ Every other pairing already has a more careful owner, and duplicating them
209
+ here would only reintroduce their false positives:
210
+
211
+ picture or table drawn over content -> check_overlaps (z-order aware)
212
+ declared figure or table occluded -> check_figure_table_occlusion
213
+ text on a card, panel or backdrop -> check_contrast, on its real fill
214
+
215
+ That last one is why an undeclared picture is not treated as content. A
216
+ gradient circle behind a cover title is a ground exactly as a navy panel is;
217
+ a picture only carries content once its description declares it as evidence,
218
+ and check_figure_table_occlusion is what reads that declaration.
219
+ """
220
+ prs = Presentation(str(path))
221
+ page_area = (prs.slide_width / 914400.0) * (prs.slide_height / 914400.0)
222
+ violations: list[dict[str, Any]] = []
223
+
224
+ for s_idx, slide in enumerate(prs.slides, start=1):
225
+ texts = [
226
+ (_bbox_in(sh), sh) for sh in slide.shapes
227
+ if _shape_kind(sh) == "text" and _bbox_in(sh) is not None
228
+ ]
229
+ for i in range(len(texts)):
230
+ for j in range(i + 1, len(texts)):
231
+ box_a, sh_a = texts[i]
232
+ box_b, sh_b = texts[j]
233
+ if _is_full_bleed(box_a, page_area) or _is_full_bleed(box_b, page_area):
234
+ continue
235
+ ow, oh = _overlap_extent(box_a, box_b)
236
+ if ow <= OVERLAP_TOL_IN or oh <= OVERLAP_TOL_IN:
237
+ continue
238
+ violations.append({
239
+ "slide": s_idx,
240
+ "a": "text",
241
+ "b": "text",
242
+ "area": round(ow * oh, 2),
243
+ "a_text": _shape_text(sh_a)[:40],
244
+ "b_text": _shape_text(sh_b)[:40],
245
+ })
246
+
247
+ return {"pass": not violations, "violations": violations[:30]}
248
+
249
+
250
+ def check_overlaps(path: Path) -> dict[str, Any]:
251
+ """A PICTURE or TABLE must never be drawn on top of (higher z-order than)
252
+ another content-bearing shape. Decorations are drawn first (behind), so they
253
+ are never the 'top' shape and never false-flag."""
254
+ prs = Presentation(str(path))
255
+ page_area = (prs.slide_width / 914400.0) * (prs.slide_height / 914400.0)
256
+ violations: list[dict[str, Any]] = []
257
+ for s_idx, slide in enumerate(prs.slides, start=1):
258
+ shapes = []
259
+ for z, sh in enumerate(slide.shapes):
260
+ box = _bbox_in(sh)
261
+ if box is None:
262
+ continue
263
+ shapes.append((z, sh, _shape_kind(sh), box))
264
+
265
+ def is_full_bleed(b):
266
+ return _is_full_bleed(b, page_area)
267
+ for z, sh, kind, box in shapes:
268
+ if kind not in ("picture", "table"):
269
+ continue
270
+ for z2, sh2, kind2, box2 in shapes:
271
+ if z2 >= z or kind2 is None:
272
+ continue # only shapes drawn *behind* this picture/table
273
+ # A full-bleed background image (mesh/gradient wash) is never an
274
+ # occlusion victim — anything may sit on top of it by design.
275
+ if kind2 == "picture" and is_full_bleed(box2):
276
+ continue
277
+ # Picture-over-picture is intentional decorative layering.
278
+ if kind == "picture" and kind2 == "picture":
279
+ continue
280
+ ow = min(box[2], box2[2]) - max(box[0], box2[0])
281
+ oh = min(box[3], box2[3]) - max(box[1], box2[1])
282
+ if ow > OVERLAP_TOL_IN and oh > OVERLAP_TOL_IN:
283
+ violations.append({
284
+ "slide": s_idx,
285
+ "top": kind,
286
+ "under": kind2,
287
+ "area": round(ow * oh, 2),
288
+ })
289
+ return {"pass": not violations, "violations": violations[:30]}
290
+
291
+
292
+ # ── objective executive/research checks ────────────────────────────────────
293
+ def _intersection(box_a, box_b) -> tuple[float, float]:
294
+ return (
295
+ min(box_a[2], box_b[2]) - max(box_a[0], box_b[0]),
296
+ min(box_a[3], box_b[3]) - max(box_a[1], box_b[1]),
297
+ )
298
+
299
+
300
+ def _iter_shape_runs(shape):
301
+ if getattr(shape, "has_text_frame", False):
302
+ for paragraph in shape.text_frame.paragraphs:
303
+ yield from paragraph.runs
304
+ if getattr(shape, "has_table", False):
305
+ for row in shape.table.rows:
306
+ for cell in row.cells:
307
+ for paragraph in cell.text_frame.paragraphs:
308
+ yield from paragraph.runs
309
+
310
+
311
+ def _shape_text(shape) -> str:
312
+ return "\n".join(run.text for run in _iter_shape_runs(shape) if run.text).strip()
313
+
314
+
315
+ def _semantic_label(shape) -> str:
316
+ values = [getattr(shape, "name", "") or ""]
317
+ try:
318
+ c_nv_pr = shape._element.xpath(".//p:cNvPr")[0]
319
+ values.extend([c_nv_pr.get("name", ""), c_nv_pr.get("descr", ""), c_nv_pr.get("title", "")])
320
+ except (AttributeError, IndexError):
321
+ pass
322
+ return " ".join(value for value in values if value).strip()
323
+
324
+
325
+ def check_side_stripes(path: Path) -> dict[str, Any]:
326
+ """Detect a narrow filled bar attached to the vertical edge of a larger card."""
327
+ prs = Presentation(str(path))
328
+ violations: list[dict[str, Any]] = []
329
+ for slide_idx, slide in enumerate(prs.slides, start=1):
330
+ filled = [
331
+ (idx, shape, _bbox_in(shape), _shape_fill_rgb(shape))
332
+ for idx, shape in enumerate(slide.shapes)
333
+ if _bbox_in(shape) is not None and _shape_fill_rgb(shape) is not None
334
+ ]
335
+ for stripe_idx, stripe, stripe_box, stripe_rgb in filled:
336
+ sw = stripe_box[2] - stripe_box[0]
337
+ sh = stripe_box[3] - stripe_box[1]
338
+ if sw > 0.15 or sh < 0.5 or sh / max(sw, 0.001) < 5:
339
+ continue
340
+ for card_idx, card, card_box, card_rgb in filled:
341
+ if card_idx == stripe_idx or card_rgb == stripe_rgb:
342
+ continue
343
+ cw = card_box[2] - card_box[0]
344
+ ch = card_box[3] - card_box[1]
345
+ if cw < 1.0 or ch < 0.5 or sh < ch * 0.7:
346
+ continue
347
+ same_vertical_band = abs(stripe_box[1] - card_box[1]) <= 0.08 and abs(stripe_box[3] - card_box[3]) <= 0.08
348
+ on_edge = abs(stripe_box[0] - card_box[0]) <= 0.08 or abs(stripe_box[2] - card_box[2]) <= 0.08
349
+ if same_vertical_band and on_edge:
350
+ violations.append({
351
+ "slide": slide_idx,
352
+ "stripe_shape": stripe_idx + 1,
353
+ "card_shape": card_idx + 1,
354
+ "stripe_width_in": round(sw, 3),
355
+ })
356
+ break
357
+ return {"pass": not violations, "violations": violations[:30]}
358
+
359
+
360
+ def check_text_size_floor(path: Path) -> dict[str, Any]:
361
+ """Fail only on explicit text below the absolute 6pt export floor."""
362
+ prs = Presentation(str(path))
363
+ violations: list[dict[str, Any]] = []
364
+ checked = 0
365
+ for slide_idx, slide in enumerate(prs.slides, start=1):
366
+ for shape_idx, shape in enumerate(slide.shapes, start=1):
367
+ for run in _iter_shape_runs(shape):
368
+ if not run.text.strip() or run.font.size is None:
369
+ continue
370
+ checked += 1
371
+ size = float(run.font.size.pt)
372
+ if size < MIN_TEXT_PT:
373
+ violations.append({
374
+ "slide": slide_idx,
375
+ "shape": shape_idx,
376
+ "text": run.text.strip()[:60],
377
+ "size_pt": round(size, 2),
378
+ "floor_pt": MIN_TEXT_PT,
379
+ })
380
+ return {"pass": not violations, "checked_runs": checked, "violations": violations[:30]}
381
+
382
+
383
+ def check_figure_table_occlusion(path: Path) -> dict[str, Any]:
384
+ """Flag content shapes drawn over a non-background figure or table."""
385
+ prs = Presentation(str(path))
386
+ violations: list[dict[str, Any]] = []
387
+ for slide_idx, slide in enumerate(prs.slides, start=1):
388
+ page_area = (prs.slide_width / 914400.0) * (prs.slide_height / 914400.0)
389
+ shapes = [
390
+ (idx, shape, _shape_kind(shape), _bbox_in(shape))
391
+ for idx, shape in enumerate(slide.shapes)
392
+ if _bbox_in(shape) is not None
393
+ ]
394
+ for victim_idx, victim, victim_kind, victim_box in shapes:
395
+ if victim_kind not in ("picture", "table"):
396
+ continue
397
+ # A generic picture may intentionally be a background or decorated
398
+ # asset. Only pictures explicitly declared as evidence/figures have
399
+ # objective occlusion semantics; tables are always data objects.
400
+ if victim_kind == "picture" and not _EVIDENCE_MARKER.search(_semantic_label(victim)):
401
+ continue
402
+ victim_area = (victim_box[2] - victim_box[0]) * (victim_box[3] - victim_box[1])
403
+ if victim_kind == "picture" and victim_area >= 0.92 * page_area:
404
+ continue
405
+ for top_idx, top, top_kind, top_box in shapes:
406
+ if top_idx <= victim_idx or top_kind is None:
407
+ continue
408
+ if victim_kind == "picture" and top_kind == "picture":
409
+ continue
410
+ ow, oh = _intersection(victim_box, top_box)
411
+ if ow <= OVERLAP_TOL_IN or oh <= OVERLAP_TOL_IN:
412
+ continue
413
+ overlap_area = ow * oh
414
+ if overlap_area < max(0.08, victim_area * 0.03):
415
+ continue
416
+ violations.append({
417
+ "slide": slide_idx,
418
+ "victim": victim_kind,
419
+ "victim_shape": victim_idx + 1,
420
+ "occluding": top_kind,
421
+ "occluding_shape": top_idx + 1,
422
+ "area": round(overlap_area, 2),
423
+ })
424
+ return {"pass": not violations, "violations": violations[:30]}
425
+
426
+
427
+ _EVIDENCE_MARKER = re.compile(
428
+ r"(?:^|\b)(?:figure|evidence|source[ -]?capture|그림|도표|근거|출처\s*캡처)\s*(?::|\d|\b)",
429
+ re.IGNORECASE,
430
+ )
431
+ _CAPTION_MARKER = re.compile(r"(?:그림|도|figure|fig\.)\s*\d+", re.IGNORECASE)
432
+ _SOURCE_MARKER = re.compile(r"(?:출처|source)\s*:", re.IGNORECASE)
433
+
434
+
435
+ def check_evidence_binding(path: Path) -> dict[str, Any]:
436
+ """Require visible, adjacent caption and source text for declared evidence figures."""
437
+ prs = Presentation(str(path))
438
+ violations: list[dict[str, Any]] = []
439
+ checked = 0
440
+ for slide_idx, slide in enumerate(prs.slides, start=1):
441
+ shapes = list(slide.shapes)
442
+ for shape_idx, shape in enumerate(shapes, start=1):
443
+ if shape.shape_type != MSO_SHAPE_TYPE.PICTURE or not _EVIDENCE_MARKER.search(_semantic_label(shape)):
444
+ continue
445
+ checked += 1
446
+ figure_box = _bbox_in(shape)
447
+ nearby: list[str] = []
448
+ if figure_box is not None:
449
+ figure_width = figure_box[2] - figure_box[0]
450
+ for other in shapes:
451
+ text = _shape_text(other)
452
+ other_box = _bbox_in(other)
453
+ if not text or other_box is None:
454
+ continue
455
+ horizontal = min(figure_box[2], other_box[2]) - max(figure_box[0], other_box[0])
456
+ vertical_gap = other_box[1] - figure_box[3]
457
+ if horizontal >= figure_width * 0.2 and -0.04 <= vertical_gap <= 1.1:
458
+ nearby.append(text)
459
+ bound_text = "\n".join(nearby)
460
+ missing = []
461
+ if not _CAPTION_MARKER.search(bound_text):
462
+ missing.append("caption")
463
+ if not _SOURCE_MARKER.search(bound_text):
464
+ missing.append("source")
465
+ if missing:
466
+ violations.append({
467
+ "slide": slide_idx,
468
+ "shape": shape_idx,
469
+ "label": _semantic_label(shape)[:100],
470
+ "missing": missing,
471
+ })
472
+ return {"pass": not violations, "checked_figures": checked, "violations": violations[:30]}
473
+
474
+
475
+ _APPENDIX_MARKER = re.compile(r"\bappendix\b|부록", re.IGNORECASE)
476
+ _RAW_URL = re.compile(r"https?://[^\s<>\]\[)}]+", re.IGNORECASE)
477
+ _DOI_VALUE = re.compile(r"(?<!doi\.org/)\b10\.\d{4,9}/[^\s<>\]\[)}]+", re.IGNORECASE)
478
+ _LINK_FIELD = re.compile(
479
+ r"\bdoi\b|canonical\s+link|source\s+link|정식\s*링크|원문(?:\s*링크)?",
480
+ re.IGNORECASE,
481
+ )
482
+ _SOURCE_LOCATOR = re.compile(r"\bsource\b|출처", re.IGNORECASE)
483
+
484
+
485
+ def _appendix_link_units(shape):
486
+ """Yield visible text and run-level hyperlink targets per paragraph/cell."""
487
+ if getattr(shape, "has_table", False):
488
+ headers = [cell.text.strip() for cell in shape.table.rows[0].cells]
489
+ for row_idx, row in enumerate(shape.table.rows):
490
+ if row_idx == 0:
491
+ continue
492
+ for column_idx, cell in enumerate(row.cells):
493
+ for paragraph in cell.text_frame.paragraphs:
494
+ runs = [(run.text, run.hyperlink.address) for run in paragraph.runs if run.text]
495
+ text = "".join(run_text for run_text, _ in runs).strip()
496
+ if text:
497
+ yield (
498
+ text,
499
+ [address for _, address in runs if address],
500
+ bool(
501
+ _LINK_FIELD.search(headers[column_idx])
502
+ or (
503
+ _SOURCE_LOCATOR.search(headers[column_idx])
504
+ and any(address for _, address in runs)
505
+ )
506
+ ),
507
+ )
508
+ return
509
+ if getattr(shape, "has_text_frame", False):
510
+ for paragraph in shape.text_frame.paragraphs:
511
+ runs = [(run.text, run.hyperlink.address) for run in paragraph.runs if run.text]
512
+ text = "".join(run_text for run_text, _ in runs).strip()
513
+ if text:
514
+ run_links = [address for _, address in runs if address]
515
+ yield text, run_links, bool(
516
+ _LINK_FIELD.search(text)
517
+ or (_SOURCE_LOCATOR.search(text) and run_links)
518
+ )
519
+
520
+
521
+ def _slide_hyperlink_targets(slide) -> list[str]:
522
+ targets = []
523
+ for relationship in slide.part.rels.values():
524
+ if relationship.reltype == RT.HYPERLINK and relationship.is_external:
525
+ targets.append(relationship.target_ref)
526
+ return targets
527
+
528
+
529
+ def check_appendix_links(path: Path) -> dict[str, Any]:
530
+ """Appendix DOI, canonical-link, and raw-URL fields must be clickable."""
531
+ prs = Presentation(str(path))
532
+ violations: list[dict[str, Any]] = []
533
+ declared = 0
534
+ relationship_targets: list[str] = []
535
+ for slide_idx, slide in enumerate(prs.slides, start=1):
536
+ slide_text = "\n".join(_shape_text(shape) for shape in slide.shapes)
537
+ if not _APPENDIX_MARKER.search(slide_text):
538
+ continue
539
+ slide_targets = _slide_hyperlink_targets(slide)
540
+ relationship_targets.extend(slide_targets)
541
+ for shape_idx, shape in enumerate(slide.shapes, start=1):
542
+ for text, run_links, is_link_field in _appendix_link_units(shape):
543
+ raw_urls = [url.rstrip(".,;:") for url in _RAW_URL.findall(text)]
544
+ doi_values = [] if raw_urls else [doi.rstrip(".,;:") for doi in _DOI_VALUE.findall(text)]
545
+ candidates = raw_urls or doi_values
546
+ if not candidates and is_link_field:
547
+ candidates = [run_links[0]] if run_links else [None]
548
+ for candidate in candidates:
549
+ declared += 1
550
+ if candidate is None:
551
+ linked = False
552
+ elif candidate.startswith(("http://", "https://")):
553
+ linked = candidate in run_links and candidate in slide_targets
554
+ else:
555
+ linked = any(
556
+ candidate.lower() in target.lower() and target in run_links
557
+ for target in slide_targets
558
+ )
559
+ if not linked:
560
+ violations.append({
561
+ "slide": slide_idx,
562
+ "shape": shape_idx,
563
+ "text": text[:100],
564
+ "locator": candidate,
565
+ "missing": "OOXML hyperlink relationship",
566
+ })
567
+ return {
568
+ "pass": not violations,
569
+ "declared_links": declared,
570
+ "hyperlink_relationships": len(relationship_targets),
571
+ "relationship_targets": relationship_targets[:20],
572
+ "violations": violations[:30],
573
+ }
574
+
575
+
576
+ def check_objective_quality(path: Path) -> dict[str, Any]:
577
+ checks = {
578
+ "side_stripes": check_side_stripes(path),
579
+ "text_size_floor": check_text_size_floor(path),
580
+ "figure_table_occlusion": check_figure_table_occlusion(path),
581
+ "evidence_binding": check_evidence_binding(path),
582
+ "appendix_links": check_appendix_links(path),
583
+ }
584
+ return {"pass": all(check["pass"] for check in checks.values()), **checks}
585
+
586
+
587
+ def check_slide_craft(path: Path) -> dict[str, Any]:
588
+ """Catch sparse table slides and visible empty frames before visual review."""
589
+ prs = Presentation(str(path))
590
+ violations: list[dict[str, Any]] = []
591
+ table_only = 0
592
+ text_only = 0
593
+ content_slides = 0
594
+ for slide_number, slide in enumerate(prs.slides, 1):
595
+ tables = [shape for shape in slide.shapes if getattr(shape, "has_table", False)]
596
+ charts = [shape for shape in slide.shapes if getattr(shape, "has_chart", False)]
597
+ pictures = [
598
+ shape for shape in slide.shapes
599
+ if shape.shape_type == MSO_SHAPE_TYPE.PICTURE
600
+ and shape.top >= prs.slide_height * 0.20
601
+ ]
602
+ body_text = [
603
+ shape for shape in slide.shapes
604
+ if getattr(shape, "has_text_frame", False)
605
+ and shape.top >= prs.slide_height * 0.16
606
+ and len(shape.text_frame.text.strip()) >= 65
607
+ ]
608
+ panels = [
609
+ shape for shape in slide.shapes
610
+ if _shape_fill_rgb(shape) is not None
611
+ and shape.width >= 0.5 * 914400 and shape.height >= 0.5 * 914400
612
+ and shape.width * shape.height < prs.slide_width * prs.slide_height * 0.8
613
+ ]
614
+ if tables or charts or body_text or len(panels) >= 2:
615
+ content_slides += 1
616
+ if body_text and not tables and not charts and not pictures and len(panels) < 2:
617
+ text_only += 1
618
+ if tables and not charts and not pictures and not body_text:
619
+ table_only += 1
620
+ usable_height = prs.slide_height * 0.62
621
+ fill_ratio = max(shape.height for shape in tables) / usable_height
622
+ if fill_ratio < 0.45:
623
+ violations.append({
624
+ "slide": slide_number, "reason": "sparse table-only slide",
625
+ "fill_ratio": round(fill_ratio, 2),
626
+ "fix": "Use a native chart or KPI cards for the takeaway, or expand the table to carry the evidence.",
627
+ })
628
+ for shape in slide.shapes:
629
+ if not getattr(shape, "has_text_frame", False) or shape.text_frame.text.strip():
630
+ continue
631
+ if shape.width < 0.7 * 914400 or shape.height < 0.5 * 914400:
632
+ continue
633
+ if shape.width * shape.height > prs.slide_width * prs.slide_height * 0.8:
634
+ continue
635
+ if shape.fill.type is None and shape.line.fill.type is not None:
636
+ violations.append({
637
+ "slide": slide_number, "reason": "empty outlined shape",
638
+ "fix": "Remove the unused frame or give it a meaningful label and content.",
639
+ })
640
+ if content_slides >= 2 and table_only / content_slides > 0.5:
641
+ violations.append({
642
+ "slide": None, "reason": "table-only deck",
643
+ "fix": "Vary the content form with editable charts, KPI cards, or a visual explanation.",
644
+ })
645
+ if content_slides >= 3 and text_only / content_slides > 0.5:
646
+ violations.append({
647
+ "slide": None, "reason": "text-only deck",
648
+ "fix": "Replace repeated bullet pages with a chart, KPI cards, diagram, image, or another visual that carries the message.",
649
+ })
650
+ return {
651
+ "pass": not violations, "content_slides": content_slides,
652
+ "table_only_slides": table_only, "text_only_slides": text_only,
653
+ "violations": violations[:30],
654
+ }
655
+
656
+
657
+ # ── inventory issues ───────────────────────────────────────────────────────
658
+ def load_inventory_issues(pptx: Path) -> dict[str, Any]:
659
+ with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as tmp:
660
+ tmp_path = Path(tmp.name)
661
+ try:
662
+ subprocess.run(
663
+ [sys.executable, str(INVENTORY), str(pptx), str(tmp_path), "--issues-only"],
664
+ capture_output=True, text=True, cwd=ROOT, timeout=120,
665
+ )
666
+ return json.loads(tmp_path.read_text(encoding="utf-8")) if tmp_path.exists() else {}
667
+ finally:
668
+ tmp_path.unlink(missing_ok=True)
669
+
670
+
671
+ def summarize_inventory(issues: dict[str, Any]) -> dict[str, Any]:
672
+ shape_count = overflow = slide_overflow = frame_overflow = overlap = 0
673
+ for shapes in issues.values():
674
+ if not isinstance(shapes, dict):
675
+ continue
676
+ for shape in shapes.values():
677
+ if not isinstance(shape, dict):
678
+ continue
679
+ shape_count += 1
680
+ ov = shape.get("overflow") or {}
681
+ if ov:
682
+ overflow += 1
683
+ if "slide" in ov:
684
+ slide_overflow += 1
685
+ if "frame" in ov:
686
+ frame_overflow += 1
687
+ # inventory.py nests this under "overlap"; reading it at the top
688
+ # level silently counted zero and left this arm dead for the whole
689
+ # life of the gate. It is repaired here but reports only — see qa().
690
+ if (shape.get("overlap") or {}).get("overlapping_shapes"):
691
+ overlap += 1
692
+ return {
693
+ "issue_shapes": shape_count,
694
+ "overflow_shapes": overflow,
695
+ "slide_overflow_shapes": slide_overflow,
696
+ "frame_overflow_shapes": frame_overflow,
697
+ "overlap_shapes": overlap,
698
+ }
699
+
700
+
701
+ def run_validate(pptx: Path) -> dict[str, Any]:
702
+ result = subprocess.run(
703
+ [sys.executable, str(VALIDATE), str(pptx)],
704
+ capture_output=True, text=True, cwd=ROOT, timeout=120,
705
+ )
706
+ try:
707
+ return json.loads(result.stdout)
708
+ except json.JSONDecodeError:
709
+ return {"pass": False, "error": "validate_pptx produced no JSON", "stderr": result.stderr[:400]}
710
+
711
+
712
+ # ── gate ───────────────────────────────────────────────────────────────────
713
+ def qa(pptx: Path) -> dict[str, Any]:
714
+ validate_report = run_validate(pptx)
715
+ inv_summary = summarize_inventory(load_inventory_issues(pptx))
716
+ contrast = check_contrast(pptx)
717
+ overlaps = check_overlaps(pptx)
718
+ semantic = check_semantic_overlaps(pptx)
719
+ objective = check_objective_quality(pptx)
720
+ craft = check_slide_craft(pptx)
721
+ office_craft = check_office_craft(pptx)
722
+
723
+ reasons: list[str] = []
724
+ if not validate_report.get("pass", False):
725
+ reasons.append("anti-slop: " + ", ".join(
726
+ k for k, v in validate_report.get("checks", {}).items() if isinstance(v, dict) and not v.get("pass", True)
727
+ ) or "anti-slop: validate_pptx failed")
728
+ # Fail only on real layout defects (overflow / off-slide). The inventory also
729
+ # flags "manual bullet symbols", which is an authoring lint for the HTML path
730
+ # — the markdown engine legitimately renders "• " bullet runs, so that lint
731
+ # must not fail the gate (issue_shapes stays informational).
732
+ #
733
+ # inv_summary["overlap_shapes"] is deliberately NOT part of this sum. It is
734
+ # raw pairwise geometry with no notion of z-order or shape kind, so every
735
+ # tinted card counts its own text as an overlap. check_semantic_overlaps owns
736
+ # the overlap verdict; this number is carried in the report for transparency.
737
+ layout_defects = inv_summary["overflow_shapes"]
738
+ if layout_defects:
739
+ reasons.append(
740
+ f"layout defects: {layout_defects} shapes "
741
+ f"({inv_summary['slide_overflow_shapes']} slide-overflow, "
742
+ f"{inv_summary['frame_overflow_shapes']} frame-overflow)"
743
+ )
744
+ if not contrast["pass"]:
745
+ reasons.append(f"contrast: {len(contrast['violations'])} run(s) below WCAG floor")
746
+ if not overlaps["pass"]:
747
+ reasons.append(
748
+ f"overlap: {len(overlaps['violations'])} picture/table shape(s) drawn over other content"
749
+ )
750
+ if not semantic["pass"]:
751
+ reasons.append(
752
+ f"semantic overlap: {len(semantic['violations'])} content pair(s) occluding each other"
753
+ )
754
+ if not objective["pass"]:
755
+ failed = [name for name, check in objective.items() if isinstance(check, dict) and not check.get("pass", True)]
756
+ reasons.append("objective quality: " + ", ".join(failed))
757
+ if not craft["pass"]:
758
+ reasons.append("slide craft: " + ", ".join(item["reason"] for item in craft["violations"]))
759
+ if not office_craft["pass"]:
760
+ reasons.append("office craft: " + ", ".join(sorted({item["rule"] for item in office_craft["findings"] if item["severity"] == "HIGH"})))
761
+
762
+ return {
763
+ "file": str(pptx),
764
+ "pass": not reasons,
765
+ "failure_reasons": reasons,
766
+ "anti_slop": {
767
+ "pass": validate_report.get("pass"),
768
+ "slop_score": validate_report.get("slop_score"),
769
+ "review_warnings": validate_report.get("checks", {}).get("forbidden_terms", {}).get("soft_warnings", []),
770
+ },
771
+ "layout": {**inv_summary, "picture_table_overlaps": len(overlaps["violations"])},
772
+ "contrast": contrast,
773
+ "overlaps": overlaps,
774
+ "semantic_overlaps": semantic,
775
+ "objective": objective,
776
+ "craft": craft,
777
+ "office_craft": office_craft,
778
+ }
779
+
780
+
781
+ def main(argv: list[str] | None = None) -> int:
782
+ parser = argparse.ArgumentParser(description="Honest renderer-agnostic QA gate for PPTX decks.")
783
+ parser.add_argument("pptx", type=Path)
784
+ args = parser.parse_args(argv)
785
+ report = qa(args.pptx.resolve())
786
+ print(json.dumps(report, ensure_ascii=False, indent=2))
787
+ return 0 if report["pass"] else 1
788
+
789
+
790
+ if __name__ == "__main__":
791
+ raise SystemExit(main())