dsh-aris-panel 0.2.8 → 0.3.1

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 (856) hide show
  1. package/README.md +117 -108
  2. package/README_CN.md +98 -89
  3. package/aris/.claude-plugin/marketplace.json +34 -0
  4. package/aris/.claude-plugin/plugin.json +19 -0
  5. package/aris/.codex-plugin/plugin.json +18 -0
  6. package/aris/.env.example +53 -0
  7. package/aris/.gitattributes +6 -0
  8. package/aris/.github/agents/aris-reviewer-claude.agent.md +10 -0
  9. package/aris/.github/agents/aris-reviewer-openai.agent.md +10 -0
  10. package/aris/.github/workflows/check-skills-inventory.yml +32 -0
  11. package/aris/.github/workflows/lint-skills-helpers.yml +38 -0
  12. package/aris/.github/workflows/test-install-aris-ps1.yml +31 -0
  13. package/aris/.github/workflows/test-suite.yml +59 -0
  14. package/aris/AGENT_GUIDE.md +222 -0
  15. package/aris/CONTRIBUTING.md +83 -0
  16. package/aris/CONTRIBUTING_CN.md +83 -0
  17. package/aris/LICENSE +21 -0
  18. package/aris/README.md +1739 -0
  19. package/aris/README_CN.md +1526 -0
  20. package/aris/SETUP_GUIDE.md +279 -0
  21. package/aris/SETUP_GUIDE_CN.md +279 -0
  22. package/aris/UPSTREAM.txt +2 -0
  23. package/aris/aris-monitor/README.md +158 -0
  24. package/aris/aris-monitor/assets/screenshot.png +0 -0
  25. package/aris/aris-monitor/focus-tty.sh +156 -0
  26. package/aris/aris-monitor/focus.py +93 -0
  27. package/aris/aris-monitor/run.sh +49 -0
  28. package/aris/aris-monitor/scanner.py +445 -0
  29. package/aris/aris-monitor/ticker.py +81 -0
  30. package/aris/aris-monitor/widget.py +372 -0
  31. package/aris/assets/claude-fleet-preview.png +0 -0
  32. package/aris/assets/community_showcase_7_10_codex.png +0 -0
  33. package/aris/assets/community_showcase_8_10.png +0 -0
  34. package/aris/assets/feishu_interactive.jpg +0 -0
  35. package/aris/assets/feishu_push.png +0 -0
  36. package/aris/assets/paper_illustration_demo.png +0 -0
  37. package/aris/commands/setup.md +77 -0
  38. package/aris/community_papers/UAV-CC.pdf +0 -0
  39. package/aris/docs/ALI_CODING_PLAN_GUIDE.md +324 -0
  40. package/aris/docs/ANTIGRAVITY_ADAPTATION.md +409 -0
  41. package/aris/docs/ANTIGRAVITY_ADAPTATION_CN.md +363 -0
  42. package/aris/docs/ARIS-Code-README_CN.md +8 -0
  43. package/aris/docs/ARIS-Code-README_EN.md +8 -0
  44. package/aris/docs/ARIS_INTRO.html +902 -0
  45. package/aris/docs/ARIS_INTRO.md +368 -0
  46. package/aris/docs/ARIS_INTRO.review.json +79 -0
  47. package/aris/docs/CODEX_CLAUDE_REVIEW_GUIDE.md +116 -0
  48. package/aris/docs/CODEX_CLAUDE_REVIEW_GUIDE_CN.md +116 -0
  49. package/aris/docs/CODEX_GEMINI_REVIEW_GUIDE.md +249 -0
  50. package/aris/docs/CODEX_GEMINI_REVIEW_GUIDE_CN.md +250 -0
  51. package/aris/docs/COPILOT_CLI_ADAPTATION.md +388 -0
  52. package/aris/docs/CURSOR_ADAPTATION.md +305 -0
  53. package/aris/docs/CUSTOMIZATION.md +155 -0
  54. package/aris/docs/CUSTOMIZATION_CN.md +154 -0
  55. package/aris/docs/GPU_SETUP.md +57 -0
  56. package/aris/docs/GPU_SETUP_CN.md +54 -0
  57. package/aris/docs/LLM_API_MIX_MATCH_GUIDE.md +256 -0
  58. package/aris/docs/MANUAL_REVIEW_GUIDE.md +128 -0
  59. package/aris/docs/MANUAL_REVIEW_GUIDE_CN.md +116 -0
  60. package/aris/docs/MINIMAX_MCP_GUIDE.md +198 -0
  61. package/aris/docs/MODELSCOPE_GUIDE.md +305 -0
  62. package/aris/docs/MODEL_COMBINATIONS.md +134 -0
  63. package/aris/docs/MODEL_COMBINATIONS_CN.md +134 -0
  64. package/aris/docs/MiniMax-GLM-Configuration.md +195 -0
  65. package/aris/docs/NARRATIVE_REPORT_EXAMPLE.md +143 -0
  66. package/aris/docs/OPENCLAW_ADAPTATION.md +59 -0
  67. package/aris/docs/OPENROUTER_GUIDE.md +255 -0
  68. package/aris/docs/OPENROUTER_GUIDE_CN.md +255 -0
  69. package/aris/docs/PROJECT_FILES_GUIDE.md +161 -0
  70. package/aris/docs/PROJECT_FILES_GUIDE_CN.md +161 -0
  71. package/aris/docs/RESUBMIT_AND_TALK.md +91 -0
  72. package/aris/docs/RESUBMIT_AND_TALK_CN.md +91 -0
  73. package/aris/docs/SESSION_RECOVERY_GUIDE.md +439 -0
  74. package/aris/docs/SESSION_RECOVERY_GUIDE_CN.md +431 -0
  75. package/aris/docs/SKILLS_CATALOG.md +204 -0
  76. package/aris/docs/TRAE_ARIS_RUNBOOK_CN.md +329 -0
  77. package/aris/docs/TRAE_ARIS_RUNBOOK_EN.md +329 -0
  78. package/aris/docs/WATCHDOG_GUIDE.md +200 -0
  79. package/aris/docs/WATCHDOG_GUIDE_CN.md +199 -0
  80. package/aris/docs/aris-code-banner.png +0 -0
  81. package/aris/docs/aris-code-screenshot.png +0 -0
  82. package/aris/docs/aris-movie-director-method.png +0 -0
  83. package/aris/docs/aris_intro_slides.pdf +0 -0
  84. package/aris/docs/aris_logo.svg +126 -0
  85. package/aris/docs/auto_review_score_curve.png +0 -0
  86. package/aris/docs/blogs/continuous_dlm_2026h1_survey.html +3262 -0
  87. package/aris/docs/hero_combined.svg +844 -0
  88. package/aris/docs/hero_idea.svg +158 -0
  89. package/aris/docs/hero_implement.svg +109 -0
  90. package/aris/docs/hero_interaction.svg +172 -0
  91. package/aris/docs/hf_daily_paper_1.svg +19 -0
  92. package/aris/docs/integrations/FEISHU.md +208 -0
  93. package/aris/docs/integrations/FEISHU_CN.md +208 -0
  94. package/aris/docs/integrations/OBSIDIAN.md +65 -0
  95. package/aris/docs/integrations/OBSIDIAN_CN.md +65 -0
  96. package/aris/docs/integrations/VAST_GPU_GUIDE.md +96 -0
  97. package/aris/docs/integrations/VAST_GPU_GUIDE_CN.md +96 -0
  98. package/aris/docs/integrations/ZOTERO.md +57 -0
  99. package/aris/docs/integrations/ZOTERO_CN.md +57 -0
  100. package/aris/docs/tutorials/3d_generation_tutorial.html +1018 -0
  101. package/aris/docs/tutorials/3d_generation_tutorial.md +1206 -0
  102. package/aris/docs/tutorials/3d_generation_tutorial.review.json +34 -0
  103. package/aris/docs/tutorials/3d_generation_tutorial_en.html +1018 -0
  104. package/aris/docs/tutorials/3d_generation_tutorial_en.md +1206 -0
  105. package/aris/docs/tutorials/README.md +77 -0
  106. package/aris/docs/tutorials/agent_foundations_tutorial.html +1081 -0
  107. package/aris/docs/tutorials/agent_foundations_tutorial.md +1229 -0
  108. package/aris/docs/tutorials/agent_foundations_tutorial.review.json +146 -0
  109. package/aris/docs/tutorials/agent_foundations_tutorial_en.html +1081 -0
  110. package/aris/docs/tutorials/agent_foundations_tutorial_en.md +1229 -0
  111. package/aris/docs/tutorials/agentic_rl_tutorial.html +1269 -0
  112. package/aris/docs/tutorials/agentic_rl_tutorial.md +1544 -0
  113. package/aris/docs/tutorials/agentic_rl_tutorial.review.json +74 -0
  114. package/aris/docs/tutorials/agentic_rl_tutorial_en.html +1269 -0
  115. package/aris/docs/tutorials/agentic_rl_tutorial_en.md +1544 -0
  116. package/aris/docs/tutorials/attention_tutorial.html +774 -0
  117. package/aris/docs/tutorials/attention_tutorial.md +812 -0
  118. package/aris/docs/tutorials/attention_tutorial.review.json +51 -0
  119. package/aris/docs/tutorials/attention_tutorial_en.html +774 -0
  120. package/aris/docs/tutorials/attention_tutorial_en.md +812 -0
  121. package/aris/docs/tutorials/diffusion_distillation_tutorial.html +1421 -0
  122. package/aris/docs/tutorials/diffusion_distillation_tutorial.md +1672 -0
  123. package/aris/docs/tutorials/diffusion_distillation_tutorial.review.json +114 -0
  124. package/aris/docs/tutorials/diffusion_distillation_tutorial_en.html +1421 -0
  125. package/aris/docs/tutorials/diffusion_distillation_tutorial_en.md +1672 -0
  126. package/aris/docs/tutorials/diffusion_foundations_tutorial.html +1239 -0
  127. package/aris/docs/tutorials/diffusion_foundations_tutorial.md +1481 -0
  128. package/aris/docs/tutorials/diffusion_foundations_tutorial.review.json +56 -0
  129. package/aris/docs/tutorials/diffusion_foundations_tutorial_en.html +1239 -0
  130. package/aris/docs/tutorials/diffusion_foundations_tutorial_en.md +1481 -0
  131. package/aris/docs/tutorials/diffusion_post_training_tutorial.html +1135 -0
  132. package/aris/docs/tutorials/diffusion_post_training_tutorial.md +1322 -0
  133. package/aris/docs/tutorials/diffusion_post_training_tutorial.review.json +63 -0
  134. package/aris/docs/tutorials/diffusion_post_training_tutorial_en.html +1138 -0
  135. package/aris/docs/tutorials/diffusion_post_training_tutorial_en.md +1325 -0
  136. package/aris/docs/tutorials/distributed_training_tutorial.html +1429 -0
  137. package/aris/docs/tutorials/distributed_training_tutorial.md +1587 -0
  138. package/aris/docs/tutorials/distributed_training_tutorial.review.json +98 -0
  139. package/aris/docs/tutorials/distributed_training_tutorial_en.html +1429 -0
  140. package/aris/docs/tutorials/distributed_training_tutorial_en.md +1587 -0
  141. package/aris/docs/tutorials/flow_matching_tutorial.html +755 -0
  142. package/aris/docs/tutorials/flow_matching_tutorial.md +537 -0
  143. package/aris/docs/tutorials/flow_matching_tutorial.review.json +37 -0
  144. package/aris/docs/tutorials/flow_matching_tutorial_en.html +755 -0
  145. package/aris/docs/tutorials/flow_matching_tutorial_en.md +537 -0
  146. package/aris/docs/tutorials/image_generation_systems_tutorial.html +1181 -0
  147. package/aris/docs/tutorials/image_generation_systems_tutorial.md +1448 -0
  148. package/aris/docs/tutorials/image_generation_systems_tutorial.review.json +49 -0
  149. package/aris/docs/tutorials/image_generation_systems_tutorial_en.html +1181 -0
  150. package/aris/docs/tutorials/image_generation_systems_tutorial_en.md +1448 -0
  151. package/aris/docs/tutorials/kl_divergence_rlhf_tutorial.html +1210 -0
  152. package/aris/docs/tutorials/kl_divergence_rlhf_tutorial.md +1502 -0
  153. package/aris/docs/tutorials/kl_divergence_rlhf_tutorial.review.json +55 -0
  154. package/aris/docs/tutorials/kl_divergence_rlhf_tutorial_en.html +1212 -0
  155. package/aris/docs/tutorials/kl_divergence_rlhf_tutorial_en.md +1504 -0
  156. package/aris/docs/tutorials/kv_cache_speculative_decoding_tutorial.html +1118 -0
  157. package/aris/docs/tutorials/kv_cache_speculative_decoding_tutorial.md +1258 -0
  158. package/aris/docs/tutorials/kv_cache_speculative_decoding_tutorial.review.json +91 -0
  159. package/aris/docs/tutorials/kv_cache_speculative_decoding_tutorial_en.html +1118 -0
  160. package/aris/docs/tutorials/kv_cache_speculative_decoding_tutorial_en.md +1258 -0
  161. package/aris/docs/tutorials/llm_opd_tutorial.html +1109 -0
  162. package/aris/docs/tutorials/llm_opd_tutorial.md +1195 -0
  163. package/aris/docs/tutorials/llm_opd_tutorial.review.json +125 -0
  164. package/aris/docs/tutorials/llm_opd_tutorial_en.html +1112 -0
  165. package/aris/docs/tutorials/llm_opd_tutorial_en.md +1198 -0
  166. package/aris/docs/tutorials/long_context_rope_yarn_mla_tutorial.html +1130 -0
  167. package/aris/docs/tutorials/long_context_rope_yarn_mla_tutorial.md +1209 -0
  168. package/aris/docs/tutorials/long_context_rope_yarn_mla_tutorial.review.json +79 -0
  169. package/aris/docs/tutorials/long_context_rope_yarn_mla_tutorial_en.html +1132 -0
  170. package/aris/docs/tutorials/long_context_rope_yarn_mla_tutorial_en.md +1211 -0
  171. package/aris/docs/tutorials/moe_tutorial.html +968 -0
  172. package/aris/docs/tutorials/moe_tutorial.md +1031 -0
  173. package/aris/docs/tutorials/moe_tutorial.review.json +54 -0
  174. package/aris/docs/tutorials/moe_tutorial_en.html +968 -0
  175. package/aris/docs/tutorials/moe_tutorial_en.md +1031 -0
  176. package/aris/docs/tutorials/multi_agent_long_horizon_tutorial.html +1409 -0
  177. package/aris/docs/tutorials/multi_agent_long_horizon_tutorial.md +1803 -0
  178. package/aris/docs/tutorials/multi_agent_long_horizon_tutorial.review.json +62 -0
  179. package/aris/docs/tutorials/multi_agent_long_horizon_tutorial_en.html +1413 -0
  180. package/aris/docs/tutorials/multi_agent_long_horizon_tutorial_en.md +1807 -0
  181. package/aris/docs/tutorials/quantization_tutorial.html +1117 -0
  182. package/aris/docs/tutorials/quantization_tutorial.md +1344 -0
  183. package/aris/docs/tutorials/quantization_tutorial.review.json +56 -0
  184. package/aris/docs/tutorials/quantization_tutorial_en.html +1117 -0
  185. package/aris/docs/tutorials/quantization_tutorial_en.md +1344 -0
  186. package/aris/docs/tutorials/reasoning_models_tutorial.html +1085 -0
  187. package/aris/docs/tutorials/reasoning_models_tutorial.md +1285 -0
  188. package/aris/docs/tutorials/reasoning_models_tutorial.review.json +66 -0
  189. package/aris/docs/tutorials/reasoning_models_tutorial_en.html +1085 -0
  190. package/aris/docs/tutorials/reasoning_models_tutorial_en.md +1285 -0
  191. package/aris/docs/tutorials/rlhf_dpo_grpo_ppo_tutorial.html +1015 -0
  192. package/aris/docs/tutorials/rlhf_dpo_grpo_ppo_tutorial.md +1125 -0
  193. package/aris/docs/tutorials/rlhf_dpo_grpo_ppo_tutorial.review.json +69 -0
  194. package/aris/docs/tutorials/rlhf_dpo_grpo_ppo_tutorial_en.html +1014 -0
  195. package/aris/docs/tutorials/rlhf_dpo_grpo_ppo_tutorial_en.md +1123 -0
  196. package/aris/docs/tutorials/self_evolving_agents_tutorial.html +1354 -0
  197. package/aris/docs/tutorials/self_evolving_agents_tutorial.md +1554 -0
  198. package/aris/docs/tutorials/self_evolving_agents_tutorial.review.json +64 -0
  199. package/aris/docs/tutorials/self_evolving_agents_tutorial_en.html +1356 -0
  200. package/aris/docs/tutorials/self_evolving_agents_tutorial_en.md +1556 -0
  201. package/aris/docs/tutorials/vae_vqvae_vqgan_tutorial.html +1197 -0
  202. package/aris/docs/tutorials/vae_vqvae_vqgan_tutorial.md +1419 -0
  203. package/aris/docs/tutorials/vae_vqvae_vqgan_tutorial.review.json +67 -0
  204. package/aris/docs/tutorials/vae_vqvae_vqgan_tutorial_en.html +1196 -0
  205. package/aris/docs/tutorials/vae_vqvae_vqgan_tutorial_en.md +1419 -0
  206. package/aris/docs/tutorials/video_generation_tutorial.html +1169 -0
  207. package/aris/docs/tutorials/video_generation_tutorial.md +1189 -0
  208. package/aris/docs/tutorials/video_generation_tutorial.review.json +72 -0
  209. package/aris/docs/tutorials/video_generation_tutorial_en.html +1170 -0
  210. package/aris/docs/tutorials/video_generation_tutorial_en.md +1190 -0
  211. package/aris/docs/tutorials/vlm_multimodal_tutorial.html +1208 -0
  212. package/aris/docs/tutorials/vlm_multimodal_tutorial.md +1446 -0
  213. package/aris/docs/tutorials/vlm_multimodal_tutorial.review.json +67 -0
  214. package/aris/docs/tutorials/vlm_multimodal_tutorial_en.html +1208 -0
  215. package/aris/docs/tutorials/vlm_multimodal_tutorial_en.md +1446 -0
  216. package/aris/docs/wechat_group.jpg +0 -0
  217. package/aris/mcp-servers/claude-review/README.md +96 -0
  218. package/aris/mcp-servers/claude-review/run_with_claude_aws.sh +49 -0
  219. package/aris/mcp-servers/claude-review/server.py +718 -0
  220. package/aris/mcp-servers/codex-exec/README.md +57 -0
  221. package/aris/mcp-servers/codex-exec/server.py +480 -0
  222. package/aris/mcp-servers/codex-image2/README.md +65 -0
  223. package/aris/mcp-servers/codex-image2/server.py +893 -0
  224. package/aris/mcp-servers/feishu-bridge/requirements.txt +1 -0
  225. package/aris/mcp-servers/feishu-bridge/server.py +240 -0
  226. package/aris/mcp-servers/gemini-review/README.md +171 -0
  227. package/aris/mcp-servers/gemini-review/server.py +1856 -0
  228. package/aris/mcp-servers/llm-chat/requirements.txt +1 -0
  229. package/aris/mcp-servers/llm-chat/server.py +664 -0
  230. package/aris/mcp-servers/manual-review/README.md +133 -0
  231. package/aris/mcp-servers/manual-review/server.py +910 -0
  232. package/aris/mcp-servers/manual-review/ui.html +279 -0
  233. package/aris/mcp-servers/minimax-chat/requirements.txt +1 -0
  234. package/aris/mcp-servers/minimax-chat/server.py +381 -0
  235. package/aris/skills/ablation-planner/SKILL.md +123 -0
  236. package/aris/skills/alphaxiv/SKILL.md +196 -0
  237. package/aris/skills/analyze-results/SKILL.md +46 -0
  238. package/aris/skills/arxiv/SKILL.md +248 -0
  239. package/aris/skills/auto-paper-improvement-loop/SKILL.md +695 -0
  240. package/aris/skills/auto-review-loop/SKILL.md +1137 -0
  241. package/aris/skills/auto-review-loop-llm/SKILL.md +259 -0
  242. package/aris/skills/auto-review-loop-minimax/SKILL.md +302 -0
  243. package/{skills/citation-audit-claude → aris/skills/citation-audit}/SKILL.md +11 -21
  244. package/aris/skills/claims-drafting/SKILL.md +227 -0
  245. package/aris/skills/comm-lit-review/SKILL.md +297 -0
  246. package/aris/skills/deepxiv/SKILL.md +263 -0
  247. package/aris/skills/dse-loop/SKILL.md +296 -0
  248. package/aris/skills/embodiment-description/SKILL.md +129 -0
  249. package/aris/skills/exa-search/SKILL.md +205 -0
  250. package/{skills/experiment-audit-claude → aris/skills/experiment-audit}/SKILL.md +16 -26
  251. package/aris/skills/experiment-bridge/SKILL.md +376 -0
  252. package/aris/skills/experiment-plan/SKILL.md +249 -0
  253. package/aris/skills/experiment-queue/SKILL.md +431 -0
  254. package/aris/skills/experiment-queue/scripts/build_manifest.py +142 -0
  255. package/aris/skills/experiment-queue/scripts/queue_manager.py +433 -0
  256. package/aris/skills/feishu-notify/SKILL.md +156 -0
  257. package/aris/skills/figure-description/SKILL.md +138 -0
  258. package/aris/skills/figure-spec/SKILL.md +262 -0
  259. package/aris/skills/figure-spec/scripts/figure_renderer.py +799 -0
  260. package/aris/skills/formula-derivation/SKILL.md +280 -0
  261. package/aris/skills/gemini-search/SKILL.md +231 -0
  262. package/aris/skills/grant-proposal/SKILL.md +698 -0
  263. package/aris/skills/idea-creator/SKILL.md +584 -0
  264. package/aris/skills/idea-discovery/SKILL.md +527 -0
  265. package/aris/skills/idea-discovery-robot/SKILL.md +363 -0
  266. package/aris/skills/integrity-forensics/SKILL.md +284 -0
  267. package/aris/skills/interview-cheatsheet/SKILL.md +245 -0
  268. package/aris/skills/invention-structuring/SKILL.md +188 -0
  269. package/aris/skills/jurisdiction-format/SKILL.md +192 -0
  270. package/aris/skills/kill-argument/SKILL.md +437 -0
  271. package/aris/skills/mermaid-diagram/SKILL.md +419 -0
  272. package/{skills/meta-apply-claude → aris/skills/meta-apply}/SKILL.md +5 -15
  273. package/aris/skills/meta-optimize/SKILL.md +437 -0
  274. package/aris/skills/monitor-experiment/SKILL.md +140 -0
  275. package/aris/skills/novelty-check/SKILL.md +142 -0
  276. package/aris/skills/openalex/SKILL.md +237 -0
  277. package/aris/skills/overleaf-sync/SKILL.md +220 -0
  278. package/{skills/paper-claim-audit-claude → aris/skills/paper-claim-audit}/SKILL.md +13 -23
  279. package/aris/skills/paper-compile/SKILL.md +266 -0
  280. package/aris/skills/paper-figure/SKILL.md +312 -0
  281. package/aris/skills/paper-illustration/SKILL.md +736 -0
  282. package/aris/skills/paper-illustration-image2/SKILL.md +391 -0
  283. package/aris/skills/paper-illustration-image2/scripts/paper_illustration_image2.py +255 -0
  284. package/aris/skills/paper-plan/SKILL.md +386 -0
  285. package/aris/skills/paper-poster/SKILL.md +19 -0
  286. package/aris/skills/paper-poster-html/DESIGN_FINAL.md +176 -0
  287. package/aris/skills/paper-poster-html/IMPLEMENTATION_CONVENTIONS.md +161 -0
  288. package/aris/skills/paper-poster-html/LICENSES/posterly-MIT.txt +21 -0
  289. package/aris/skills/paper-poster-html/NOTICE.md +57 -0
  290. package/aris/skills/paper-poster-html/SKILL.md +323 -0
  291. package/aris/skills/paper-poster-html/scripts/_posterly/__init__.py +0 -0
  292. package/aris/skills/paper-poster-html/scripts/_posterly/canvas.py +200 -0
  293. package/aris/skills/paper-poster-html/scripts/_posterly/measure.py +588 -0
  294. package/aris/skills/paper-poster-html/scripts/_posterly/polish.py +498 -0
  295. package/aris/skills/paper-poster-html/scripts/_posterly/preflight.py +489 -0
  296. package/aris/skills/paper-poster-html/scripts/_posterly/render.py +215 -0
  297. package/aris/skills/paper-poster-html/scripts/_posterly/textutil.py +16 -0
  298. package/aris/skills/paper-poster-html/scripts/_posterly/verify_final.py +171 -0
  299. package/aris/skills/paper-poster-html/scripts/asset_check.py +897 -0
  300. package/aris/skills/paper-poster-html/scripts/extract_pdf_figures.py +666 -0
  301. package/aris/skills/paper-poster-html/scripts/poster_check.py +251 -0
  302. package/aris/skills/paper-poster-html/scripts/preprocess_figures.py +238 -0
  303. package/aris/skills/paper-poster-html/scripts/render_preview.py +217 -0
  304. package/aris/skills/paper-poster-html/scripts/run_gates.py +556 -0
  305. package/aris/skills/paper-poster-html/scripts/style_check.py +1324 -0
  306. package/aris/skills/paper-poster-html/templates/COMPONENTS.md +462 -0
  307. package/aris/skills/paper-poster-html/templates/README.md +170 -0
  308. package/aris/skills/paper-poster-html/templates/landscape_4col.html +1032 -0
  309. package/aris/skills/paper-poster-html/templates/landscape_hero.html +1046 -0
  310. package/aris/skills/paper-poster-html/templates/portrait_2col.html +947 -0
  311. package/aris/skills/paper-poster-html/templates/tokens/acl.json +9 -0
  312. package/aris/skills/paper-poster-html/templates/tokens/cvpr.json +9 -0
  313. package/aris/skills/paper-poster-html/templates/tokens/generic.json +9 -0
  314. package/aris/skills/paper-poster-html/templates/tokens/iclr.json +9 -0
  315. package/aris/skills/paper-poster-html/templates/tokens/icml.json +9 -0
  316. package/aris/skills/paper-poster-html/templates/tokens/neurips.json +9 -0
  317. package/aris/skills/paper-slides/SKILL.md +635 -0
  318. package/aris/skills/paper-talk/SKILL.md +381 -0
  319. package/aris/skills/paper-write/SKILL.md +682 -0
  320. package/aris/skills/paper-write/templates/IEEEtran.bst +2409 -0
  321. package/aris/skills/paper-write/templates/IEEEtran.cls +6347 -0
  322. package/aris/skills/paper-write/templates/iclr2026.tex +84 -0
  323. package/aris/skills/paper-write/templates/icml2025.tex +87 -0
  324. package/aris/skills/paper-write/templates/ieee_conference.tex +89 -0
  325. package/aris/skills/paper-write/templates/ieee_journal.tex +93 -0
  326. package/aris/skills/paper-write/templates/math_commands.tex +48 -0
  327. package/aris/skills/paper-write/templates/neurips2025.tex +80 -0
  328. package/aris/skills/paper-writing/SKILL.md +929 -0
  329. package/aris/skills/patent-novelty-check/SKILL.md +153 -0
  330. package/aris/skills/patent-pipeline/SKILL.md +344 -0
  331. package/aris/skills/patent-review/SKILL.md +203 -0
  332. package/aris/skills/pixel-art/SKILL.md +137 -0
  333. package/aris/skills/prior-art-search/SKILL.md +146 -0
  334. package/aris/skills/proof-checker/SKILL.md +866 -0
  335. package/aris/skills/proof-orchestrator/NOTICE.md +24 -0
  336. package/aris/skills/proof-orchestrator/SKILL.md +254 -0
  337. package/aris/skills/proof-orchestrator/references/audit-output-contract.md +126 -0
  338. package/aris/skills/proof-orchestrator/references/deepseek-routing.md +74 -0
  339. package/aris/skills/proof-orchestrator/references/dispatch-prompts.md +227 -0
  340. package/aris/skills/proof-orchestrator/references/notation-audit.md +135 -0
  341. package/aris/skills/proof-orchestrator/references/proof-audit-rubric.md +70 -0
  342. package/aris/skills/proof-orchestrator/references/stress-tests.md +38 -0
  343. package/aris/skills/proof-writer/SKILL.md +223 -0
  344. package/aris/skills/qzcli/SKILL.md +324 -0
  345. package/aris/skills/rebuttal/SKILL.md +376 -0
  346. package/aris/skills/render-html/SKILL.md +316 -0
  347. package/aris/skills/render-html/scripts/render_html.py +1006 -0
  348. package/aris/skills/render-html/scripts/templates/academic.html +703 -0
  349. package/aris/skills/render-html/scripts/templates/dashboard.html +333 -0
  350. package/aris/skills/research-implement-feature/SKILL.md +583 -0
  351. package/aris/skills/research-lit/SKILL.md +756 -0
  352. package/aris/skills/research-pipeline/SKILL.md +390 -0
  353. package/aris/skills/research-refine/SKILL.md +770 -0
  354. package/aris/skills/research-refine-pipeline/SKILL.md +186 -0
  355. package/aris/skills/research-review/SKILL.md +223 -0
  356. package/aris/skills/research-wiki/SKILL.md +461 -0
  357. package/aris/skills/resubmit-pipeline/SKILL.md +447 -0
  358. package/{skills/result-to-claim-claude → aris/skills/result-to-claim}/SKILL.md +7 -17
  359. package/aris/skills/run-experiment/SKILL.md +313 -0
  360. package/aris/skills/semantic-scholar/SKILL.md +236 -0
  361. package/aris/skills/serverless-modal/SKILL.md +335 -0
  362. package/aris/skills/shared-references/acceptance-gate.md +324 -0
  363. package/aris/skills/shared-references/assurance-contract.md +248 -0
  364. package/aris/skills/shared-references/capture-antipatterns.md +78 -0
  365. package/aris/skills/shared-references/citation-discipline.md +583 -0
  366. package/aris/skills/shared-references/compute-env-contract.md +163 -0
  367. package/aris/skills/shared-references/effort-contract.md +183 -0
  368. package/aris/skills/shared-references/evidence-precheck.md +65 -0
  369. package/aris/skills/shared-references/experiment-integrity.md +49 -0
  370. package/aris/skills/shared-references/external-cadence.md +326 -0
  371. package/aris/skills/shared-references/fan-out-pattern.md +366 -0
  372. package/aris/skills/shared-references/injection-hygiene.md +127 -0
  373. package/aris/skills/shared-references/integration-contract.md +461 -0
  374. package/aris/skills/shared-references/output-composition.md +93 -0
  375. package/aris/skills/shared-references/output-language.md +45 -0
  376. package/aris/skills/shared-references/output-manifest.md +49 -0
  377. package/aris/skills/shared-references/output-versioning.md +111 -0
  378. package/aris/skills/shared-references/patent-format-cn.md +199 -0
  379. package/aris/skills/shared-references/patent-format-ep.md +173 -0
  380. package/aris/skills/shared-references/patent-format-us.md +161 -0
  381. package/aris/skills/shared-references/patent-writing-principles.md +197 -0
  382. package/aris/skills/shared-references/prior-art-databases.md +141 -0
  383. package/aris/skills/shared-references/resumable-runs.md +109 -0
  384. package/aris/skills/shared-references/review-scope-limits.md +81 -0
  385. package/aris/skills/shared-references/review-tracing.md +391 -0
  386. package/aris/skills/shared-references/reviewer-independence.md +79 -0
  387. package/aris/skills/shared-references/reviewer-routing.md +852 -0
  388. package/aris/skills/shared-references/skill-governance.md +104 -0
  389. package/aris/skills/shared-references/taste-calibration.md +85 -0
  390. package/aris/skills/shared-references/venue-checklists.md +114 -0
  391. package/aris/skills/shared-references/wiki-helper-resolution.md +134 -0
  392. package/aris/skills/shared-references/writing-principles.md +525 -0
  393. package/aris/skills/skills-codex/README.md +102 -0
  394. package/aris/skills/skills-codex/README_CN.md +100 -0
  395. package/aris/skills/skills-codex/ablation-planner/SKILL.md +126 -0
  396. package/aris/skills/skills-codex/alphaxiv/SKILL.md +186 -0
  397. package/aris/skills/skills-codex/analyze-results/SKILL.md +45 -0
  398. package/aris/skills/skills-codex/arxiv/SKILL.md +210 -0
  399. package/aris/skills/skills-codex/auto-paper-improvement-loop/SKILL.md +618 -0
  400. package/aris/skills/skills-codex/auto-review-loop/SKILL.md +500 -0
  401. package/aris/skills/skills-codex/auto-review-loop-llm/SKILL.md +247 -0
  402. package/aris/skills/skills-codex/auto-review-loop-minimax/SKILL.md +290 -0
  403. package/aris/skills/skills-codex/citation-audit/SKILL.md +504 -0
  404. package/aris/skills/skills-codex/claims-drafting/SKILL.md +239 -0
  405. package/aris/skills/skills-codex/comm-lit-review/SKILL.md +299 -0
  406. package/aris/skills/skills-codex/comm-lit-review/references/domain-taxonomy.md +57 -0
  407. package/aris/skills/skills-codex/comm-lit-review/references/output-template.md +37 -0
  408. package/aris/skills/skills-codex/comm-lit-review/references/source-policy.md +99 -0
  409. package/aris/skills/skills-codex/comm-lit-review/references/venue-tiering.md +112 -0
  410. package/aris/skills/skills-codex/deepxiv/SKILL.md +142 -0
  411. package/aris/skills/skills-codex/dse-loop/SKILL.md +285 -0
  412. package/aris/skills/skills-codex/embodiment-description/SKILL.md +129 -0
  413. package/aris/skills/skills-codex/exa-search/SKILL.md +192 -0
  414. package/aris/skills/skills-codex/experiment-audit/SKILL.md +286 -0
  415. package/aris/skills/skills-codex/experiment-bridge/SKILL.md +356 -0
  416. package/aris/skills/skills-codex/experiment-plan/SKILL.md +249 -0
  417. package/aris/skills/skills-codex/experiment-queue/SKILL.md +401 -0
  418. package/aris/skills/skills-codex/feishu-notify/SKILL.md +155 -0
  419. package/aris/skills/skills-codex/figure-description/SKILL.md +138 -0
  420. package/aris/skills/skills-codex/figure-spec/SKILL.md +252 -0
  421. package/aris/skills/skills-codex/formula-derivation/SKILL.md +280 -0
  422. package/aris/skills/skills-codex/gemini-search/SKILL.md +205 -0
  423. package/aris/skills/skills-codex/grant-proposal/SKILL.md +626 -0
  424. package/aris/skills/skills-codex/idea-creator/SKILL.md +438 -0
  425. package/aris/skills/skills-codex/idea-discovery/SKILL.md +477 -0
  426. package/aris/skills/skills-codex/idea-discovery-robot/SKILL.md +362 -0
  427. package/aris/skills/skills-codex/integrity-forensics/SKILL.md +108 -0
  428. package/aris/skills/skills-codex/interview-cheatsheet/SKILL.md +245 -0
  429. package/aris/skills/skills-codex/invention-structuring/SKILL.md +188 -0
  430. package/aris/skills/skills-codex/jurisdiction-format/SKILL.md +192 -0
  431. package/aris/skills/skills-codex/kill-argument/SKILL.md +403 -0
  432. package/aris/skills/skills-codex/mermaid-diagram/SKILL.md +379 -0
  433. package/aris/skills/skills-codex/meta-apply/SKILL.md +154 -0
  434. package/aris/skills/skills-codex/meta-optimize/SKILL.md +348 -0
  435. package/aris/skills/skills-codex/monitor-experiment/SKILL.md +98 -0
  436. package/aris/skills/skills-codex/novelty-check/SKILL.md +130 -0
  437. package/aris/skills/skills-codex/openalex/SKILL.md +228 -0
  438. package/aris/skills/skills-codex/overleaf-sync/SKILL.md +220 -0
  439. package/aris/skills/skills-codex/paper-claim-audit/SKILL.md +350 -0
  440. package/aris/skills/skills-codex/paper-compile/SKILL.md +253 -0
  441. package/aris/skills/skills-codex/paper-figure/SKILL.md +311 -0
  442. package/aris/skills/skills-codex/paper-illustration/SKILL.md +690 -0
  443. package/aris/skills/skills-codex/paper-illustration-image2/SKILL.md +383 -0
  444. package/aris/skills/skills-codex/paper-illustration-image2/scripts/paper_illustration_image2.py +255 -0
  445. package/aris/skills/skills-codex/paper-plan/SKILL.md +278 -0
  446. package/aris/skills/skills-codex/paper-poster/SKILL.md +19 -0
  447. package/aris/skills/skills-codex/paper-poster-html/SKILL.md +377 -0
  448. package/aris/skills/skills-codex/paper-slides/SKILL.md +571 -0
  449. package/aris/skills/skills-codex/paper-talk/SKILL.md +381 -0
  450. package/aris/skills/skills-codex/paper-write/SKILL.md +487 -0
  451. package/aris/skills/skills-codex/paper-write/templates/IEEEtran.bst +2409 -0
  452. package/aris/skills/skills-codex/paper-write/templates/IEEEtran.cls +6347 -0
  453. package/aris/skills/skills-codex/paper-write/templates/aaai2026.bst +1493 -0
  454. package/aris/skills/skills-codex/paper-write/templates/aaai2026.sty +315 -0
  455. package/aris/skills/skills-codex/paper-write/templates/aaai2026.tex +952 -0
  456. package/aris/skills/skills-codex/paper-write/templates/acl.sty +312 -0
  457. package/aris/skills/skills-codex/paper-write/templates/acl2026.tex +377 -0
  458. package/aris/skills/skills-codex/paper-write/templates/acl_natbib.bst +1940 -0
  459. package/aris/skills/skills-codex/paper-write/templates/acm.bst +3081 -0
  460. package/aris/skills/skills-codex/paper-write/templates/acm_mm2026.tex +204 -0
  461. package/aris/skills/skills-codex/paper-write/templates/acmart.cls +3520 -0
  462. package/aris/skills/skills-codex/paper-write/templates/cvpr.bst +1448 -0
  463. package/aris/skills/skills-codex/paper-write/templates/cvpr.sty +508 -0
  464. package/aris/skills/skills-codex/paper-write/templates/cvpr2026.tex +63 -0
  465. package/aris/skills/skills-codex/paper-write/templates/iclr2026.tex +84 -0
  466. package/aris/skills/skills-codex/paper-write/templates/iclr2026_conference.bst +1440 -0
  467. package/aris/skills/skills-codex/paper-write/templates/iclr2026_conference.sty +246 -0
  468. package/aris/skills/skills-codex/paper-write/templates/icml2026.sty +767 -0
  469. package/aris/skills/skills-codex/paper-write/templates/icml2026.tex +662 -0
  470. package/aris/skills/skills-codex/paper-write/templates/ieee_conference.tex +89 -0
  471. package/aris/skills/skills-codex/paper-write/templates/ieee_journal.tex +93 -0
  472. package/aris/skills/skills-codex/paper-write/templates/math_commands.tex +48 -0
  473. package/aris/skills/skills-codex/paper-write/templates/neurips2026.tex +493 -0
  474. package/aris/skills/skills-codex/paper-write/templates/neurips_2026.sty +437 -0
  475. package/aris/skills/skills-codex/paper-writing/SKILL.md +744 -0
  476. package/aris/skills/skills-codex/patent-novelty-check/SKILL.md +153 -0
  477. package/aris/skills/skills-codex/patent-pipeline/SKILL.md +344 -0
  478. package/aris/skills/skills-codex/patent-review/SKILL.md +202 -0
  479. package/aris/skills/skills-codex/pixel-art/SKILL.md +139 -0
  480. package/aris/skills/skills-codex/prior-art-search/SKILL.md +146 -0
  481. package/aris/skills/skills-codex/proof-checker/SKILL.md +554 -0
  482. package/aris/skills/skills-codex/proof-orchestrator/SKILL.md +260 -0
  483. package/aris/skills/skills-codex/proof-orchestrator/references/audit-output-contract.md +126 -0
  484. package/aris/skills/skills-codex/proof-orchestrator/references/deepseek-routing.md +76 -0
  485. package/aris/skills/skills-codex/proof-orchestrator/references/dispatch-prompts.md +227 -0
  486. package/aris/skills/skills-codex/proof-orchestrator/references/notation-audit.md +135 -0
  487. package/aris/skills/skills-codex/proof-orchestrator/references/proof-audit-rubric.md +70 -0
  488. package/aris/skills/skills-codex/proof-orchestrator/references/stress-tests.md +38 -0
  489. package/aris/skills/skills-codex/proof-writer/SKILL.md +222 -0
  490. package/aris/skills/skills-codex/qzcli/SKILL.md +324 -0
  491. package/aris/skills/skills-codex/rebuttal/SKILL.md +305 -0
  492. package/aris/skills/skills-codex/render-html/SKILL.md +305 -0
  493. package/aris/skills/skills-codex/render-html/scripts/render_html.py +909 -0
  494. package/aris/skills/skills-codex/render-html/scripts/templates/academic.html +342 -0
  495. package/aris/skills/skills-codex/render-html/scripts/templates/dashboard.html +333 -0
  496. package/aris/skills/skills-codex/research-implement-feature/SKILL.md +406 -0
  497. package/aris/skills/skills-codex/research-lit/SKILL.md +464 -0
  498. package/aris/skills/skills-codex/research-pipeline/SKILL.md +346 -0
  499. package/aris/skills/skills-codex/research-refine/SKILL.md +721 -0
  500. package/aris/skills/skills-codex/research-refine-pipeline/SKILL.md +186 -0
  501. package/aris/skills/skills-codex/research-review/SKILL.md +160 -0
  502. package/aris/skills/skills-codex/research-wiki/SKILL.md +421 -0
  503. package/aris/skills/skills-codex/resubmit-pipeline/SKILL.md +444 -0
  504. package/aris/skills/skills-codex/result-to-claim/SKILL.md +246 -0
  505. package/aris/skills/skills-codex/run-experiment/SKILL.md +236 -0
  506. package/aris/skills/skills-codex/semantic-scholar/SKILL.md +219 -0
  507. package/aris/skills/skills-codex/serverless-modal/SKILL.md +335 -0
  508. package/aris/skills/skills-codex/shared-references/acceptance-gate.md +336 -0
  509. package/aris/skills/skills-codex/shared-references/assurance-contract.md +139 -0
  510. package/aris/skills/skills-codex/shared-references/capture-antipatterns.md +84 -0
  511. package/aris/skills/skills-codex/shared-references/citation-discipline.md +452 -0
  512. package/aris/skills/skills-codex/shared-references/compute-env-contract.md +163 -0
  513. package/aris/skills/skills-codex/shared-references/effort-contract.md +143 -0
  514. package/aris/skills/skills-codex/shared-references/evidence-precheck.md +73 -0
  515. package/aris/skills/skills-codex/shared-references/experiment-integrity.md +49 -0
  516. package/aris/skills/skills-codex/shared-references/external-cadence.md +334 -0
  517. package/aris/skills/skills-codex/shared-references/fan-out-pattern.md +375 -0
  518. package/aris/skills/skills-codex/shared-references/injection-hygiene.md +132 -0
  519. package/aris/skills/skills-codex/shared-references/integration-contract.md +372 -0
  520. package/aris/skills/skills-codex/shared-references/output-composition.md +98 -0
  521. package/aris/skills/skills-codex/shared-references/output-language.md +45 -0
  522. package/aris/skills/skills-codex/shared-references/output-manifest.md +40 -0
  523. package/aris/skills/skills-codex/shared-references/output-versioning.md +111 -0
  524. package/aris/skills/skills-codex/shared-references/patent-format-cn.md +199 -0
  525. package/aris/skills/skills-codex/shared-references/patent-format-ep.md +173 -0
  526. package/aris/skills/skills-codex/shared-references/patent-format-us.md +161 -0
  527. package/aris/skills/skills-codex/shared-references/patent-writing-principles.md +197 -0
  528. package/aris/skills/skills-codex/shared-references/prior-art-databases.md +141 -0
  529. package/aris/skills/skills-codex/shared-references/resumable-runs.md +125 -0
  530. package/aris/skills/skills-codex/shared-references/review-scope-limits.md +81 -0
  531. package/aris/skills/skills-codex/shared-references/review-tracing.md +144 -0
  532. package/aris/skills/skills-codex/shared-references/reviewer-independence.md +66 -0
  533. package/aris/skills/skills-codex/shared-references/reviewer-routing.md +128 -0
  534. package/aris/skills/skills-codex/shared-references/skill-governance.md +119 -0
  535. package/aris/skills/skills-codex/shared-references/taste-calibration.md +90 -0
  536. package/aris/skills/skills-codex/shared-references/venue-checklists.md +73 -0
  537. package/aris/skills/skills-codex/shared-references/wiki-helper-resolution.md +69 -0
  538. package/aris/skills/skills-codex/shared-references/writing-principles.md +525 -0
  539. package/aris/skills/skills-codex/slides-polish/SKILL.md +563 -0
  540. package/aris/skills/skills-codex/specification-writing/SKILL.md +211 -0
  541. package/aris/skills/skills-codex/system-profile/SKILL.md +103 -0
  542. package/aris/skills/skills-codex/training-check/SKILL.md +83 -0
  543. package/aris/skills/skills-codex/vast-gpu/SKILL.md +394 -0
  544. package/aris/skills/skills-codex/web-debug-search/SKILL.md +334 -0
  545. package/aris/skills/skills-codex/wiki-enrich/SKILL.md +255 -0
  546. package/aris/skills/skills-codex/writing-systems-papers/SKILL.md +184 -0
  547. package/aris/skills/skills-codex-claude-review/README.md +79 -0
  548. package/aris/skills/skills-codex-claude-review/README_CN.md +78 -0
  549. package/aris/skills/skills-codex-claude-review/auto-paper-improvement-loop/SKILL.md +625 -0
  550. package/aris/skills/skills-codex-claude-review/auto-review-loop/SKILL.md +516 -0
  551. package/{skills/novelty-check-claude → aris/skills/skills-codex-claude-review/novelty-check}/SKILL.md +2 -10
  552. package/aris/skills/skills-codex-claude-review/paper-figure/SKILL.md +319 -0
  553. package/aris/skills/skills-codex-claude-review/paper-plan/SKILL.md +287 -0
  554. package/aris/skills/skills-codex-claude-review/paper-write/SKILL.md +496 -0
  555. package/aris/skills/skills-codex-claude-review/research-refine/SKILL.md +732 -0
  556. package/aris/skills/skills-codex-claude-review/research-review/SKILL.md +177 -0
  557. package/aris/skills/skills-codex-gemini-review/README.md +176 -0
  558. package/aris/skills/skills-codex-gemini-review/README_CN.md +175 -0
  559. package/aris/skills/skills-codex-gemini-review/auto-paper-improvement-loop/SKILL.md +376 -0
  560. package/aris/skills/skills-codex-gemini-review/auto-review-loop/SKILL.md +304 -0
  561. package/aris/skills/skills-codex-gemini-review/grant-proposal/SKILL.md +630 -0
  562. package/aris/skills/skills-codex-gemini-review/idea-creator/SKILL.md +287 -0
  563. package/aris/skills/skills-codex-gemini-review/idea-discovery/SKILL.md +275 -0
  564. package/aris/skills/skills-codex-gemini-review/idea-discovery-robot/SKILL.md +365 -0
  565. package/aris/skills/skills-codex-gemini-review/novelty-check/SKILL.md +133 -0
  566. package/aris/skills/skills-codex-gemini-review/paper-figure/SKILL.md +289 -0
  567. package/aris/skills/skills-codex-gemini-review/paper-plan/SKILL.md +265 -0
  568. package/aris/skills/skills-codex-gemini-review/paper-poster-html/SKILL.md +102 -0
  569. package/aris/skills/skills-codex-gemini-review/paper-slides/SKILL.md +582 -0
  570. package/aris/skills/skills-codex-gemini-review/paper-write/SKILL.md +422 -0
  571. package/aris/skills/skills-codex-gemini-review/paper-writing/SKILL.md +325 -0
  572. package/aris/skills/skills-codex-gemini-review/research-refine/SKILL.md +674 -0
  573. package/aris/skills/skills-codex-gemini-review/research-review/SKILL.md +137 -0
  574. package/aris/skills/slides-polish/SKILL.md +565 -0
  575. package/aris/skills/specification-writing/SKILL.md +211 -0
  576. package/aris/skills/system-profile/SKILL.md +103 -0
  577. package/aris/skills/training-check/SKILL.md +132 -0
  578. package/aris/skills/vast-gpu/SKILL.md +394 -0
  579. package/aris/skills/web-debug-search/SKILL.md +334 -0
  580. package/aris/skills/wiki-enrich/SKILL.md +257 -0
  581. package/aris/skills/writing-systems-papers/SKILL.md +184 -0
  582. package/aris/templates/CLAUDE_MD_TEMPLATE.md +29 -0
  583. package/aris/templates/EXPERIMENT_LOG_TEMPLATE.md +47 -0
  584. package/aris/templates/EXPERIMENT_PLAN_TEMPLATE.md +51 -0
  585. package/aris/templates/EXPERIMENT_PLAN_TEMPLATE_CN.md +53 -0
  586. package/aris/templates/FINDINGS_TEMPLATE.md +52 -0
  587. package/aris/templates/IDEA_CANDIDATES_TEMPLATE.md +47 -0
  588. package/aris/templates/IDEA_CANDIDATES_TEMPLATE_CN.md +47 -0
  589. package/aris/templates/INVENTION_BRIEF_TEMPLATE.md +87 -0
  590. package/aris/templates/MANIFEST_TEMPLATE.md +7 -0
  591. package/aris/templates/NARRATIVE_REPORT_TEMPLATE.md +49 -0
  592. package/aris/templates/PAPER_PLAN_TEMPLATE.md +47 -0
  593. package/aris/templates/PATENT_CLAIMS_TEMPLATE.md +78 -0
  594. package/aris/templates/PATENT_SPECIFICATION_TEMPLATE.md +68 -0
  595. package/aris/templates/README.md +57 -0
  596. package/aris/templates/RESEARCH_BRIEF_TEMPLATE.md +35 -0
  597. package/aris/templates/RESEARCH_BRIEF_TEMPLATE_CN.md +41 -0
  598. package/aris/templates/RESEARCH_CONTRACT_TEMPLATE.md +60 -0
  599. package/aris/templates/claude-hooks/corpus_write_guard.json +16 -0
  600. package/aris/templates/claude-hooks/corpus_write_guard.py +85 -0
  601. package/aris/templates/claude-hooks/meta_logging.json +74 -0
  602. package/aris/templates/gitignore-trace.txt +3 -0
  603. package/aris/tests/__init__.py +0 -0
  604. package/aris/tests/_feishu_bridge_helpers.py +82 -0
  605. package/aris/tests/_llm_chat_helpers.py +179 -0
  606. package/aris/tests/_minimax_helpers.py +189 -0
  607. package/aris/tests/test_argument_hint_lint.py +183 -0
  608. package/aris/tests/test_arxiv_fetch.py +266 -0
  609. package/aris/tests/test_auto_proceed_contract.py +106 -0
  610. package/aris/tests/test_capture_filter.py +86 -0
  611. package/aris/tests/test_claude_review_artifact_tools.py +171 -0
  612. package/aris/tests/test_claude_review_generator.py +51 -0
  613. package/aris/tests/test_claude_review_overlay.py +64 -0
  614. package/aris/tests/test_claude_review_parse.py +290 -0
  615. package/aris/tests/test_codex_exec_bridge.py +306 -0
  616. package/aris/tests/test_codex_image2_server.py +424 -0
  617. package/aris/tests/test_codex_install_update.py +511 -0
  618. package/aris/tests/test_codex_skill_mirror.py +547 -0
  619. package/aris/tests/test_copilot_install.py +1202 -0
  620. package/aris/tests/test_copilot_native_evidence.py +562 -0
  621. package/aris/tests/test_corpus_write_guard.py +76 -0
  622. package/aris/tests/test_deepxiv_fetch.py +180 -0
  623. package/aris/tests/test_evidence_check.py +189 -0
  624. package/aris/tests/test_exa_search.py +220 -0
  625. package/aris/tests/test_feishu_bridge_server.py +289 -0
  626. package/aris/tests/test_forensics_gate.py +565 -0
  627. package/aris/tests/test_http_reviewer_fallback_contract.py +43 -0
  628. package/aris/tests/test_http_reviewer_provenance_alignment.py +25 -0
  629. package/aris/tests/test_http_reviewer_review_gate.py +112 -0
  630. package/aris/tests/test_idea_creator_query_pack_scan.py +238 -0
  631. package/aris/tests/test_idea_discovery_gate.py +400 -0
  632. package/aris/tests/test_install_aris_agent_profiles.py +98 -0
  633. package/aris/tests/test_install_aris_ps1.py +359 -0
  634. package/aris/tests/test_install_aris_replace_link.py +119 -0
  635. package/aris/tests/test_install_aris_selective.py +235 -0
  636. package/aris/tests/test_install_aris_tools_symlink.py +196 -0
  637. package/aris/tests/test_iteration_log.py +85 -0
  638. package/aris/tests/test_llm_chat_converter.py +61 -0
  639. package/aris/tests/test_llm_chat_review_fallback.py +516 -0
  640. package/aris/tests/test_llm_chat_server.py +414 -0
  641. package/aris/tests/test_mainline_large_payload_prompts.py +72 -0
  642. package/aris/tests/test_manual_review.py +804 -0
  643. package/aris/tests/test_manual_review_integration.py +305 -0
  644. package/aris/tests/test_minimax_chat_server.py +464 -0
  645. package/aris/tests/test_minimax_integration.py +241 -0
  646. package/aris/tests/test_openalex_fetch.py +197 -0
  647. package/aris/tests/test_plugin_manifest.py +40 -0
  648. package/aris/tests/test_proof_suite_integration.py +66 -0
  649. package/aris/tests/test_provenance.py +284 -0
  650. package/aris/tests/test_research_wiki_claims.py +93 -0
  651. package/aris/tests/test_research_wiki_encoding.py +74 -0
  652. package/aris/tests/test_research_wiki_experiments.py +88 -0
  653. package/aris/tests/test_research_wiki_fetch_arxiv_metadata.py +242 -0
  654. package/aris/tests/test_research_wiki_helper_resolution.py +253 -0
  655. package/aris/tests/test_research_wiki_ideas.py +113 -0
  656. package/aris/tests/test_review_gate.py +358 -0
  657. package/aris/tests/test_reviewer_pins.py +90 -0
  658. package/aris/tests/test_run_state.py +313 -0
  659. package/aris/tests/test_skill_groups.py +111 -0
  660. package/aris/tests/test_skill_picker.py +207 -0
  661. package/aris/tests/test_threat_scan.py +139 -0
  662. package/aris/tests/test_trigger_eval.py +171 -0
  663. package/aris/tests/test_verify_paper_audits.py +198 -0
  664. package/aris/tests/test_watchdog.py +496 -0
  665. package/aris/tests/test_watchdog_loop.py +148 -0
  666. package/aris/tests/test_web_debug_search.py +157 -0
  667. package/aris/tools/arxiv_fetch.py +311 -0
  668. package/aris/tools/capture_filter.py +126 -0
  669. package/aris/tools/check_skills_inventory.py +287 -0
  670. package/aris/tools/convert_skills_to_llm_chat.py +282 -0
  671. package/aris/tools/copilot_native_evidence.py +818 -0
  672. package/aris/tools/deepxiv_fetch.py +213 -0
  673. package/aris/tools/evidence_check.py +212 -0
  674. package/aris/tools/exa_search.py +425 -0
  675. package/aris/tools/experiment_queue/README.md +118 -0
  676. package/aris/tools/experiment_queue/build_manifest.py +44 -0
  677. package/aris/tools/experiment_queue/queue_manager.py +44 -0
  678. package/aris/tools/extract_paper_style.py +560 -0
  679. package/aris/tools/figure_renderer.py +69 -0
  680. package/aris/tools/forensics_gate.py +669 -0
  681. package/aris/tools/generate_codex_claude_review_overrides.py +355 -0
  682. package/aris/tools/idea_discovery_gate.py +256 -0
  683. package/aris/tools/install_aris.ps1 +1372 -0
  684. package/aris/tools/install_aris.sh +1416 -0
  685. package/aris/tools/install_aris_codex.sh +1023 -0
  686. package/aris/tools/install_aris_copilot.sh +1052 -0
  687. package/aris/tools/iteration_log.py +143 -0
  688. package/aris/tools/lint_skills_helpers.sh +84 -0
  689. package/aris/tools/meta_opt/check_ready.sh +80 -0
  690. package/aris/tools/meta_opt/log_event.sh +91 -0
  691. package/aris/tools/meta_opt/trigger_eval.py +280 -0
  692. package/aris/tools/meta_opt/trigger_evals.sample.json +28 -0
  693. package/aris/tools/openalex_fetch.py +326 -0
  694. package/aris/tools/overleaf_audit.sh +104 -0
  695. package/aris/tools/overleaf_setup.sh +150 -0
  696. package/aris/tools/paper_illustration_image2.py +62 -0
  697. package/aris/tools/provenance.py +294 -0
  698. package/aris/tools/research_wiki.py +1720 -0
  699. package/aris/tools/review_gate.py +502 -0
  700. package/aris/tools/run_state.py +399 -0
  701. package/aris/tools/save_trace.sh +477 -0
  702. package/aris/tools/semantic_scholar_fetch.py +438 -0
  703. package/aris/tools/skill-groups.tsv +117 -0
  704. package/aris/tools/skill_picker.py +238 -0
  705. package/aris/tools/smart_update.ps1 +521 -0
  706. package/aris/tools/smart_update.sh +591 -0
  707. package/aris/tools/smart_update_codex.sh +419 -0
  708. package/aris/tools/smart_update_copilot.sh +605 -0
  709. package/aris/tools/threat_scan.py +222 -0
  710. package/aris/tools/verify_paper_audits.sh +487 -0
  711. package/aris/tools/verify_papers.py +613 -0
  712. package/aris/tools/verify_wiki_coverage.sh +176 -0
  713. package/aris/tools/watchdog.py +485 -0
  714. package/aris/xhs_post.md +125 -0
  715. package/dsh/client.js +42 -13
  716. package/mcp-servers/codex-exec/README.md +4 -4
  717. package/mcp-servers/codex-exec/server.py +1 -1
  718. package/package.json +4 -5
  719. package/skills/ablation-planner/SKILL.md +1 -1
  720. package/skills/auto-paper-improvement-loop/SKILL.md +10 -10
  721. package/skills/auto-paper-improvement-loop-claude/SKILL.md +25 -612
  722. package/skills/auto-review-loop/SKILL.md +4 -4
  723. package/skills/auto-review-loop-claude/SKILL.md +25 -503
  724. package/skills/citation-audit/SKILL.md +3 -3
  725. package/skills/claims-drafting/SKILL.md +2 -2
  726. package/skills/experiment-audit/SKILL.md +4 -4
  727. package/skills/experiment-bridge/SKILL.md +14 -12
  728. package/skills/figure-spec/SKILL.md +1 -1
  729. package/skills/grant-proposal/SKILL.md +7 -7
  730. package/skills/human-in-loop-refine/SKILL.md +119 -0
  731. package/skills/human-in-loop-refine/scripts/refine_round.py +449 -0
  732. package/skills/human-in-loop-refine/scripts/test_refine_round.py +200 -0
  733. package/skills/human-in-loop-refine/templates/INPUTS.md +20 -0
  734. package/skills/human-in-loop-refine/templates/PAPER_NOTE.md +29 -0
  735. package/skills/human-in-loop-refine/templates/REVISION.md +27 -0
  736. package/skills/idea-creator/SKILL.md +2 -2
  737. package/skills/idea-discovery/SKILL.md +15 -13
  738. package/skills/idea-discovery-robot/SKILL.md +11 -9
  739. package/skills/integrity-forensics/SKILL.md +1 -1
  740. package/skills/interview-cheatsheet/SKILL.md +3 -3
  741. package/skills/invention-structuring/SKILL.md +2 -2
  742. package/skills/kill-argument/SKILL.md +8 -8
  743. package/skills/kill-argument-claude/SKILL.md +30 -431
  744. package/skills/meta-apply/SKILL.md +1 -1
  745. package/skills/meta-optimize/SKILL.md +2 -2
  746. package/skills/novelty-check/SKILL.md +2 -2
  747. package/skills/paper-claim-audit/SKILL.md +5 -5
  748. package/skills/paper-figure/SKILL.md +4 -3
  749. package/skills/paper-figure-claude/SKILL.md +24 -305
  750. package/skills/paper-plan/SKILL.md +3 -3
  751. package/skills/paper-plan-claude/SKILL.md +24 -273
  752. package/skills/paper-poster-html/SKILL.md +11 -9
  753. package/skills/paper-slides/SKILL.md +5 -5
  754. package/skills/paper-talk/SKILL.md +10 -8
  755. package/skills/paper-write/SKILL.md +3 -3
  756. package/skills/paper-write-claude/SKILL.md +24 -482
  757. package/skills/paper-writing/SKILL.md +19 -17
  758. package/skills/patent-novelty-check/SKILL.md +3 -3
  759. package/skills/patent-pipeline/SKILL.md +2 -2
  760. package/skills/patent-review/SKILL.md +3 -3
  761. package/skills/proof-checker/SKILL.md +12 -12
  762. package/skills/proof-checker-claude/SKILL.md +30 -860
  763. package/skills/rebuttal/SKILL.md +12 -10
  764. package/skills/render-html/SKILL.md +1 -1
  765. package/skills/research-implement-feature/SKILL.md +1 -1
  766. package/skills/research-pipeline/SKILL.md +15 -13
  767. package/skills/research-refine/SKILL.md +6 -6
  768. package/skills/research-refine-claude/SKILL.md +24 -719
  769. package/skills/research-refine-pipeline/SKILL.md +10 -8
  770. package/skills/research-review/SKILL.md +7 -7
  771. package/skills/research-review-claude/SKILL.md +25 -164
  772. package/skills/resubmit-pipeline/SKILL.md +14 -12
  773. package/skills/result-to-claim/SKILL.md +2 -2
  774. package/skills/shared-references/assurance-contract.md +1 -1
  775. package/skills/shared-references/effort-contract.md +1 -1
  776. package/skills/shared-references/fan-out-pattern.md +4 -4
  777. package/skills/shared-references/resumable-runs.md +2 -2
  778. package/skills/shared-references/review-scope-limits.md +1 -1
  779. package/skills/shared-references/review-tracing.md +2 -2
  780. package/skills/shared-references/reviewer-routing.md +16 -14
  781. package/skills/shared-references/skill-governance.md +3 -3
  782. package/skills/skills-codex/ablation-planner/SKILL.md +1 -1
  783. package/skills/skills-codex/auto-paper-improvement-loop/SKILL.md +8 -8
  784. package/skills/skills-codex/auto-review-loop/SKILL.md +2 -2
  785. package/skills/skills-codex/citation-audit/SKILL.md +4 -4
  786. package/skills/skills-codex/claims-drafting/SKILL.md +2 -2
  787. package/skills/skills-codex/experiment-audit/SKILL.md +9 -9
  788. package/skills/skills-codex/experiment-bridge/SKILL.md +1 -1
  789. package/skills/skills-codex/figure-spec/SKILL.md +1 -1
  790. package/skills/skills-codex/grant-proposal/SKILL.md +7 -7
  791. package/skills/skills-codex/idea-creator/SKILL.md +4 -4
  792. package/skills/skills-codex/idea-discovery/SKILL.md +6 -6
  793. package/skills/skills-codex/idea-discovery-robot/SKILL.md +1 -1
  794. package/skills/skills-codex/integrity-forensics/SKILL.md +4 -2
  795. package/skills/skills-codex/interview-cheatsheet/SKILL.md +3 -3
  796. package/skills/skills-codex/invention-structuring/SKILL.md +2 -2
  797. package/skills/skills-codex/kill-argument/SKILL.md +7 -7
  798. package/skills/skills-codex/meta-apply/SKILL.md +1 -1
  799. package/skills/skills-codex/meta-optimize/SKILL.md +2 -2
  800. package/skills/skills-codex/novelty-check/SKILL.md +1 -1
  801. package/skills/skills-codex/paper-claim-audit/SKILL.md +6 -6
  802. package/skills/skills-codex/paper-figure/SKILL.md +3 -3
  803. package/skills/skills-codex/paper-plan/SKILL.md +3 -3
  804. package/skills/skills-codex/paper-poster-html/SKILL.md +3 -3
  805. package/skills/skills-codex/paper-slides/SKILL.md +4 -4
  806. package/skills/skills-codex/paper-write/SKILL.md +3 -3
  807. package/skills/skills-codex/paper-writing/SKILL.md +7 -7
  808. package/skills/skills-codex/patent-novelty-check/SKILL.md +3 -3
  809. package/skills/skills-codex/patent-pipeline/SKILL.md +2 -2
  810. package/skills/skills-codex/patent-review/SKILL.md +3 -3
  811. package/skills/skills-codex/proof-checker/SKILL.md +11 -11
  812. package/skills/skills-codex/rebuttal/SKILL.md +2 -2
  813. package/skills/skills-codex/render-html/SKILL.md +1 -1
  814. package/skills/skills-codex/research-implement-feature/SKILL.md +1 -1
  815. package/skills/skills-codex/research-pipeline/SKILL.md +5 -5
  816. package/skills/skills-codex/research-refine/SKILL.md +6 -6
  817. package/skills/skills-codex/research-review/SKILL.md +6 -6
  818. package/skills/skills-codex/resubmit-pipeline/SKILL.md +4 -4
  819. package/skills/skills-codex/result-to-claim/SKILL.md +2 -2
  820. package/skills/skills-codex/shared-references/assurance-contract.md +2 -2
  821. package/skills/skills-codex/shared-references/resumable-runs.md +3 -3
  822. package/skills/skills-codex/shared-references/review-scope-limits.md +1 -1
  823. package/skills/skills-codex/shared-references/review-tracing.md +3 -3
  824. package/skills/skills-codex/shared-references/reviewer-independence.md +2 -2
  825. package/skills/skills-codex/shared-references/reviewer-routing.md +10 -10
  826. package/skills/skills-codex/slides-polish/SKILL.md +3 -3
  827. package/skills/skills-codex/specification-writing/SKILL.md +2 -2
  828. package/skills/slides-polish/SKILL.md +3 -3
  829. package/skills/specification-writing/SKILL.md +2 -2
  830. package/skills/training-check/SKILL.md +2 -2
  831. package/skills/verify-manager/SKILL.md +88 -0
  832. package/skills/verify-manager/phases/1-specify.md +17 -0
  833. package/skills/verify-manager/phases/2-plan.md +14 -0
  834. package/skills/verify-manager/phases/3-tasks.md +19 -0
  835. package/skills/verify-manager/phases/4-run.md +12 -0
  836. package/skills/verify-manager/phases/5-analyze.md +21 -0
  837. package/skills/verify-manager/phases/6-next.md +19 -0
  838. package/skills/verify-manager/scripts/test_verify_space.py +202 -0
  839. package/skills/verify-manager/scripts/verify_space.py +451 -0
  840. package/skills/verify-manager/templates/HYPOTHESIS.md +37 -0
  841. package/skills/verify-manager/templates/NEXT_IDEAS.md +20 -0
  842. package/skills/verify-manager/templates/PILOT_PLAN.md +56 -0
  843. package/skills/verify-manager/templates/PILOT_TASKS.md +28 -0
  844. package/skills/verify-manager/templates/PROMOTION.md +17 -0
  845. package/skills/verify-manager/templates/RESULT.md +36 -0
  846. package/test/client-render-smoke.mjs +12 -0
  847. package/test/test_claude_overlays.py +99 -0
  848. package/test/transport-smoke.mjs +16 -4
  849. package/tools/__pycache__/build_claude_overlays.cpython-314.pyc +0 -0
  850. package/tools/__pycache__/verify_lane.cpython-314.pyc +0 -0
  851. package/tools/build_claude_overlays.py +187 -0
  852. package/tools/check_skills_inventory.py +17 -3
  853. package/tools/convert_skills_to_llm_chat.py +15 -15
  854. package/tools/generate_codex_claude_review_overrides.py +12 -12
  855. package/tools/run_state.py +1 -1
  856. package/tools/save_trace.sh +2 -2
