@research-copilot/plugin 1.1.16 → 1.1.18

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 (799) hide show
  1. package/dist/.claude-plugin/plugin.json +1 -1
  2. package/dist/.codex-plugin/plugin.toml +1 -1
  3. package/dist/.cursor-plugin/plugin.json +1 -1
  4. package/dist/.gemini-plugin/plugin.json +1 -1
  5. package/dist/.opencode-plugin/plugin.json +1 -1
  6. package/dist/.windsurf-plugin/plugin.json +1 -1
  7. package/dist/skills/ablation-planner/SKILL.md +126 -0
  8. package/dist/skills/alphaxiv/SKILL.md +186 -0
  9. package/dist/skills/analyze-results/SKILL.md +45 -0
  10. package/dist/skills/arxiv/SKILL.md +210 -0
  11. package/dist/skills/auto-paper-improvement-loop/SKILL.md +574 -0
  12. package/dist/skills/auto-review-loop/SKILL.md +370 -0
  13. package/dist/skills/auto-review-loop-llm/SKILL.md +247 -0
  14. package/dist/skills/auto-review-loop-minimax/SKILL.md +290 -0
  15. package/dist/skills/benchmark-paper-template/SKILL.md +142 -0
  16. package/dist/skills/benchmark-paper-template/references/benchmark-design.md +180 -0
  17. package/dist/skills/benchmark-paper-template/references/checklist.md +113 -0
  18. package/dist/skills/benchmark-paper-template/references/construction-pipeline.md +127 -0
  19. package/dist/skills/benchmark-paper-template/references/experiments.md +159 -0
  20. package/dist/skills/benchmark-paper-template/references/gap-analysis.md +86 -0
  21. package/dist/skills/benchmark-paper-template/references/instantiation-template.md +194 -0
  22. package/dist/skills/benchmark-paper-template/references/orchestrator-notes.md +96 -0
  23. package/dist/skills/benchmark-paper-template/references/paper-structure.md +209 -0
  24. package/dist/skills/citation-audit/SKILL.md +494 -0
  25. package/dist/skills/claims-drafting/SKILL.md +239 -0
  26. package/dist/skills/comm-lit-review/SKILL.md +299 -0
  27. package/dist/skills/comm-lit-review/references/domain-taxonomy.md +57 -0
  28. package/dist/skills/comm-lit-review/references/output-template.md +37 -0
  29. package/dist/skills/comm-lit-review/references/source-policy.md +99 -0
  30. package/dist/skills/comm-lit-review/references/venue-tiering.md +112 -0
  31. package/dist/skills/composer/SKILL.md +840 -0
  32. package/dist/skills/composer/references/section_guides.md +675 -0
  33. package/dist/skills/composer/references/writing_standards.md +629 -0
  34. package/dist/skills/composer/scripts/chapter_quality_check.py +470 -0
  35. package/dist/skills/composer/scripts/final_evaluation.py +550 -0
  36. package/dist/skills/deepxiv/SKILL.md +142 -0
  37. package/dist/skills/dse-loop/SKILL.md +277 -0
  38. package/dist/skills/embodiment-description/SKILL.md +129 -0
  39. package/dist/skills/exa-search/SKILL.md +192 -0
  40. package/dist/skills/experiment-audit/SKILL.md +262 -0
  41. package/dist/skills/experiment-bridge/SKILL.md +316 -0
  42. package/dist/skills/experiment-plan/SKILL.md +249 -0
  43. package/dist/skills/experiment-queue/SKILL.md +380 -0
  44. package/dist/skills/experiment-queue/scripts/build_manifest.py +142 -0
  45. package/dist/skills/experiment-queue/scripts/queue_manager.py +426 -0
  46. package/dist/skills/feishu-notify/SKILL.md +155 -0
  47. package/dist/skills/figure-description/SKILL.md +138 -0
  48. package/dist/skills/figure-designer/SKILL.md +207 -0
  49. package/dist/skills/figure-designer/references/design-rules.md +99 -0
  50. package/dist/skills/figure-designer/references/experimental-results.md +127 -0
  51. package/dist/skills/figure-designer/references/motivated-example.md +138 -0
  52. package/dist/skills/figure-designer/references/solution-overview.md +145 -0
  53. package/dist/skills/figure-designer/references/tools.md +124 -0
  54. package/dist/skills/figure-spec/SKILL.md +252 -0
  55. package/dist/skills/figure-spec/scripts/figure_renderer.py +799 -0
  56. package/dist/skills/formula-derivation/SKILL.md +280 -0
  57. package/dist/skills/gemini-search/SKILL.md +205 -0
  58. package/dist/skills/grant-proposal/SKILL.md +625 -0
  59. package/dist/skills/humanizer/AGENTS.md +25 -0
  60. package/dist/skills/humanizer/SKILL.md +621 -0
  61. package/dist/skills/idea-creator/SKILL.md +311 -0
  62. package/dist/skills/idea-discovery/SKILL.md +340 -0
  63. package/dist/skills/idea-discovery-robot/SKILL.md +362 -0
  64. package/dist/skills/idea-evaluator/SKILL.md +265 -0
  65. package/dist/skills/idea-evaluator/references/fatal-flaws.md +192 -0
  66. package/dist/skills/idea-evaluator/references/five-dimensions.md +251 -0
  67. package/dist/skills/idea-evaluator/references/lifecycle-capability-matching.md +177 -0
  68. package/dist/skills/idea-evaluator/references/paradigm-elephant.md +112 -0
  69. package/dist/skills/idea-evaluator/references/paradigm-examples.md +107 -0
  70. package/dist/skills/idea-evaluator/references/paradigm-first-principles.md +115 -0
  71. package/dist/skills/idea-evaluator/references/paradigm-hamming.md +137 -0
  72. package/dist/skills/idea-evaluator/references/paradigm-shift-probe.md +160 -0
  73. package/dist/skills/idea-evaluator/references/paradigm-technology-cycle.md +116 -0
  74. package/dist/skills/idea-evaluator/references/worked-examples.md +206 -0
  75. package/dist/skills/interview-cheatsheet/SKILL.md +245 -0
  76. package/dist/skills/intro-drafter/SKILL.md +250 -0
  77. package/dist/skills/intro-drafter/references/contribution-patterns.md +116 -0
  78. package/dist/skills/intro-drafter/references/flowchart.md +204 -0
  79. package/dist/skills/intro-drafter/references/paper-types.md +155 -0
  80. package/dist/skills/intro-drafter/references/running-example.md +133 -0
  81. package/dist/skills/intro-drafter/references/worked-examples.md +161 -0
  82. package/dist/skills/invention-structuring/SKILL.md +188 -0
  83. package/dist/skills/jurisdiction-format/SKILL.md +192 -0
  84. package/dist/skills/kill-argument/SKILL.md +384 -0
  85. package/dist/skills/llm-wiki/.mise.toml +2 -0
  86. package/dist/skills/llm-wiki/.nvmrc +1 -0
  87. package/dist/skills/llm-wiki/AGENTS.md +132 -0
  88. package/dist/skills/llm-wiki/CHANGELOG.md +459 -0
  89. package/dist/skills/llm-wiki/CLAUDE.md +109 -0
  90. package/dist/skills/llm-wiki/HERMES.md +44 -0
  91. package/dist/skills/llm-wiki/SKILL.md +1133 -0
  92. package/dist/skills/llm-wiki/assets/graph-demo.gif +0 -0
  93. package/dist/skills/llm-wiki/assets/graph-preview.png +0 -0
  94. package/dist/skills/llm-wiki/deps/LICENSE-d3.txt +13 -0
  95. package/dist/skills/llm-wiki/deps/LICENSE-marked.txt +44 -0
  96. package/dist/skills/llm-wiki/deps/LICENSE-purify.txt +568 -0
  97. package/{LICENSE → dist/skills/llm-wiki/deps/LICENSE-roughjs.txt} +1 -1
  98. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/SKILL.md +261 -0
  99. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/references/config/first-time-setup.md +106 -0
  100. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/cdp.ts +179 -0
  101. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/constants.ts +13 -0
  102. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/defuddle-converter.ts +58 -0
  103. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/html-to-markdown.ts +135 -0
  104. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/legacy-converter.ts +629 -0
  105. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/main.ts +314 -0
  106. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/markdown-conversion-shared.ts +305 -0
  107. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/media-localizer.ts +317 -0
  108. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/package.json +14 -0
  109. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/paths.ts +29 -0
  110. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/vendor/baoyu-chrome-cdp/package.json +9 -0
  111. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/vendor/baoyu-chrome-cdp/src/index.test.ts +307 -0
  112. package/dist/skills/llm-wiki/deps/baoyu-url-to-markdown/scripts/vendor/baoyu-chrome-cdp/src/index.ts +523 -0
  113. package/dist/skills/llm-wiki/deps/d3.min.js +2 -0
  114. package/dist/skills/llm-wiki/deps/marked.min.js +6 -0
  115. package/dist/skills/llm-wiki/deps/purify.min.js +3 -0
  116. package/dist/skills/llm-wiki/deps/rough.min.js +7 -0
  117. package/dist/skills/llm-wiki/deps/youtube-transcript/SKILL.md +47 -0
  118. package/dist/skills/llm-wiki/deps/youtube-transcript/scripts/get_transcript.py +72 -0
  119. package/dist/skills/llm-wiki/docs/design/oriental-atlas/DESIGN.md +183 -0
  120. package/dist/skills/llm-wiki/docs/design/oriental-atlas/design-brief.md +170 -0
  121. package/dist/skills/llm-wiki/docs/design/oriental-atlas/oriental-editorial-atlas.html +1908 -0
  122. package/dist/skills/llm-wiki/docs/design/oriental-atlas/reviews/result-review.md +68 -0
  123. package/dist/skills/llm-wiki/docs/plans/2026-06-15-omp-tool-status-events-phased-plan.md +655 -0
  124. package/dist/skills/llm-wiki/docs/plans/2026-06-15-omp-tool-status-events-progress.json +913 -0
  125. package/dist/skills/llm-wiki/docs/spark/2026-06-15-omp-tool-status-events-design.md +174 -0
  126. package/dist/skills/llm-wiki/install.ps1 +87 -0
  127. package/dist/skills/llm-wiki/install.sh +722 -0
  128. package/dist/skills/llm-wiki/package-lock.json +10309 -0
  129. package/dist/skills/llm-wiki/package.json +24 -0
  130. package/dist/skills/llm-wiki/packages/graph-engine/package.json +33 -0
  131. package/dist/skills/llm-wiki/packages/graph-engine/src/anim/.gitkeep +1 -0
  132. package/dist/skills/llm-wiki/packages/graph-engine/src/anim/index.ts +179 -0
  133. package/dist/skills/llm-wiki/packages/graph-engine/src/diff.ts +121 -0
  134. package/dist/skills/llm-wiki/packages/graph-engine/src/graph-node.ts +37 -0
  135. package/dist/skills/llm-wiki/packages/graph-engine/src/index.ts +140 -0
  136. package/dist/skills/llm-wiki/packages/graph-engine/src/model/atlas.ts +18 -0
  137. package/dist/skills/llm-wiki/packages/graph-engine/src/model/index.ts +6 -0
  138. package/dist/skills/llm-wiki/packages/graph-engine/src/model/labels.ts +11 -0
  139. package/dist/skills/llm-wiki/packages/graph-engine/src/model/learning.ts +8 -0
  140. package/dist/skills/llm-wiki/packages/graph-engine/src/model/legacy-helpers.ts +1373 -0
  141. package/dist/skills/llm-wiki/packages/graph-engine/src/model/queue.ts +7 -0
  142. package/dist/skills/llm-wiki/packages/graph-engine/src/model/storage.ts +4 -0
  143. package/dist/skills/llm-wiki/packages/graph-engine/src/model/visibility.ts +9 -0
  144. package/dist/skills/llm-wiki/packages/graph-engine/src/render/.gitkeep +1 -0
  145. package/dist/skills/llm-wiki/packages/graph-engine/src/render/index.ts +59 -0
  146. package/dist/skills/llm-wiki/packages/graph-engine/src/render/legend.ts +25 -0
  147. package/dist/skills/llm-wiki/packages/graph-engine/src/render/model.ts +593 -0
  148. package/dist/skills/llm-wiki/packages/graph-engine/src/render/preview.ts +65 -0
  149. package/dist/skills/llm-wiki/packages/graph-engine/src/render/search.ts +48 -0
  150. package/dist/skills/llm-wiki/packages/graph-engine/src/render/static-renderer.ts +2783 -0
  151. package/dist/skills/llm-wiki/packages/graph-engine/src/render/toolbar.ts +40 -0
  152. package/dist/skills/llm-wiki/packages/graph-engine/src/render/viewport.ts +272 -0
  153. package/dist/skills/llm-wiki/packages/graph-engine/src/select/.gitkeep +1 -0
  154. package/dist/skills/llm-wiki/packages/graph-engine/src/select/index.ts +189 -0
  155. package/dist/skills/llm-wiki/packages/graph-engine/src/sim/index.ts +298 -0
  156. package/dist/skills/llm-wiki/packages/graph-engine/src/sim/pins.ts +96 -0
  157. package/dist/skills/llm-wiki/packages/graph-engine/src/themes/.gitkeep +1 -0
  158. package/dist/skills/llm-wiki/packages/graph-engine/src/themes/index.ts +8 -0
  159. package/dist/skills/llm-wiki/packages/graph-engine/src/themes/tokens.ts +107 -0
  160. package/dist/skills/llm-wiki/packages/graph-engine/src/types.ts +313 -0
  161. package/dist/skills/llm-wiki/packages/graph-engine/test/anim.test.ts +100 -0
  162. package/dist/skills/llm-wiki/packages/graph-engine/test/diff.test.ts +133 -0
  163. package/dist/skills/llm-wiki/packages/graph-engine/test/helpers.test.ts +275 -0
  164. package/dist/skills/llm-wiki/packages/graph-engine/test/learning.test.ts +259 -0
  165. package/dist/skills/llm-wiki/packages/graph-engine/test/preview.test.ts +56 -0
  166. package/dist/skills/llm-wiki/packages/graph-engine/test/queue.test.ts +114 -0
  167. package/dist/skills/llm-wiki/packages/graph-engine/test/render-model.test.ts +322 -0
  168. package/dist/skills/llm-wiki/packages/graph-engine/test/runtime-state.test.ts +281 -0
  169. package/dist/skills/llm-wiki/packages/graph-engine/test/search-and-legend.test.ts +111 -0
  170. package/dist/skills/llm-wiki/packages/graph-engine/test/select.test.ts +168 -0
  171. package/dist/skills/llm-wiki/packages/graph-engine/test/sim.test.ts +123 -0
  172. package/dist/skills/llm-wiki/packages/graph-engine/test/themes.test.ts +62 -0
  173. package/dist/skills/llm-wiki/packages/graph-engine/test/toolbar.test.ts +64 -0
  174. package/dist/skills/llm-wiki/packages/graph-engine/test/viewport.test.ts +167 -0
  175. package/dist/skills/llm-wiki/packages/graph-engine/tsconfig.json +17 -0
  176. package/dist/skills/llm-wiki/packages/graph-engine/vite.config.ts +15 -0
  177. package/dist/skills/llm-wiki/platforms/claude/CLAUDE.md +37 -0
  178. package/dist/skills/llm-wiki/platforms/claude/companions/llm-wiki-upgrade/SKILL.md +95 -0
  179. package/dist/skills/llm-wiki/platforms/codex/AGENTS.md +23 -0
  180. package/dist/skills/llm-wiki/scripts/adapter-state.sh +424 -0
  181. package/dist/skills/llm-wiki/scripts/build-graph-data.sh +395 -0
  182. package/dist/skills/llm-wiki/scripts/build-graph-html.sh +453 -0
  183. package/dist/skills/llm-wiki/scripts/cache.sh +352 -0
  184. package/dist/skills/llm-wiki/scripts/create-source-page.sh +100 -0
  185. package/dist/skills/llm-wiki/scripts/delete-helper.sh +68 -0
  186. package/dist/skills/llm-wiki/scripts/graph-analysis.js +732 -0
  187. package/dist/skills/llm-wiki/scripts/hook-session-start.sh +46 -0
  188. package/dist/skills/llm-wiki/scripts/init-wiki.sh +95 -0
  189. package/dist/skills/llm-wiki/scripts/lib/source-signal-eligibility.js +158 -0
  190. package/dist/skills/llm-wiki/scripts/lint-fix.sh +114 -0
  191. package/dist/skills/llm-wiki/scripts/lint-runner.sh +217 -0
  192. package/dist/skills/llm-wiki/scripts/runtime-context.sh +77 -0
  193. package/dist/skills/llm-wiki/scripts/shared-config.sh +83 -0
  194. package/dist/skills/llm-wiki/scripts/source-record-contract.tsv +10 -0
  195. package/dist/skills/llm-wiki/scripts/source-registry.sh +349 -0
  196. package/dist/skills/llm-wiki/scripts/source-registry.tsv +10 -0
  197. package/dist/skills/llm-wiki/scripts/source-signal-coverage.js +83 -0
  198. package/dist/skills/llm-wiki/scripts/validate-step1.sh +144 -0
  199. package/dist/skills/llm-wiki/scripts/wiki-compat.sh +267 -0
  200. package/dist/skills/llm-wiki/setup.sh +8 -0
  201. package/dist/skills/llm-wiki/templates/entity-template.md +30 -0
  202. package/dist/skills/llm-wiki/templates/graph-styles/wash/footer.html +27 -0
  203. package/dist/skills/llm-wiki/templates/graph-styles/wash/graph-wash-helpers.js +1326 -0
  204. package/dist/skills/llm-wiki/templates/graph-styles/wash/graph-wash.js +1009 -0
  205. package/dist/skills/llm-wiki/templates/graph-styles/wash/header.html +2002 -0
  206. package/dist/skills/llm-wiki/templates/index-en-template.md +51 -0
  207. package/dist/skills/llm-wiki/templates/index-template.md +51 -0
  208. package/dist/skills/llm-wiki/templates/log-en-template.md +11 -0
  209. package/dist/skills/llm-wiki/templates/log-template.md +11 -0
  210. package/dist/skills/llm-wiki/templates/overview-en-template.md +41 -0
  211. package/dist/skills/llm-wiki/templates/overview-template.md +41 -0
  212. package/dist/skills/llm-wiki/templates/purpose-en-template.md +16 -0
  213. package/dist/skills/llm-wiki/templates/purpose-template.md +16 -0
  214. package/dist/skills/llm-wiki/templates/query-template.md +19 -0
  215. package/dist/skills/llm-wiki/templates/schema-template.md +185 -0
  216. package/dist/skills/llm-wiki/templates/source-template.md +51 -0
  217. package/dist/skills/llm-wiki/templates/synthesis-template.md +25 -0
  218. package/dist/skills/llm-wiki/templates/topic-template.md +35 -0
  219. package/dist/skills/llm-wiki/tests/adapter-state.sh +338 -0
  220. package/dist/skills/llm-wiki/tests/browser/graph-density-preview.mjs +53 -0
  221. package/dist/skills/llm-wiki/tests/browser/graph-html-insights.mjs +41 -0
  222. package/dist/skills/llm-wiki/tests/browser/graph-node-slim.mjs +71 -0
  223. package/dist/skills/llm-wiki/tests/browser/graph-stage-4-5.mjs +717 -0
  224. package/dist/skills/llm-wiki/tests/expected/graph-data-empty.json +60 -0
  225. package/dist/skills/llm-wiki/tests/expected/graph-data-sample.json +396 -0
  226. package/dist/skills/llm-wiki/tests/expected/graph-interactive-basic.html +1736 -0
  227. package/dist/skills/llm-wiki/tests/expected/lint-output.txt +44 -0
  228. package/dist/skills/llm-wiki/tests/fixtures/coverage-sample-wiki/index.md +1 -0
  229. package/dist/skills/llm-wiki/tests/fixtures/coverage-sample-wiki/wiki/entities/Alpha.md +10 -0
  230. package/dist/skills/llm-wiki/tests/fixtures/coverage-sample-wiki/wiki/entities/Beta.md +7 -0
  231. package/dist/skills/llm-wiki/tests/fixtures/coverage-sample-wiki/wiki/entities/Delta.md +8 -0
  232. package/dist/skills/llm-wiki/tests/fixtures/coverage-sample-wiki/wiki/entities/Gamma.md +8 -0
  233. package/dist/skills/llm-wiki/tests/fixtures/coverage-sample-wiki/wiki/queries/20260422-abc.md +10 -0
  234. package/dist/skills/llm-wiki/tests/fixtures/coverage-sample-wiki/wiki/synthesis/Crystal.md +8 -0
  235. package/dist/skills/llm-wiki/tests/fixtures/coverage-sample-wiki/wiki/topics/Overview.md +9 -0
  236. package/dist/skills/llm-wiki/tests/fixtures/graph-data-empty-wiki/wiki/entities/.gitkeep +0 -0
  237. package/dist/skills/llm-wiki/tests/fixtures/graph-data-sample-wiki/purpose.md +3 -0
  238. package/dist/skills/llm-wiki/tests/fixtures/graph-data-sample-wiki/wiki/entities/Attention.md +7 -0
  239. package/dist/skills/llm-wiki/tests/fixtures/graph-data-sample-wiki/wiki/entities/Decoder.md +7 -0
  240. package/dist/skills/llm-wiki/tests/fixtures/graph-data-sample-wiki/wiki/entities/Encoder.md +7 -0
  241. package/dist/skills/llm-wiki/tests/fixtures/graph-data-sample-wiki/wiki/entities/GPT.md +7 -0
  242. package/dist/skills/llm-wiki/tests/fixtures/graph-data-sample-wiki/wiki/entities/Transformer.md +13 -0
  243. package/dist/skills/llm-wiki/tests/fixtures/graph-data-sample-wiki/wiki/sources/paper.md +9 -0
  244. package/dist/skills/llm-wiki/tests/fixtures/graph-data-sample-wiki/wiki/topics/arch.md +12 -0
  245. package/dist/skills/llm-wiki/tests/fixtures/graph-data-sample-wiki/wiki/topics/finetune.md +9 -0
  246. package/dist/skills/llm-wiki/tests/fixtures/graph-interactive-basic/wiki/graph-data.json +40 -0
  247. package/dist/skills/llm-wiki/tests/fixtures/graph-interactive-dense/wiki/graph-data.json +3965 -0
  248. package/dist/skills/llm-wiki/tests/fixtures/graph-interactive-multicomm/wiki/graph-data.json +212 -0
  249. package/dist/skills/llm-wiki/tests/fixtures/lint-sample-wiki/index.md +14 -0
  250. package/dist/skills/llm-wiki/tests/fixtures/lint-sample-wiki/wiki/entities/C++.md +6 -0
  251. package/dist/skills/llm-wiki/tests/fixtures/lint-sample-wiki/wiki/entities/Lonely.md +4 -0
  252. package/dist/skills/llm-wiki/tests/fixtures/lint-sample-wiki/wiki/entities/Other.md +8 -0
  253. package/dist/skills/llm-wiki/tests/fixtures/lint-sample-wiki/wiki/entities/Real.md +7 -0
  254. package/dist/skills/llm-wiki/tests/fixtures/lint-sample-wiki/wiki/sources/2026-01-01-test-source.md +15 -0
  255. package/dist/skills/llm-wiki/tests/fixtures/lint-sample-wiki/wiki/sources/2026-02-01-inline-img.md +12 -0
  256. package/dist/skills/llm-wiki/tests/fixtures/lint-sample-wiki/wiki/topics/AIbasic.md +3 -0
  257. package/dist/skills/llm-wiki/tests/graph-analysis-helper.regression-1.sh +237 -0
  258. package/dist/skills/llm-wiki/tests/graph-browser-stage-4-5.regression-1.sh +157 -0
  259. package/dist/skills/llm-wiki/tests/graph-build-failures.regression-1.sh +91 -0
  260. package/dist/skills/llm-wiki/tests/graph-data-confidence-merge.regression-1.sh +309 -0
  261. package/dist/skills/llm-wiki/tests/graph-data-source-paths.regression-1.sh +49 -0
  262. package/dist/skills/llm-wiki/tests/graph-html-a11y.regression-1.sh +47 -0
  263. package/dist/skills/llm-wiki/tests/graph-html-brand-link.regression-1.sh +47 -0
  264. package/dist/skills/llm-wiki/tests/graph-html-density.regression-1.sh +228 -0
  265. package/dist/skills/llm-wiki/tests/graph-html-drawer-neighbors.regression-1.sh +47 -0
  266. package/dist/skills/llm-wiki/tests/graph-html-insights.regression-1.sh +80 -0
  267. package/dist/skills/llm-wiki/tests/graph-html-long-label.regression-1.sh +92 -0
  268. package/dist/skills/llm-wiki/tests/graph-html-minimap.regression-1.sh +51 -0
  269. package/dist/skills/llm-wiki/tests/graph-html-mobile.regression-1.sh +48 -0
  270. package/dist/skills/llm-wiki/tests/graph-html-oriental-atlas-contract.regression-1.sh +94 -0
  271. package/dist/skills/llm-wiki/tests/graph-html-oriental-design-contract.regression-1.sh +82 -0
  272. package/dist/skills/llm-wiki/tests/graph-html-search.regression-1.sh +52 -0
  273. package/dist/skills/llm-wiki/tests/graph-html-styles.regression-1.sh +66 -0
  274. package/dist/skills/llm-wiki/tests/graph-html-toolbar.regression-1.sh +62 -0
  275. package/dist/skills/llm-wiki/tests/js/graph-wash-bootstrap.test.js +110 -0
  276. package/dist/skills/llm-wiki/tests/js/graph-wash-helpers.test.js +296 -0
  277. package/dist/skills/llm-wiki/tests/js/graph-wash-learning.test.js +260 -0
  278. package/dist/skills/llm-wiki/tests/js/graph-wash-queue.test.js +115 -0
  279. package/dist/skills/llm-wiki/tests/js/graph-wash-runtime-state.test.js +282 -0
  280. package/dist/skills/llm-wiki/tests/js/source-signal-coverage.test.js +48 -0
  281. package/dist/skills/llm-wiki/tests/js/source-signal-eligibility.test.js +202 -0
  282. package/dist/skills/llm-wiki/tests/lib/graph-html-engine-helpers.sh +110 -0
  283. package/dist/skills/llm-wiki/tests/lint-output.regression-1.sh +32 -0
  284. package/dist/skills/llm-wiki/tests/regression.sh +1754 -0
  285. package/dist/skills/llm-wiki/workbench/.claude/skills/.gitkeep +0 -0
  286. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/SKILL.md +590 -0
  287. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/__init__.py +1 -0
  288. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/accept_changes.py +135 -0
  289. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/comment.py +318 -0
  290. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/helpers/__init__.py +0 -0
  291. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/helpers/merge_runs.py +199 -0
  292. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/helpers/simplify_redlines.py +197 -0
  293. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/pack.py +159 -0
  294. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  295. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  296. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  297. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  298. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  299. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  300. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  301. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  302. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  303. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  304. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  305. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  306. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  307. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  308. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  309. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  310. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  311. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  312. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  313. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  314. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  315. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  316. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  317. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  318. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  319. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  320. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  321. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  322. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  323. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  324. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  325. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/mce/mc.xsd +75 -0
  326. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  327. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  328. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  329. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  330. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  331. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  332. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  333. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/soffice.py +183 -0
  334. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/unpack.py +132 -0
  335. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/validate.py +111 -0
  336. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/validators/__init__.py +15 -0
  337. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/validators/base.py +847 -0
  338. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/validators/docx.py +446 -0
  339. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/validators/pptx.py +275 -0
  340. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/office/validators/redlining.py +247 -0
  341. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/templates/comments.xml +3 -0
  342. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/templates/commentsExtended.xml +3 -0
  343. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
  344. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/templates/commentsIds.xml +3 -0
  345. package/dist/skills/llm-wiki/workbench/.claude/skills/docx/scripts/templates/people.xml +3 -0
  346. package/dist/skills/llm-wiki/workbench/.claude/skills/pdf/SKILL.md +314 -0
  347. package/dist/skills/llm-wiki/workbench/.claude/skills/pdf/forms.md +294 -0
  348. package/dist/skills/llm-wiki/workbench/.claude/skills/pdf/reference.md +612 -0
  349. package/dist/skills/llm-wiki/workbench/.claude/skills/pdf/scripts/check_bounding_boxes.py +65 -0
  350. package/dist/skills/llm-wiki/workbench/.claude/skills/pdf/scripts/check_fillable_fields.py +11 -0
  351. package/dist/skills/llm-wiki/workbench/.claude/skills/pdf/scripts/convert_pdf_to_images.py +33 -0
  352. package/dist/skills/llm-wiki/workbench/.claude/skills/pdf/scripts/create_validation_image.py +37 -0
  353. package/dist/skills/llm-wiki/workbench/.claude/skills/pdf/scripts/extract_form_field_info.py +122 -0
  354. package/dist/skills/llm-wiki/workbench/.claude/skills/pdf/scripts/extract_form_structure.py +115 -0
  355. package/dist/skills/llm-wiki/workbench/.claude/skills/pdf/scripts/fill_fillable_fields.py +98 -0
  356. package/dist/skills/llm-wiki/workbench/.claude/skills/pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
  357. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/SKILL.md +232 -0
  358. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/editing.md +205 -0
  359. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/pptxgenjs.md +420 -0
  360. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/__init__.py +0 -0
  361. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/add_slide.py +195 -0
  362. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/clean.py +286 -0
  363. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/helpers/__init__.py +0 -0
  364. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/helpers/merge_runs.py +199 -0
  365. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/helpers/simplify_redlines.py +197 -0
  366. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/pack.py +159 -0
  367. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  368. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  369. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  370. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  371. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  372. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  373. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  374. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  375. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  376. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  377. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  378. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  379. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  380. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  381. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  382. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  383. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  384. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  385. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  386. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  387. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  388. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  389. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  390. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  391. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  392. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  393. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  394. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  395. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  396. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  397. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  398. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/mce/mc.xsd +75 -0
  399. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  400. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  401. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  402. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  403. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  404. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  405. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  406. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/soffice.py +183 -0
  407. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/unpack.py +132 -0
  408. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/validate.py +111 -0
  409. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/validators/__init__.py +15 -0
  410. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/validators/base.py +847 -0
  411. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/validators/docx.py +446 -0
  412. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/validators/pptx.py +275 -0
  413. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/office/validators/redlining.py +247 -0
  414. package/dist/skills/llm-wiki/workbench/.claude/skills/pptx/scripts/thumbnail.py +289 -0
  415. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/SKILL.md +292 -0
  416. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/helpers/__init__.py +0 -0
  417. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/helpers/merge_runs.py +199 -0
  418. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/helpers/simplify_redlines.py +197 -0
  419. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/pack.py +159 -0
  420. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  421. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  422. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  423. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  424. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  425. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  426. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  427. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  428. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  429. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  430. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  431. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  432. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  433. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  434. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  435. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  436. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  437. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  438. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  439. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  440. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  441. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  442. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  443. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  444. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  445. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  446. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  447. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  448. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  449. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  450. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  451. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/mce/mc.xsd +75 -0
  452. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  453. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  454. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  455. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  456. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  457. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  458. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  459. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/soffice.py +183 -0
  460. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/unpack.py +132 -0
  461. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/validate.py +111 -0
  462. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/validators/__init__.py +15 -0
  463. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/validators/base.py +847 -0
  464. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/validators/docx.py +446 -0
  465. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/validators/pptx.py +275 -0
  466. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/office/validators/redlining.py +247 -0
  467. package/dist/skills/llm-wiki/workbench/.claude/skills/xlsx/scripts/recalc.py +184 -0
  468. package/dist/skills/llm-wiki/workbench/AGENTS.md +110 -0
  469. package/dist/skills/llm-wiki/workbench/CLAUDE.md +54 -0
  470. package/dist/skills/llm-wiki/workbench/PRODUCT.md +1268 -0
  471. package/dist/skills/llm-wiki/workbench/docs/current-kb-retrieval-design.md +587 -0
  472. package/dist/skills/llm-wiki/workbench/docs/graph-evolution-1-design.md +210 -0
  473. package/dist/skills/llm-wiki/workbench/docs/stage-2-design.md +1139 -0
  474. package/dist/skills/llm-wiki/workbench/docs/stage-3-design.md +1269 -0
  475. package/dist/skills/llm-wiki/workbench/docs/stage-3.5-design.md +1164 -0
  476. package/dist/skills/llm-wiki/workbench/docs/stage-4-design.md +504 -0
  477. package/dist/skills/llm-wiki/workbench/docs/stage-4.5-design.md +195 -0
  478. package/dist/skills/llm-wiki/workbench/docs/superpowers/specs/2026-05-28-resizable-preview-layout-design.md +165 -0
  479. package/dist/skills/llm-wiki/workbench/docs/superpowers/specs/2026-05-28-settings-panel-scroll-fix.md +50 -0
  480. package/dist/skills/llm-wiki/workbench/server/package.json +29 -0
  481. package/dist/skills/llm-wiki/workbench/server/src/agent.ts +457 -0
  482. package/dist/skills/llm-wiki/workbench/server/src/artifacts.ts +248 -0
  483. package/dist/skills/llm-wiki/workbench/server/src/auth.ts +164 -0
  484. package/dist/skills/llm-wiki/workbench/server/src/config.ts +130 -0
  485. package/dist/skills/llm-wiki/workbench/server/src/conversations.test.ts +121 -0
  486. package/dist/skills/llm-wiki/workbench/server/src/conversations.ts +255 -0
  487. package/dist/skills/llm-wiki/workbench/server/src/digest/batch.ts +189 -0
  488. package/dist/skills/llm-wiki/workbench/server/src/digest/concurrency.test.ts +36 -0
  489. package/dist/skills/llm-wiki/workbench/server/src/digest/concurrency.ts +31 -0
  490. package/dist/skills/llm-wiki/workbench/server/src/digest/subagent.ts +91 -0
  491. package/dist/skills/llm-wiki/workbench/server/src/extensions/artifacts.ts +81 -0
  492. package/dist/skills/llm-wiki/workbench/server/src/extensions/knowledge-base.ts +263 -0
  493. package/dist/skills/llm-wiki/workbench/server/src/extensions/new-wiki.ts +42 -0
  494. package/dist/skills/llm-wiki/workbench/server/src/extensions/synthesis.ts +172 -0
  495. package/dist/skills/llm-wiki/workbench/server/src/graph-watcher.test.ts +196 -0
  496. package/dist/skills/llm-wiki/workbench/server/src/graph.test.ts +84 -0
  497. package/dist/skills/llm-wiki/workbench/server/src/graph.ts +379 -0
  498. package/dist/skills/llm-wiki/workbench/server/src/index.ts +1148 -0
  499. package/dist/skills/llm-wiki/workbench/server/src/knowledge-bases.test.ts +30 -0
  500. package/dist/skills/llm-wiki/workbench/server/src/knowledge-bases.ts +321 -0
  501. package/dist/skills/llm-wiki/workbench/server/src/pages.test.ts +16 -0
  502. package/dist/skills/llm-wiki/workbench/server/src/pages.ts +158 -0
  503. package/dist/skills/llm-wiki/workbench/server/src/retrieval.test.ts +151 -0
  504. package/dist/skills/llm-wiki/workbench/server/src/retrieval.ts +427 -0
  505. package/dist/skills/llm-wiki/workbench/server/src/tool-status-events.test.ts +363 -0
  506. package/dist/skills/llm-wiki/workbench/server/src/tool-status-events.ts +616 -0
  507. package/dist/skills/llm-wiki/workbench/server/src/wiki-init.test.ts +19 -0
  508. package/dist/skills/llm-wiki/workbench/server/src/wiki-init.ts +188 -0
  509. package/dist/skills/llm-wiki/workbench/server/tsconfig.json +10 -0
  510. package/dist/skills/llm-wiki/workbench/tsconfig.json +20 -0
  511. package/dist/skills/llm-wiki/workbench/web/components.json +21 -0
  512. package/dist/skills/llm-wiki/workbench/web/eslint.config.js +22 -0
  513. package/dist/skills/llm-wiki/workbench/web/index.html +13 -0
  514. package/dist/skills/llm-wiki/workbench/web/package.json +48 -0
  515. package/dist/skills/llm-wiki/workbench/web/src/App.tsx +749 -0
  516. package/dist/skills/llm-wiki/workbench/web/src/components/AddExternalDialog.tsx +373 -0
  517. package/dist/skills/llm-wiki/workbench/web/src/components/ArtifactView.tsx +15 -0
  518. package/dist/skills/llm-wiki/workbench/web/src/components/BatchDigestPanel.tsx +106 -0
  519. package/dist/skills/llm-wiki/workbench/web/src/components/ChatPanel.tsx +821 -0
  520. package/dist/skills/llm-wiki/workbench/web/src/components/CommandMenu.tsx +65 -0
  521. package/dist/skills/llm-wiki/workbench/web/src/components/ExportButtons.tsx +54 -0
  522. package/dist/skills/llm-wiki/workbench/web/src/components/GraphPanel.tsx +516 -0
  523. package/dist/skills/llm-wiki/workbench/web/src/components/GraphReader.tsx +48 -0
  524. package/dist/skills/llm-wiki/workbench/web/src/components/GraphSelection.tsx +100 -0
  525. package/dist/skills/llm-wiki/workbench/web/src/components/MarkdownView.tsx +56 -0
  526. package/dist/skills/llm-wiki/workbench/web/src/components/NewWikiDialog.tsx +101 -0
  527. package/dist/skills/llm-wiki/workbench/web/src/components/RefMenu.tsx +40 -0
  528. package/dist/skills/llm-wiki/workbench/web/src/components/RightDrawer.tsx +227 -0
  529. package/dist/skills/llm-wiki/workbench/web/src/components/SettingsPanel.tsx +331 -0
  530. package/dist/skills/llm-wiki/workbench/web/src/components/Sidebar.tsx +436 -0
  531. package/dist/skills/llm-wiki/workbench/web/src/components/ToolHistorySummary.tsx +123 -0
  532. package/dist/skills/llm-wiki/workbench/web/src/components/ToolStatusRunway.tsx +90 -0
  533. package/dist/skills/llm-wiki/workbench/web/src/components/renderers/DownloadOnlyRenderer.tsx +88 -0
  534. package/dist/skills/llm-wiki/workbench/web/src/components/renderers/HtmlRenderer.tsx +92 -0
  535. package/dist/skills/llm-wiki/workbench/web/src/components/ui/button.tsx +52 -0
  536. package/dist/skills/llm-wiki/workbench/web/src/components/ui/command.tsx +50 -0
  537. package/dist/skills/llm-wiki/workbench/web/src/components/ui/dialog.tsx +156 -0
  538. package/dist/skills/llm-wiki/workbench/web/src/components/ui/input.tsx +21 -0
  539. package/dist/skills/llm-wiki/workbench/web/src/components/ui/tooltip.tsx +57 -0
  540. package/dist/skills/llm-wiki/workbench/web/src/index.css +1737 -0
  541. package/dist/skills/llm-wiki/workbench/web/src/lib/api.ts +648 -0
  542. package/dist/skills/llm-wiki/workbench/web/src/lib/drawer-state.ts +79 -0
  543. package/dist/skills/llm-wiki/workbench/web/src/lib/graph-reader.ts +25 -0
  544. package/dist/skills/llm-wiki/workbench/web/src/lib/graph-selection-drawer.ts +35 -0
  545. package/dist/skills/llm-wiki/workbench/web/src/lib/graph-selection.ts +96 -0
  546. package/dist/skills/llm-wiki/workbench/web/src/lib/legacy-tool-status.ts +113 -0
  547. package/dist/skills/llm-wiki/workbench/web/src/lib/sse.ts +72 -0
  548. package/dist/skills/llm-wiki/workbench/web/src/lib/tool-status-format.ts +170 -0
  549. package/dist/skills/llm-wiki/workbench/web/src/lib/tool-status-model.ts +392 -0
  550. package/dist/skills/llm-wiki/workbench/web/src/lib/utils.ts +10 -0
  551. package/dist/skills/llm-wiki/workbench/web/src/lib/wiki-links.ts +32 -0
  552. package/dist/skills/llm-wiki/workbench/web/src/main.tsx +10 -0
  553. package/dist/skills/llm-wiki/workbench/web/test/api.test.ts +61 -0
  554. package/dist/skills/llm-wiki/workbench/web/test/chat-panel-tool-status.test.tsx +26 -0
  555. package/dist/skills/llm-wiki/workbench/web/test/graph-drawer-state.test.ts +78 -0
  556. package/dist/skills/llm-wiki/workbench/web/test/graph-reader-request.test.ts +32 -0
  557. package/dist/skills/llm-wiki/workbench/web/test/graph-reader.test.ts +62 -0
  558. package/dist/skills/llm-wiki/workbench/web/test/graph-selection-drawer.test.ts +56 -0
  559. package/dist/skills/llm-wiki/workbench/web/test/graph-selection.test.ts +83 -0
  560. package/dist/skills/llm-wiki/workbench/web/test/tool-history-summary.test.tsx +124 -0
  561. package/dist/skills/llm-wiki/workbench/web/test/tool-status-format.test.ts +107 -0
  562. package/dist/skills/llm-wiki/workbench/web/test/tool-status-model.test.ts +324 -0
  563. package/dist/skills/llm-wiki/workbench/web/test/tool-status-runway.test.ts +134 -0
  564. package/dist/skills/llm-wiki/workbench/web/test/wiki-links.test.ts +23 -0
  565. package/dist/skills/llm-wiki/workbench/web/tsconfig.app.json +30 -0
  566. package/dist/skills/llm-wiki/workbench/web/tsconfig.json +7 -0
  567. package/dist/skills/llm-wiki/workbench/web/tsconfig.node.json +24 -0
  568. package/dist/skills/llm-wiki/workbench/web/vite.config.ts +26 -0
  569. package/dist/skills/mean-reviewer/SECURITY.md +23 -0
  570. package/dist/skills/mean-reviewer/SKILL.md +203 -0
  571. package/dist/skills/mean-reviewer/examples/13446_1000_Layer_Networks_for_.pdf +0 -0
  572. package/dist/skills/mean-reviewer/examples/13446_mean_reviewer.md +116 -0
  573. package/dist/skills/mean-reviewer/examples/13446_mean_reviewer_author_rebuttal_simulated.md +282 -0
  574. package/dist/skills/mean-reviewer/examples/13446_mean_reviewer_reply.md +91 -0
  575. package/dist/skills/mean-reviewer/examples/13446_official_review.json +973 -0
  576. package/dist/skills/mean-reviewer/examples/ChatGPT_AC_Roleplay.jpeg +0 -0
  577. package/dist/skills/medical-imaging-review/SKILL.md +295 -0
  578. package/dist/skills/medical-imaging-review/references/CITATION_INTEGRITY.md +233 -0
  579. package/dist/skills/medical-imaging-review/references/DOMAINS.md +302 -0
  580. package/dist/skills/medical-imaging-review/references/HALLUCINATION_PATTERNS.md +211 -0
  581. package/dist/skills/medical-imaging-review/references/MCP_SETUP.md +216 -0
  582. package/dist/skills/medical-imaging-review/references/PARADIGM.md +221 -0
  583. package/dist/skills/medical-imaging-review/references/QUALITY_CHECKLIST.md +169 -0
  584. package/dist/skills/medical-imaging-review/references/TEMPLATES.md +291 -0
  585. package/dist/skills/medical-imaging-review/references/WORKFLOW.md +289 -0
  586. package/dist/skills/mermaid-diagram/SKILL.md +379 -0
  587. package/dist/skills/meta-apply/SKILL.md +137 -0
  588. package/dist/skills/meta-optimize/SKILL.md +253 -0
  589. package/dist/skills/monitor-experiment/SKILL.md +98 -0
  590. package/dist/skills/novelty-check/SKILL.md +89 -0
  591. package/dist/skills/openalex/SKILL.md +228 -0
  592. package/dist/skills/overleaf-sync/SKILL.md +220 -0
  593. package/dist/skills/paper-claim-audit/SKILL.md +340 -0
  594. package/dist/skills/paper-compile/SKILL.md +253 -0
  595. package/dist/skills/paper-figure/SKILL.md +279 -0
  596. package/dist/skills/paper-illustration/SKILL.md +690 -0
  597. package/dist/skills/paper-illustration-image2/SKILL.md +383 -0
  598. package/dist/skills/paper-illustration-image2/scripts/paper_illustration_image2.py +255 -0
  599. package/dist/skills/paper-plan/SKILL.md +278 -0
  600. package/dist/skills/paper-poster/SKILL.md +19 -0
  601. package/dist/skills/paper-poster-html/DESIGN_FINAL.md +176 -0
  602. package/dist/skills/paper-poster-html/IMPLEMENTATION_CONVENTIONS.md +161 -0
  603. package/dist/skills/paper-poster-html/LICENSES/posterly-MIT.txt +21 -0
  604. package/dist/skills/paper-poster-html/NOTICE.md +57 -0
  605. package/dist/skills/paper-poster-html/SKILL.md +357 -0
  606. package/dist/skills/paper-poster-html/scripts/_posterly/__init__.py +0 -0
  607. package/dist/skills/paper-poster-html/scripts/_posterly/canvas.py +200 -0
  608. package/dist/skills/paper-poster-html/scripts/_posterly/measure.py +588 -0
  609. package/dist/skills/paper-poster-html/scripts/_posterly/polish.py +498 -0
  610. package/dist/skills/paper-poster-html/scripts/_posterly/preflight.py +489 -0
  611. package/dist/skills/paper-poster-html/scripts/_posterly/render.py +215 -0
  612. package/dist/skills/paper-poster-html/scripts/_posterly/textutil.py +16 -0
  613. package/dist/skills/paper-poster-html/scripts/_posterly/verify_final.py +171 -0
  614. package/dist/skills/paper-poster-html/scripts/asset_check.py +897 -0
  615. package/dist/skills/paper-poster-html/scripts/extract_pdf_figures.py +666 -0
  616. package/dist/skills/paper-poster-html/scripts/poster_check.py +251 -0
  617. package/dist/skills/paper-poster-html/scripts/preprocess_figures.py +238 -0
  618. package/dist/skills/paper-poster-html/scripts/render_preview.py +217 -0
  619. package/dist/skills/paper-poster-html/scripts/run_gates.py +556 -0
  620. package/dist/skills/paper-poster-html/scripts/style_check.py +1324 -0
  621. package/dist/skills/paper-poster-html/templates/COMPONENTS.md +462 -0
  622. package/dist/skills/paper-poster-html/templates/landscape_4col.html +1032 -0
  623. package/dist/skills/paper-poster-html/templates/landscape_hero.html +1046 -0
  624. package/dist/skills/paper-poster-html/templates/portrait_2col.html +947 -0
  625. package/dist/skills/paper-poster-html/templates/tokens/acl.json +9 -0
  626. package/dist/skills/paper-poster-html/templates/tokens/cvpr.json +9 -0
  627. package/dist/skills/paper-poster-html/templates/tokens/generic.json +9 -0
  628. package/dist/skills/paper-poster-html/templates/tokens/iclr.json +9 -0
  629. package/dist/skills/paper-poster-html/templates/tokens/icml.json +9 -0
  630. package/dist/skills/paper-poster-html/templates/tokens/neurips.json +9 -0
  631. package/dist/skills/paper-slide-deck/SKILL.md +464 -0
  632. package/dist/skills/paper-slide-deck/references/analysis-framework.md +392 -0
  633. package/dist/skills/paper-slide-deck/references/base-prompt.md +70 -0
  634. package/dist/skills/paper-slide-deck/references/content-rules.md +95 -0
  635. package/dist/skills/paper-slide-deck/references/figure-container-template.md +177 -0
  636. package/dist/skills/paper-slide-deck/references/modification-guide.md +85 -0
  637. package/dist/skills/paper-slide-deck/references/outline-template.md +437 -0
  638. package/dist/skills/paper-slide-deck/references/styles/academic-paper.md +109 -0
  639. package/dist/skills/paper-slide-deck/references/styles/blueprint.md +67 -0
  640. package/dist/skills/paper-slide-deck/references/styles/bold-editorial.md +70 -0
  641. package/dist/skills/paper-slide-deck/references/styles/chalkboard.md +68 -0
  642. package/dist/skills/paper-slide-deck/references/styles/corporate.md +69 -0
  643. package/dist/skills/paper-slide-deck/references/styles/dark-atmospheric.md +69 -0
  644. package/dist/skills/paper-slide-deck/references/styles/editorial-infographic.md +73 -0
  645. package/dist/skills/paper-slide-deck/references/styles/fantasy-animation.md +69 -0
  646. package/dist/skills/paper-slide-deck/references/styles/intuition-machine.md +72 -0
  647. package/dist/skills/paper-slide-deck/references/styles/minimal.md +64 -0
  648. package/dist/skills/paper-slide-deck/references/styles/notion.md +69 -0
  649. package/dist/skills/paper-slide-deck/references/styles/pixel-art.md +67 -0
  650. package/dist/skills/paper-slide-deck/references/styles/scientific.md +73 -0
  651. package/dist/skills/paper-slide-deck/references/styles/sketch-notes.md +66 -0
  652. package/dist/skills/paper-slide-deck/references/styles/vector-illustration.md +72 -0
  653. package/dist/skills/paper-slide-deck/references/styles/vintage.md +73 -0
  654. package/dist/skills/paper-slide-deck/references/styles/watercolor.md +68 -0
  655. package/dist/skills/paper-slide-deck/scripts/apply-template.ts +282 -0
  656. package/dist/skills/paper-slide-deck/scripts/detect-figures.ts +283 -0
  657. package/dist/skills/paper-slide-deck/scripts/extract-figure.ts +140 -0
  658. package/dist/skills/paper-slide-deck/scripts/generate-slides.py +182 -0
  659. package/dist/skills/paper-slide-deck/scripts/merge-to-pdf.ts +116 -0
  660. package/dist/skills/paper-slide-deck/scripts/merge-to-pptx.ts +137 -0
  661. package/dist/skills/paper-slide-deck/scripts/package-lock.json +899 -0
  662. package/dist/skills/paper-slide-deck/scripts/package.json +8 -0
  663. package/dist/skills/paper-slides/SKILL.md +571 -0
  664. package/dist/skills/paper-talk/SKILL.md +381 -0
  665. package/dist/skills/paper-write/SKILL.md +411 -0
  666. package/dist/skills/paper-write/templates/IEEEtran.bst +2409 -0
  667. package/dist/skills/paper-write/templates/IEEEtran.cls +6347 -0
  668. package/dist/skills/paper-write/templates/aaai2026.bst +1493 -0
  669. package/dist/skills/paper-write/templates/aaai2026.sty +315 -0
  670. package/dist/skills/paper-write/templates/aaai2026.tex +952 -0
  671. package/dist/skills/paper-write/templates/acl.sty +312 -0
  672. package/dist/skills/paper-write/templates/acl2026.tex +377 -0
  673. package/dist/skills/paper-write/templates/acl_natbib.bst +1940 -0
  674. package/dist/skills/paper-write/templates/acm.bst +3081 -0
  675. package/dist/skills/paper-write/templates/acm_mm2026.tex +204 -0
  676. package/dist/skills/paper-write/templates/acmart.cls +3520 -0
  677. package/dist/skills/paper-write/templates/cvpr.bst +1448 -0
  678. package/dist/skills/paper-write/templates/cvpr.sty +508 -0
  679. package/dist/skills/paper-write/templates/cvpr2026.tex +63 -0
  680. package/dist/skills/paper-write/templates/iclr2026.tex +84 -0
  681. package/dist/skills/paper-write/templates/iclr2026_conference.bst +1440 -0
  682. package/dist/skills/paper-write/templates/iclr2026_conference.sty +246 -0
  683. package/dist/skills/paper-write/templates/icml2025.tex +87 -0
  684. package/dist/skills/paper-write/templates/icml2026.sty +767 -0
  685. package/dist/skills/paper-write/templates/icml2026.tex +662 -0
  686. package/dist/skills/paper-write/templates/ieee_conference.tex +89 -0
  687. package/dist/skills/paper-write/templates/ieee_journal.tex +93 -0
  688. package/dist/skills/paper-write/templates/math_commands.tex +48 -0
  689. package/dist/skills/paper-write/templates/neurips2025.tex +80 -0
  690. package/dist/skills/paper-write/templates/neurips2026.tex +493 -0
  691. package/dist/skills/paper-write/templates/neurips_2026.sty +437 -0
  692. package/dist/skills/paper-writing/SKILL.md +632 -0
  693. package/dist/skills/patent-novelty-check/SKILL.md +153 -0
  694. package/dist/skills/patent-pipeline/SKILL.md +344 -0
  695. package/dist/skills/patent-review/SKILL.md +202 -0
  696. package/dist/skills/pixel-art/SKILL.md +139 -0
  697. package/dist/skills/pre-submission-reviewer/SKILL.md +247 -0
  698. package/dist/skills/pre-submission-reviewer/references/forbidden-patterns.md +163 -0
  699. package/dist/skills/pre-submission-reviewer/references/grammar-rules.md +135 -0
  700. package/dist/skills/pre-submission-reviewer/references/latex-rules.md +158 -0
  701. package/dist/skills/pre-submission-reviewer/references/logic-and-structure.md +125 -0
  702. package/dist/skills/pre-submission-reviewer/references/section-guides.md +169 -0
  703. package/dist/skills/prior-art-search/SKILL.md +146 -0
  704. package/dist/skills/proof-checker/SKILL.md +499 -0
  705. package/dist/skills/proof-writer/SKILL.md +222 -0
  706. package/dist/skills/qzcli/SKILL.md +316 -0
  707. package/dist/skills/rebuttal/SKILL.md +304 -0
  708. package/dist/skills/render-html/SKILL.md +299 -0
  709. package/dist/skills/render-html/scripts/render_html.py +905 -0
  710. package/dist/skills/render-html/scripts/templates/academic.html +342 -0
  711. package/dist/skills/render-html/scripts/templates/dashboard.html +333 -0
  712. package/dist/skills/research-lit/SKILL.md +353 -0
  713. package/dist/skills/research-paper-writing/SKILL.md +99 -0
  714. package/dist/skills/research-paper-writing/agents/openai.yaml +4 -0
  715. package/dist/skills/research-paper-writing/references/abstract.md +102 -0
  716. package/dist/skills/research-paper-writing/references/conclusion.md +35 -0
  717. package/dist/skills/research-paper-writing/references/does-my-writing-flow-source.md +45 -0
  718. package/dist/skills/research-paper-writing/references/examples/abstract/template-a.md +21 -0
  719. package/dist/skills/research-paper-writing/references/examples/abstract/template-b.md +34 -0
  720. package/dist/skills/research-paper-writing/references/examples/abstract/template-c.md +28 -0
  721. package/dist/skills/research-paper-writing/references/examples/abstract-examples.md +13 -0
  722. package/dist/skills/research-paper-writing/references/examples/index.md +21 -0
  723. package/dist/skills/research-paper-writing/references/examples/introduction/novel-task-challenge-decomposition.md +18 -0
  724. package/dist/skills/research-paper-writing/references/examples/introduction/pipeline-not-recommended-abstract-only.md +30 -0
  725. package/dist/skills/research-paper-writing/references/examples/introduction/pipeline-version-1-one-contribution-multi-advantages.md +30 -0
  726. package/dist/skills/research-paper-writing/references/examples/introduction/pipeline-version-2-two-contributions.md +34 -0
  727. package/dist/skills/research-paper-writing/references/examples/introduction/pipeline-version-3-new-module-on-existing-pipeline.md +18 -0
  728. package/dist/skills/research-paper-writing/references/examples/introduction/pipeline-version-4-observation-driven.md +16 -0
  729. package/dist/skills/research-paper-writing/references/examples/introduction/technical-challenge-version-1-existing-task.md +32 -0
  730. package/dist/skills/research-paper-writing/references/examples/introduction/technical-challenge-version-2-existing-task-insight-backed-by-traditional.md +33 -0
  731. package/dist/skills/research-paper-writing/references/examples/introduction/technical-challenge-version-3-novel-task.md +21 -0
  732. package/dist/skills/research-paper-writing/references/examples/introduction/version-1-task-then-application.md +14 -0
  733. package/dist/skills/research-paper-writing/references/examples/introduction/version-2-application-first.md +10 -0
  734. package/dist/skills/research-paper-writing/references/examples/introduction/version-3-general-to-specific-setting.md +14 -0
  735. package/dist/skills/research-paper-writing/references/examples/introduction/version-4-open-with-challenge.md +20 -0
  736. package/dist/skills/research-paper-writing/references/examples/introduction-examples.md +25 -0
  737. package/dist/skills/research-paper-writing/references/examples/method/example-of-the-three-elements.md +67 -0
  738. package/dist/skills/research-paper-writing/references/examples/method/method-writing-common-issues-note.md +10 -0
  739. package/dist/skills/research-paper-writing/references/examples/method/module-design-instant-ngp.md +55 -0
  740. package/dist/skills/research-paper-writing/references/examples/method/module-motivation-patterns.md +15 -0
  741. package/dist/skills/research-paper-writing/references/examples/method/module-triad-neural-body.md +19 -0
  742. package/dist/skills/research-paper-writing/references/examples/method/neural-body-annotated-figure-text.md +66 -0
  743. package/dist/skills/research-paper-writing/references/examples/method/overview-template.md +30 -0
  744. package/dist/skills/research-paper-writing/references/examples/method/pre-writing-questions.md +17 -0
  745. package/dist/skills/research-paper-writing/references/examples/method/section-skeleton.md +9 -0
  746. package/dist/skills/research-paper-writing/references/examples/method-examples.md +24 -0
  747. package/dist/skills/research-paper-writing/references/experiments.md +102 -0
  748. package/dist/skills/research-paper-writing/references/introduction.md +408 -0
  749. package/dist/skills/research-paper-writing/references/method.md +196 -0
  750. package/dist/skills/research-paper-writing/references/paper-review.md +86 -0
  751. package/dist/skills/research-paper-writing/references/related-work.md +41 -0
  752. package/dist/skills/research-pipeline/SKILL.md +257 -0
  753. package/dist/skills/research-proposal/SKILL.md +573 -0
  754. package/dist/skills/research-proposal/assets/proposal_scaffold_en.md +323 -0
  755. package/dist/skills/research-proposal/assets/proposal_scaffold_zh.md +346 -0
  756. package/dist/skills/research-proposal/references/DOMAIN_TEMPLATES.md +518 -0
  757. package/dist/skills/research-proposal/references/LITERATURE_WORKFLOW.md +529 -0
  758. package/dist/skills/research-proposal/references/QUALITY_CHECKLIST.md +325 -0
  759. package/dist/skills/research-proposal/references/STRUCTURE_GUIDE.md +398 -0
  760. package/dist/skills/research-proposal/references/WRITING_STYLE_GUIDE.md +661 -0
  761. package/dist/skills/research-refine/SKILL.md +716 -0
  762. package/dist/skills/research-refine-pipeline/SKILL.md +186 -0
  763. package/dist/skills/research-review/SKILL.md +119 -0
  764. package/dist/skills/research-wiki/SKILL.md +373 -0
  765. package/dist/skills/resubmit-pipeline/SKILL.md +427 -0
  766. package/dist/skills/result-to-claim/SKILL.md +190 -0
  767. package/dist/skills/run-experiment/SKILL.md +227 -0
  768. package/dist/skills/scientific-figure-making/SKILL.md +38 -0
  769. package/dist/skills/scientific-figure-making/references/api.md +152 -0
  770. package/dist/skills/scientific-figure-making/references/common-patterns.md +74 -0
  771. package/dist/skills/scientific-figure-making/references/demos.md +30 -0
  772. package/dist/skills/scientific-figure-making/references/design-theory.md +138 -0
  773. package/dist/skills/scientific-figure-making/references/tutorials.md +135 -0
  774. package/dist/skills/semantic-scholar/SKILL.md +219 -0
  775. package/dist/skills/serverless-modal/SKILL.md +324 -0
  776. package/dist/skills/slides-polish/SKILL.md +563 -0
  777. package/dist/skills/specification-writing/SKILL.md +211 -0
  778. package/dist/skills/strategist/SKILL.md +670 -0
  779. package/dist/skills/strategist/references/quality_standards.md +336 -0
  780. package/dist/skills/strategist/references/search_strategy.md +459 -0
  781. package/dist/skills/strategist/scripts/evaluate_samples.py +300 -0
  782. package/dist/skills/strategist/scripts/gap_analysis.py +399 -0
  783. package/dist/skills/system-profile/SKILL.md +103 -0
  784. package/dist/skills/tech-paper-template/SKILL.md +192 -0
  785. package/dist/skills/tech-paper-template/references/consistency-checks.md +166 -0
  786. package/dist/skills/tech-paper-template/references/paper-types.md +127 -0
  787. package/dist/skills/tech-paper-template/references/thinking-template.md +194 -0
  788. package/dist/skills/tech-paper-template/references/worked-examples.md +124 -0
  789. package/dist/skills/training-check/SKILL.md +83 -0
  790. package/dist/skills/vast-gpu/SKILL.md +380 -0
  791. package/dist/skills/vibe-research-workflow/SKILL.md +189 -0
  792. package/dist/skills/vibe-research-workflow/references/behavior-guidelines.md +149 -0
  793. package/dist/skills/vibe-research-workflow/references/tool-selection.md +144 -0
  794. package/dist/skills/vibe-research-workflow/references/vibe-coding.md +154 -0
  795. package/dist/skills/vibe-research-workflow/references/vibe-figure.md +131 -0
  796. package/dist/skills/vibe-research-workflow/references/vibe-writing.md +181 -0
  797. package/dist/skills/wiki-enrich/SKILL.md +254 -0
  798. package/dist/skills/writing-systems-papers/SKILL.md +184 -0
  799. package/package.json +1 -1
