dsh-aris-panel 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (442) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +98 -0
  3. package/README_CN.md +87 -0
  4. package/dsh/checkout.patch.yml +38 -0
  5. package/dsh/client.js +634 -0
  6. package/dsh/cordis.patch.yml +44 -0
  7. package/dsh/index.mjs +76 -0
  8. package/dsh/run-status.mjs +182 -0
  9. package/dsh/scope-limits.mjs +50 -0
  10. package/dsh/workbench.mjs +291 -0
  11. package/mcp-servers/claude-review/README.md +93 -0
  12. package/mcp-servers/claude-review/run_with_claude_aws.sh +49 -0
  13. package/mcp-servers/claude-review/server.py +718 -0
  14. package/mcp-servers/codex-image2/README.md +65 -0
  15. package/mcp-servers/codex-image2/server.py +893 -0
  16. package/mcp-servers/feishu-bridge/requirements.txt +1 -0
  17. package/mcp-servers/feishu-bridge/server.py +240 -0
  18. package/mcp-servers/gemini-review/README.md +171 -0
  19. package/mcp-servers/gemini-review/server.py +1856 -0
  20. package/mcp-servers/llm-chat/requirements.txt +1 -0
  21. package/mcp-servers/llm-chat/server.py +664 -0
  22. package/mcp-servers/manual-review/README.md +133 -0
  23. package/mcp-servers/manual-review/server.py +910 -0
  24. package/mcp-servers/manual-review/ui.html +279 -0
  25. package/mcp-servers/minimax-chat/requirements.txt +1 -0
  26. package/mcp-servers/minimax-chat/server.py +381 -0
  27. package/package.json +51 -0
  28. package/skills/ablation-planner/SKILL.md +123 -0
  29. package/skills/alphaxiv/SKILL.md +196 -0
  30. package/skills/analyze-results/SKILL.md +46 -0
  31. package/skills/arxiv/SKILL.md +248 -0
  32. package/skills/auto-paper-improvement-loop/SKILL.md +651 -0
  33. package/skills/auto-review-loop/SKILL.md +1137 -0
  34. package/skills/auto-review-loop-llm/SKILL.md +259 -0
  35. package/skills/auto-review-loop-minimax/SKILL.md +302 -0
  36. package/skills/citation-audit/SKILL.md +502 -0
  37. package/skills/claims-drafting/SKILL.md +227 -0
  38. package/skills/comm-lit-review/SKILL.md +297 -0
  39. package/skills/deepxiv/SKILL.md +263 -0
  40. package/skills/dse-loop/SKILL.md +296 -0
  41. package/skills/embodiment-description/SKILL.md +129 -0
  42. package/skills/exa-search/SKILL.md +205 -0
  43. package/skills/experiment-audit/SKILL.md +311 -0
  44. package/skills/experiment-bridge/SKILL.md +376 -0
  45. package/skills/experiment-plan/SKILL.md +249 -0
  46. package/skills/experiment-queue/SKILL.md +431 -0
  47. package/skills/experiment-queue/scripts/build_manifest.py +142 -0
  48. package/skills/experiment-queue/scripts/queue_manager.py +433 -0
  49. package/skills/feishu-notify/SKILL.md +156 -0
  50. package/skills/figure-description/SKILL.md +138 -0
  51. package/skills/figure-spec/SKILL.md +262 -0
  52. package/skills/figure-spec/scripts/figure_renderer.py +799 -0
  53. package/skills/formula-derivation/SKILL.md +280 -0
  54. package/skills/gemini-search/SKILL.md +231 -0
  55. package/skills/grant-proposal/SKILL.md +698 -0
  56. package/skills/idea-creator/SKILL.md +542 -0
  57. package/skills/idea-discovery/SKILL.md +521 -0
  58. package/skills/idea-discovery-robot/SKILL.md +363 -0
  59. package/skills/integrity-forensics/SKILL.md +284 -0
  60. package/skills/interview-cheatsheet/SKILL.md +245 -0
  61. package/skills/invention-structuring/SKILL.md +188 -0
  62. package/skills/jurisdiction-format/SKILL.md +192 -0
  63. package/skills/kill-argument/SKILL.md +437 -0
  64. package/skills/mermaid-diagram/SKILL.md +419 -0
  65. package/skills/meta-apply/SKILL.md +141 -0
  66. package/skills/meta-optimize/SKILL.md +437 -0
  67. package/skills/monitor-experiment/SKILL.md +140 -0
  68. package/skills/novelty-check/SKILL.md +101 -0
  69. package/skills/openalex/SKILL.md +237 -0
  70. package/skills/overleaf-sync/SKILL.md +220 -0
  71. package/skills/paper-claim-audit/SKILL.md +348 -0
  72. package/skills/paper-compile/SKILL.md +266 -0
  73. package/skills/paper-figure/SKILL.md +312 -0
  74. package/skills/paper-illustration/SKILL.md +736 -0
  75. package/skills/paper-illustration-image2/SKILL.md +391 -0
  76. package/skills/paper-illustration-image2/scripts/paper_illustration_image2.py +255 -0
  77. package/skills/paper-plan/SKILL.md +386 -0
  78. package/skills/paper-poster/SKILL.md +19 -0
  79. package/skills/paper-poster-html/DESIGN_FINAL.md +176 -0
  80. package/skills/paper-poster-html/IMPLEMENTATION_CONVENTIONS.md +161 -0
  81. package/skills/paper-poster-html/LICENSES/posterly-MIT.txt +21 -0
  82. package/skills/paper-poster-html/NOTICE.md +57 -0
  83. package/skills/paper-poster-html/SKILL.md +323 -0
  84. package/skills/paper-poster-html/scripts/_posterly/__init__.py +0 -0
  85. package/skills/paper-poster-html/scripts/_posterly/canvas.py +200 -0
  86. package/skills/paper-poster-html/scripts/_posterly/measure.py +588 -0
  87. package/skills/paper-poster-html/scripts/_posterly/polish.py +498 -0
  88. package/skills/paper-poster-html/scripts/_posterly/preflight.py +489 -0
  89. package/skills/paper-poster-html/scripts/_posterly/render.py +215 -0
  90. package/skills/paper-poster-html/scripts/_posterly/textutil.py +16 -0
  91. package/skills/paper-poster-html/scripts/_posterly/verify_final.py +171 -0
  92. package/skills/paper-poster-html/scripts/asset_check.py +897 -0
  93. package/skills/paper-poster-html/scripts/extract_pdf_figures.py +666 -0
  94. package/skills/paper-poster-html/scripts/poster_check.py +251 -0
  95. package/skills/paper-poster-html/scripts/preprocess_figures.py +238 -0
  96. package/skills/paper-poster-html/scripts/render_preview.py +217 -0
  97. package/skills/paper-poster-html/scripts/run_gates.py +556 -0
  98. package/skills/paper-poster-html/scripts/style_check.py +1324 -0
  99. package/skills/paper-poster-html/templates/COMPONENTS.md +462 -0
  100. package/skills/paper-poster-html/templates/README.md +170 -0
  101. package/skills/paper-poster-html/templates/landscape_4col.html +1032 -0
  102. package/skills/paper-poster-html/templates/landscape_hero.html +1046 -0
  103. package/skills/paper-poster-html/templates/portrait_2col.html +947 -0
  104. package/skills/paper-poster-html/templates/tokens/acl.json +9 -0
  105. package/skills/paper-poster-html/templates/tokens/cvpr.json +9 -0
  106. package/skills/paper-poster-html/templates/tokens/generic.json +9 -0
  107. package/skills/paper-poster-html/templates/tokens/iclr.json +9 -0
  108. package/skills/paper-poster-html/templates/tokens/icml.json +9 -0
  109. package/skills/paper-poster-html/templates/tokens/neurips.json +9 -0
  110. package/skills/paper-slides/SKILL.md +635 -0
  111. package/skills/paper-talk/SKILL.md +381 -0
  112. package/skills/paper-write/SKILL.md +604 -0
  113. package/skills/paper-write/templates/IEEEtran.bst +2409 -0
  114. package/skills/paper-write/templates/IEEEtran.cls +6347 -0
  115. package/skills/paper-write/templates/iclr2026.tex +84 -0
  116. package/skills/paper-write/templates/icml2025.tex +87 -0
  117. package/skills/paper-write/templates/ieee_conference.tex +89 -0
  118. package/skills/paper-write/templates/ieee_journal.tex +93 -0
  119. package/skills/paper-write/templates/math_commands.tex +48 -0
  120. package/skills/paper-write/templates/neurips2025.tex +80 -0
  121. package/skills/paper-writing/SKILL.md +916 -0
  122. package/skills/patent-novelty-check/SKILL.md +153 -0
  123. package/skills/patent-pipeline/SKILL.md +344 -0
  124. package/skills/patent-review/SKILL.md +203 -0
  125. package/skills/pixel-art/SKILL.md +137 -0
  126. package/skills/prior-art-search/SKILL.md +146 -0
  127. package/skills/proof-checker/SKILL.md +866 -0
  128. package/skills/proof-orchestrator/NOTICE.md +24 -0
  129. package/skills/proof-orchestrator/SKILL.md +254 -0
  130. package/skills/proof-orchestrator/references/audit-output-contract.md +126 -0
  131. package/skills/proof-orchestrator/references/deepseek-routing.md +74 -0
  132. package/skills/proof-orchestrator/references/dispatch-prompts.md +227 -0
  133. package/skills/proof-orchestrator/references/notation-audit.md +135 -0
  134. package/skills/proof-orchestrator/references/proof-audit-rubric.md +70 -0
  135. package/skills/proof-orchestrator/references/stress-tests.md +38 -0
  136. package/skills/proof-writer/SKILL.md +223 -0
  137. package/skills/qzcli/SKILL.md +324 -0
  138. package/skills/rebuttal/SKILL.md +376 -0
  139. package/skills/render-html/SKILL.md +316 -0
  140. package/skills/render-html/scripts/render_html.py +1006 -0
  141. package/skills/render-html/scripts/templates/academic.html +703 -0
  142. package/skills/render-html/scripts/templates/dashboard.html +333 -0
  143. package/skills/research-lit/SKILL.md +756 -0
  144. package/skills/research-pipeline/SKILL.md +384 -0
  145. package/skills/research-refine/SKILL.md +770 -0
  146. package/skills/research-refine-pipeline/SKILL.md +186 -0
  147. package/skills/research-review/SKILL.md +198 -0
  148. package/skills/research-wiki/SKILL.md +461 -0
  149. package/skills/resubmit-pipeline/SKILL.md +447 -0
  150. package/skills/result-to-claim/SKILL.md +311 -0
  151. package/skills/run-experiment/SKILL.md +313 -0
  152. package/skills/semantic-scholar/SKILL.md +236 -0
  153. package/skills/serverless-modal/SKILL.md +335 -0
  154. package/skills/shared-references/acceptance-gate.md +324 -0
  155. package/skills/shared-references/assurance-contract.md +248 -0
  156. package/skills/shared-references/capture-antipatterns.md +78 -0
  157. package/skills/shared-references/citation-discipline.md +583 -0
  158. package/skills/shared-references/compute-env-contract.md +163 -0
  159. package/skills/shared-references/effort-contract.md +183 -0
  160. package/skills/shared-references/evidence-precheck.md +65 -0
  161. package/skills/shared-references/experiment-integrity.md +49 -0
  162. package/skills/shared-references/external-cadence.md +326 -0
  163. package/skills/shared-references/fan-out-pattern.md +366 -0
  164. package/skills/shared-references/injection-hygiene.md +127 -0
  165. package/skills/shared-references/integration-contract.md +461 -0
  166. package/skills/shared-references/output-composition.md +93 -0
  167. package/skills/shared-references/output-language.md +45 -0
  168. package/skills/shared-references/output-manifest.md +49 -0
  169. package/skills/shared-references/output-versioning.md +111 -0
  170. package/skills/shared-references/patent-format-cn.md +199 -0
  171. package/skills/shared-references/patent-format-ep.md +173 -0
  172. package/skills/shared-references/patent-format-us.md +161 -0
  173. package/skills/shared-references/patent-writing-principles.md +197 -0
  174. package/skills/shared-references/prior-art-databases.md +141 -0
  175. package/skills/shared-references/resumable-runs.md +109 -0
  176. package/skills/shared-references/review-scope-limits.md +81 -0
  177. package/skills/shared-references/review-tracing.md +391 -0
  178. package/skills/shared-references/reviewer-independence.md +79 -0
  179. package/skills/shared-references/reviewer-routing.md +852 -0
  180. package/skills/shared-references/skill-governance.md +104 -0
  181. package/skills/shared-references/taste-calibration.md +85 -0
  182. package/skills/shared-references/venue-checklists.md +114 -0
  183. package/skills/shared-references/wiki-helper-resolution.md +134 -0
  184. package/skills/shared-references/writing-principles.md +525 -0
  185. package/skills/skills-codex/README.md +102 -0
  186. package/skills/skills-codex/README_CN.md +100 -0
  187. package/skills/skills-codex/ablation-planner/SKILL.md +126 -0
  188. package/skills/skills-codex/alphaxiv/SKILL.md +186 -0
  189. package/skills/skills-codex/analyze-results/SKILL.md +45 -0
  190. package/skills/skills-codex/arxiv/SKILL.md +210 -0
  191. package/skills/skills-codex/auto-paper-improvement-loop/SKILL.md +574 -0
  192. package/skills/skills-codex/auto-review-loop/SKILL.md +500 -0
  193. package/skills/skills-codex/auto-review-loop-llm/SKILL.md +247 -0
  194. package/skills/skills-codex/auto-review-loop-minimax/SKILL.md +290 -0
  195. package/skills/skills-codex/citation-audit/SKILL.md +504 -0
  196. package/skills/skills-codex/claims-drafting/SKILL.md +239 -0
  197. package/skills/skills-codex/comm-lit-review/SKILL.md +299 -0
  198. package/skills/skills-codex/comm-lit-review/references/domain-taxonomy.md +57 -0
  199. package/skills/skills-codex/comm-lit-review/references/output-template.md +37 -0
  200. package/skills/skills-codex/comm-lit-review/references/source-policy.md +99 -0
  201. package/skills/skills-codex/comm-lit-review/references/venue-tiering.md +112 -0
  202. package/skills/skills-codex/deepxiv/SKILL.md +142 -0
  203. package/skills/skills-codex/dse-loop/SKILL.md +285 -0
  204. package/skills/skills-codex/embodiment-description/SKILL.md +129 -0
  205. package/skills/skills-codex/exa-search/SKILL.md +192 -0
  206. package/skills/skills-codex/experiment-audit/SKILL.md +286 -0
  207. package/skills/skills-codex/experiment-bridge/SKILL.md +356 -0
  208. package/skills/skills-codex/experiment-plan/SKILL.md +249 -0
  209. package/skills/skills-codex/experiment-queue/SKILL.md +401 -0
  210. package/skills/skills-codex/feishu-notify/SKILL.md +155 -0
  211. package/skills/skills-codex/figure-description/SKILL.md +138 -0
  212. package/skills/skills-codex/figure-spec/SKILL.md +252 -0
  213. package/skills/skills-codex/formula-derivation/SKILL.md +280 -0
  214. package/skills/skills-codex/gemini-search/SKILL.md +205 -0
  215. package/skills/skills-codex/grant-proposal/SKILL.md +626 -0
  216. package/skills/skills-codex/idea-creator/SKILL.md +405 -0
  217. package/skills/skills-codex/idea-discovery/SKILL.md +475 -0
  218. package/skills/skills-codex/idea-discovery-robot/SKILL.md +362 -0
  219. package/skills/skills-codex/integrity-forensics/SKILL.md +106 -0
  220. package/skills/skills-codex/interview-cheatsheet/SKILL.md +245 -0
  221. package/skills/skills-codex/invention-structuring/SKILL.md +188 -0
  222. package/skills/skills-codex/jurisdiction-format/SKILL.md +192 -0
  223. package/skills/skills-codex/kill-argument/SKILL.md +403 -0
  224. package/skills/skills-codex/mermaid-diagram/SKILL.md +379 -0
  225. package/skills/skills-codex/meta-apply/SKILL.md +154 -0
  226. package/skills/skills-codex/meta-optimize/SKILL.md +348 -0
  227. package/skills/skills-codex/monitor-experiment/SKILL.md +98 -0
  228. package/skills/skills-codex/novelty-check/SKILL.md +89 -0
  229. package/skills/skills-codex/openalex/SKILL.md +228 -0
  230. package/skills/skills-codex/overleaf-sync/SKILL.md +220 -0
  231. package/skills/skills-codex/paper-claim-audit/SKILL.md +350 -0
  232. package/skills/skills-codex/paper-compile/SKILL.md +253 -0
  233. package/skills/skills-codex/paper-figure/SKILL.md +311 -0
  234. package/skills/skills-codex/paper-illustration/SKILL.md +690 -0
  235. package/skills/skills-codex/paper-illustration-image2/SKILL.md +383 -0
  236. package/skills/skills-codex/paper-illustration-image2/scripts/paper_illustration_image2.py +255 -0
  237. package/skills/skills-codex/paper-plan/SKILL.md +278 -0
  238. package/skills/skills-codex/paper-poster/SKILL.md +19 -0
  239. package/skills/skills-codex/paper-poster-html/SKILL.md +377 -0
  240. package/skills/skills-codex/paper-slides/SKILL.md +571 -0
  241. package/skills/skills-codex/paper-talk/SKILL.md +381 -0
  242. package/skills/skills-codex/paper-write/SKILL.md +411 -0
  243. package/skills/skills-codex/paper-write/templates/IEEEtran.bst +2409 -0
  244. package/skills/skills-codex/paper-write/templates/IEEEtran.cls +6347 -0
  245. package/skills/skills-codex/paper-write/templates/aaai2026.bst +1493 -0
  246. package/skills/skills-codex/paper-write/templates/aaai2026.sty +315 -0
  247. package/skills/skills-codex/paper-write/templates/aaai2026.tex +952 -0
  248. package/skills/skills-codex/paper-write/templates/acl.sty +312 -0
  249. package/skills/skills-codex/paper-write/templates/acl2026.tex +377 -0
  250. package/skills/skills-codex/paper-write/templates/acl_natbib.bst +1940 -0
  251. package/skills/skills-codex/paper-write/templates/acm.bst +3081 -0
  252. package/skills/skills-codex/paper-write/templates/acm_mm2026.tex +204 -0
  253. package/skills/skills-codex/paper-write/templates/acmart.cls +3520 -0
  254. package/skills/skills-codex/paper-write/templates/cvpr.bst +1448 -0
  255. package/skills/skills-codex/paper-write/templates/cvpr.sty +508 -0
  256. package/skills/skills-codex/paper-write/templates/cvpr2026.tex +63 -0
  257. package/skills/skills-codex/paper-write/templates/iclr2026.tex +84 -0
  258. package/skills/skills-codex/paper-write/templates/iclr2026_conference.bst +1440 -0
  259. package/skills/skills-codex/paper-write/templates/iclr2026_conference.sty +246 -0
  260. package/skills/skills-codex/paper-write/templates/icml2026.sty +767 -0
  261. package/skills/skills-codex/paper-write/templates/icml2026.tex +662 -0
  262. package/skills/skills-codex/paper-write/templates/ieee_conference.tex +89 -0
  263. package/skills/skills-codex/paper-write/templates/ieee_journal.tex +93 -0
  264. package/skills/skills-codex/paper-write/templates/math_commands.tex +48 -0
  265. package/skills/skills-codex/paper-write/templates/neurips2026.tex +493 -0
  266. package/skills/skills-codex/paper-write/templates/neurips_2026.sty +437 -0
  267. package/skills/skills-codex/paper-writing/SKILL.md +731 -0
  268. package/skills/skills-codex/patent-novelty-check/SKILL.md +153 -0
  269. package/skills/skills-codex/patent-pipeline/SKILL.md +344 -0
  270. package/skills/skills-codex/patent-review/SKILL.md +202 -0
  271. package/skills/skills-codex/pixel-art/SKILL.md +139 -0
  272. package/skills/skills-codex/prior-art-search/SKILL.md +146 -0
  273. package/skills/skills-codex/proof-checker/SKILL.md +554 -0
  274. package/skills/skills-codex/proof-orchestrator/SKILL.md +260 -0
  275. package/skills/skills-codex/proof-orchestrator/references/audit-output-contract.md +126 -0
  276. package/skills/skills-codex/proof-orchestrator/references/deepseek-routing.md +76 -0
  277. package/skills/skills-codex/proof-orchestrator/references/dispatch-prompts.md +227 -0
  278. package/skills/skills-codex/proof-orchestrator/references/notation-audit.md +135 -0
  279. package/skills/skills-codex/proof-orchestrator/references/proof-audit-rubric.md +70 -0
  280. package/skills/skills-codex/proof-orchestrator/references/stress-tests.md +38 -0
  281. package/skills/skills-codex/proof-writer/SKILL.md +222 -0
  282. package/skills/skills-codex/qzcli/SKILL.md +324 -0
  283. package/skills/skills-codex/rebuttal/SKILL.md +305 -0
  284. package/skills/skills-codex/render-html/SKILL.md +305 -0
  285. package/skills/skills-codex/render-html/scripts/__pycache__/render_html.cpython-314.pyc +0 -0
  286. package/skills/skills-codex/render-html/scripts/render_html.py +909 -0
  287. package/skills/skills-codex/render-html/scripts/templates/academic.html +342 -0
  288. package/skills/skills-codex/render-html/scripts/templates/dashboard.html +333 -0
  289. package/skills/skills-codex/research-lit/SKILL.md +464 -0
  290. package/skills/skills-codex/research-pipeline/SKILL.md +340 -0
  291. package/skills/skills-codex/research-refine/SKILL.md +721 -0
  292. package/skills/skills-codex/research-refine-pipeline/SKILL.md +186 -0
  293. package/skills/skills-codex/research-review/SKILL.md +135 -0
  294. package/skills/skills-codex/research-wiki/SKILL.md +421 -0
  295. package/skills/skills-codex/resubmit-pipeline/SKILL.md +444 -0
  296. package/skills/skills-codex/result-to-claim/SKILL.md +246 -0
  297. package/skills/skills-codex/run-experiment/SKILL.md +236 -0
  298. package/skills/skills-codex/semantic-scholar/SKILL.md +219 -0
  299. package/skills/skills-codex/serverless-modal/SKILL.md +335 -0
  300. package/skills/skills-codex/shared-references/acceptance-gate.md +336 -0
  301. package/skills/skills-codex/shared-references/assurance-contract.md +139 -0
  302. package/skills/skills-codex/shared-references/capture-antipatterns.md +84 -0
  303. package/skills/skills-codex/shared-references/citation-discipline.md +452 -0
  304. package/skills/skills-codex/shared-references/compute-env-contract.md +163 -0
  305. package/skills/skills-codex/shared-references/effort-contract.md +143 -0
  306. package/skills/skills-codex/shared-references/evidence-precheck.md +73 -0
  307. package/skills/skills-codex/shared-references/experiment-integrity.md +49 -0
  308. package/skills/skills-codex/shared-references/external-cadence.md +334 -0
  309. package/skills/skills-codex/shared-references/fan-out-pattern.md +375 -0
  310. package/skills/skills-codex/shared-references/injection-hygiene.md +132 -0
  311. package/skills/skills-codex/shared-references/integration-contract.md +372 -0
  312. package/skills/skills-codex/shared-references/output-composition.md +98 -0
  313. package/skills/skills-codex/shared-references/output-language.md +45 -0
  314. package/skills/skills-codex/shared-references/output-manifest.md +40 -0
  315. package/skills/skills-codex/shared-references/output-versioning.md +111 -0
  316. package/skills/skills-codex/shared-references/patent-format-cn.md +199 -0
  317. package/skills/skills-codex/shared-references/patent-format-ep.md +173 -0
  318. package/skills/skills-codex/shared-references/patent-format-us.md +161 -0
  319. package/skills/skills-codex/shared-references/patent-writing-principles.md +197 -0
  320. package/skills/skills-codex/shared-references/prior-art-databases.md +141 -0
  321. package/skills/skills-codex/shared-references/resumable-runs.md +125 -0
  322. package/skills/skills-codex/shared-references/review-scope-limits.md +81 -0
  323. package/skills/skills-codex/shared-references/review-tracing.md +144 -0
  324. package/skills/skills-codex/shared-references/reviewer-independence.md +66 -0
  325. package/skills/skills-codex/shared-references/reviewer-routing.md +128 -0
  326. package/skills/skills-codex/shared-references/skill-governance.md +119 -0
  327. package/skills/skills-codex/shared-references/taste-calibration.md +90 -0
  328. package/skills/skills-codex/shared-references/venue-checklists.md +73 -0
  329. package/skills/skills-codex/shared-references/wiki-helper-resolution.md +69 -0
  330. package/skills/skills-codex/shared-references/writing-principles.md +525 -0
  331. package/skills/skills-codex/slides-polish/SKILL.md +563 -0
  332. package/skills/skills-codex/specification-writing/SKILL.md +211 -0
  333. package/skills/skills-codex/system-profile/SKILL.md +103 -0
  334. package/skills/skills-codex/training-check/SKILL.md +83 -0
  335. package/skills/skills-codex/vast-gpu/SKILL.md +394 -0
  336. package/skills/skills-codex/web-debug-search/SKILL.md +334 -0
  337. package/skills/skills-codex/wiki-enrich/SKILL.md +255 -0
  338. package/skills/skills-codex/writing-systems-papers/SKILL.md +184 -0
  339. package/skills/skills-codex-claude-review/README.md +79 -0
  340. package/skills/skills-codex-claude-review/README_CN.md +78 -0
  341. package/skills/skills-codex-claude-review/auto-paper-improvement-loop/SKILL.md +581 -0
  342. package/skills/skills-codex-claude-review/auto-review-loop/SKILL.md +510 -0
  343. package/skills/skills-codex-claude-review/novelty-check/SKILL.md +102 -0
  344. package/skills/skills-codex-claude-review/paper-figure/SKILL.md +319 -0
  345. package/skills/skills-codex-claude-review/paper-plan/SKILL.md +287 -0
  346. package/skills/skills-codex-claude-review/paper-write/SKILL.md +420 -0
  347. package/skills/skills-codex-claude-review/research-refine/SKILL.md +732 -0
  348. package/skills/skills-codex-claude-review/research-review/SKILL.md +149 -0
  349. package/skills/skills-codex-gemini-review/README.md +176 -0
  350. package/skills/skills-codex-gemini-review/README_CN.md +175 -0
  351. package/skills/skills-codex-gemini-review/auto-paper-improvement-loop/SKILL.md +331 -0
  352. package/skills/skills-codex-gemini-review/auto-review-loop/SKILL.md +304 -0
  353. package/skills/skills-codex-gemini-review/grant-proposal/SKILL.md +630 -0
  354. package/skills/skills-codex-gemini-review/idea-creator/SKILL.md +263 -0
  355. package/skills/skills-codex-gemini-review/idea-discovery/SKILL.md +275 -0
  356. package/skills/skills-codex-gemini-review/idea-discovery-robot/SKILL.md +365 -0
  357. package/skills/skills-codex-gemini-review/novelty-check/SKILL.md +92 -0
  358. package/skills/skills-codex-gemini-review/paper-figure/SKILL.md +289 -0
  359. package/skills/skills-codex-gemini-review/paper-plan/SKILL.md +265 -0
  360. package/skills/skills-codex-gemini-review/paper-poster-html/SKILL.md +102 -0
  361. package/skills/skills-codex-gemini-review/paper-slides/SKILL.md +582 -0
  362. package/skills/skills-codex-gemini-review/paper-write/SKILL.md +346 -0
  363. package/skills/skills-codex-gemini-review/paper-writing/SKILL.md +312 -0
  364. package/skills/skills-codex-gemini-review/research-refine/SKILL.md +674 -0
  365. package/skills/skills-codex-gemini-review/research-review/SKILL.md +112 -0
  366. package/skills/slides-polish/SKILL.md +565 -0
  367. package/skills/specification-writing/SKILL.md +211 -0
  368. package/skills/system-profile/SKILL.md +103 -0
  369. package/skills/training-check/SKILL.md +132 -0
  370. package/skills/vast-gpu/SKILL.md +394 -0
  371. package/skills/web-debug-search/SKILL.md +334 -0
  372. package/skills/wiki-enrich/SKILL.md +257 -0
  373. package/skills/writing-systems-papers/SKILL.md +184 -0
  374. package/templates/CLAUDE_MD_TEMPLATE.md +29 -0
  375. package/templates/EXPERIMENT_LOG_TEMPLATE.md +47 -0
  376. package/templates/EXPERIMENT_PLAN_TEMPLATE.md +51 -0
  377. package/templates/EXPERIMENT_PLAN_TEMPLATE_CN.md +53 -0
  378. package/templates/FINDINGS_TEMPLATE.md +52 -0
  379. package/templates/IDEA_CANDIDATES_TEMPLATE.md +47 -0
  380. package/templates/IDEA_CANDIDATES_TEMPLATE_CN.md +47 -0
  381. package/templates/INVENTION_BRIEF_TEMPLATE.md +87 -0
  382. package/templates/MANIFEST_TEMPLATE.md +7 -0
  383. package/templates/NARRATIVE_REPORT_TEMPLATE.md +49 -0
  384. package/templates/PAPER_PLAN_TEMPLATE.md +47 -0
  385. package/templates/PATENT_CLAIMS_TEMPLATE.md +78 -0
  386. package/templates/PATENT_SPECIFICATION_TEMPLATE.md +68 -0
  387. package/templates/README.md +57 -0
  388. package/templates/RESEARCH_BRIEF_TEMPLATE.md +35 -0
  389. package/templates/RESEARCH_BRIEF_TEMPLATE_CN.md +41 -0
  390. package/templates/RESEARCH_CONTRACT_TEMPLATE.md +60 -0
  391. package/templates/claude-hooks/corpus_write_guard.json +16 -0
  392. package/templates/claude-hooks/corpus_write_guard.py +85 -0
  393. package/templates/claude-hooks/meta_logging.json +74 -0
  394. package/templates/gitignore-trace.txt +3 -0
  395. package/tools/__pycache__/check_skills_inventory.cpython-314.pyc +0 -0
  396. package/tools/arxiv_fetch.py +311 -0
  397. package/tools/capture_filter.py +126 -0
  398. package/tools/check_skills_inventory.py +273 -0
  399. package/tools/convert_skills_to_llm_chat.py +282 -0
  400. package/tools/copilot_native_evidence.py +818 -0
  401. package/tools/deepxiv_fetch.py +213 -0
  402. package/tools/evidence_check.py +212 -0
  403. package/tools/exa_search.py +425 -0
  404. package/tools/experiment_queue/README.md +118 -0
  405. package/tools/experiment_queue/build_manifest.py +44 -0
  406. package/tools/experiment_queue/queue_manager.py +44 -0
  407. package/tools/extract_paper_style.py +560 -0
  408. package/tools/figure_renderer.py +69 -0
  409. package/tools/forensics_gate.py +669 -0
  410. package/tools/generate_codex_claude_review_overrides.py +299 -0
  411. package/tools/idea_discovery_gate.py +256 -0
  412. package/tools/install_aris.ps1 +1372 -0
  413. package/tools/install_aris.sh +1370 -0
  414. package/tools/install_aris_codex.sh +1023 -0
  415. package/tools/install_aris_copilot.sh +1052 -0
  416. package/tools/iteration_log.py +143 -0
  417. package/tools/lint_skills_helpers.sh +84 -0
  418. package/tools/meta_opt/check_ready.sh +80 -0
  419. package/tools/meta_opt/log_event.sh +91 -0
  420. package/tools/meta_opt/trigger_eval.py +280 -0
  421. package/tools/meta_opt/trigger_evals.sample.json +28 -0
  422. package/tools/openalex_fetch.py +326 -0
  423. package/tools/overleaf_audit.sh +104 -0
  424. package/tools/overleaf_setup.sh +150 -0
  425. package/tools/paper_illustration_image2.py +62 -0
  426. package/tools/provenance.py +294 -0
  427. package/tools/research_wiki.py +1720 -0
  428. package/tools/review_gate.py +502 -0
  429. package/tools/run_state.py +399 -0
  430. package/tools/save_trace.sh +477 -0
  431. package/tools/semantic_scholar_fetch.py +438 -0
  432. package/tools/skill-groups.tsv +116 -0
  433. package/tools/skill_picker.py +238 -0
  434. package/tools/smart_update.ps1 +521 -0
  435. package/tools/smart_update.sh +591 -0
  436. package/tools/smart_update_codex.sh +419 -0
  437. package/tools/smart_update_copilot.sh +605 -0
  438. package/tools/threat_scan.py +222 -0
  439. package/tools/verify_paper_audits.sh +487 -0
  440. package/tools/verify_papers.py +613 -0
  441. package/tools/verify_wiki_coverage.sh +176 -0
  442. package/tools/watchdog.py +485 -0