@@ -0,0 +1,799 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ ARIS FigureSpec → SVG Renderer v2
4
+
5
+ Converts a FigureSpec JSON into publication-quality SVG for academic papers.
6
+ Deterministic: same spec = same SVG, every time.
7
+
8
+ Usage:
9
+ python3 figure_renderer.py render spec.json [--output figures/output.svg] [--preview]
10
+ python3 figure_renderer.py validate spec.json
11
+ python3 figure_renderer.py schema
12
+ """
13
+
14
+ import argparse
15
+ import json
16
+ import math
17
+ import os
18
+ import re
19
+ import sys
20
+ from pathlib import Path
21
+ from xml.etree.ElementTree import Element, SubElement, tostring
22
+ from xml.dom.minidom import parseString
23
+
24
+
25
+ # ============================================================
26
+ # Constants & Defaults
27
+ # ============================================================
28
+
29
+ ARROW_SIZE = 8
30
+ SELF_LOOP_RADIUS = 25
31
+
32
+ # Allowed values for sanitization
33
+ ALLOWED_SHAPES = {"rect", "rounded", "circle", "diamond", "ellipse"}
34
+ ALLOWED_STYLES = {"solid", "dashed", "dotted"}
35
+ ALLOWED_ANCHORS = {"start", "middle", "end"}
36
+ HEX_COLOR_RE = re.compile(r"^#[0-9a-fA-F]{6}$")
37
+
38
+ DEFAULT_STYLE = {
39
+ "font_family": "Arial, Helvetica, sans-serif",
40
+ "font_size": 14,
41
+ "bg_color": "#FFFFFF",
42
+ "palette": ["#2563EB", "#10B981", "#7C3AED", "#EA580C", "#C62828",
43
+ "#0D47A1", "#1B5E20", "#4A148C", "#BF360C", "#37474F"],
44
+ }
45
+
46
+ DEFAULT_NODE = {
47
+ "width": 120,
48
+ "height": 50,
49
+ "shape": "rounded",
50
+ "text_color": "#333333",
51
+ "font_size": None,
52
+ }
53
+
54
+ DEFAULT_EDGE = {
55
+ "style": "solid",
56
+ "color": "#555555",
57
+ "thickness": 2,
58
+ "curve": False,
59
+ }
60
+
61
+
62
+ # ============================================================
63
+ # Sanitization
64
+ # ============================================================
65
+
66
+ def sanitize_color(val: str, fallback: str = "#555555") -> str:
67
+ """Validate hex color, reject anything else."""
68
+ if isinstance(val, str) and HEX_COLOR_RE.match(val):
69
+ return val
70
+ return fallback
71
+
72
+
73
+ def sanitize_text(val: str) -> str:
74
+ """Strip XML-illegal characters from text. ElementTree escapes &<>, but some code points are invalid in XML."""
75
+ if not isinstance(val, str):
76
+ return str(val)
77
+ # Remove XML-illegal code points: C0 controls (except \t\n\r), C1, surrogates, noncharacters
78
+ val = re.sub(r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]", "", val)
79
+ # Remove BMP noncharacters (U+FDD0-U+FDEF, U+FFFE-U+FFFF) and surrogates
80
+ val = re.sub(r"[\ud800-\udfff\ufdd0-\ufdef\ufffe\uffff]", "", val)
81
+ return val
82
+
83
+
84
+ def estimate_text_width(text: str, font_size: int) -> float:
85
+ """Estimate text width in px. Rough but consistent."""
86
+ # Average character width ≈ 0.6 × font_size for sans-serif
87
+ # CJK characters ≈ 1.0 × font_size
88
+ width = 0
89
+ for ch in text:
90
+ if ord(ch) > 0x2E80: # CJK range
91
+ width += font_size * 1.0
92
+ else:
93
+ width += font_size * 0.6
94
+ return width
95
+
96
+
97
+ # ============================================================
98
+ # Color Utilities
99
+ # ============================================================
100
+
101
+ def lighten_color(hex_color: str, factor: float = 0.85) -> str:
102
+ """Lighten a hex color for node fills."""
103
+ hex_color = hex_color.lstrip("#")
104
+ r, g, b = int(hex_color[0:2], 16), int(hex_color[2:4], 16), int(hex_color[4:6], 16)
105
+ r = min(255, int(r + (255 - r) * factor))
106
+ g = min(255, int(g + (255 - g) * factor))
107
+ b = min(255, int(b + (255 - b) * factor))
108
+ return f"#{r:02x}{g:02x}{b:02x}"
109
+
110
+
111
+ # ============================================================
112
+ # Geometry: Shape-Aware Edge Clipping
113
+ # ============================================================
114
+
115
+ def clip_to_shape(cx, cy, target_x, target_y, w, h, shape):
116
+ """Clip a line from (cx,cy) toward (target_x,target_y) to the shape boundary."""
117
+ dx = target_x - cx
118
+ dy = target_y - cy
119
+ if dx == 0 and dy == 0:
120
+ return cx, cy - h / 2 # default: top
121
+
122
+ if shape == "circle":
123
+ # True circle: use max(w,h)/2 to match renderer
124
+ r = max(w, h) / 2
125
+ angle = math.atan2(dy, dx)
126
+ return cx + r * math.cos(angle), cy + r * math.sin(angle)
127
+
128
+ elif shape == "ellipse":
129
+ # Ellipse clipping: x²/a² + y²/b² = 1
130
+ a = w / 2
131
+ b = h / 2
132
+ angle = math.atan2(dy, dx)
133
+ return cx + a * math.cos(angle), cy + b * math.sin(angle)
134
+
135
+ elif shape == "diamond":
136
+ # Diamond: |x/a| + |y/b| = 1
137
+ a = w / 2
138
+ b = h / 2
139
+ angle = math.atan2(dy, dx)
140
+ cos_a = abs(math.cos(angle))
141
+ sin_a = abs(math.sin(angle))
142
+ if cos_a * b + sin_a * a == 0:
143
+ return cx, cy
144
+ scale = (a * b) / (cos_a * b + sin_a * a)
145
+ return cx + scale * math.cos(angle), cy + scale * math.sin(angle)
146
+
147
+ else:
148
+ # Rectangle clipping
149
+ a = w / 2
150
+ b = h / 2
151
+ if abs(dx) * b > abs(dy) * a:
152
+ scale = a / abs(dx)
153
+ else:
154
+ scale = b / abs(dy)
155
+ return cx + dx * scale, cy + dy * scale
156
+
157
+
158
+ # ============================================================
159
+ # Validation
160
+ # ============================================================
161
+
162
+ def validate_spec(spec: dict) -> list:
163
+ """Validate FigureSpec, return list of issues."""
164
+ issues = []
165
+
166
+ # Top-level must be a dict
167
+ if not isinstance(spec, dict):
168
+ return [f"CRITICAL: spec must be a JSON object, got {type(spec).__name__}"]
169
+
170
+ # Structure validation — ensure top-level fields are correct types
171
+ canvas = spec.get("canvas", {})
172
+ if not isinstance(canvas, dict):
173
+ issues.append("CRITICAL: 'canvas' must be a dict")
174
+ canvas = {}
175
+ st_raw = spec.get("style", {})
176
+ if not isinstance(st_raw, dict):
177
+ issues.append("CRITICAL: 'style' must be a dict")
178
+ if not isinstance(spec.get("nodes", []), list):
179
+ issues.append("CRITICAL: 'nodes' must be a list")
180
+ if not isinstance(spec.get("edges", []), list):
181
+ issues.append("CRITICAL: 'edges' must be a list")
182
+ if not isinstance(spec.get("groups", []), list):
183
+ issues.append("CRITICAL: 'groups' must be a list")
184
+ if not isinstance(spec.get("labels", []), list):
185
+ issues.append("CRITICAL: 'labels' must be a list")
186
+
187
+ # Early return if structure is fundamentally broken
188
+ if any(i.startswith("CRITICAL: '") for i in issues):
189
+ return issues
190
+
191
+ # Canvas validation
192
+ for dim in ("width", "height"):
193
+ val = canvas.get(dim)
194
+ if val is not None:
195
+ if isinstance(val, bool) or not isinstance(val, (int, float)):
196
+ issues.append(f"CRITICAL: canvas.{dim} must be a number")
197
+ elif val <= 0:
198
+ issues.append(f"CRITICAL: canvas.{dim} must be positive")
199
+
200
+ # Style validation
201
+ st = spec.get("style", {})
202
+ fs = st.get("font_size")
203
+ if fs is not None and (isinstance(fs, bool) or not isinstance(fs, (int, float)) or fs <= 0):
204
+ issues.append(f"CRITICAL: style.font_size must be a positive number")
205
+ pal = st.get("palette")
206
+ if pal is not None:
207
+ if not isinstance(pal, list) or len(pal) == 0:
208
+ issues.append(f"CRITICAL: style.palette must be a non-empty list of hex colors")
209
+ else:
210
+ for pi, pc in enumerate(pal):
211
+ if not isinstance(pc, str) or not HEX_COLOR_RE.match(pc):
212
+ issues.append(f"CRITICAL: style.palette[{pi}] '{pc}' is not a valid hex color (#RRGGBB)")
213
+
214
+ nodes = spec.get("nodes", [])
215
+ if not nodes:
216
+ issues.append("WARN: no nodes defined (labels/groups-only figure)")
217
+
218
+ node_ids = set()
219
+ for i, node in enumerate(nodes):
220
+ if not isinstance(node, dict):
221
+ issues.append(f"CRITICAL: nodes[{i}] must be a dict, got {type(node).__name__}")
222
+ continue
223
+ nid = node.get("id")
224
+ if not nid:
225
+ issues.append(f"CRITICAL: node[{i}] missing 'id'")
226
+ continue
227
+ if nid in node_ids:
228
+ issues.append(f"CRITICAL: duplicate node id '{nid}'")
229
+ node_ids.add(nid)
230
+ if "label" not in node:
231
+ issues.append(f"WARN: node '{nid}' missing 'label'")
232
+ for coord in ("x", "y"):
233
+ if coord not in node:
234
+ issues.append(f"CRITICAL: node '{nid}' missing '{coord}'")
235
+ elif isinstance(node[coord], bool) or not isinstance(node[coord], (int, float)):
236
+ issues.append(f"CRITICAL: node '{nid}' {coord} must be a number, got {type(node[coord]).__name__}")
237
+ for dim in ("width", "height"):
238
+ val = node.get(dim)
239
+ if val is not None and (isinstance(val, bool) or not isinstance(val, (int, float))):
240
+ issues.append(f"CRITICAL: node '{nid}' {dim} must be a number, got {type(val).__name__}")
241
+ elif val is not None and val <= 0:
242
+ issues.append(f"CRITICAL: node '{nid}' {dim} must be positive ({val})")
243
+ shape = node.get("shape", "rounded")
244
+ if shape not in ALLOWED_SHAPES:
245
+ issues.append(f"WARN: node '{nid}' unknown shape '{shape}', will use 'rounded'")
246
+
247
+ for i, edge in enumerate(spec.get("edges", [])):
248
+ if not isinstance(edge, dict):
249
+ issues.append(f"CRITICAL: edges[{i}] must be a dict")
250
+ continue
251
+ src, dst = edge.get("from"), edge.get("to")
252
+ if not src or not dst:
253
+ issues.append(f"CRITICAL: edge[{i}] missing 'from' or 'to'")
254
+ else:
255
+ if src not in node_ids:
256
+ issues.append(f"CRITICAL: edge[{i}] 'from' references unknown node '{src}'")
257
+ if dst not in node_ids:
258
+ issues.append(f"CRITICAL: edge[{i}] 'to' references unknown node '{dst}'")
259
+ style = edge.get("style", "solid")
260
+ if style not in ALLOWED_STYLES:
261
+ issues.append(f"WARN: edge[{i}] unknown style '{style}', will use 'solid'")
262
+
263
+ for i, group in enumerate(spec.get("groups", [])):
264
+ if not isinstance(group, dict):
265
+ issues.append(f"CRITICAL: groups[{i}] must be a dict")
266
+ continue
267
+ node_ids_list = group.get("node_ids", [])
268
+ if not isinstance(node_ids_list, list):
269
+ issues.append(f"WARN: group[{i}] node_ids must be a list")
270
+ continue
271
+ for nid in node_ids_list:
272
+ if nid not in node_ids:
273
+ issues.append(f"WARN: group[{i}] references unknown node '{nid}'")
274
+
275
+ # Edge numeric validation
276
+ for i, edge in enumerate(spec.get("edges", [])):
277
+ for field in ("thickness",):
278
+ val = edge.get(field)
279
+ if val is not None and (isinstance(val, bool) or not isinstance(val, (int, float))):
280
+ issues.append(f"WARN: edge[{i}] {field} must be a number")
281
+
282
+ # Group numeric validation
283
+ for i, group in enumerate(spec.get("groups", [])):
284
+ val = group.get("padding")
285
+ if val is not None and (isinstance(val, bool) or not isinstance(val, (int, float))):
286
+ issues.append(f"WARN: group[{i}] padding must be a number")
287
+
288
+ # Free label numeric + anchor validation
289
+ for i, label in enumerate(spec.get("labels", [])):
290
+ if not isinstance(label, dict):
291
+ issues.append(f"CRITICAL: labels[{i}] must be a dict")
292
+ continue
293
+ anchor = label.get("anchor")
294
+ if anchor and anchor not in ALLOWED_ANCHORS:
295
+ issues.append(f"WARN: label[{i}] unknown anchor '{anchor}', will use 'middle'")
296
+ for field in ("x", "y", "font_size"):
297
+ val = label.get(field)
298
+ if val is not None and (isinstance(val, bool) or not isinstance(val, (int, float))):
299
+ issues.append(f"WARN: label[{i}] {field} must be a number")
300
+
301
+ # Node font_size validation
302
+ for node in nodes:
303
+ if not isinstance(node, dict):
304
+ continue
305
+ nid = node.get("id", "?")
306
+ nfs = node.get("font_size")
307
+ if nfs is not None and (isinstance(nfs, bool) or not isinstance(nfs, (int, float)) or nfs <= 0):
308
+ issues.append(f"WARN: node '{nid}' font_size must be a positive number")
309
+
310
+ # Overlap detection (shape-aware bounding for circles)
311
+ def _effective_bounds(n):
312
+ w = n.get("width", DEFAULT_NODE["width"])
313
+ h = n.get("height", DEFAULT_NODE["height"])
314
+ if n.get("shape") == "circle":
315
+ d = max(w, h)
316
+ return d, d
317
+ return w, h
318
+
319
+ for i, a in enumerate(nodes):
320
+ if not isinstance(a, dict):
321
+ continue
322
+ for j, b in enumerate(nodes):
323
+ if i >= j or not isinstance(b, dict):
324
+ continue
325
+ ax, ay = a.get("x", 0), a.get("y", 0)
326
+ bx, by = b.get("x", 0), b.get("y", 0)
327
+ aw, ah = _effective_bounds(a)
328
+ bw, bh = _effective_bounds(b)
329
+ if (abs(ax - bx) < (aw + bw) / 2 - 5 and
330
+ abs(ay - by) < (ah + bh) / 2 - 5):
331
+ issues.append(f"WARN: nodes '{a.get('id')}' and '{b.get('id')}' may overlap")
332
+
333
+ return issues
334
+
335
+
336
+ # ============================================================
337
+ # SVG Renderer
338
+ # ============================================================
339
+
340
+ def render_svg(spec: dict) -> str:
341
+ """Render FigureSpec to SVG string."""
342
+ canvas = spec.get("canvas", {})
343
+ width = canvas.get("width", 800)
344
+ height = canvas.get("height", 400)
345
+ style = {**DEFAULT_STYLE, **spec.get("style", {})}
346
+ palette = style["palette"]
347
+ base_fs = style["font_size"]
348
+
349
+ svg = Element("svg", {
350
+ "xmlns": "http://www.w3.org/2000/svg",
351
+ "viewBox": f"0 0 {width} {height}",
352
+ "width": str(width),
353
+ "height": str(height),
354
+ "font-family": sanitize_text(style["font_family"]),
355
+ })
356
+
357
+ # Background
358
+ SubElement(svg, "rect", {
359
+ "width": str(width), "height": str(height),
360
+ "fill": sanitize_color(style.get("bg_color", "#FFFFFF")),
361
+ })
362
+
363
+ # Defs: arrow markers
364
+ defs = SubElement(svg, "defs")
365
+ marker_colors = {"default": sanitize_color(DEFAULT_EDGE["color"])}
366
+ for i, c in enumerate(palette):
367
+ marker_colors[f"c{i}"] = sanitize_color(c)
368
+
369
+ for name, color in marker_colors.items():
370
+ marker = SubElement(defs, "marker", {
371
+ "id": f"arrow-{name}",
372
+ "markerWidth": str(ARROW_SIZE + 2),
373
+ "markerHeight": str(ARROW_SIZE + 2),
374
+ "refX": str(ARROW_SIZE),
375
+ "refY": str(ARROW_SIZE // 2),
376
+ "orient": "auto",
377
+ "markerUnits": "strokeWidth",
378
+ })
379
+ SubElement(marker, "polygon", {
380
+ "points": f"0 0, {ARROW_SIZE} {ARROW_SIZE // 2}, 0 {ARROW_SIZE}",
381
+ "fill": color,
382
+ })
383
+
384
+ # Build node lookup with defaults applied
385
+ node_map = {}
386
+ for i, node in enumerate(spec.get("nodes", [])):
387
+ n = {**DEFAULT_NODE, **node}
388
+ n["fill"] = sanitize_color(n.get("fill") or lighten_color(palette[i % len(palette)]))
389
+ n["stroke"] = sanitize_color(n.get("stroke") or palette[i % len(palette)])
390
+ n["text_color"] = sanitize_color(n.get("text_color", DEFAULT_NODE["text_color"]))
391
+ n["label"] = sanitize_text(n.get("label", ""))
392
+ if n.get("sublabel"):
393
+ n["sublabel"] = sanitize_text(n["sublabel"])
394
+ if n.get("shape") not in ALLOWED_SHAPES:
395
+ n["shape"] = "rounded"
396
+ node_map[n["id"]] = n
397
+
398
+ # --- Render groups (background layer) ---
399
+ for group in spec.get("groups", []):
400
+ gnodes = [node_map[nid] for nid in group.get("node_ids", []) if nid in node_map]
401
+ if not gnodes:
402
+ continue
403
+ pad = group.get("padding", 20)
404
+ def _node_extent(n):
405
+ w, h = n["width"], n["height"]
406
+ if n.get("shape") == "circle":
407
+ d = max(w, h)
408
+ return d, d
409
+ return w, h
410
+
411
+ min_x = min(n["x"] - _node_extent(n)[0] / 2 for n in gnodes) - pad
412
+ min_y = min(n["y"] - _node_extent(n)[1] / 2 for n in gnodes) - pad
413
+ max_x = max(n["x"] + _node_extent(n)[0] / 2 for n in gnodes) + pad
414
+ max_y = max(n["y"] + _node_extent(n)[1] / 2 for n in gnodes) + pad
415
+
416
+ SubElement(svg, "rect", {
417
+ "x": f"{min_x:.1f}", "y": f"{min_y:.1f}",
418
+ "width": f"{max_x - min_x:.1f}", "height": f"{max_y - min_y:.1f}",
419
+ "fill": sanitize_color(group.get("fill", "#F5F5F5")),
420
+ "stroke": sanitize_color(group.get("stroke", "#E0E0E0")),
421
+ "stroke-width": "1", "rx": "8",
422
+ })
423
+ if group.get("label"):
424
+ lbl = SubElement(svg, "text", {
425
+ "x": f"{min_x + 8:.1f}", "y": f"{min_y + 16:.1f}",
426
+ "font-size": str(base_fs - 2),
427
+ "fill": "#999999", "font-weight": "bold",
428
+ })
429
+ lbl.text = sanitize_text(group["label"])
430
+
431
+ # --- Render edges ---
432
+ for edge in spec.get("edges", []):
433
+ e = {**DEFAULT_EDGE, **edge}
434
+ src = node_map.get(e.get("from"))
435
+ dst = node_map.get(e.get("to"))
436
+ if not src or not dst:
437
+ continue
438
+
439
+ color = sanitize_color(e["color"])
440
+ e_style = e["style"] if e["style"] in ALLOWED_STYLES else "solid"
441
+
442
+ # Find matching arrow marker
443
+ marker_id = "arrow-default"
444
+ for ci, pc in enumerate(palette):
445
+ if color.lower() == pc.lower():
446
+ marker_id = f"arrow-c{ci}"
447
+ break
448
+
449
+ # Dash pattern
450
+ dash_map = {"solid": "", "dashed": "8,4", "dotted": "3,3"}
451
+ dash = dash_map.get(e_style, "")
452
+
453
+ # Self-loop (shape-aware: find top boundary)
454
+ if src["id"] == dst["id"]:
455
+ cx, cy = src["x"], src["y"]
456
+ r = SELF_LOOP_RADIUS
457
+ src_shape = src.get("shape", "rounded")
458
+ # Find top anchor point based on shape
459
+ top_x, top_y_pt = clip_to_shape(cx, cy, cx, cy - 100, src["width"], src["height"], src_shape)
460
+ path_d = (f"M {top_x - 10},{top_y_pt} "
461
+ f"C {cx - r},{top_y_pt - r * 1.5} {cx + r},{top_y_pt - r * 1.5} {top_x + 10},{top_y_pt}")
462
+ attrs = {
463
+ "d": path_d,
464
+ "stroke": color,
465
+ "stroke-width": str(e["thickness"]),
466
+ "fill": "none",
467
+ "marker-end": f"url(#{marker_id})",
468
+ }
469
+ if dash:
470
+ attrs["stroke-dasharray"] = dash
471
+ SubElement(svg, "path", attrs)
472
+ else:
473
+ # Shape-aware clipping
474
+ sx, sy = clip_to_shape(src["x"], src["y"], dst["x"], dst["y"],
475
+ src["width"], src["height"], src.get("shape", "rounded"))
476
+ dx, dy = clip_to_shape(dst["x"], dst["y"], src["x"], src["y"],
477
+ dst["width"], dst["height"], dst.get("shape", "rounded"))
478
+
479
+ if e.get("curve"):
480
+ # Quadratic bezier with midpoint offset
481
+ mx = (sx + dx) / 2
482
+ my = (sy + dy) / 2
483
+ # Offset perpendicular to the line
484
+ length = math.sqrt((dx - sx) ** 2 + (dy - sy) ** 2) or 1
485
+ offset = 30
486
+ nx = -(dy - sy) / length * offset
487
+ ny = (dx - sx) / length * offset
488
+ path_d = f"M {sx:.1f},{sy:.1f} Q {mx + nx:.1f},{my + ny:.1f} {dx:.1f},{dy:.1f}"
489
+ else:
490
+ path_d = f"M {sx:.1f},{sy:.1f} L {dx:.1f},{dy:.1f}"
491
+
492
+ attrs = {
493
+ "d": path_d,
494
+ "stroke": color,
495
+ "stroke-width": str(e["thickness"]),
496
+ "fill": "none",
497
+ "marker-end": f"url(#{marker_id})",
498
+ }
499
+ if dash:
500
+ attrs["stroke-dasharray"] = dash
501
+ SubElement(svg, "path", attrs)
502
+
503
+ # Edge label
504
+ if e.get("label"):
505
+ label_text = sanitize_text(e["label"])
506
+ if src["id"] == dst["id"]:
507
+ lx = src["x"]
508
+ src_shape = src.get("shape", "rounded")
509
+ _, top_pt = clip_to_shape(src["x"], src["y"], src["x"], src["y"] - 100,
510
+ src["width"], src["height"], src_shape)
511
+ ly = top_pt - SELF_LOOP_RADIUS * 1.2
512
+ elif e.get("curve"):
513
+ # Bezier midpoint at t=0.5: B(0.5) = (1-t)^2*P0 + 2(1-t)t*P1 + t^2*P2
514
+ mx_ctrl = (sx + dx) / 2
515
+ my_ctrl = (sy + dy) / 2
516
+ length = math.sqrt((dx - sx) ** 2 + (dy - sy) ** 2) or 1
517
+ offset = 30
518
+ nx_ctrl = -(dy - sy) / length * offset
519
+ ny_ctrl = (dx - sx) / length * offset
520
+ qx, qy = mx_ctrl + nx_ctrl, my_ctrl + ny_ctrl
521
+ lx = 0.25 * sx + 0.5 * qx + 0.25 * dx
522
+ ly = 0.25 * sy + 0.5 * qy + 0.25 * dy - 8
523
+ else:
524
+ lx = (sx + dx) / 2
525
+ ly = (sy + dy) / 2 - 8
526
+
527
+ tw = estimate_text_width(label_text, base_fs - 3) + 8
528
+ SubElement(svg, "rect", {
529
+ "x": f"{lx - tw / 2:.1f}", "y": f"{ly - 10:.1f}",
530
+ "width": f"{tw:.1f}", "height": "16",
531
+ "fill": "#FFFFFF", "rx": "3", "opacity": "0.85",
532
+ })
533
+ lbl = SubElement(svg, "text", {
534
+ "x": f"{lx:.1f}", "y": f"{ly + 2:.1f}",
535
+ "font-size": str(base_fs - 3),
536
+ "fill": "#777777", "text-anchor": "middle",
537
+ })
538
+ lbl.text = label_text
539
+
540
+ # --- Render nodes ---
541
+ for node in spec.get("nodes", []):
542
+ n = node_map[node["id"]]
543
+ x, y = n["x"], n["y"]
544
+ w, h = n["width"], n["height"]
545
+ left, top = x - w / 2, y - h / 2
546
+ shape = n.get("shape", "rounded")
547
+ fill = n["fill"]
548
+ stroke = n["stroke"]
549
+
550
+ if shape == "circle":
551
+ r = max(w, h) / 2
552
+ SubElement(svg, "circle", {
553
+ "cx": f"{x:.1f}", "cy": f"{y:.1f}", "r": f"{r:.1f}",
554
+ "fill": fill, "stroke": stroke, "stroke-width": "2",
555
+ })
556
+ elif shape == "ellipse":
557
+ SubElement(svg, "ellipse", {
558
+ "cx": f"{x:.1f}", "cy": f"{y:.1f}",
559
+ "rx": f"{w / 2:.1f}", "ry": f"{h / 2:.1f}",
560
+ "fill": fill, "stroke": stroke, "stroke-width": "2",
561
+ })
562
+ elif shape == "diamond":
563
+ points = (f"{x:.1f},{top:.1f} {x + w / 2:.1f},{y:.1f} "
564
+ f"{x:.1f},{top + h:.1f} {x - w / 2:.1f},{y:.1f}")
565
+ SubElement(svg, "polygon", {
566
+ "points": points,
567
+ "fill": fill, "stroke": stroke, "stroke-width": "2",
568
+ })
569
+ else:
570
+ rx = "8" if shape == "rounded" else "0"
571
+ SubElement(svg, "rect", {
572
+ "x": f"{left:.1f}", "y": f"{top:.1f}",
573
+ "width": f"{w:.1f}", "height": f"{h:.1f}",
574
+ "fill": fill, "stroke": stroke,
575
+ "stroke-width": "2", "rx": rx,
576
+ })
577
+
578
+ # Main label (supports \n for multi-line)
579
+ fs = n.get("font_size") or base_fs
580
+ # Handle multi-line: first replace literal \\n with \n, then split
581
+ raw_label = n["label"].replace("\\n", "\n")
582
+ label_lines = [line for line in raw_label.split("\n") if line]
583
+ has_sub = bool(n.get("sublabel"))
584
+ total_lines = len(label_lines) + (1 if has_sub else 0)
585
+ line_height = fs + 2
586
+ start_y = y - (total_lines - 1) * line_height / 2 + fs * 0.35
587
+
588
+ for li, line in enumerate(label_lines):
589
+ lbl = SubElement(svg, "text", {
590
+ "x": f"{x:.1f}",
591
+ "y": f"{start_y + li * line_height:.1f}",
592
+ "font-size": str(fs),
593
+ "fill": n["text_color"],
594
+ "text-anchor": "middle",
595
+ "font-weight": "bold",
596
+ })
597
+ lbl.text = line
598
+
599
+ # Sublabel
600
+ if has_sub:
601
+ sub = SubElement(svg, "text", {
602
+ "x": f"{x:.1f}",
603
+ "y": f"{start_y + len(label_lines) * line_height:.1f}",
604
+ "font-size": str(fs - 3),
605
+ "fill": "#888888",
606
+ "text-anchor": "middle",
607
+ })
608
+ sub.text = sanitize_text(n["sublabel"])
609
+
610
+ # --- Free labels ---
611
+ for label in spec.get("labels", []):
612
+ anchor = label.get("anchor", "middle")
613
+ if anchor not in ALLOWED_ANCHORS:
614
+ anchor = "middle"
615
+ lbl = SubElement(svg, "text", {
616
+ "x": f"{label.get('x', 0):.1f}",
617
+ "y": f"{label.get('y', 0):.1f}",
618
+ "font-size": str(label.get("font_size", base_fs)),
619
+ "fill": sanitize_color(label.get("color", "#555555")),
620
+ "text-anchor": anchor,
621
+ })
622
+ lbl.text = sanitize_text(label.get("text", ""))
623
+
624
+ # Pretty print
625
+ raw = tostring(svg, encoding="unicode")
626
+ pretty = parseString(raw).toprettyxml(indent=" ")
627
+ # Remove xml declaration line
628
+ lines = pretty.split("\n")
629
+ return "\n".join(lines[1:])
630
+
631
+
632
+ # ============================================================
633
+ # PNG Preview
634
+ # ============================================================
635
+
636
+ def svg_to_png(svg_path: str, png_path: str) -> bool:
637
+ """Convert SVG to PNG preview."""
638
+ import subprocess
639
+
640
+ # Try rsvg-convert
641
+ try:
642
+ result = subprocess.run(
643
+ ["rsvg-convert", "-o", png_path, svg_path],
644
+ capture_output=True, timeout=30
645
+ )
646
+ if result.returncode == 0:
647
+ return True
648
+ except (FileNotFoundError, subprocess.TimeoutExpired):
649
+ pass
650
+
651
+ # Try cairosvg
652
+ try:
653
+ os.environ.setdefault("DYLD_LIBRARY_PATH", "/opt/homebrew/lib")
654
+ import cairosvg
655
+ cairosvg.svg2png(url=svg_path, write_to=png_path)
656
+ return True
657
+ except Exception:
658
+ pass
659
+
660
+ print("Warning: could not convert SVG to PNG (install rsvg-convert or cairosvg)")
661
+ return False
662
+
663
+
664
+ # ============================================================
665
+ # Schema (for documentation)
666
+ # ============================================================
667
+
668
+ SCHEMA_DOC = """\
669
+ FigureSpec JSON Schema:
670
+ {
671
+ "title": "string — figure title (metadata only, not rendered)",
672
+ "canvas": {"width": int, "height": int},
673
+ "style": {
674
+ "font_family": "CSS font string",
675
+ "font_size": int (default 14),
676
+ "bg_color": "#RRGGBB",
677
+ "palette": ["#color1", "#color2", ...]
678
+ },
679
+ "nodes": [{
680
+ "id": "string (required, unique)",
681
+ "label": "string (required, supports \\\\n for multi-line)",
682
+ "x": int (required, center x),
683
+ "y": int (required, center y),
684
+ "width": int (default 120),
685
+ "height": int (default 50),
686
+ "shape": "rounded | rect | circle | ellipse | diamond",
687
+ "fill": "#RRGGBB (auto from palette)",
688
+ "stroke": "#RRGGBB (auto from palette)",
689
+ "text_color": "#RRGGBB (default #333333)",
690
+ "font_size": int (override),
691
+ "sublabel": "string (smaller text below label)"
692
+ }],
693
+ "edges": [{
694
+ "from": "node_id (required)",
695
+ "to": "node_id (required, same as from = self-loop)",
696
+ "label": "string",
697
+ "style": "solid | dashed | dotted",
698
+ "color": "#RRGGBB (default #555555)",
699
+ "thickness": int (default 2),
700
+ "curve": bool (default false)
701
+ }],
702
+ "groups": [{
703
+ "id": "string",
704
+ "label": "string",
705
+ "node_ids": ["id1", "id2"],
706
+ "fill": "#RRGGBB",
707
+ "stroke": "#RRGGBB",
708
+ "padding": int (default 20)
709
+ }],
710
+ "labels": [{
711
+ "text": "string",
712
+ "x": int, "y": int,
713
+ "font_size": int,
714
+ "color": "#RRGGBB",
715
+ "anchor": "start | middle | end"
716
+ }]
717
+ }
718
+ """
719
+
720
+
721
+ # ============================================================
722
+ # CLI
723
+ # ============================================================
724
+
725
+ def main():
726
+ parser = argparse.ArgumentParser(description="ARIS FigureSpec → SVG Renderer")
727
+ subparsers = parser.add_subparsers(dest="command")
728
+
729
+ p_render = subparsers.add_parser("render", help="Render FigureSpec JSON to SVG")
730
+ p_render.add_argument("spec_file", help="FigureSpec JSON file")
731
+ p_render.add_argument("--output", "-o", default=None, help="Output SVG path")
732
+ p_render.add_argument("--preview", action="store_true", help="Also generate PNG preview")
733
+
734
+ p_validate = subparsers.add_parser("validate", help="Validate FigureSpec JSON")
735
+ p_validate.add_argument("spec_file", help="FigureSpec JSON file")
736
+
737
+ subparsers.add_parser("schema", help="Print FigureSpec schema documentation")
738
+
739
+ args = parser.parse_args()
740
+
741
+ if args.command == "schema":
742
+ print(SCHEMA_DOC)
743
+ return
744
+
745
+ if args.command == "validate":
746
+ with open(args.spec_file, encoding="utf-8") as f:
747
+ spec = json.load(f)
748
+ issues = validate_spec(spec)
749
+ critical = sum(1 for i in issues if i.startswith("CRITICAL"))
750
+ if not issues:
751
+ print("✅ FigureSpec is valid")
752
+ else:
753
+ for issue in issues:
754
+ print(f" {issue}")
755
+ print(f"\n{len(issues)} issues ({critical} critical)")
756
+ sys.exit(1 if critical else 0)
757
+
758
+ if args.command == "render":
759
+ with open(args.spec_file, encoding="utf-8") as f:
760
+ spec = json.load(f)
761
+
762
+ issues = validate_spec(spec)
763
+ critical = [i for i in issues if i.startswith("CRITICAL")]
764
+ if critical:
765
+ print("❌ Cannot render — critical issues:")
766
+ for i in critical:
767
+ print(f" {i}")
768
+ sys.exit(1)
769
+
770
+ if issues:
771
+ print(f"⚠️ {len(issues)} warnings:")
772
+ for i in issues:
773
+ print(f" {i}")
774
+
775
+ svg_content = render_svg(spec)
776
+
777
+ # Output path
778
+ if args.output:
779
+ output = args.output
780
+ else:
781
+ base = Path(args.spec_file).stem
782
+ output = str(Path(args.spec_file).parent / f"{base}.svg")
783
+
784
+ Path(output).parent.mkdir(parents=True, exist_ok=True)
785
+ with open(output, "w", encoding="utf-8") as f:
786
+ f.write(svg_content)
787
+ print(f"✅ SVG written: {output}")
788
+
789
+ if args.preview:
790
+ png_path = str(Path(output).with_suffix(".png"))
791
+ if svg_to_png(output, png_path):
792
+ print(f"✅ PNG preview: {png_path}")
793
+ return
794
+
795
+ parser.print_help()
796
+
797
+
798
+ if __name__ == "__main__":
799
+ main()