@@ -0,0 +1,1324 @@
1
+ #!/usr/bin/env python3
2
+ """style_check — the style HARD gate for HTML academic posters.
3
+
4
+ Implements the 12 style rules of DESIGN_FINAL.md §3 (plus the §12.5 nit-1
5
+ refinement on rule 8) split across two gates:
6
+
7
+ Source gate (rules 1,2,3,5,6,7,8,9,10,11) — pure static analysis. We
8
+ parse the HTML with ``html.parser`` to walk tags+attributes, extract
9
+ the ``<style>`` blocks, strip CSS comments, locate the design-token
10
+ block via the canonical comment pair, and then scan for: color
11
+ literals outside the token block, forbidden ``style=`` attributes,
12
+ gradients, font-family stacks against the whitelist, font-size values
13
+ against the ``--fs-*`` scale, font-size token count, and the
14
+ data-attribute / inline-SVG contracts.
15
+
16
+ Render gate (rules 4 and 12) — needs computed style, so it lazy-imports
17
+ Playwright, prints-emulates the poster at the @page viewport (reusing
18
+ the vendored ``_posterly`` canvas+render helpers so the viewport basis
19
+ matches ``measure``/``polish`` exactly), reads every element's
20
+ computed colors, and runs the non-neutral hue-clustering check (rule
21
+ 4) and the large-dark-area check (rule 12).
22
+
23
+ WHY two gates: the source gate is cheap, deterministic, and runnable with
24
+ no browser — it is what Phase 3 scaffolding relies on. The render gate
25
+ catches what static analysis cannot (a token resolving to an off-palette
26
+ hue, or a dark slab assembled from many small boxes). ``--no-render``
27
+ keeps the source gate live while the heavier render gate is SKIPPED — the
28
+ overall status can still PASS from the source gate alone, but we print a
29
+ notice so the user knows two rules were not evaluated.
30
+
31
+ CLI (IMPLEMENTATION_CONVENTIONS.md §C):
32
+ python3 style_check.py POSTER.html [--tokens TOKENS.json] [--json OUT.json]
33
+ [--no-render]
34
+
35
+ Exit codes: 0 = PASS (no hard failures), 1 = at least one HARD rule
36
+ failed, 2 = usage / environment error.
37
+ """
38
+ from __future__ import annotations
39
+
40
+ import argparse
41
+ import colorsys
42
+ import json
43
+ import math
44
+ import os
45
+ import re
46
+ import sys
47
+ from html.parser import HTMLParser
48
+ from pathlib import Path
49
+ from typing import Any
50
+
51
+ # Make `_posterly` importable when this file is run directly via
52
+ # `python style_check.py …` (it lives in the same scripts/ dir).
53
+ _THIS_DIR = os.path.dirname(os.path.abspath(__file__))
54
+ if _THIS_DIR not in sys.path:
55
+ sys.path.insert(0, _THIS_DIR)
56
+
57
+ from _posterly.textutil import ascii_safe # noqa: E402
58
+
59
+
60
+ def _eprint(*args: Any, **kw: Any) -> None:
61
+ print(*args, file=sys.stderr, **kw)
62
+
63
+
64
+ # ---------------------------------------------------------------------------
65
+ # Whitelists (DESIGN_FINAL §2 / IMPLEMENTATION_CONVENTIONS §A).
66
+ # ---------------------------------------------------------------------------
67
+
68
+ #: Serif families allowed for body text (and the `--font-serif` token).
69
+ SERIF_WHITELIST = {
70
+ "charter", "source serif pro", "georgia", "times new roman", "serif",
71
+ }
72
+ #: Sans families allowed for headings / headers / table labels (and
73
+ #: `--font-sans`). "aptos" and "helvetica neue" / "arial" per §2.
74
+ SANS_WHITELIST = {
75
+ "inter", "aptos", "helvetica neue", "arial", "sans-serif",
76
+ }
77
+ #: Mono families allowed for code only.
78
+ MONO_WHITELIST = {"menlo", "consolas", "monospace"}
79
+
80
+ #: The canonical font-size scale tokens. font-size declarations must use
81
+ #: one of these via `var(--fs-N)` (rule 8); the count gate (rule 9) warns
82
+ #: when MORE than 9 distinct --fs tokens are *defined* in the token block.
83
+ FS_TOKEN_RE = re.compile(r"--fs-(\d+)\b")
84
+
85
+ #: A `var(--fs-N)` reference inside a font-size value.
86
+ FS_VAR_REF_RE = re.compile(r"var\(\s*--fs-\d+\s*\)")
87
+
88
+ #: A predefined component variant suffix on a selector: BEM-ish `--<word>`
89
+ #: appended to a class, e.g. `.eqn--large`, `.card--compact`,
90
+ #: `.figure--wide`. §12.5 nit 1: `calc(var(--fs-*) * k)` is allowed ONLY
91
+ #: in a rule whose selector carries such a variant suffix; everywhere else
92
+ #: it is a HARD fail (an arbitrary per-element font-size override sneaking
93
+ #: past the token scale).
94
+ VARIANT_SUFFIX_RE = re.compile(r"\.[A-Za-z][\w-]*--[A-Za-z][\w-]*")
95
+
96
+ #: A `calc(... var(--fs-N) ...)` expression inside a font-size value — the
97
+ #: only legal non-bare-token form, and only inside a variant rule.
98
+ CALC_FS_RE = re.compile(r"calc\([^)]*var\(\s*--fs-\d+\s*\)[^)]*\)")
99
+
100
+
101
+ # ---------------------------------------------------------------------------
102
+ # Color-literal detection (rules 1, 2, 3).
103
+ # ---------------------------------------------------------------------------
104
+
105
+ #: Matches any CSS color literal we forbid outside the token block:
106
+ #: #hex (3/4/6/8 digit), rgb()/rgba(), hsl()/hsla(). Named colors are NOT
107
+ #: matched — they are caught structurally where it matters (the templates
108
+ #: ship `transparent` / `currentColor` which are intentionally allowed),
109
+ #: and chasing the full CSS named-color list would create false positives
110
+ #: on words like "red" appearing in prose. The literals above are the
111
+ #: forms a hand-edited off-palette color actually takes.
112
+ COLOR_LITERAL_RE = re.compile(
113
+ r"""
114
+ (?:\#[0-9a-fA-F]{3,8}\b) # #hex
115
+ | (?:\brgba?\s*\([^)]*\)) # rgb( ) / rgba( )
116
+ | (?:\bhsla?\s*\([^)]*\)) # hsl( ) / hsla( )
117
+ """,
118
+ re.VERBOSE,
119
+ )
120
+
121
+ #: A single rgb/rgba/hsl/hsla literal we need to PARSE (for the
122
+ #: radial-gradient alpha check in rule 5). Captures the function name and
123
+ #: the comma/space separated args.
124
+ _FUNC_COLOR_RE = re.compile(
125
+ r"\b(rgb|rgba|hsl|hsla)\s*\(([^)]*)\)", re.IGNORECASE
126
+ )
127
+
128
+
129
+ def _parse_alpha(func: str, args: str) -> float | None:
130
+ """Extract the alpha channel from an rgba()/hsla() literal.
131
+
132
+ Returns 1.0 for the opaque rgb()/hsl() forms (no alpha), the parsed
133
+ alpha for rgba()/hsla(), or ``None`` if the literal can't be parsed.
134
+ We only need alpha here (rule 5's radial-gradient stop check), so we
135
+ don't bother converting the color channels.
136
+ """
137
+ parts = [p.strip() for p in re.split(r"[,/]", args) if p.strip()]
138
+ func = func.lower()
139
+ if func in ("rgb", "hsl"):
140
+ return 1.0
141
+ if func in ("rgba", "hsla"):
142
+ if len(parts) < 4:
143
+ return None
144
+ a = parts[3]
145
+ try:
146
+ if a.endswith("%"):
147
+ return float(a[:-1]) / 100.0
148
+ return float(a)
149
+ except ValueError:
150
+ return None
151
+ return None
152
+
153
+
154
+ # ---------------------------------------------------------------------------
155
+ # HTML parsing: collect <style> CSS and every element's attributes.
156
+ # ---------------------------------------------------------------------------
157
+
158
+
159
+ class _PosterParser(HTMLParser):
160
+ """Walk the poster HTML, recording two things the source gate needs:
161
+
162
+ 1. The concatenated text of all ``<style>`` blocks (so we can run
163
+ CSS-level checks against the same content the browser sees).
164
+ 2. A flat list of ``(tag, attrs_dict, exempt_path)`` for every
165
+ start/startend tag, where ``exempt_path`` flags whether the
166
+ element sits inside a ``data-color-exempt="logo"`` subtree — so
167
+ rule 1/2/11 exemptions can be applied without a full DOM.
168
+
169
+ We track the exempt-subtree depth with a small stack: when we enter an
170
+ element carrying ``data-color-exempt="logo"`` (or a ``data-source``
171
+ "paper" subtree, for the SVG/style exemptions), every descendant
172
+ inherits the exemption until the matching close tag.
173
+ """
174
+
175
+ #: Void elements never get a matching end tag, so they must not push
176
+ #: onto the exempt stack (otherwise the stack desyncs).
177
+ _VOID = {
178
+ "area", "base", "br", "col", "embed", "hr", "img", "input",
179
+ "link", "meta", "param", "source", "track", "wbr",
180
+ }
181
+
182
+ def __init__(self) -> None:
183
+ super().__init__(convert_charrefs=True)
184
+ self.style_css_parts: list[str] = []
185
+ self._in_style = False
186
+ # Each element record: tag, attrs(dict), inside_logo, inside_paper.
187
+ self.elements: list[dict[str, Any]] = []
188
+ # Stack of (tag, opened_logo, opened_paper) for open non-void
189
+ # elements, used to inherit exemption to descendants.
190
+ self._stack: list[tuple[str, bool, bool]] = []
191
+
192
+ # -- exemption bookkeeping ------------------------------------------
193
+ def _inside_logo(self) -> bool:
194
+ return any(s[1] for s in self._stack)
195
+
196
+ def _inside_paper(self) -> bool:
197
+ return any(s[2] for s in self._stack)
198
+
199
+ def _record(self, tag: str, attrs: list[tuple[str, str | None]],
200
+ self_closing: bool) -> tuple[bool, bool]:
201
+ d = {k.lower(): (v if v is not None else "") for k, v in attrs}
202
+ opens_logo = d.get("data-color-exempt", "").lower() == "logo"
203
+ opens_paper = d.get("data-source", "").lower() == "paper"
204
+ inside_logo = self._inside_logo() or opens_logo
205
+ inside_paper = self._inside_paper() or opens_paper
206
+ self.elements.append({
207
+ "tag": tag,
208
+ "attrs": d,
209
+ "inside_logo": inside_logo,
210
+ "inside_paper": inside_paper,
211
+ "self_closing": self_closing,
212
+ })
213
+ return opens_logo, opens_paper
214
+
215
+ # -- HTMLParser hooks ----------------------------------------------
216
+ def handle_starttag(self, tag, attrs):
217
+ tag = tag.lower()
218
+ if tag == "style":
219
+ self._in_style = True
220
+ opens_logo, opens_paper = self._record(tag, attrs, self_closing=False)
221
+ if tag not in self._VOID:
222
+ self._stack.append((tag, opens_logo, opens_paper))
223
+
224
+ def handle_startendtag(self, tag, attrs):
225
+ # `<svg .../>`, `<img .../>` — self-closing; record but never push.
226
+ self._record(tag.lower(), attrs, self_closing=True)
227
+
228
+ def handle_endtag(self, tag):
229
+ tag = tag.lower()
230
+ if tag == "style":
231
+ self._in_style = False
232
+ # Pop back to (and including) the matching open tag. Tolerant of
233
+ # minor mismatches so a stray close tag can't desync the stack.
234
+ for i in range(len(self._stack) - 1, -1, -1):
235
+ if self._stack[i][0] == tag:
236
+ del self._stack[i:]
237
+ break
238
+
239
+ def handle_data(self, data):
240
+ if self._in_style:
241
+ self.style_css_parts.append(data)
242
+
243
+ @property
244
+ def style_css(self) -> str:
245
+ return "\n".join(self.style_css_parts)
246
+
247
+
248
+ def _strip_css_comments(css: str) -> str:
249
+ """Strip ``/* … */`` comments. We must NOT strip before locating the
250
+ token block (the markers ARE comments), so callers strip on a copy.
251
+ """
252
+ return re.sub(r"/\*.*?\*/", "", css, flags=re.DOTALL)
253
+
254
+
255
+ def _blank_allowed_radial_stops(css: str) -> str:
256
+ """Blank (space-fill) the contents of every ``radial-gradient(…)`` so
257
+ its color stops don't trip the rule-1 literal scan.
258
+
259
+ WHY: the ARIS fork keeps a faint radial tint on ``.poster``'s
260
+ background with ``rgba(…)`` stops whose alpha is <= 0.06
261
+ (IMPLEMENTATION_CONVENTIONS §E.2). Those rgba literals are the one
262
+ documented place a color literal legitimately appears OUTSIDE the token
263
+ block — rule 5 validates them (right selector, alpha bound), so rule 1
264
+ must not double-flag them. We blank (not delete) to preserve char
265
+ offsets. Selector/alpha legality is rule 5's job, not rule 1's.
266
+ """
267
+ out = []
268
+ for pre, body, _close in _iter_radial_gradients(css):
269
+ out.append(pre)
270
+ out.append(" " * len(body)) # blank the (…) body (incl. parens)
271
+ return "".join(out)
272
+
273
+
274
+ def _iter_radial_gradients(css: str):
275
+ """Yield ``(text_before, gradient_text, end_index)`` for each
276
+ ``radial-gradient(…)`` with BALANCED parentheses, so a nested
277
+ ``rgba(…)`` stop is captured whole.
278
+
279
+ ``gradient_text`` spans from ``radial-gradient`` through its matching
280
+ ``)``. The naive ``radial-gradient\\((.*?)\\)`` regex stops at the
281
+ first inner ``)`` (the rgba close), splitting the literal — so rule 5's
282
+ alpha check missed stops and rule 1's blanker under-blanked. Balanced
283
+ walking fixes both. Pure + stdlib.
284
+ """
285
+ low = css.lower()
286
+ i = 0
287
+ while i < len(css):
288
+ j = low.find("radial-gradient(", i)
289
+ if j == -1:
290
+ yield css[i:], "", len(css)
291
+ break
292
+ pre = css[i:j]
293
+ k = j + len("radial-gradient")
294
+ depth = 0
295
+ while k < len(css):
296
+ if css[k] == "(":
297
+ depth += 1
298
+ elif css[k] == ")":
299
+ depth -= 1
300
+ if depth == 0:
301
+ k += 1
302
+ break
303
+ k += 1
304
+ yield pre, css[j:k], k
305
+ i = k
306
+
307
+
308
+ # Canonical token-block markers (IMPLEMENTATION_CONVENTIONS §A). The
309
+ # token block is the ONLY place color literals are allowed (rule 1).
310
+ _TOKEN_START_RE = re.compile(
311
+ r"/\*\s*=+\s*DESIGN TOKENS\s*=+\s*\*/", re.IGNORECASE
312
+ )
313
+ _TOKEN_END_RE = re.compile(
314
+ r"/\*\s*=+\s*END DESIGN TOKENS\s*=+\s*\*/", re.IGNORECASE
315
+ )
316
+
317
+
318
+ def _locate_token_block(css: str) -> tuple[int, int] | None:
319
+ """Return ``(start, end)`` char offsets of the token block content
320
+ (between the two marker comments) in the *raw* (comment-bearing) CSS,
321
+ or ``None`` if the canonical marker pair isn't present.
322
+ """
323
+ m_start = _TOKEN_START_RE.search(css)
324
+ if not m_start:
325
+ return None
326
+ m_end = _TOKEN_END_RE.search(css, m_start.end())
327
+ if not m_end:
328
+ return None
329
+ return m_start.end(), m_end.start()
330
+
331
+
332
+ # ---------------------------------------------------------------------------
333
+ # CSS rule splitting (for selector-scoped checks: rules 3, 5, 8).
334
+ # ---------------------------------------------------------------------------
335
+
336
+
337
+ def _iter_css_rules(css_no_comments: str):
338
+ """Yield ``(selector, body)`` for each top-level ``sel { … }`` rule.
339
+
340
+ Naive but adequate for the templates: it does not recurse into nested
341
+ at-rules' inner braces beyond one level, which is fine because the
342
+ declarations we inspect (color, font-size, background) live in flat
343
+ rule bodies. ``@page``/``@media`` wrappers are skipped as selectors
344
+ but their inner rules are still seen on the next pass via a second
345
+ scan of the wrapper body. WHY a hand split instead of a CSS parser:
346
+ stdlib has none, and the contract is "small functions, stdlib only".
347
+ """
348
+ # First, pull out @media/@supports wrapper bodies and re-scan them so
349
+ # rules inside print/screen media are inspected too (the templates put
350
+ # `@media print { :root { --u: 1mm } }` and component overrides there).
351
+ def _scan(block: str):
352
+ i = 0
353
+ n = len(block)
354
+ while i < n:
355
+ brace = block.find("{", i)
356
+ if brace == -1:
357
+ break
358
+ selector = block[i:brace].strip()
359
+ depth = 1
360
+ j = brace + 1
361
+ while j < n and depth:
362
+ if block[j] == "{":
363
+ depth += 1
364
+ elif block[j] == "}":
365
+ depth -= 1
366
+ j += 1
367
+ body = block[brace + 1:j - 1]
368
+ if selector.startswith("@") and "{" in body:
369
+ # Nested at-rule: recurse one level into its body.
370
+ yield from _scan(body)
371
+ else:
372
+ yield selector, body
373
+ i = j
374
+
375
+ yield from _scan(css_no_comments)
376
+
377
+
378
+ # ---------------------------------------------------------------------------
379
+ # Rule result container.
380
+ # ---------------------------------------------------------------------------
381
+
382
+
383
+ class RuleResult:
384
+ """One row in the JSON ``rules`` array.
385
+
386
+ ``status`` is PASS / FAIL / WARN / SKIPPED. ``detail`` is a single
387
+ human-readable line (ascii-safe at output). A HARD rule with status
388
+ FAIL drives the overall gate to FAIL; a WARN rule never does.
389
+ """
390
+
391
+ def __init__(self, rid: int, severity: str, title: str) -> None:
392
+ self.id = rid
393
+ self.severity = severity # "hard" | "warn"
394
+ self.title = title
395
+ self.status = "PASS"
396
+ self.detail = "ok"
397
+
398
+ def fail(self, detail: str) -> "RuleResult":
399
+ self.status = "FAIL"
400
+ self.detail = detail
401
+ return self
402
+
403
+ def warn(self, detail: str) -> "RuleResult":
404
+ self.status = "WARN"
405
+ self.detail = detail
406
+ return self
407
+
408
+ def skip(self, detail: str) -> "RuleResult":
409
+ self.status = "SKIPPED"
410
+ self.detail = detail
411
+ return self
412
+
413
+ def to_dict(self) -> dict[str, Any]:
414
+ return {
415
+ "id": self.id,
416
+ "severity": self.severity,
417
+ "status": self.status,
418
+ "detail": ascii_safe(self.detail),
419
+ }
420
+
421
+
422
+ # ---------------------------------------------------------------------------
423
+ # Source gate.
424
+ # ---------------------------------------------------------------------------
425
+
426
+
427
+ def run_source_gate(
428
+ html_text: str, html_path: Path
429
+ ) -> tuple[list[RuleResult], _PosterParser, str | None]:
430
+ """Run rules 1,2,3,5,6,7,8,9,10,11 (the static rules).
431
+
432
+ Returns ``(results, parser, token_block_or_None)``. The parser and
433
+ located token block are returned so the render gate can reuse them
434
+ (token block holds the :root --accent/--gold for hue centers fallback).
435
+ """
436
+ parser = _PosterParser()
437
+ parser.feed(html_text)
438
+ parser.close()
439
+
440
+ raw_css = parser.style_css
441
+ token_span = _locate_token_block(raw_css)
442
+ token_block_text: str | None = None
443
+ if token_span is not None:
444
+ token_block_text = raw_css[token_span[0]:token_span[1]]
445
+
446
+ results: list[RuleResult] = []
447
+
448
+ # --- Rule 1: color literals only inside the token block / logo SVG ---
449
+ r1 = RuleResult(1, "hard", "color literals only in token block")
450
+ if token_span is None:
451
+ r1.fail(
452
+ "design-token block not found: expected the comment pair "
453
+ "/* ===== DESIGN TOKENS ===== */ ... "
454
+ "/* ===== END DESIGN TOKENS ===== */ inside a <style> block. "
455
+ "Cannot locate where color literals are allowed."
456
+ )
457
+ else:
458
+ # Build a comment-stripped copy where the token-block region is
459
+ # blanked out (so literals inside it are exempt), then scan the
460
+ # REMAINING CSS for color literals. We blank-not-delete to keep
461
+ # char offsets meaningful for the error message.
462
+ css_scan = (
463
+ raw_css[:token_span[0]]
464
+ + (" " * (token_span[1] - token_span[0]))
465
+ + raw_css[token_span[1]:]
466
+ )
467
+ css_scan = _strip_css_comments(css_scan)
468
+ # The documented radial-tint exemption: alpha<=0.06 rgba stops on
469
+ # the .poster background are validated by rule 5, not rule 1.
470
+ css_scan = _blank_allowed_radial_stops(css_scan)
471
+ offenders = sorted(set(
472
+ m.group(0) for m in COLOR_LITERAL_RE.finditer(css_scan)
473
+ ))
474
+ if offenders:
475
+ r1.fail(
476
+ "color literal(s) outside the token block: "
477
+ + ", ".join(offenders[:8])
478
+ + (" …" if len(offenders) > 8 else "")
479
+ + ". Move them to a --token in the DESIGN TOKENS block and "
480
+ "reference via var(--…) (rule 3)."
481
+ )
482
+ results.append(r1)
483
+
484
+ # --- Rule 2: forbidden inline `style=` attributes --------------------
485
+ # Zero tolerance EXCEPT the two documented exemptions:
486
+ # (a) any element inside a data-color-exempt="logo" subtree;
487
+ # (b) `style="width: NN%"` on an <img data-source="paper">.
488
+ r2 = RuleResult(2, "hard", "no inline style= (two documented exemptions)")
489
+ style_offenders: list[str] = []
490
+ for el in parser.elements:
491
+ style = el["attrs"].get("style")
492
+ if style is None or style.strip() == "":
493
+ continue
494
+ if el["inside_logo"]:
495
+ continue # exemption (a)
496
+ # exemption (b): img[data-source=paper] with only `width: NN%`.
497
+ is_paper_img = (
498
+ el["tag"] == "img"
499
+ and el["attrs"].get("data-source", "").lower() == "paper"
500
+ )
501
+ if is_paper_img and re.fullmatch(
502
+ r"\s*width\s*:\s*\d+(?:\.\d+)?%\s*;?\s*", style
503
+ ):
504
+ continue
505
+ style_offenders.append(f"<{el['tag']} style=\"{style.strip()}\">")
506
+ if style_offenders:
507
+ r2.fail(
508
+ f"{len(style_offenders)} inline style= attribute(s) "
509
+ "(zero-tolerance; use utility classes). First: "
510
+ + style_offenders[0]
511
+ + ". Exemptions: inside data-color-exempt=\"logo\", or "
512
+ "style=\"width: NN%\" on img[data-source=\"paper\"]."
513
+ )
514
+ results.append(r2)
515
+
516
+ # --- Rule 3: component CSS colors must be var(--…) --------------------
517
+ # Any color/background/border-color declaration in a NON-:root rule
518
+ # whose value contains a color literal fails (literals belong only in
519
+ # the token block, which is inside :root). This overlaps rule 1 but
520
+ # targets the *property* level for a clearer message.
521
+ r3 = RuleResult(3, "hard", "component colors use var(--…)")
522
+ css_nc = _strip_css_comments(raw_css)
523
+ color_prop_offenders: list[str] = []
524
+ color_prop_re = re.compile(
525
+ r"(?<![\w-])(color|background|background-color|border|border-color|"
526
+ r"border-top-color|border-bottom-color|border-left-color|"
527
+ r"border-right-color|fill|stroke|box-shadow|outline|outline-color)"
528
+ r"\s*:\s*([^;{}]+)",
529
+ re.IGNORECASE,
530
+ )
531
+ for selector, body in _iter_css_rules(css_nc):
532
+ sel_l = selector.lower()
533
+ # :root is where tokens live — literals there are expected.
534
+ if ":root" in sel_l:
535
+ continue
536
+ # Same radial-tint exemption as rule 1: the alpha<=0.06 stops on
537
+ # the .poster background are rule 5's responsibility, not rule 3's.
538
+ body_scan = _blank_allowed_radial_stops(body)
539
+ for m in color_prop_re.finditer(body_scan):
540
+ value = m.group(2)
541
+ if COLOR_LITERAL_RE.search(value):
542
+ color_prop_offenders.append(
543
+ f"{selector.strip()[:40]} {{ {m.group(1)}: "
544
+ f"{value.strip()[:40]} }}"
545
+ )
546
+ if color_prop_offenders:
547
+ r3.fail(
548
+ f"{len(color_prop_offenders)} component color declaration(s) "
549
+ "with a literal instead of var(--…). First: "
550
+ + color_prop_offenders[0]
551
+ )
552
+ results.append(r3)
553
+
554
+ # --- Rule 5: no linear-gradient; radial only on .poster, alpha<=0.06 -
555
+ r5 = RuleResult(5, "hard", "no linear-gradient; radial only .poster bg")
556
+ if re.search(r"\blinear-gradient\s*\(", css_nc, re.IGNORECASE):
557
+ r5.fail(
558
+ "linear-gradient is forbidden (flat, de-gradient design). "
559
+ "Replace with a solid var(--…) fill."
560
+ )
561
+ else:
562
+ radial_problems: list[str] = []
563
+ for selector, body in _iter_css_rules(css_nc):
564
+ if "radial-gradient" not in body.lower():
565
+ continue
566
+ sel_l = selector.lower()
567
+ # Allowed ONLY on the .poster background.
568
+ if ".poster" not in sel_l:
569
+ radial_problems.append(
570
+ f"radial-gradient on '{selector.strip()[:50]}' "
571
+ "(only .poster background may use it)"
572
+ )
573
+ continue
574
+ # Every color stop's alpha must be <= 0.06. Use balanced
575
+ # extraction so a nested rgba() stop is captured whole.
576
+ for _pre, grad_text, _end in _iter_radial_gradients(body):
577
+ if not grad_text:
578
+ continue
579
+ for fm in _FUNC_COLOR_RE.finditer(grad_text):
580
+ a = _parse_alpha(fm.group(1), fm.group(2))
581
+ if a is None:
582
+ continue
583
+ if a > 0.06 + 1e-9:
584
+ radial_problems.append(
585
+ f"radial-gradient stop alpha={a:g} > 0.06 on "
586
+ f"'{selector.strip()[:40]}'"
587
+ )
588
+ if radial_problems:
589
+ r5.fail("; ".join(radial_problems[:4]))
590
+ results.append(r5)
591
+
592
+ # --- Rules 6 + 7: font pairing + whitelist ---------------------------
593
+ # We inspect every `font-family:` declaration. The token convention is
594
+ # that body text uses `--font-serif` and headings/headers/table labels
595
+ # use `--font-sans`; the actual stacks are DEFINED once on those two
596
+ # tokens. So we (a) whitelist-check the families in --font-serif /
597
+ # --font-sans / --font-mono token definitions (rule 7), and (b) verify
598
+ # the serif token is a serif stack, the sans token a sans stack, and
599
+ # that components reference the *right* token for their role (rule 6).
600
+ r6 = RuleResult(6, "hard", "font pairing: serif body / sans heading")
601
+ r7 = RuleResult(7, "hard", "font-family whitelist")
602
+
603
+ # Pull token stack definitions from the token block (if present),
604
+ # else from any :root.
605
+ def _families(value: str) -> list[str]:
606
+ out = []
607
+ for raw in value.split(","):
608
+ fam = raw.strip().strip('"').strip("'").lower()
609
+ if fam:
610
+ out.append(fam)
611
+ return out
612
+
613
+ serif_def = sans_def = mono_def = None
614
+ scope = token_block_text if token_block_text is not None else css_nc
615
+ m = re.search(r"--font-serif\s*:\s*([^;{}]+)", scope, re.IGNORECASE)
616
+ if m:
617
+ serif_def = _families(m.group(1))
618
+ m = re.search(r"--font-sans\s*:\s*([^;{}]+)", scope, re.IGNORECASE)
619
+ if m:
620
+ sans_def = _families(m.group(1))
621
+ m = re.search(r"--font-mono\s*:\s*([^;{}]+)", scope, re.IGNORECASE)
622
+ if m:
623
+ mono_def = _families(m.group(1))
624
+
625
+ wl_problems: list[str] = []
626
+ if serif_def is not None:
627
+ bad = [f for f in serif_def if f not in SERIF_WHITELIST]
628
+ if bad:
629
+ wl_problems.append(
630
+ f"--font-serif has non-whitelisted family/ies: "
631
+ f"{', '.join(bad)}"
632
+ )
633
+ if sans_def is not None:
634
+ bad = [f for f in sans_def if f not in SANS_WHITELIST]
635
+ if bad:
636
+ wl_problems.append(
637
+ f"--font-sans has non-whitelisted family/ies: "
638
+ f"{', '.join(bad)}"
639
+ )
640
+ if mono_def is not None:
641
+ bad = [f for f in mono_def if f not in MONO_WHITELIST]
642
+ if bad:
643
+ wl_problems.append(
644
+ f"--font-mono has non-whitelisted family/ies: "
645
+ f"{', '.join(bad)}"
646
+ )
647
+ # Also catch literal font stacks used directly in component rules
648
+ # (bypassing the tokens) and whitelist-check them.
649
+ for selector, body in _iter_css_rules(css_nc):
650
+ for fm in re.finditer(
651
+ r"font-family\s*:\s*([^;{}]+)", body, re.IGNORECASE
652
+ ):
653
+ val = fm.group(1)
654
+ if "var(" in val:
655
+ continue # token reference — checked via the token def
656
+ fams = _families(val)
657
+ allowed = SERIF_WHITELIST | SANS_WHITELIST | MONO_WHITELIST
658
+ bad = [f for f in fams if f not in allowed]
659
+ if bad:
660
+ wl_problems.append(
661
+ f"literal font stack on '{selector.strip()[:30]}' has "
662
+ f"non-whitelisted: {', '.join(bad)}"
663
+ )
664
+ if wl_problems:
665
+ r7.fail("; ".join(wl_problems[:4]))
666
+ results.append(r7)
667
+
668
+ # Rule 6 pairing: the --font-serif token must resolve to a serif stack
669
+ # (last family `serif`) and --font-sans to a sans stack (last family
670
+ # `sans-serif`). This is the structural guarantee that body=serif,
671
+ # headings=sans hold once components reference the right token.
672
+ pairing_problems: list[str] = []
673
+ if serif_def is not None and (not serif_def or serif_def[-1] != "serif"):
674
+ pairing_problems.append(
675
+ "--font-serif must end in the generic `serif` family"
676
+ )
677
+ if sans_def is not None and (
678
+ not sans_def or sans_def[-1] != "sans-serif"
679
+ ):
680
+ pairing_problems.append(
681
+ "--font-sans must end in the generic `sans-serif` family"
682
+ )
683
+ if serif_def is None and sans_def is None:
684
+ pairing_problems.append(
685
+ "neither --font-serif nor --font-sans token is defined; cannot "
686
+ "verify the serif-body / sans-heading pairing"
687
+ )
688
+ if pairing_problems:
689
+ r6.fail("; ".join(pairing_problems))
690
+ results.append(r6)
691
+
692
+ # --- Rule 8 (+ §12.5 nit 1): font-size must use --fs-* token ---------
693
+ # Every `font-size:` value must be either a bare `var(--fs-N)` ref OR a
694
+ # `calc(... var(--fs-N) ...)` AND in that calc case the rule's selector
695
+ # must carry a predefined `--variant` suffix (e.g. `.eqn--large`).
696
+ # Anything else (raw px, raw calc(N*var(--u)), bare numbers) is a HARD
697
+ # fail: it is an off-scale font-size drift.
698
+ r8 = RuleResult(8, "hard", "font-size via --fs-* token / variant calc")
699
+ fs_problems: list[str] = []
700
+ for selector, body in _iter_css_rules(css_nc):
701
+ sel_has_variant = bool(VARIANT_SUFFIX_RE.search(selector))
702
+ for fm in re.finditer(
703
+ r"font-size\s*:\s*([^;{}]+)", body, re.IGNORECASE
704
+ ):
705
+ value = fm.group(1).strip()
706
+ if FS_VAR_REF_RE.fullmatch(value):
707
+ continue # bare var(--fs-N) — always allowed
708
+ # Relative `em` <= 1.0 is allowed: it can only SHRINK a parent
709
+ # whose size is already on the token scale (the standard
710
+ # typographic pattern for sup/sub), so it cannot introduce a
711
+ # new absolute size the way raw px / raw calc drift can.
712
+ m_em = re.fullmatch(r"(0?\.\d+|1(?:\.0+)?)\s*em", value)
713
+ if m_em and float(m_em.group(1)) <= 1.0:
714
+ continue
715
+ if CALC_FS_RE.search(value):
716
+ # calc with an --fs token: allowed ONLY on a variant rule.
717
+ if sel_has_variant:
718
+ continue
719
+ fs_problems.append(
720
+ f"calc() font-size with --fs token on non-variant "
721
+ f"selector '{selector.strip()[:40]}' "
722
+ f"(value '{value[:40]}'): calc(var(--fs-*) * k) is only "
723
+ "allowed on a COMPONENTS.md variant like .eqn--large"
724
+ )
725
+ continue
726
+ # No --fs token at all → off-scale drift.
727
+ fs_problems.append(
728
+ f"off-scale font-size on '{selector.strip()[:40]}': "
729
+ f"'{value[:40]}' (use var(--fs-N))"
730
+ )
731
+ if fs_problems:
732
+ r8.fail("; ".join(fs_problems[:4]) + (
733
+ f" (+{len(fs_problems) - 4} more)" if len(fs_problems) > 4 else ""
734
+ ))
735
+ results.append(r8)
736
+
737
+ # --- Rule 9 (WARN): > 9 distinct --fs tokens defined -----------------
738
+ r9 = RuleResult(9, "warn", "<= 9 font-size tokens")
739
+ defined_fs = sorted(set(
740
+ int(m.group(1)) for m in FS_TOKEN_RE.finditer(
741
+ token_block_text if token_block_text is not None else css_nc
742
+ )
743
+ ))
744
+ if len(defined_fs) > 9:
745
+ r9.warn(
746
+ f"{len(defined_fs)} --fs-* tokens defined "
747
+ f"({', '.join('--fs-%d' % n for n in defined_fs)}); the scale "
748
+ "should stay <= 9 steps to keep the typographic hierarchy tight."
749
+ )
750
+ else:
751
+ r9.detail = f"{len(defined_fs)} --fs-* token(s) defined"
752
+ results.append(r9)
753
+
754
+ # --- Rule 10: data-attribute contracts -------------------------------
755
+ # img[data-source="paper"] MUST also carry data-asset-id; logo
756
+ # exemptions MUST be explicitly marked data-color-exempt="logo".
757
+ r10 = RuleResult(10, "hard", "data-source/asset-id + logo-exempt marks")
758
+ contract_problems: list[str] = []
759
+ for el in parser.elements:
760
+ a = el["attrs"]
761
+ if (el["tag"] == "img"
762
+ and a.get("data-source", "").lower() == "paper"):
763
+ if not a.get("data-asset-id", "").strip():
764
+ src = a.get("src", "?")
765
+ contract_problems.append(
766
+ f"<img data-source=\"paper\" src=\"{src[:40]}\"> is "
767
+ "missing data-asset-id"
768
+ )
769
+ if contract_problems:
770
+ r10.fail("; ".join(contract_problems[:4]))
771
+ results.append(r10)
772
+
773
+ # --- Rule 11: no hand-rolled decorative SVG --------------------------
774
+ # inline <svg> is allowed ONLY when it is (a) inside a logo-exempt
775
+ # subtree, OR (b) itself marked as a catalogued structural diagram
776
+ # (data-component="diagram"), OR (c) a QR fallback (data-component=
777
+ # "qr"). Everything else is a forbidden decorative SVG.
778
+ r11 = RuleResult(11, "hard", "no decorative inline SVG")
779
+ svg_problems: list[str] = []
780
+ for el in parser.elements:
781
+ if el["tag"] != "svg":
782
+ continue
783
+ a = el["attrs"]
784
+ comp = a.get("data-component", "").lower()
785
+ if el["inside_logo"]:
786
+ continue
787
+ if comp in ("diagram", "qr"):
788
+ continue
789
+ svg_problems.append(
790
+ "inline <svg> that is not inside a data-color-exempt=\"logo\" "
791
+ "subtree and is not marked data-component=\"diagram\"/\"qr\""
792
+ )
793
+ if svg_problems:
794
+ r11.fail(
795
+ f"{len(svg_problems)} disallowed inline <svg>: "
796
+ + svg_problems[0]
797
+ + ". Decorative SVG is banned; only logos, QR fallbacks, and "
798
+ "COMPONENTS.md-catalogued structural diagrams are allowed."
799
+ )
800
+ results.append(r11)
801
+
802
+ return results, parser, token_block_text
803
+
804
+
805
+ # ---------------------------------------------------------------------------
806
+ # Hue-cluster helpers (rule 4) — pure functions, unit-testable.
807
+ # ---------------------------------------------------------------------------
808
+
809
+
810
+ def _hue_of_rgb(r: int, g: int, b: int) -> tuple[float, float, float]:
811
+ """Return (hue_deg, saturation, lightness) using HSL (colorsys.rgb_to_hls
812
+ returns H,L,S — note the order). Hue in [0,360), S/L in [0,1].
813
+ """
814
+ h, l, s = colorsys.rgb_to_hls(r / 255.0, g / 255.0, b / 255.0)
815
+ return h * 360.0, s, l
816
+
817
+
818
+ def _hue_dist(a: float, b: float) -> float:
819
+ """Circular distance between two hue angles in degrees (0..180)."""
820
+ d = abs(a - b) % 360.0
821
+ return min(d, 360.0 - d)
822
+
823
+
824
+ def cluster_hues(hues: list[float], radius_deg: float = 18.0) -> list[float]:
825
+ """Greedy 1-D circular clustering of hue angles.
826
+
827
+ Sort the hues, sweep once, and start a new cluster whenever the next
828
+ hue is more than ``radius_deg`` (circular) from the current cluster's
829
+ *seed*. Returns the list of cluster center hues (circular mean of each
830
+ cluster's members). WHY greedy + seed-anchored: it is deterministic,
831
+ matches the spec's "cluster radius 18 degrees", and is cheap. Pure so
832
+ it can be unit-tested without a browser.
833
+ """
834
+ if not hues:
835
+ return []
836
+ ordered = sorted(hues)
837
+ clusters: list[list[float]] = [[ordered[0]]]
838
+ for h in ordered[1:]:
839
+ # Anchor on the cluster seed (its first/min member) so a long run
840
+ # of slowly drifting hues doesn't merge into one giant cluster.
841
+ seed = clusters[-1][0]
842
+ if _hue_dist(h, seed) <= radius_deg:
843
+ clusters[-1].append(h)
844
+ else:
845
+ clusters.append([h])
846
+ # The circular wrap-around case: if the last cluster's members are
847
+ # within radius of the first cluster's seed, merge them (e.g. reds at
848
+ # 350 deg and 5 deg are one hue family).
849
+ if len(clusters) > 1:
850
+ if _hue_dist(clusters[-1][-1], clusters[0][0]) <= radius_deg:
851
+ clusters[0] = clusters[-1] + clusters[0]
852
+ clusters.pop()
853
+ return [_circular_mean(c) for c in clusters]
854
+
855
+
856
+ def _circular_mean(angles: list[float]) -> float:
857
+ """Mean of angles on the unit circle, returned in [0,360)."""
858
+ x = sum(math.cos(math.radians(a)) for a in angles)
859
+ y = sum(math.sin(math.radians(a)) for a in angles)
860
+ return math.degrees(math.atan2(y, x)) % 360.0
861
+
862
+
863
+ # JS that collects computed colors for every element NOT inside an exempt
864
+ # subtree, plus its on-screen area + background lightness (for rule 12).
865
+ _RENDER_JS = r"""
866
+ () => {
867
+ // An element is exempt (rule 4) if it (or an ancestor) is an <img>, a
868
+ // [data-color-exempt] subtree, a [data-source="paper"] subtree, or a
869
+ // QR block ([data-component="qr"] / .qr-block). We walk up once per
870
+ // element; cheap enough for a single-page poster.
871
+ const isExempt = (el) => {
872
+ let n = el;
873
+ while (n && n.nodeType === 1) {
874
+ const tag = n.tagName ? n.tagName.toLowerCase() : '';
875
+ if (tag === 'img') return true;
876
+ if (n.hasAttribute && n.hasAttribute('data-color-exempt')) return true;
877
+ if (n.getAttribute && n.getAttribute('data-source') === 'paper')
878
+ return true;
879
+ const comp = n.getAttribute && n.getAttribute('data-component');
880
+ if (comp === 'qr') return true;
881
+ if (n.classList && n.classList.contains('qr-block')) return true;
882
+ n = n.parentElement;
883
+ }
884
+ return false;
885
+ };
886
+
887
+ const poster = document.querySelector('[data-measure-role="poster"]')
888
+ || document.querySelector('.poster')
889
+ || document.body;
890
+ const pr = poster.getBoundingClientRect();
891
+ const posterArea = Math.max(1, pr.width * pr.height);
892
+
893
+ const colors = []; // {prop, rgba} for rule 4
894
+ let darkArea = 0; // sum of on-screen area with bg L < 0.18 (rule 12)
895
+
896
+ const all = Array.from(document.querySelectorAll('*'));
897
+ for (const el of all) {
898
+ const cs = window.getComputedStyle(el);
899
+ const r = el.getBoundingClientRect();
900
+ const onscreen = r.width > 0 && r.height > 0;
901
+
902
+ // Rule 12: large dark background area. Measure regardless of color
903
+ // exemption (a dark slab is a dark slab even behind a figure), but
904
+ // only count visible boxes inside the poster bbox.
905
+ if (onscreen) {
906
+ const bg = cs.backgroundColor;
907
+ const L = bgLightness(bg);
908
+ if (L !== null && L < 0.18) {
909
+ // Clip to poster bounds so off-canvas overflow doesn't inflate.
910
+ const w = Math.max(0, Math.min(r.right, pr.right)
911
+ - Math.max(r.left, pr.left));
912
+ const h = Math.max(0, Math.min(r.bottom, pr.bottom)
913
+ - Math.max(r.top, pr.top));
914
+ darkArea += w * h;
915
+ }
916
+ }
917
+
918
+ if (isExempt(el)) continue;
919
+ // Rule 4: collect every color-bearing computed property.
920
+ colors.push({prop: 'color', rgba: cs.color});
921
+ colors.push({prop: 'background-color', rgba: cs.backgroundColor});
922
+ colors.push({prop: 'border-top-color', rgba: cs.borderTopColor});
923
+ colors.push({prop: 'border-right-color', rgba: cs.borderRightColor});
924
+ colors.push({prop: 'border-bottom-color', rgba: cs.borderBottomColor});
925
+ colors.push({prop: 'border-left-color', rgba: cs.borderLeftColor});
926
+ colors.push({prop: 'fill', rgba: cs.fill});
927
+ colors.push({prop: 'stroke', rgba: cs.stroke});
928
+ }
929
+
930
+ function bgLightness(s) {
931
+ const m = parseRGBA(s);
932
+ if (!m || m.a < 0.5) return null; // transparent-ish: not a dark slab
933
+ const r = m.r / 255, g = m.g / 255, b = m.b / 255;
934
+ return (Math.max(r, g, b) + Math.min(r, g, b)) / 2;
935
+ }
936
+ function parseRGBA(s) {
937
+ const mm = /rgba?\(([^)]+)\)/i.exec(s || '');
938
+ if (!mm) return null;
939
+ const p = mm[1].split(/[ ,\/]+/).filter(Boolean);
940
+ if (p.length < 3) return null;
941
+ return {
942
+ r: parseFloat(p[0]), g: parseFloat(p[1]), b: parseFloat(p[2]),
943
+ a: p.length >= 4 ? parseFloat(p[3]) : 1,
944
+ };
945
+ }
946
+
947
+ return {colors, posterArea, darkArea};
948
+ }
949
+ """
950
+
951
+
952
+ def _parse_rgba_str(s: str) -> tuple[int, int, int, float] | None:
953
+ """Parse a computed ``rgb()/rgba()`` string into (r,g,b,a). Returns
954
+ None for non-rgb forms (e.g. ``transparent`` resolves to
955
+ rgba(0,0,0,0), which DOES parse, so this only fails on unexpected
956
+ formats).
957
+ """
958
+ m = re.search(
959
+ r"rgba?\(\s*([\d.]+)[ ,]+([\d.]+)[ ,]+([\d.]+)"
960
+ r"(?:[ ,/]+([\d.]+%?))?\s*\)",
961
+ s or "", re.IGNORECASE,
962
+ )
963
+ if not m:
964
+ return None
965
+ r, g, b = int(float(m.group(1))), int(float(m.group(2))), int(float(m.group(3)))
966
+ a_raw = m.group(4)
967
+ if a_raw is None:
968
+ a = 1.0
969
+ elif a_raw.endswith("%"):
970
+ a = float(a_raw[:-1]) / 100.0
971
+ else:
972
+ a = float(a_raw)
973
+ return r, g, b, a
974
+
975
+
976
+ def run_render_gate(
977
+ html_path: Path,
978
+ hue_centers: dict[str, float],
979
+ *,
980
+ cluster_radius_deg: float = 18.0,
981
+ center_tol_deg: float = 22.0,
982
+ nonneutral_alpha: float = 0.10,
983
+ nonneutral_sat: float = 0.18,
984
+ dark_area_frac: float = 0.08,
985
+ mathjax_timeout_ms: int = 15000,
986
+ settle_ms: int = 500,
987
+ ) -> tuple[list[RuleResult], int | None]:
988
+ """Run rules 4 and 12 via Playwright print-emulation.
989
+
990
+ Returns ``(results, env_exit)``. ``env_exit`` is non-None (==2) only
991
+ when the environment is unusable (no playwright, no @page canvas, nav
992
+ failure) — the caller surfaces it as a usage/env error. Otherwise the
993
+ two rule results carry PASS/FAIL/WARN as normal.
994
+
995
+ WHY lazy import: playwright may still be installing; importing at
996
+ module top would break the source-gate-only ``--no-render`` path.
997
+ """
998
+ try:
999
+ from playwright.sync_api import sync_playwright
1000
+ from playwright.sync_api import TimeoutError as PWTimeoutError
1001
+ except ImportError:
1002
+ _eprint(
1003
+ "ERROR: playwright is not available, so the render gate "
1004
+ "(rules 4 and 12) cannot run. Install it with:\n"
1005
+ " python -m pip install playwright\n"
1006
+ " python -m playwright install chromium\n"
1007
+ "or re-run with --no-render to skip rules 4 and 12 (the source "
1008
+ "gate still runs and can PASS on its own)."
1009
+ )
1010
+ return [], 2
1011
+
1012
+ # Reuse the vendored canvas+render helpers so the viewport basis and
1013
+ # MathJax settling exactly match measure/polish.
1014
+ from _posterly import canvas as _canvas
1015
+ from _posterly import render as _render
1016
+
1017
+ resolved = _canvas.resolve_canvas(html_path, None, label="[style]")
1018
+ if resolved is None:
1019
+ _eprint(
1020
+ "ERROR: could not find `@page { size: <W> <H> }` in HTML for "
1021
+ "the render gate. Add an @page rule or re-run with --no-render."
1022
+ )
1023
+ return [], 2
1024
+ _canvas_in, viewport = resolved
1025
+
1026
+ with sync_playwright() as p:
1027
+ browser, _ctx, page = _render.open_print_emulated_page(p, viewport)
1028
+ nav_timed_out = False
1029
+ try:
1030
+ page.goto(html_path.as_uri(), wait_until="networkidle",
1031
+ timeout=mathjax_timeout_ms)
1032
+ except PWTimeoutError:
1033
+ nav_timed_out = True
1034
+
1035
+ settle = _render.settle_page(
1036
+ page, mathjax_timeout_ms=mathjax_timeout_ms, settle_ms=settle_ms,
1037
+ )
1038
+ fail = _render.hard_fail_on_settle_problems(
1039
+ settle, mathjax_timeout_ms=mathjax_timeout_ms,
1040
+ )
1041
+ if fail is not None:
1042
+ browser.close()
1043
+ _eprint(f"ERROR (render gate): {fail}")
1044
+ return [], 2
1045
+ if nav_timed_out:
1046
+ browser.close()
1047
+ _eprint(
1048
+ "ERROR (render gate): page did not reach network-idle "
1049
+ f"within {mathjax_timeout_ms} ms; a blocked remote resource "
1050
+ "is the usual cause. Inline assets or re-run --no-render."
1051
+ )
1052
+ return [], 2
1053
+
1054
+ data = page.evaluate(_RENDER_JS)
1055
+ browser.close()
1056
+
1057
+ results: list[RuleResult] = []
1058
+
1059
+ # --- Rule 4: non-neutral hue clustering ------------------------------
1060
+ r4 = RuleResult(4, "hard", "<=2 non-neutral hue clusters on palette")
1061
+ hues: list[float] = []
1062
+ for c in data["colors"]:
1063
+ parsed = _parse_rgba_str(c["rgba"])
1064
+ if parsed is None:
1065
+ continue
1066
+ r, g, b, a = parsed
1067
+ if a < nonneutral_alpha:
1068
+ continue
1069
+ hue, sat, _l = _hue_of_rgb(r, g, b)
1070
+ # Perceptual chroma, not raw saturation: HSL saturation blows up
1071
+ # near white/black (a warm paper tone like #F6F2F0 reads S=0.25 at
1072
+ # L=0.95 yet is visually neutral). Chroma = S * (1 - |2L - 1|)
1073
+ # discounts by lightness extremity, so near-white tints and
1074
+ # near-black shadows stay neutral while real accents still count.
1075
+ chroma = sat * (1.0 - abs(2.0 * _l - 1.0))
1076
+ if chroma < nonneutral_sat * 0.56: # 0.18 * 0.56 ~= 0.10 chroma floor
1077
+ continue # neutral (gray / near-white tint) — not a hue family
1078
+ hues.append(hue)
1079
+
1080
+ centers = cluster_hues(hues, radius_deg=cluster_radius_deg)
1081
+ target_hues = [h for h in (
1082
+ hue_centers.get("accent"), hue_centers.get("gold")
1083
+ ) if h is not None]
1084
+ problems: list[str] = []
1085
+ if len(centers) > 2:
1086
+ problems.append(
1087
+ f"{len(centers)} non-neutral hue clusters "
1088
+ f"({', '.join('%.0f deg' % c for c in centers)}); at most 2 "
1089
+ "(accent + gold) are allowed."
1090
+ )
1091
+ if target_hues:
1092
+ for c in centers:
1093
+ nearest = min(_hue_dist(c, t) for t in target_hues)
1094
+ if nearest > center_tol_deg:
1095
+ problems.append(
1096
+ f"hue cluster at {c:.0f} deg is {nearest:.0f} deg from "
1097
+ f"the nearest palette center "
1098
+ f"({'/'.join('%.0f' % t for t in target_hues)} deg); "
1099
+ f"tolerance is {center_tol_deg:.0f} deg"
1100
+ )
1101
+ else:
1102
+ problems.append(
1103
+ "no accent/gold hue centers available (pass --tokens or define "
1104
+ "--accent/--gold in :root) — cannot verify cluster proximity"
1105
+ )
1106
+ if problems:
1107
+ r4.fail("; ".join(problems[:4]))
1108
+ else:
1109
+ r4.detail = (
1110
+ f"{len(centers)} hue cluster(s), all within {center_tol_deg:.0f}"
1111
+ f" deg of the palette"
1112
+ )
1113
+ results.append(r4)
1114
+
1115
+ # --- Rule 12 (WARN): large dark area ---------------------------------
1116
+ r12 = RuleResult(12, "warn", "large dark area (kitsch warning)")
1117
+ frac = data["darkArea"] / data["posterArea"] if data["posterArea"] else 0.0
1118
+ if frac > dark_area_frac:
1119
+ r12.warn(
1120
+ f"dark (L<0.18) backgrounds cover {frac * 100:.1f}% of the "
1121
+ f"poster (> {dark_area_frac * 100:.0f}% threshold); large dark "
1122
+ "slabs read as 'kitsch'. Lighten or shrink them."
1123
+ )
1124
+ else:
1125
+ r12.detail = f"dark area = {frac * 100:.1f}% of poster (<= 8%)"
1126
+ results.append(r12)
1127
+
1128
+ return results, None
1129
+
1130
+
1131
+ # ---------------------------------------------------------------------------
1132
+ # Hue-center resolution (--tokens JSON, else :root --accent/--gold).
1133
+ # ---------------------------------------------------------------------------
1134
+
1135
+
1136
+ def resolve_hue_centers(
1137
+ tokens_path: Path | None, token_block_text: str | None
1138
+ ) -> dict[str, float]:
1139
+ """Resolve accent/gold hue centers for rule 4.
1140
+
1141
+ Priority: ``--tokens`` JSON ``hue_centers`` block (authoritative,
1142
+ IMPLEMENTATION_CONVENTIONS §F), then derive from the JSON's accent/gold
1143
+ base hexes, then fall back to parsing ``--accent`` / ``--gold`` literals
1144
+ out of the :root token block.
1145
+ """
1146
+ centers: dict[str, float] = {}
1147
+ if tokens_path is not None:
1148
+ try:
1149
+ doc = json.loads(tokens_path.read_text(encoding="utf-8"))
1150
+ except (OSError, json.JSONDecodeError) as e:
1151
+ _eprint(f"WARNING: could not read --tokens {ascii_safe(tokens_path)}"
1152
+ f": {ascii_safe(e)}; falling back to :root.")
1153
+ doc = {}
1154
+ hc = doc.get("hue_centers") or {}
1155
+ for key in ("accent", "gold"):
1156
+ if key in hc:
1157
+ try:
1158
+ centers[key] = float(hc[key]) % 360.0
1159
+ except (TypeError, ValueError):
1160
+ pass
1161
+ # Derive from base hexes if hue_centers omitted a key.
1162
+ for key, sub in (("accent", "accent"), ("gold", "gold")):
1163
+ if key in centers:
1164
+ continue
1165
+ base = (doc.get(sub) or {}).get("base")
1166
+ h = _hue_from_hex(base) if base else None
1167
+ if h is not None:
1168
+ centers[key] = h
1169
+ # Fallback: parse --accent / --gold from the :root token block.
1170
+ if token_block_text:
1171
+ for key, var in (("accent", "--accent"), ("gold", "--gold")):
1172
+ if key in centers:
1173
+ continue
1174
+ m = re.search(
1175
+ re.escape(var) + r"\s*:\s*(#[0-9a-fA-F]{3,8})",
1176
+ token_block_text,
1177
+ )
1178
+ if m:
1179
+ h = _hue_from_hex(m.group(1))
1180
+ if h is not None:
1181
+ centers[key] = h
1182
+ return centers
1183
+
1184
+
1185
+ def _hue_from_hex(hex_str: str | None) -> float | None:
1186
+ """Hue (degrees) of a #RGB / #RRGGBB literal, or None if unparsable."""
1187
+ if not hex_str or not hex_str.startswith("#"):
1188
+ return None
1189
+ h = hex_str[1:]
1190
+ if len(h) == 3:
1191
+ h = "".join(c * 2 for c in h)
1192
+ if len(h) not in (6, 8):
1193
+ return None
1194
+ try:
1195
+ r, g, b = int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16)
1196
+ except ValueError:
1197
+ return None
1198
+ hue, _s, _l = _hue_of_rgb(r, g, b)
1199
+ return hue
1200
+
1201
+
1202
+ # ---------------------------------------------------------------------------
1203
+ # Orchestration + CLI.
1204
+ # ---------------------------------------------------------------------------
1205
+
1206
+
1207
+ def _overall_status(results: list[RuleResult]) -> str:
1208
+ """PASS unless a HARD rule FAILED; WARN if no hard fail but a warning
1209
+ fired; else PASS. (SKIPPED rules never change the verdict.)
1210
+ """
1211
+ if any(r.severity == "hard" and r.status == "FAIL" for r in results):
1212
+ return "FAIL"
1213
+ if any(r.status == "WARN" for r in results):
1214
+ return "WARN"
1215
+ return "PASS"
1216
+
1217
+
1218
+ def cmd_style_check(args: argparse.Namespace) -> int:
1219
+ html_path = Path(args.html).resolve()
1220
+ if not html_path.exists():
1221
+ _eprint(f"ERROR: HTML not found: {ascii_safe(html_path)}")
1222
+ return 2
1223
+ try:
1224
+ html_text = html_path.read_text(encoding="utf-8", errors="ignore")
1225
+ except OSError as e:
1226
+ _eprint(f"ERROR: cannot read HTML: {ascii_safe(e)}")
1227
+ return 2
1228
+
1229
+ tokens_path = Path(args.tokens).resolve() if args.tokens else None
1230
+ if tokens_path is not None and not tokens_path.exists():
1231
+ _eprint(f"ERROR: --tokens not found: {ascii_safe(tokens_path)}")
1232
+ return 2
1233
+
1234
+ # --- Source gate (always runs) --------------------------------------
1235
+ source_results, _parser, token_block_text = run_source_gate(
1236
+ html_text, html_path
1237
+ )
1238
+
1239
+ # --- Render gate (rules 4, 12) --------------------------------------
1240
+ render_results: list[RuleResult]
1241
+ if args.no_render:
1242
+ render_results = [
1243
+ RuleResult(4, "hard", "<=2 non-neutral hue clusters").skip(
1244
+ "render gate skipped (--no-render)"
1245
+ ),
1246
+ RuleResult(12, "warn", "large dark area").skip(
1247
+ "render gate skipped (--no-render)"
1248
+ ),
1249
+ ]
1250
+ print("[style] NOTICE: --no-render set; rules 4 and 12 are SKIPPED. "
1251
+ "Overall status reflects the source gate only.")
1252
+ else:
1253
+ hue_centers = resolve_hue_centers(tokens_path, token_block_text)
1254
+ render_results, env_exit = run_render_gate(html_path, hue_centers)
1255
+ if env_exit is not None:
1256
+ # Environment unusable for the render gate. Per the CLI
1257
+ # contract an env error is exit 2 — don't masquerade as a PASS.
1258
+ return env_exit
1259
+
1260
+ # Merge + order by rule id for stable output.
1261
+ all_results = sorted(
1262
+ source_results + render_results, key=lambda r: r.id
1263
+ )
1264
+ status = _overall_status(all_results)
1265
+
1266
+ report = {
1267
+ "gate": "style",
1268
+ "status": status,
1269
+ "rules": [r.to_dict() for r in all_results],
1270
+ }
1271
+
1272
+ if args.json:
1273
+ Path(args.json).write_text(
1274
+ json.dumps(report, indent=2), encoding="utf-8"
1275
+ )
1276
+ print(f"[style] report -> {ascii_safe(args.json)}")
1277
+
1278
+ # Human-readable summary to stdout.
1279
+ print(f"[style] overall = {status}")
1280
+ for r in all_results:
1281
+ print(f" rule {r.id:>2} [{r.severity:>4}] {r.status:<7} "
1282
+ f"{ascii_safe(r.detail)}")
1283
+
1284
+ return 1 if status == "FAIL" else 0
1285
+
1286
+
1287
+ def build_parser() -> argparse.ArgumentParser:
1288
+ p = argparse.ArgumentParser(
1289
+ prog="style_check",
1290
+ description="Style HARD gate for HTML academic posters: 12 rules "
1291
+ "(DESIGN_FINAL.md §3 + §12.5 nit 1). Source gate "
1292
+ "(rules 1-3,5-11) is pure static analysis; render gate "
1293
+ "(rules 4,12) print-emulates via Playwright.",
1294
+ formatter_class=argparse.RawDescriptionHelpFormatter,
1295
+ epilog="exit codes: 0=PASS (no hard fail), 1=hard fail, "
1296
+ "2=usage/environment error.",
1297
+ )
1298
+ p.add_argument("html", help="path to poster.html")
1299
+ p.add_argument(
1300
+ "--tokens", default=None,
1301
+ help="tokens JSON (IMPLEMENTATION_CONVENTIONS §F); its "
1302
+ "hue_centers/accent/gold drive rule 4. Default: derive accent/"
1303
+ "gold hue from the :root token block.",
1304
+ )
1305
+ p.add_argument(
1306
+ "--json", default=None,
1307
+ help="write the JSON report ({gate,status,rules}) to this path",
1308
+ )
1309
+ p.add_argument(
1310
+ "--no-render", action="store_true",
1311
+ help="skip the render gate; rules 4 and 12 are marked SKIPPED. The "
1312
+ "source gate still runs and can PASS on its own (a notice is "
1313
+ "printed).",
1314
+ )
1315
+ return p
1316
+
1317
+
1318
+ def main(argv: list[str] | None = None) -> int:
1319
+ args = build_parser().parse_args(argv)
1320
+ return cmd_style_check(args)
1321
+
1322
+
1323
+ if __name__ == "__main__":
1324
+ sys.exit(main())