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,176 @@
1
+ #!/usr/bin/env bash
2
+ # verify_wiki_coverage.sh — Diagnostic (NOT a gate) for research-wiki coverage.
3
+ #
4
+ # Compares arXiv IDs referenced in recent session artifacts against those
5
+ # already ingested into `research-wiki/papers/`. Reports the delta so the
6
+ # user can run `/research-wiki sync --arxiv-ids <missing>` to backfill.
7
+ #
8
+ # This is a best-effort diagnostic. Because ARIS has no canonical "papers
9
+ # I actually read" log (unlike paper-writing's audit artifacts), scans
10
+ # grep over likely sources and will miss papers read via channels it
11
+ # doesn't scan. See `shared-references/integration-contract.md` §6
12
+ # (Verifier or diagnostic) — this tool is intentionally non-blocking.
13
+ #
14
+ # Usage:
15
+ # bash tools/verify_wiki_coverage.sh <wiki_root> [--scan <path> ...] [--json-out <path>]
16
+ #
17
+ # Defaults:
18
+ # --scan: scans CWD-level `.aris/traces/`, `paper/`, `PAPER_PLAN.md`,
19
+ # `NARRATIVE_REPORT.md`, `references.bib` if they exist.
20
+ # --json-out: <wiki_root>/.wiki-coverage-report.json
21
+ #
22
+ # Exit codes:
23
+ # 0 Diagnostic ran. Any coverage gap is reported but not blocking.
24
+ # 2 Bad arguments / wiki_root not found.
25
+ #
26
+ # Note: exit 0 regardless of coverage outcome — this is NOT a gate.
27
+
28
+ set -uo pipefail
29
+
30
+ WIKI_ROOT=""
31
+ JSON_OUT=""
32
+ SCAN_PATHS=()
33
+
34
+ while [[ $# -gt 0 ]]; do
35
+ case "$1" in
36
+ --scan) SCAN_PATHS+=("${2:?--scan requires a path}"); shift 2 ;;
37
+ --json-out) JSON_OUT="${2:?--json-out requires a path}"; shift 2 ;;
38
+ -h|--help) sed -n '2,22p' "$0" | sed 's/^# \?//'; exit 0 ;;
39
+ --*) echo "unknown option: $1" >&2; exit 2 ;;
40
+ *)
41
+ if [[ -z "$WIKI_ROOT" ]]; then WIKI_ROOT="$1"
42
+ else echo "unexpected positional: $1" >&2; exit 2; fi
43
+ shift ;;
44
+ esac
45
+ done
46
+
47
+ [[ -n "$WIKI_ROOT" ]] || { echo "usage: $0 <wiki_root> [--scan <path> ...] [--json-out <path>]" >&2; exit 2; }
48
+ [[ -d "$WIKI_ROOT" ]] || { echo "wiki_root not found: $WIKI_ROOT" >&2; exit 2; }
49
+ WIKI_ROOT="$(cd "$WIKI_ROOT" && pwd)"
50
+
51
+ # Default scan set: obvious places where arxiv IDs show up
52
+ if [[ ${#SCAN_PATHS[@]} -eq 0 ]]; then
53
+ for p in .aris/traces paper PAPER_PLAN.md NARRATIVE_REPORT.md references.bib; do
54
+ [[ -e "$p" ]] && SCAN_PATHS+=("$p")
55
+ done
56
+ fi
57
+
58
+ [[ -n "$JSON_OUT" ]] || JSON_OUT="$WIKI_ROOT/.wiki-coverage-report.json"
59
+
60
+ # Regex: 4-digit year + . + 4-5 digits + optional vN (modern arXiv) OR
61
+ # category(.subclass)?/Ndigits (legacy, e.g. cs/0601001, cs.LG/0703124)
62
+ ARXIV_RE='\b(arXiv:|arxiv:|abs/)?([0-9]{4}\.[0-9]{4,5}(v[0-9]+)?|[a-z\-]+(\.[A-Z]{2})?\/[0-9]{7}(v[0-9]+)?)\b'
63
+
64
+ # Collect referenced arxiv ids from scan set
65
+ REFERENCED=$(mktemp)
66
+ trap 'rm -f "$REFERENCED" "$INGESTED" "$MISSING"' EXIT
67
+
68
+ # bash 3.2: empty-array "${ARR[@]}" trips `set -u`; SCAN_PATHS is empty when no
69
+ # scan target exists in the invoking directory — the report should just be empty.
70
+ {
71
+ if [[ ${#SCAN_PATHS[@]} -gt 0 ]]; then
72
+ for path in "${SCAN_PATHS[@]}"; do
73
+ if [[ -e "$path" ]]; then
74
+ # grep recursively for files, flat for directories; suppress "is a directory"
75
+ grep -rohE "$ARXIV_RE" "$path" 2>/dev/null || true
76
+ fi
77
+ done
78
+ fi
79
+ } \
80
+ | sed -E 's#^(arXiv:|arxiv:|abs/)##; s/v[0-9]+$//' \
81
+ | grep -v '^$' \
82
+ | sort -u > "$REFERENCED"
83
+
84
+ # Collect ingested ids from wiki frontmatter.
85
+ # Use POSIX [[:space:]] (BSD sed on macOS does not understand \s).
86
+ INGESTED=$(mktemp)
87
+ if [[ -d "$WIKI_ROOT/papers" ]]; then
88
+ grep -hoE 'arxiv:[[:space:]]*"?[^"[:space:]]+' "$WIKI_ROOT/papers"/*.md 2>/dev/null \
89
+ | sed -E 's/^arxiv:[[:space:]]*"?//; s/"$//; s/v[0-9]+$//' \
90
+ | grep -v '^null$' \
91
+ | grep -v '^$' \
92
+ | sort -u > "$INGESTED"
93
+ else
94
+ > "$INGESTED"
95
+ fi
96
+
97
+ # Missing = referenced but not ingested
98
+ MISSING=$(mktemp)
99
+ comm -23 "$REFERENCED" "$INGESTED" > "$MISSING"
100
+
101
+ REF_COUNT=$(wc -l < "$REFERENCED" | tr -d ' ')
102
+ ING_COUNT=$(wc -l < "$INGESTED" | tr -d ' ')
103
+ MISS_COUNT=$(wc -l < "$MISSING" | tr -d ' ')
104
+
105
+ # Write JSON report
106
+ mkdir -p "$(dirname "$JSON_OUT")"
107
+ {
108
+ echo "{"
109
+ echo " \"tool\": \"verify_wiki_coverage.sh\","
110
+ echo " \"wiki_root\": \"$WIKI_ROOT\","
111
+ echo " \"scanned_paths\": ["
112
+ if [[ ${#SCAN_PATHS[@]} -gt 0 ]]; then
113
+ printf ' "%s"' "${SCAN_PATHS[0]}"
114
+ for ((i=1; i<${#SCAN_PATHS[@]}; i++)); do printf ',\n "%s"' "${SCAN_PATHS[$i]}"; done
115
+ echo ""
116
+ fi
117
+ echo " ],"
118
+ echo " \"generated_at\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\","
119
+ echo " \"referenced_arxiv_ids\": $REF_COUNT,"
120
+ echo " \"ingested_arxiv_ids\": $ING_COUNT,"
121
+ echo " \"missing_from_wiki\": $MISS_COUNT,"
122
+ echo " \"diagnostic_only\": true,"
123
+ echo " \"missing_ids\": ["
124
+ if [[ "$MISS_COUNT" -gt 0 ]]; then
125
+ first=1
126
+ while read -r id; do
127
+ if [[ "$first" -eq 1 ]]; then
128
+ printf ' "%s"' "$id"
129
+ first=0
130
+ else
131
+ printf ',\n "%s"' "$id"
132
+ fi
133
+ done < "$MISSING"
134
+ echo ""
135
+ fi
136
+ echo " ]"
137
+ echo "}"
138
+ } > "$JSON_OUT"
139
+
140
+ # Human-readable summary
141
+ echo "" >&2
142
+ echo "Research wiki coverage (diagnostic — not blocking)" >&2
143
+ echo " wiki: $WIKI_ROOT" >&2
144
+ echo " scanned: ${SCAN_PATHS[*]:-<none>}" >&2
145
+ echo " referenced: $REF_COUNT arxiv id(s)" >&2
146
+ echo " ingested: $ING_COUNT arxiv id(s)" >&2
147
+ echo " missing: $MISS_COUNT" >&2
148
+ echo " report: $JSON_OUT" >&2
149
+ echo "" >&2
150
+
151
+ if [[ "$MISS_COUNT" -gt 0 ]]; then
152
+ echo "Missing from wiki (first 20):" >&2
153
+ head -20 "$MISSING" | sed 's/^/ /' >&2
154
+ [[ "$MISS_COUNT" -gt 20 ]] && echo " ... ($((MISS_COUNT-20)) more)" >&2
155
+ echo "" >&2
156
+ # Build comma-joined list up to 20 ids for backfill hint
157
+ HINT_IDS=$(head -20 "$MISSING" | paste -sd, -)
158
+ # Resolve helper path the same way the skills do (per
159
+ # skills/shared-references/wiki-helper-resolution.md)
160
+ HINT_SCRIPT=".aris/tools/research_wiki.py"
161
+ [[ -f "$HINT_SCRIPT" ]] || HINT_SCRIPT="tools/research_wiki.py"
162
+ [[ -f "$HINT_SCRIPT" ]] || { [[ -n "${ARIS_REPO:-}" ]] && HINT_SCRIPT="$ARIS_REPO/tools/research_wiki.py"; }
163
+ # Layer 4: global pointer file written by the installer/updater (#366).
164
+ if [[ -z "${ARIS_REPO:-}" && ! -f "$HINT_SCRIPT" && -f "$HOME/.aris/repo" ]]; then
165
+ ARIS_REPO=$(cat "$HOME/.aris/repo" 2>/dev/null) || true
166
+ [[ -n "$ARIS_REPO" ]] && HINT_SCRIPT="$ARIS_REPO/tools/research_wiki.py"
167
+ fi
168
+ [[ -f "$HINT_SCRIPT" ]] || HINT_SCRIPT="<resolve-via-shared-ref>/research_wiki.py"
169
+ echo "Backfill suggestion:" >&2
170
+ echo " python3 \"$HINT_SCRIPT\" sync $WIKI_ROOT --arxiv-ids $HINT_IDS" >&2
171
+ [[ "$MISS_COUNT" -gt 20 ]] && echo " (or --from-file to pass the full list)" >&2
172
+ echo "" >&2
173
+ fi
174
+
175
+ # Always exit 0 — this is a diagnostic, not a gate.
176
+ exit 0
@@ -0,0 +1,485 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ watchdog.py — Server-side unified monitoring daemon for ARIS.
4
+
5
+ One process per server, monitors all registered tasks (training / download / loop).
6
+ Outputs per-task status JSON + aggregated summary.txt for low-frequency polling.
7
+
8
+ Usage:
9
+ # Start the daemon (runs in foreground, use tmux/screen to persist)
10
+ python3 watchdog.py [--base-dir /tmp/aris-watchdog] [--interval 60]
11
+
12
+ # Register a training task
13
+ python3 watchdog.py --register '{"name":"exp01","type":"training","session":"exp01","session_type":"screen","gpus":[0,1,2,3]}'
14
+
15
+ # Register a download task
16
+ python3 watchdog.py --register '{"name":"dl01","type":"download","session":"dl01","session_type":"tmux","target_path":"/path/to/file"}'
17
+
18
+ # Unregister a task
19
+ python3 watchdog.py --unregister exp01
20
+
21
+ # Check current summary
22
+ python3 watchdog.py --status
23
+
24
+ Directory structure:
25
+ /tmp/aris-watchdog/
26
+ ├── watchdog.pid
27
+ ├── tasks.json # [{name, type, session, session_type, ...}, ...]
28
+ ├── alerts.log # anomaly log (DEAD/STALLED/IDLE) for cross-session recovery
29
+ └── status/
30
+ ├── <task-name>.json # per-task status
31
+ └── summary.txt # one-line-per-task summary for CronCreate polling
32
+ """
33
+
34
+ import argparse
35
+ import json
36
+ import os
37
+ import signal
38
+ import subprocess
39
+ import sys
40
+ import time
41
+ from pathlib import Path
42
+
43
+ DEFAULT_BASE = "/tmp/aris-watchdog"
44
+ DEFAULT_INTERVAL = 60
45
+ SLOW_SPEED_THRESHOLD = 1 * 1024 * 1024 # 1 MB/s
46
+ GPU_IDLE_THRESHOLD = 5 # percent
47
+
48
+
49
+ def get_paths(base_dir):
50
+ base = Path(base_dir)
51
+ return {
52
+ "base": base,
53
+ "pid": base / "watchdog.pid",
54
+ "tasks": base / "tasks.json",
55
+ "status": base / "status",
56
+ "alerts": base / "alerts.log",
57
+ }
58
+
59
+
60
+ # ── Task registration ────────────────────────────────────────────
61
+
62
+
63
+ def register_task(base_dir, task_json):
64
+ paths = get_paths(base_dir)
65
+ paths["base"].mkdir(parents=True, exist_ok=True)
66
+ paths["status"].mkdir(parents=True, exist_ok=True)
67
+
68
+ task = json.loads(task_json)
69
+ missing = {"name", "type"} - set(task.keys())
70
+ if missing:
71
+ print(f"error: missing required fields: {missing}", file=sys.stderr)
72
+ sys.exit(1)
73
+
74
+ ttype = task["type"]
75
+ if ttype not in ("training", "download", "loop"):
76
+ print(f"error: type must be 'training', 'download', or 'loop', got '{ttype}'", file=sys.stderr)
77
+ sys.exit(1)
78
+ if ttype in ("training", "download") and "session" not in task:
79
+ print(f"error: {ttype} task requires 'session'", file=sys.stderr)
80
+ sys.exit(1)
81
+ if ttype == "loop" and ("state_file" not in task or "stale_after_seconds" not in task):
82
+ print("error: loop task requires 'state_file' and 'stale_after_seconds'", file=sys.stderr)
83
+ sys.exit(1)
84
+
85
+ if ttype == "loop":
86
+ try:
87
+ if int(task["stale_after_seconds"]) <= 0:
88
+ raise ValueError
89
+ except (ValueError, TypeError):
90
+ print("error: loop 'stale_after_seconds' must be a positive integer (seconds)", file=sys.stderr)
91
+ sys.exit(1)
92
+ # Absolutize WITHOUT resolving symlinks: a loop may update its state file via
93
+ # atomic os.replace, which swaps a symlink for a new file; we must keep watching
94
+ # the registered path, not its resolve()-time target. (os is imported at module top.)
95
+ task["state_file"] = os.path.abspath(os.path.expanduser(task["state_file"]))
96
+ elif "session_type" not in task:
97
+ # Default session_type for session-backed tasks: fallback to screen
98
+ task["session_type"] = "screen"
99
+
100
+ tasks = []
101
+ if paths["tasks"].exists():
102
+ try:
103
+ tasks = json.loads(paths["tasks"].read_text())
104
+ except (json.JSONDecodeError, OSError):
105
+ tasks = []
106
+
107
+ # Deduplicate: replace existing task with same name
108
+ tasks = [t for t in tasks if t["name"] != task["name"]]
109
+ task["registered_at"] = time.strftime("%Y-%m-%dT%H:%M:%S")
110
+ task["registered_epoch"] = time.time() # tz-free grace anchor (loop PENDING→MISSING)
111
+ tasks.append(task)
112
+
113
+ paths["tasks"].write_text(json.dumps(tasks, indent=2))
114
+ detail = f"stale_after={task['stale_after_seconds']}s" if ttype == "loop" else task["session_type"]
115
+ print(f"registered: {task['name']} ({ttype}, {detail})")
116
+
117
+
118
+ def unregister_task(base_dir, name):
119
+ paths = get_paths(base_dir)
120
+ if not paths["tasks"].exists():
121
+ print(f"no tasks file found")
122
+ return
123
+ try:
124
+ tasks = json.loads(paths["tasks"].read_text())
125
+ except (json.JSONDecodeError, OSError):
126
+ return
127
+ tasks = [t for t in tasks if t["name"] != name]
128
+ paths["tasks"].write_text(json.dumps(tasks, indent=2))
129
+ status_file = paths["status"] / f"{name}.json"
130
+ if status_file.exists():
131
+ status_file.unlink()
132
+ print(f"unregistered: {name}")
133
+
134
+
135
+ # ── Session checks (tmux + screen) ──────────────────────────────
136
+
137
+
138
+ def session_alive(session_name, session_type="screen"):
139
+ """Check if a tmux or screen session is alive."""
140
+ if session_type == "tmux":
141
+ r = subprocess.run(
142
+ ["tmux", "has-session", "-t", session_name],
143
+ capture_output=True,
144
+ )
145
+ return r.returncode == 0
146
+ else: # screen
147
+ r = subprocess.run(
148
+ ["screen", "-list"], capture_output=True, text=True,
149
+ )
150
+ return session_name in r.stdout
151
+
152
+
153
+ # ── GPU checks ───────────────────────────────────────────────────
154
+
155
+
156
+ def get_gpu_util():
157
+ """Return list of GPU utilization percentages, e.g. [85, 92, 0, ...]"""
158
+ try:
159
+ r = subprocess.run(
160
+ ["nvidia-smi", "--query-gpu=utilization.gpu", "--format=csv,noheader,nounits"],
161
+ capture_output=True, text=True, timeout=10,
162
+ )
163
+ return [int(x.strip()) for x in r.stdout.strip().split("\n") if x.strip()]
164
+ except Exception:
165
+ return []
166
+
167
+
168
+ # ── File size checks ─────────────────────────────────────────────
169
+
170
+
171
+ def get_path_size(path):
172
+ """Get size of a file or directory in bytes."""
173
+ try:
174
+ r = subprocess.run(
175
+ ["du", "-sb", path], capture_output=True, text=True, timeout=30,
176
+ )
177
+ return int(r.stdout.split()[0])
178
+ except Exception:
179
+ return 0
180
+
181
+
182
+ # ── Task checking logic ─────────────────────────────────────────
183
+
184
+
185
+ def check_download(task, status_dir, interval):
186
+ name = task["name"]
187
+ session = task["session"]
188
+ session_type = task.get("session_type", "screen")
189
+ target = task.get("target_path", "")
190
+ status_file = status_dir / f"{name}.json"
191
+ now = time.strftime("%Y-%m-%dT%H:%M:%S")
192
+
193
+ if not session_alive(session, session_type):
194
+ return write_status(status_file, {
195
+ "status": "DEAD", "task": name, "type": "download",
196
+ "msg": f"{session_type} session gone", "ts": now,
197
+ })
198
+
199
+ if not target:
200
+ return write_status(status_file, {
201
+ "status": "OK", "task": name, "type": "download",
202
+ "msg": "alive, no target_path to check size", "ts": now,
203
+ })
204
+
205
+ current_size = get_path_size(target)
206
+
207
+ # Read previous size for delta
208
+ prev_size = 0
209
+ if status_file.exists():
210
+ try:
211
+ prev = json.loads(status_file.read_text())
212
+ prev_size = prev.get("size", 0)
213
+ except Exception:
214
+ pass
215
+
216
+ if current_size == prev_size and current_size > 0:
217
+ return write_status(status_file, {
218
+ "status": "STALLED", "task": name, "type": "download",
219
+ "size": current_size, "msg": "no size growth", "ts": now,
220
+ })
221
+
222
+ speed = (current_size - prev_size) / max(interval, 1)
223
+
224
+ if 0 < speed < SLOW_SPEED_THRESHOLD:
225
+ return write_status(status_file, {
226
+ "status": "SLOW", "task": name, "type": "download",
227
+ "size": current_size, "speed_mbps": round(speed / 1024 / 1024, 2),
228
+ "ts": now,
229
+ })
230
+
231
+ return write_status(status_file, {
232
+ "status": "OK", "task": name, "type": "download",
233
+ "size": current_size, "speed_mbps": round(speed / 1024 / 1024, 2),
234
+ "ts": now,
235
+ })
236
+
237
+
238
+ def check_training(task, status_dir):
239
+ name = task["name"]
240
+ session = task["session"]
241
+ session_type = task.get("session_type", "screen")
242
+ status_file = status_dir / f"{name}.json"
243
+ now = time.strftime("%Y-%m-%dT%H:%M:%S")
244
+
245
+ if not session_alive(session, session_type):
246
+ return write_status(status_file, {
247
+ "status": "DEAD", "task": name, "type": "training",
248
+ "msg": f"{session_type} session gone", "ts": now,
249
+ })
250
+
251
+ gpu_utils = get_gpu_util()
252
+
253
+ # Check specified GPUs for activity
254
+ gpus = task.get("gpus", [])
255
+ if gpus and gpu_utils:
256
+ used_utils = [gpu_utils[i] for i in gpus if i < len(gpu_utils)]
257
+ if used_utils and all(u < GPU_IDLE_THRESHOLD for u in used_utils):
258
+ return write_status(status_file, {
259
+ "status": "IDLE", "task": name, "type": "training",
260
+ "gpu_util": {str(i): gpu_utils[i] for i in gpus if i < len(gpu_utils)},
261
+ "msg": f"GPUs idle (<{GPU_IDLE_THRESHOLD}%)", "ts": now,
262
+ })
263
+
264
+ return write_status(status_file, {
265
+ "status": "OK", "task": name, "type": "training",
266
+ "gpu_util": gpu_utils, "ts": now,
267
+ })
268
+
269
+
270
+ # ── Loop-liveness check (detect-only) ───────────────────────────
271
+
272
+
273
+ LOOP_COMPLETED_STATUSES = {"completed", "done", "finished"}
274
+
275
+
276
+ def _loop_is_completed(state):
277
+ """True if the watched JSON shows the loop finished (silence is then expected, not a stall)."""
278
+ if not isinstance(state, dict):
279
+ return False
280
+ if str(state.get("status", "")).lower() in LOOP_COMPLETED_STATUSES:
281
+ return True
282
+ phases = state.get("phases") # run_state.py shape: all phases terminal ⇒ done
283
+ if isinstance(phases, list) and phases and all(
284
+ isinstance(p, dict) and p.get("status") in ("accepted", "skipped") for p in phases
285
+ ):
286
+ return True
287
+ return False
288
+
289
+
290
+ def check_loop(task, status_dir):
291
+ """Loop-liveness via state-file mtime, DETECT-ONLY.
292
+
293
+ Liveness = freshness of the file the loop rewrites/touches each iteration, judged against the
294
+ loop's OWN declared `stale_after_seconds` (NOT the daemon interval). This function only reads
295
+ files and writes a status; it NEVER restarts the loop, spawns a process, or invokes a skill —
296
+ a STALE loop is surfaced via alerts.log + summary.txt for a human/cron, honoring
297
+ external-cadence.md's fence on verdict-bearing loops. JSON is read best-effort only to
298
+ recognize a COMPLETED loop; an unparseable file never causes STALE on its own.
299
+ """
300
+ name = task["name"]
301
+ state_file = Path(task.get("state_file", ""))
302
+ stale_after = int(task.get("stale_after_seconds", 21600))
303
+ status_file = status_dir / f"{name}.json"
304
+ now_str = time.strftime("%Y-%m-%dT%H:%M:%S")
305
+
306
+ if not state_file.exists():
307
+ # Fail-safe: a missing registered_epoch ⇒ epoch 0 ⇒ MISSING, never infinite grace.
308
+ grace = time.time() - float(task.get("registered_epoch", 0.0))
309
+ if grace <= stale_after:
310
+ return write_status(status_file, {
311
+ "status": "PENDING", "task": name, "type": "loop",
312
+ "msg": "state file not present yet", "ts": now_str})
313
+ return write_status(status_file, {
314
+ "status": "MISSING", "task": name, "type": "loop",
315
+ "msg": f"state file absent {int(grace)}s after register (path typo?)", "ts": now_str})
316
+
317
+ try:
318
+ state = json.loads(state_file.read_text())
319
+ if _loop_is_completed(state):
320
+ return write_status(status_file, {
321
+ "status": "COMPLETED", "task": name, "type": "loop",
322
+ "msg": "loop reports completion", "ts": now_str})
323
+ except (json.JSONDecodeError, OSError):
324
+ pass # not a terminal state we can read → fall through to mtime liveness
325
+
326
+ age = int(time.time() - state_file.stat().st_mtime)
327
+ if age > stale_after:
328
+ return write_status(status_file, {
329
+ "status": "STALE", "task": name, "type": "loop",
330
+ "age_s": age, "stale_after": stale_after,
331
+ "msg": f"no state write in {age}s (> {stale_after}s)", "ts": now_str})
332
+ return write_status(status_file, {
333
+ "status": "OK", "task": name, "type": "loop",
334
+ "age_s": age, "stale_after": stale_after, "ts": now_str})
335
+
336
+
337
+ # ── Status output ────────────────────────────────────────────────
338
+
339
+
340
+ def write_status(path, data):
341
+ """Write per-task status and append to alerts.log on anomalies."""
342
+ path.write_text(json.dumps(data))
343
+
344
+ status = data.get("status", "OK")
345
+ if status in ("DEAD", "STALLED", "STALE", "MISSING", "IDLE", "ERROR"):
346
+ alert_file = path.parent.parent / "alerts.log"
347
+ ts = data.get("ts", time.strftime("%Y-%m-%dT%H:%M:%S"))
348
+ task = data.get("task", "?")
349
+ msg = data.get("msg", "")
350
+ alert_line = f"[{ts}] {task}: {status} — {msg}\n"
351
+ with open(alert_file, "a") as f:
352
+ f.write(alert_line)
353
+
354
+ return data
355
+
356
+
357
+ def write_summary(status_dir):
358
+ """Aggregate all task statuses into summary.txt (one line per task)."""
359
+ lines = []
360
+ for f in sorted(status_dir.glob("*.json")):
361
+ try:
362
+ d = json.loads(f.read_text())
363
+ name = d.get("task", f.stem)
364
+ status = d.get("status", "?")
365
+ typ = d.get("type", "?")
366
+ extra = ""
367
+ if status == "SLOW":
368
+ extra = f" speed={d.get('speed_mbps', '?')}MB/s"
369
+ elif status == "IDLE":
370
+ extra = f" gpu={d.get('gpu_util', '?')}"
371
+ elif status == "DEAD":
372
+ extra = f" {d.get('msg', '')}"
373
+ elif status in ("STALE", "MISSING"):
374
+ extra = f" {d.get('msg', '')}"
375
+ elif status == "PENDING":
376
+ extra = " (awaiting first state write)"
377
+ elif status == "COMPLETED":
378
+ extra = " ✓"
379
+ lines.append(f"{name}({typ}): {status}{extra}")
380
+ except Exception:
381
+ continue
382
+
383
+ summary = "\n".join(lines) if lines else "no tasks"
384
+ (status_dir / "summary.txt").write_text(summary)
385
+ return summary
386
+
387
+
388
+ # ── Main loop ────────────────────────────────────────────────────
389
+
390
+
391
+ def run_watchdog(base_dir, interval):
392
+ paths = get_paths(base_dir)
393
+ paths["base"].mkdir(parents=True, exist_ok=True)
394
+ paths["status"].mkdir(parents=True, exist_ok=True)
395
+
396
+ # Write PID for liveness checks
397
+ paths["pid"].write_text(str(os.getpid()))
398
+
399
+ def handle_signal(sig, frame):
400
+ paths["pid"].unlink(missing_ok=True)
401
+ sys.exit(0)
402
+
403
+ signal.signal(signal.SIGTERM, handle_signal)
404
+ signal.signal(signal.SIGINT, handle_signal)
405
+
406
+ print(f"watchdog started (pid={os.getpid()}, base={base_dir}, interval={interval}s)")
407
+
408
+ while True:
409
+ if not paths["tasks"].exists():
410
+ time.sleep(interval)
411
+ continue
412
+
413
+ try:
414
+ tasks = json.loads(paths["tasks"].read_text())
415
+ except (json.JSONDecodeError, OSError):
416
+ time.sleep(interval)
417
+ continue
418
+
419
+ for task in tasks:
420
+ try:
421
+ if task["type"] == "download":
422
+ check_download(task, paths["status"], interval)
423
+ elif task["type"] == "training":
424
+ check_training(task, paths["status"])
425
+ elif task["type"] == "loop":
426
+ check_loop(task, paths["status"])
427
+ except Exception as e:
428
+ write_status(
429
+ paths["status"] / f"{task['name']}.json",
430
+ {"status": "ERROR", "task": task["name"], "msg": str(e),
431
+ "ts": time.strftime("%Y-%m-%dT%H:%M:%S")},
432
+ )
433
+
434
+ write_summary(paths["status"])
435
+ time.sleep(interval)
436
+
437
+
438
+ # ── CLI ──────────────────────────────────────────────────────────
439
+
440
+
441
+ def main():
442
+ parser = argparse.ArgumentParser(
443
+ description="ARIS Watchdog — server-side task monitoring daemon",
444
+ formatter_class=argparse.RawDescriptionHelpFormatter,
445
+ epilog="""
446
+ Examples:
447
+ # Start daemon
448
+ python3 watchdog.py
449
+
450
+ # Register a training task (screen session)
451
+ python3 watchdog.py --register '{"name":"exp01","type":"training","session":"exp01","gpus":[0,1]}'
452
+
453
+ # Register a download task (tmux session)
454
+ python3 watchdog.py --register '{"name":"dl01","type":"download","session":"dl01","session_type":"tmux","target_path":"/data/imagenet"}'
455
+
456
+ # Check summary
457
+ python3 watchdog.py --status
458
+ """,
459
+ )
460
+ parser.add_argument("--base-dir", default=DEFAULT_BASE,
461
+ help=f"Working directory (default: {DEFAULT_BASE})")
462
+ parser.add_argument("--interval", type=int, default=DEFAULT_INTERVAL,
463
+ help=f"Check interval in seconds (default: {DEFAULT_INTERVAL})")
464
+ parser.add_argument("--register", metavar="JSON",
465
+ help="Register a task (JSON with name, type, session)")
466
+ parser.add_argument("--unregister", metavar="NAME",
467
+ help="Unregister a task by name")
468
+ parser.add_argument("--status", action="store_true",
469
+ help="Print current summary and exit")
470
+ args = parser.parse_args()
471
+
472
+ if args.register:
473
+ register_task(args.base_dir, args.register)
474
+ elif args.unregister:
475
+ unregister_task(args.base_dir, args.unregister)
476
+ elif args.status:
477
+ paths = get_paths(args.base_dir)
478
+ summary = paths["status"] / "summary.txt"
479
+ print(summary.read_text() if summary.exists() else "no status")
480
+ else:
481
+ run_watchdog(args.base_dir, args.interval)
482
+
483
+
484
+ if __name__ == "__main__":
485
+ main()