@@ -0,0 +1,251 @@
1
+ #!/usr/bin/env python3
2
+ """poster_check — unified CLI for HTML academic posters.
3
+
4
+ Four subcommands:
5
+
6
+ measure Print-emulate HTML in headless Chromium, measure all
7
+ ``[data-measure-role]`` elements, report column-bottom
8
+ spread and gap to the next horizontal strip. The HARD
9
+ alignment gate (spread < 5 px is non-negotiable).
10
+ preflight Static HTML scan: LaTeX residue, raw '<' inside
11
+ ``$…$`` / ``$$…$$`` / ``\\(…\\)`` / ``\\[…\\]``,
12
+ missing local images, missing data-measure-role.
13
+ polish Visual-polish warnings on figure sizing, broken
14
+ images, typography orphans, and space-between fill.
15
+ Soft gate; warns by default. ``--strict`` to fail.
16
+ Hard-fails if there's no measurement markup at all.
17
+ verify-final Run ``pdfinfo`` on a rendered PDF; check page count,
18
+ dimensions match the expected canvas (``--canvas`` or
19
+ ``--from-html``), and file size under a limit.
20
+
21
+ All logic lives in the ``_posterly`` package next to this file. This
22
+ script is a thin argparse dispatcher.
23
+ """
24
+ from __future__ import annotations
25
+
26
+ import argparse
27
+ import os
28
+ import sys
29
+
30
+ # Make `_posterly` importable when this file is run directly via
31
+ # `python tools/poster_check.py …`.
32
+ _THIS_DIR = os.path.dirname(os.path.abspath(__file__))
33
+ if _THIS_DIR not in sys.path:
34
+ sys.path.insert(0, _THIS_DIR)
35
+
36
+ from _posterly import canvas as _canvas # noqa: E402
37
+ from _posterly import measure as _measure # noqa: E402
38
+ from _posterly import polish as _polish # noqa: E402
39
+ from _posterly import preflight as _preflight # noqa: E402
40
+ from _posterly import verify_final as _verify_final # noqa: E402
41
+
42
+
43
+ def build_parser() -> argparse.ArgumentParser:
44
+ p = argparse.ArgumentParser(
45
+ prog="poster_check",
46
+ description="Measure / preflight / polish / verify a poster "
47
+ "HTML+PDF pair.",
48
+ )
49
+ sub = p.add_subparsers(dest="cmd", required=True)
50
+
51
+ # --- measure --------------------------------------------------------
52
+ pm = sub.add_parser(
53
+ "measure",
54
+ help="alignment + gap gate (print-emulated, HARD gate)",
55
+ )
56
+ pm.add_argument("html", help="path to poster.html")
57
+ pm.add_argument(
58
+ "--max-spread", type=float, default=5.0,
59
+ help="hard gate: max column-bottom spread in px "
60
+ "(default 5.0; aim < 3.0)",
61
+ )
62
+ pm.add_argument(
63
+ "--min-gap", type=float, default=30.0,
64
+ help="min gap to footer-strip/footer (default 30 px)",
65
+ )
66
+ pm.add_argument(
67
+ "--max-gap", type=float, default=50.0,
68
+ help="max gap to footer-strip/footer (default 50 px)",
69
+ )
70
+ pm.add_argument(
71
+ "--canvas", type=_canvas.parse_canvas_arg, default=None,
72
+ help="override canvas (e.g. '60x36in' or 'A0 portrait'); "
73
+ "by default we parse @page from the HTML",
74
+ )
75
+ pm.add_argument(
76
+ "--allow-empty-column", action="store_true",
77
+ help="don't fail when a column has no cards "
78
+ "(fallback to column.bottom; risky)",
79
+ )
80
+ pm.add_argument(
81
+ "--allow-no-footer-gap", action="store_true",
82
+ help="don't fail when neither footer-strip nor footer exists "
83
+ "below the content",
84
+ )
85
+ pm.add_argument(
86
+ "--settle-ms", type=int, default=500,
87
+ help="extra wait after MathJax + fonts.ready settle "
88
+ "(default 500)",
89
+ )
90
+ pm.add_argument(
91
+ "--mathjax-timeout-ms", type=int, default=15000,
92
+ help="hard timeout for MathJax typeset (default 15000); "
93
+ "exceeding it FAILS the gate, not warns",
94
+ )
95
+ pm.add_argument(
96
+ "--min-canvas-fill", type=float, default=0.95,
97
+ help="hard gate: [data-measure-role='poster'] must fill at "
98
+ "least this fraction of the print viewport in BOTH "
99
+ "dimensions (default 0.95). Catches the silent 'forgot "
100
+ "the print media-query unit override' bug where the "
101
+ "poster renders at screen scale into a much bigger "
102
+ "print page.",
103
+ )
104
+ pm.add_argument(
105
+ "--max-canvas-fill", type=float, default=1.01,
106
+ help="hard gate: poster must NOT exceed this fraction of the "
107
+ "print viewport (default 1.01; i.e. <=1%% overshoot is "
108
+ "tolerated for sub-pixel rounding). The symmetric of "
109
+ "--min-canvas-fill catches the case where a hardcoded "
110
+ "`width` in px exceeds `@page size`.",
111
+ )
112
+ pm.add_argument(
113
+ "--position-tol-px", type=float, default=2.0,
114
+ help="hard gate: poster's bbox edges must align with the print "
115
+ "viewport's origin within this many px (default 2.0). "
116
+ "Catches `transform: translate*`, mis-positioned absolute "
117
+ "layout, and stray body margin in print.",
118
+ )
119
+ pm.add_argument(
120
+ "--max-clip-px", type=float, default=2.0,
121
+ help="hard gate: a card/column/hero whose content is clipped by "
122
+ "overflow:hidden|clip|scroll|auto by MORE than this many px "
123
+ "(scrollHeight-clientHeight) FAILS -- clipped content is "
124
+ "silently lost in print while the box still looks aligned "
125
+ "(default 2.0; sub-pixel rounding tolerated).",
126
+ )
127
+ pm.add_argument(
128
+ "--max-intercard-gap", type=float,
129
+ default=_measure.DEFAULT_MAX_INTERCARD_GAP,
130
+ help="hard gate: max whitespace between consecutive stacked "
131
+ "cards in a column (default 50 px, same ceiling as the "
132
+ "footer gap). Catches `justify-content: space-between` "
133
+ "faking bottom alignment on an under-filled column -- "
134
+ "spread reads ~0 while a void sits mid-column.",
135
+ )
136
+ pm.add_argument(
137
+ "--min-intercard-gap", type=float,
138
+ default=_measure.DEFAULT_MIN_INTERCARD_GAP,
139
+ help="hard gate: min whitespace between consecutive stacked "
140
+ "cards (default 12 px). Tighter gaps bury the card's drop "
141
+ "shadow (templates ship `0 2u 6u`) under the next card, "
142
+ "fusing the stack into one slab. Set 0 to disable for "
143
+ "shadowless themes.",
144
+ )
145
+ pm.add_argument(
146
+ "--json-out", default=None,
147
+ help="dump raw measurement to JSON",
148
+ )
149
+ pm.set_defaults(func=_measure.cmd_measure)
150
+
151
+ # --- preflight ------------------------------------------------------
152
+ pp = sub.add_parser(
153
+ "preflight",
154
+ help="static HTML lint (LaTeX residue, math, images, roles)",
155
+ )
156
+ pp.add_argument("html", help="path to poster.html")
157
+ pp.set_defaults(func=_preflight.cmd_preflight)
158
+
159
+ # --- polish ---------------------------------------------------------
160
+ ppl = sub.add_parser(
161
+ "polish",
162
+ help="visual-polish warnings (figure size, orphans, "
163
+ "space-between, flex/<br>)",
164
+ )
165
+ ppl.add_argument("html", help="path to poster.html")
166
+ ppl.add_argument(
167
+ "--canvas", type=_canvas.parse_canvas_arg, default=None,
168
+ help="override canvas (default: parse @page from HTML)",
169
+ )
170
+ ppl.add_argument(
171
+ "--settle-ms", type=int, default=500,
172
+ help="extra wait after layout settles (default 500)",
173
+ )
174
+ ppl.add_argument(
175
+ "--mathjax-timeout-ms", type=int, default=15000,
176
+ help="hard timeout for MathJax typeset (default 15000)",
177
+ )
178
+ ppl.add_argument(
179
+ "--wide-min-ratio", type=float, default=0.65,
180
+ help="wide figures (AR>1.3) must occupy >= this fraction of "
181
+ "card width (default 0.65)",
182
+ )
183
+ ppl.add_argument(
184
+ "--tall-max-ratio", type=float, default=0.70,
185
+ help="tall figures (AR<0.8) above this fraction trigger a "
186
+ "text-right recommendation (default 0.70)",
187
+ )
188
+ ppl.add_argument(
189
+ "--square-min-ratio", type=float, default=0.55,
190
+ help="square figures (0.8<=AR<=1.3) must occupy >= this "
191
+ "fraction (default 0.55)",
192
+ )
193
+ ppl.add_argument(
194
+ "--max-space-between-fill", type=float, default=0.05,
195
+ help="warn if a space-between column has an inter-card gap "
196
+ "exceeding this fraction of column height (default 0.05)",
197
+ )
198
+ ppl.add_argument(
199
+ "--max-card-trailing", type=float, default=0.10,
200
+ help="warn (CARD/TRAILING) if a card leaves more than this "
201
+ "fraction of its height blank below the last line "
202
+ "(default 0.10); catches a flex-stretched card padded "
203
+ "with whitespace to fake a full page",
204
+ )
205
+ ppl.add_argument(
206
+ "--strict", action="store_true",
207
+ help="exit non-zero when any warning is emitted",
208
+ )
209
+ ppl.set_defaults(func=_polish.cmd_polish)
210
+
211
+ # --- verify-final ---------------------------------------------------
212
+ pv = sub.add_parser(
213
+ "verify-final",
214
+ help="run pdfinfo + size/dimension/page checks on PDF",
215
+ )
216
+ pv.add_argument("pdf", help="path to poster.pdf")
217
+ pv.add_argument(
218
+ "--canvas", type=_canvas.parse_canvas_arg, default=None,
219
+ help="expected canvas (e.g. '60x36in' or 'A0 portrait'); "
220
+ "either this or --from-html is required",
221
+ )
222
+ pv.add_argument(
223
+ "--from-html", default=None,
224
+ help="read expected canvas from `@page { size }` in this "
225
+ "HTML; mutually exclusive with --canvas",
226
+ )
227
+ pv.add_argument(
228
+ "--dim-tol-in", type=float, default=0.05,
229
+ help="dimension tolerance in inches (default 0.05)",
230
+ )
231
+ pv.add_argument(
232
+ "--max-size-mb", type=float, default=20.0,
233
+ help="max file size in MB (default 20)",
234
+ )
235
+ pv.add_argument(
236
+ "--allow-rotated", action="store_true",
237
+ help="accept swapped W/H even when PDF declares no page "
238
+ "rotation (most posters should NOT need this)",
239
+ )
240
+ pv.set_defaults(func=_verify_final.cmd_verify_final)
241
+
242
+ return p
243
+
244
+
245
+ def main(argv: list[str] | None = None) -> int:
246
+ args = build_parser().parse_args(argv)
247
+ return args.func(args)
248
+
249
+
250
+ if __name__ == "__main__":
251
+ sys.exit(main())
@@ -0,0 +1,238 @@
1
+ #!/usr/bin/env python3
2
+ """preprocess_figures -- clean up extracted crops before they go on a poster.
3
+
4
+ A bbox crop off a PDF page (see ``extract_pdf_figures.py crop``) almost
5
+ always carries a band of near-white page margin: the bbox is read off a
6
+ coordinate grid by eye, so it's deliberately generous. Those margins make
7
+ the figure render small inside its card and trip the asset_check
8
+ "natural >= 1.5x rendered" resolution gate for the WRONG reason (the real
9
+ pixels are fine; they're just padded with whitespace). This script:
10
+
11
+ * autocrops the near-white border (PIL ImageChops.difference against a
12
+ white backdrop, configurable threshold), leaving ``--pad`` px of
13
+ breathing room so nothing touches the card edge;
14
+ * reports each image's natural pixel size;
15
+ * warns when an image is below ``--min-px`` (low-res crops upscale ugly
16
+ on a printed A0); and
17
+ * keeps FIGURE_MANIFEST.json honest -- when a crop is modified, its
18
+ ``natural_px`` and ``sha256`` in the manifest are updated to match,
19
+ so the sha chain asset_check relies on stays valid.
20
+
21
+ PIL is imported lazily so ``--help`` works without it and a missing dep
22
+ yields a readable install hint instead of an import traceback.
23
+ """
24
+ from __future__ import annotations
25
+
26
+ import argparse
27
+ import hashlib
28
+ import json
29
+ import os
30
+ import sys
31
+ from pathlib import Path
32
+
33
+ _THIS_DIR = os.path.dirname(os.path.abspath(__file__))
34
+ if _THIS_DIR not in sys.path:
35
+ sys.path.insert(0, _THIS_DIR)
36
+
37
+ from _posterly.textutil import ascii_safe # noqa: E402
38
+
39
+ # Near-white threshold: pixels at/above this in all channels count as
40
+ # "blank margin". 248 (of 255) keeps faint antialiased plot edges while
41
+ # trimming JPEG-ish off-white page background.
42
+ DEFAULT_WHITE_THRESHOLD = 248
43
+
44
+
45
+ def _eprint(*args: object, **kw: object) -> None:
46
+ print(*args, file=sys.stderr, **kw) # type: ignore[arg-type]
47
+
48
+
49
+ def _load_pil():
50
+ """Import PIL pieces or exit(2) with an actionable message."""
51
+ try:
52
+ from PIL import Image, ImageChops # type: ignore
53
+ return Image, ImageChops
54
+ except ImportError:
55
+ _eprint(
56
+ "ERROR: Pillow (PIL) not installed -- required for "
57
+ "preprocess_figures. Install with:\n"
58
+ " python -m pip install pillow"
59
+ )
60
+ raise SystemExit(2)
61
+
62
+
63
+ def _sha256_file(path: Path) -> str:
64
+ h = hashlib.sha256()
65
+ with open(path, "rb") as fh:
66
+ for chunk in iter(lambda: fh.read(1 << 20), b""):
67
+ h.update(chunk)
68
+ return h.hexdigest()
69
+
70
+
71
+ def _autocrop_box(img, ImageChops, threshold: int):
72
+ """Return the bbox of the non-white content, or None if all-white.
73
+
74
+ We build a white backdrop the same size, take the absolute per-pixel
75
+ difference, then threshold to a 1-bit mask whose ``getbbox`` is the
76
+ tight content box. Using difference-vs-white (rather than scanning for
77
+ exact 255) tolerates the slightly-off-white background of rasterised
78
+ PDF pages.
79
+ """
80
+ rgb = img.convert("RGB")
81
+ bg = rgb.point(lambda _p: 255) # solid white, same size/mode
82
+ diff = ImageChops.difference(rgb, bg)
83
+ # Map "close to white" (diff small) -> 0, content -> 255.
84
+ cutoff = 255 - threshold # e.g. threshold 248 -> 7
85
+ mask = diff.convert("L").point(
86
+ lambda p: 255 if p > cutoff else 0)
87
+ return mask.getbbox()
88
+
89
+
90
+ def _apply_pad(box, pad: int, size):
91
+ """Expand a (l,t,r,b) box by ``pad`` px, clamped to image size."""
92
+ w, h = size
93
+ l, t, r, b = box
94
+ return (max(0, l - pad), max(0, t - pad),
95
+ min(w, r + pad), min(h, b + pad))
96
+
97
+
98
+ def _load_manifest(path: Path) -> dict | None:
99
+ if not path.exists():
100
+ _eprint(f"WARN: manifest not found, skipping sync: "
101
+ f"{ascii_safe(path)}")
102
+ return None
103
+ try:
104
+ return json.loads(path.read_text(encoding="utf-8"))
105
+ except (json.JSONDecodeError, OSError) as exc:
106
+ _eprint(f"ERROR: manifest unreadable: {ascii_safe(exc)}")
107
+ raise SystemExit(1)
108
+
109
+
110
+ def _sync_manifest_entry(manifest: dict, manifest_path: Path,
111
+ img_path: Path, natural_px, sha: str) -> bool:
112
+ """Update natural_px + sha256 for the manifest entry whose 'file'
113
+ resolves to this image. Match by resolved absolute path so a relative
114
+ manifest 'file' (assets/paper_figures/x.png) still matches an image
115
+ given by any path spelling. Returns True if an entry was updated.
116
+ """
117
+ target = img_path.resolve()
118
+ updated = False
119
+ for fig in manifest.get("figures", []):
120
+ fpath = (manifest_path.parent / fig.get("file", "")).resolve()
121
+ if fpath == target:
122
+ fig["natural_px"] = [natural_px[0], natural_px[1]]
123
+ fig["sha256"] = sha
124
+ updated = True
125
+ return updated
126
+
127
+
128
+ def build_parser() -> argparse.ArgumentParser:
129
+ p = argparse.ArgumentParser(
130
+ prog="preprocess_figures",
131
+ description="Autocrop near-white margins, report natural size, "
132
+ "warn on low-res, and sync FIGURE_MANIFEST.json.",
133
+ )
134
+ p.add_argument("images", nargs="+", metavar="IMG",
135
+ help="one or more image files to process in place")
136
+ p.add_argument("--autocrop", action="store_true",
137
+ help="trim near-white border (default: report only)")
138
+ p.add_argument("--pad", type=int, default=6,
139
+ help="px of padding kept around content after autocrop "
140
+ "(default 6)")
141
+ p.add_argument("--threshold", type=int, default=DEFAULT_WHITE_THRESHOLD,
142
+ help="near-white cutoff 0-255; pixels brighter than this "
143
+ f"are margin (default {DEFAULT_WHITE_THRESHOLD})")
144
+ p.add_argument("--min-px", type=int, nargs=2, metavar=("W", "H"),
145
+ default=None,
146
+ help="warn if natural size is below W x H px "
147
+ "(e.g. --min-px 1200 700)")
148
+ p.add_argument("--manifest", default=None,
149
+ help="FIGURE_MANIFEST.json to keep in sync after edits")
150
+ return p
151
+
152
+
153
+ def main(argv: list[str] | None = None) -> int:
154
+ args = build_parser().parse_args(argv)
155
+ Image, ImageChops = _load_pil()
156
+
157
+ manifest_path = Path(args.manifest).resolve() if args.manifest else None
158
+ manifest = _load_manifest(manifest_path) if manifest_path else None
159
+ manifest_dirty = False
160
+
161
+ any_warn = False
162
+ any_missing = False
163
+ for img_arg in args.images:
164
+ img_path = Path(img_arg)
165
+ if not img_path.exists():
166
+ _eprint(f"ERROR: image not found: {ascii_safe(img_path)}")
167
+ any_missing = True
168
+ continue
169
+ try:
170
+ img = Image.open(img_path)
171
+ img.load()
172
+ except Exception as exc:
173
+ _eprint(f"ERROR: cannot open {ascii_safe(img_path)}: "
174
+ f"{ascii_safe(exc)}")
175
+ any_missing = True
176
+ continue
177
+
178
+ before = img.size
179
+ changed = False
180
+
181
+ if args.autocrop:
182
+ box = _autocrop_box(img, ImageChops, args.threshold)
183
+ if box is None:
184
+ _eprint(f"[preprocess] WARN: {ascii_safe(img_path.name)} "
185
+ f"is all near-white; skipping autocrop.")
186
+ else:
187
+ box = _apply_pad(box, args.pad, img.size)
188
+ if box != (0, 0, img.width, img.height):
189
+ img = img.crop(box)
190
+ img.save(img_path)
191
+ changed = True
192
+
193
+ natural = img.size
194
+ crop_note = (f" (autocropped from {before[0]}x{before[1]})"
195
+ if changed else "")
196
+ print(f"[preprocess] {ascii_safe(img_path.name)}: "
197
+ f"{natural[0]}x{natural[1]}px{crop_note}")
198
+
199
+ # Low-res warning -- a printed A0 poster magnifies every missing pixel.
200
+ if args.min_px is not None:
201
+ mw, mh = args.min_px
202
+ if natural[0] < mw or natural[1] < mh:
203
+ _eprint(f"[preprocess] WARN: {ascii_safe(img_path.name)} "
204
+ f"{natural[0]}x{natural[1]}px is below "
205
+ f"--min-px {mw}x{mh}; may upscale poorly in print.")
206
+ any_warn = True
207
+
208
+ # Manifest sync: refresh natural_px + sha256 for the matching entry.
209
+ # We re-hash whenever a manifest is supplied (even without a crop)
210
+ # so a manifest can be reconciled to images edited out-of-band.
211
+ if manifest is not None:
212
+ sha = _sha256_file(img_path)
213
+ if _sync_manifest_entry(manifest, manifest_path, img_path,
214
+ natural, sha):
215
+ manifest_dirty = True
216
+ else:
217
+ _eprint(f"[preprocess] note: no manifest entry references "
218
+ f"{ascii_safe(img_path.name)}; not synced.")
219
+
220
+ if manifest is not None and manifest_dirty:
221
+ manifest_path.write_text(
222
+ json.dumps(manifest, indent=2, ensure_ascii=False) + "\n",
223
+ encoding="utf-8",
224
+ )
225
+ print(f"[preprocess] manifest synced: {ascii_safe(manifest_path)}")
226
+
227
+ if any_missing:
228
+ return 2
229
+ if any_warn:
230
+ # Low-res is a WARN, not a hard fail -- the asset gate decides
231
+ # severity. Return 0 so a pipeline keeps going but the WARN lines
232
+ # are visible on stderr.
233
+ return 0
234
+ return 0
235
+
236
+
237
+ if __name__ == "__main__":
238
+ sys.exit(main())