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.
- package/LICENSE +21 -0
- package/README.md +98 -0
- package/README_CN.md +87 -0
- package/dsh/checkout.patch.yml +38 -0
- package/dsh/client.js +634 -0
- package/dsh/cordis.patch.yml +44 -0
- package/dsh/index.mjs +76 -0
- package/dsh/run-status.mjs +182 -0
- package/dsh/scope-limits.mjs +50 -0
- package/dsh/workbench.mjs +291 -0
- package/mcp-servers/claude-review/README.md +93 -0
- package/mcp-servers/claude-review/run_with_claude_aws.sh +49 -0
- package/mcp-servers/claude-review/server.py +718 -0
- package/mcp-servers/codex-image2/README.md +65 -0
- package/mcp-servers/codex-image2/server.py +893 -0
- package/mcp-servers/feishu-bridge/requirements.txt +1 -0
- package/mcp-servers/feishu-bridge/server.py +240 -0
- package/mcp-servers/gemini-review/README.md +171 -0
- package/mcp-servers/gemini-review/server.py +1856 -0
- package/mcp-servers/llm-chat/requirements.txt +1 -0
- package/mcp-servers/llm-chat/server.py +664 -0
- package/mcp-servers/manual-review/README.md +133 -0
- package/mcp-servers/manual-review/server.py +910 -0
- package/mcp-servers/manual-review/ui.html +279 -0
- package/mcp-servers/minimax-chat/requirements.txt +1 -0
- package/mcp-servers/minimax-chat/server.py +381 -0
- package/package.json +51 -0
- package/skills/ablation-planner/SKILL.md +123 -0
- package/skills/alphaxiv/SKILL.md +196 -0
- package/skills/analyze-results/SKILL.md +46 -0
- package/skills/arxiv/SKILL.md +248 -0
- package/skills/auto-paper-improvement-loop/SKILL.md +651 -0
- package/skills/auto-review-loop/SKILL.md +1137 -0
- package/skills/auto-review-loop-llm/SKILL.md +259 -0
- package/skills/auto-review-loop-minimax/SKILL.md +302 -0
- package/skills/citation-audit/SKILL.md +502 -0
- package/skills/claims-drafting/SKILL.md +227 -0
- package/skills/comm-lit-review/SKILL.md +297 -0
- package/skills/deepxiv/SKILL.md +263 -0
- package/skills/dse-loop/SKILL.md +296 -0
- package/skills/embodiment-description/SKILL.md +129 -0
- package/skills/exa-search/SKILL.md +205 -0
- package/skills/experiment-audit/SKILL.md +311 -0
- package/skills/experiment-bridge/SKILL.md +376 -0
- package/skills/experiment-plan/SKILL.md +249 -0
- package/skills/experiment-queue/SKILL.md +431 -0
- package/skills/experiment-queue/scripts/build_manifest.py +142 -0
- package/skills/experiment-queue/scripts/queue_manager.py +433 -0
- package/skills/feishu-notify/SKILL.md +156 -0
- package/skills/figure-description/SKILL.md +138 -0
- package/skills/figure-spec/SKILL.md +262 -0
- package/skills/figure-spec/scripts/figure_renderer.py +799 -0
- package/skills/formula-derivation/SKILL.md +280 -0
- package/skills/gemini-search/SKILL.md +231 -0
- package/skills/grant-proposal/SKILL.md +698 -0
- package/skills/idea-creator/SKILL.md +542 -0
- package/skills/idea-discovery/SKILL.md +521 -0
- package/skills/idea-discovery-robot/SKILL.md +363 -0
- package/skills/integrity-forensics/SKILL.md +284 -0
- package/skills/interview-cheatsheet/SKILL.md +245 -0
- package/skills/invention-structuring/SKILL.md +188 -0
- package/skills/jurisdiction-format/SKILL.md +192 -0
- package/skills/kill-argument/SKILL.md +437 -0
- package/skills/mermaid-diagram/SKILL.md +419 -0
- package/skills/meta-apply/SKILL.md +141 -0
- package/skills/meta-optimize/SKILL.md +437 -0
- package/skills/monitor-experiment/SKILL.md +140 -0
- package/skills/novelty-check/SKILL.md +101 -0
- package/skills/openalex/SKILL.md +237 -0
- package/skills/overleaf-sync/SKILL.md +220 -0
- package/skills/paper-claim-audit/SKILL.md +348 -0
- package/skills/paper-compile/SKILL.md +266 -0
- package/skills/paper-figure/SKILL.md +312 -0
- package/skills/paper-illustration/SKILL.md +736 -0
- package/skills/paper-illustration-image2/SKILL.md +391 -0
- package/skills/paper-illustration-image2/scripts/paper_illustration_image2.py +255 -0
- package/skills/paper-plan/SKILL.md +386 -0
- package/skills/paper-poster/SKILL.md +19 -0
- package/skills/paper-poster-html/DESIGN_FINAL.md +176 -0
- package/skills/paper-poster-html/IMPLEMENTATION_CONVENTIONS.md +161 -0
- package/skills/paper-poster-html/LICENSES/posterly-MIT.txt +21 -0
- package/skills/paper-poster-html/NOTICE.md +57 -0
- package/skills/paper-poster-html/SKILL.md +323 -0
- package/skills/paper-poster-html/scripts/_posterly/__init__.py +0 -0
- package/skills/paper-poster-html/scripts/_posterly/canvas.py +200 -0
- package/skills/paper-poster-html/scripts/_posterly/measure.py +588 -0
- package/skills/paper-poster-html/scripts/_posterly/polish.py +498 -0
- package/skills/paper-poster-html/scripts/_posterly/preflight.py +489 -0
- package/skills/paper-poster-html/scripts/_posterly/render.py +215 -0
- package/skills/paper-poster-html/scripts/_posterly/textutil.py +16 -0
- package/skills/paper-poster-html/scripts/_posterly/verify_final.py +171 -0
- package/skills/paper-poster-html/scripts/asset_check.py +897 -0
- package/skills/paper-poster-html/scripts/extract_pdf_figures.py +666 -0
- package/skills/paper-poster-html/scripts/poster_check.py +251 -0
- package/skills/paper-poster-html/scripts/preprocess_figures.py +238 -0
- package/skills/paper-poster-html/scripts/render_preview.py +217 -0
- package/skills/paper-poster-html/scripts/run_gates.py +556 -0
- package/skills/paper-poster-html/scripts/style_check.py +1324 -0
- package/skills/paper-poster-html/templates/COMPONENTS.md +462 -0
- package/skills/paper-poster-html/templates/README.md +170 -0
- package/skills/paper-poster-html/templates/landscape_4col.html +1032 -0
- package/skills/paper-poster-html/templates/landscape_hero.html +1046 -0
- package/skills/paper-poster-html/templates/portrait_2col.html +947 -0
- package/skills/paper-poster-html/templates/tokens/acl.json +9 -0
- package/skills/paper-poster-html/templates/tokens/cvpr.json +9 -0
- package/skills/paper-poster-html/templates/tokens/generic.json +9 -0
- package/skills/paper-poster-html/templates/tokens/iclr.json +9 -0
- package/skills/paper-poster-html/templates/tokens/icml.json +9 -0
- package/skills/paper-poster-html/templates/tokens/neurips.json +9 -0
- package/skills/paper-slides/SKILL.md +635 -0
- package/skills/paper-talk/SKILL.md +381 -0
- package/skills/paper-write/SKILL.md +604 -0
- package/skills/paper-write/templates/IEEEtran.bst +2409 -0
- package/skills/paper-write/templates/IEEEtran.cls +6347 -0
- package/skills/paper-write/templates/iclr2026.tex +84 -0
- package/skills/paper-write/templates/icml2025.tex +87 -0
- package/skills/paper-write/templates/ieee_conference.tex +89 -0
- package/skills/paper-write/templates/ieee_journal.tex +93 -0
- package/skills/paper-write/templates/math_commands.tex +48 -0
- package/skills/paper-write/templates/neurips2025.tex +80 -0
- package/skills/paper-writing/SKILL.md +916 -0
- package/skills/patent-novelty-check/SKILL.md +153 -0
- package/skills/patent-pipeline/SKILL.md +344 -0
- package/skills/patent-review/SKILL.md +203 -0
- package/skills/pixel-art/SKILL.md +137 -0
- package/skills/prior-art-search/SKILL.md +146 -0
- package/skills/proof-checker/SKILL.md +866 -0
- package/skills/proof-orchestrator/NOTICE.md +24 -0
- package/skills/proof-orchestrator/SKILL.md +254 -0
- package/skills/proof-orchestrator/references/audit-output-contract.md +126 -0
- package/skills/proof-orchestrator/references/deepseek-routing.md +74 -0
- package/skills/proof-orchestrator/references/dispatch-prompts.md +227 -0
- package/skills/proof-orchestrator/references/notation-audit.md +135 -0
- package/skills/proof-orchestrator/references/proof-audit-rubric.md +70 -0
- package/skills/proof-orchestrator/references/stress-tests.md +38 -0
- package/skills/proof-writer/SKILL.md +223 -0
- package/skills/qzcli/SKILL.md +324 -0
- package/skills/rebuttal/SKILL.md +376 -0
- package/skills/render-html/SKILL.md +316 -0
- package/skills/render-html/scripts/render_html.py +1006 -0
- package/skills/render-html/scripts/templates/academic.html +703 -0
- package/skills/render-html/scripts/templates/dashboard.html +333 -0
- package/skills/research-lit/SKILL.md +756 -0
- package/skills/research-pipeline/SKILL.md +384 -0
- package/skills/research-refine/SKILL.md +770 -0
- package/skills/research-refine-pipeline/SKILL.md +186 -0
- package/skills/research-review/SKILL.md +198 -0
- package/skills/research-wiki/SKILL.md +461 -0
- package/skills/resubmit-pipeline/SKILL.md +447 -0
- package/skills/result-to-claim/SKILL.md +311 -0
- package/skills/run-experiment/SKILL.md +313 -0
- package/skills/semantic-scholar/SKILL.md +236 -0
- package/skills/serverless-modal/SKILL.md +335 -0
- package/skills/shared-references/acceptance-gate.md +324 -0
- package/skills/shared-references/assurance-contract.md +248 -0
- package/skills/shared-references/capture-antipatterns.md +78 -0
- package/skills/shared-references/citation-discipline.md +583 -0
- package/skills/shared-references/compute-env-contract.md +163 -0
- package/skills/shared-references/effort-contract.md +183 -0
- package/skills/shared-references/evidence-precheck.md +65 -0
- package/skills/shared-references/experiment-integrity.md +49 -0
- package/skills/shared-references/external-cadence.md +326 -0
- package/skills/shared-references/fan-out-pattern.md +366 -0
- package/skills/shared-references/injection-hygiene.md +127 -0
- package/skills/shared-references/integration-contract.md +461 -0
- package/skills/shared-references/output-composition.md +93 -0
- package/skills/shared-references/output-language.md +45 -0
- package/skills/shared-references/output-manifest.md +49 -0
- package/skills/shared-references/output-versioning.md +111 -0
- package/skills/shared-references/patent-format-cn.md +199 -0
- package/skills/shared-references/patent-format-ep.md +173 -0
- package/skills/shared-references/patent-format-us.md +161 -0
- package/skills/shared-references/patent-writing-principles.md +197 -0
- package/skills/shared-references/prior-art-databases.md +141 -0
- package/skills/shared-references/resumable-runs.md +109 -0
- package/skills/shared-references/review-scope-limits.md +81 -0
- package/skills/shared-references/review-tracing.md +391 -0
- package/skills/shared-references/reviewer-independence.md +79 -0
- package/skills/shared-references/reviewer-routing.md +852 -0
- package/skills/shared-references/skill-governance.md +104 -0
- package/skills/shared-references/taste-calibration.md +85 -0
- package/skills/shared-references/venue-checklists.md +114 -0
- package/skills/shared-references/wiki-helper-resolution.md +134 -0
- package/skills/shared-references/writing-principles.md +525 -0
- package/skills/skills-codex/README.md +102 -0
- package/skills/skills-codex/README_CN.md +100 -0
- package/skills/skills-codex/ablation-planner/SKILL.md +126 -0
- package/skills/skills-codex/alphaxiv/SKILL.md +186 -0
- package/skills/skills-codex/analyze-results/SKILL.md +45 -0
- package/skills/skills-codex/arxiv/SKILL.md +210 -0
- package/skills/skills-codex/auto-paper-improvement-loop/SKILL.md +574 -0
- package/skills/skills-codex/auto-review-loop/SKILL.md +500 -0
- package/skills/skills-codex/auto-review-loop-llm/SKILL.md +247 -0
- package/skills/skills-codex/auto-review-loop-minimax/SKILL.md +290 -0
- package/skills/skills-codex/citation-audit/SKILL.md +504 -0
- package/skills/skills-codex/claims-drafting/SKILL.md +239 -0
- package/skills/skills-codex/comm-lit-review/SKILL.md +299 -0
- package/skills/skills-codex/comm-lit-review/references/domain-taxonomy.md +57 -0
- package/skills/skills-codex/comm-lit-review/references/output-template.md +37 -0
- package/skills/skills-codex/comm-lit-review/references/source-policy.md +99 -0
- package/skills/skills-codex/comm-lit-review/references/venue-tiering.md +112 -0
- package/skills/skills-codex/deepxiv/SKILL.md +142 -0
- package/skills/skills-codex/dse-loop/SKILL.md +285 -0
- package/skills/skills-codex/embodiment-description/SKILL.md +129 -0
- package/skills/skills-codex/exa-search/SKILL.md +192 -0
- package/skills/skills-codex/experiment-audit/SKILL.md +286 -0
- package/skills/skills-codex/experiment-bridge/SKILL.md +356 -0
- package/skills/skills-codex/experiment-plan/SKILL.md +249 -0
- package/skills/skills-codex/experiment-queue/SKILL.md +401 -0
- package/skills/skills-codex/feishu-notify/SKILL.md +155 -0
- package/skills/skills-codex/figure-description/SKILL.md +138 -0
- package/skills/skills-codex/figure-spec/SKILL.md +252 -0
- package/skills/skills-codex/formula-derivation/SKILL.md +280 -0
- package/skills/skills-codex/gemini-search/SKILL.md +205 -0
- package/skills/skills-codex/grant-proposal/SKILL.md +626 -0
- package/skills/skills-codex/idea-creator/SKILL.md +405 -0
- package/skills/skills-codex/idea-discovery/SKILL.md +475 -0
- package/skills/skills-codex/idea-discovery-robot/SKILL.md +362 -0
- package/skills/skills-codex/integrity-forensics/SKILL.md +106 -0
- package/skills/skills-codex/interview-cheatsheet/SKILL.md +245 -0
- package/skills/skills-codex/invention-structuring/SKILL.md +188 -0
- package/skills/skills-codex/jurisdiction-format/SKILL.md +192 -0
- package/skills/skills-codex/kill-argument/SKILL.md +403 -0
- package/skills/skills-codex/mermaid-diagram/SKILL.md +379 -0
- package/skills/skills-codex/meta-apply/SKILL.md +154 -0
- package/skills/skills-codex/meta-optimize/SKILL.md +348 -0
- package/skills/skills-codex/monitor-experiment/SKILL.md +98 -0
- package/skills/skills-codex/novelty-check/SKILL.md +89 -0
- package/skills/skills-codex/openalex/SKILL.md +228 -0
- package/skills/skills-codex/overleaf-sync/SKILL.md +220 -0
- package/skills/skills-codex/paper-claim-audit/SKILL.md +350 -0
- package/skills/skills-codex/paper-compile/SKILL.md +253 -0
- package/skills/skills-codex/paper-figure/SKILL.md +311 -0
- package/skills/skills-codex/paper-illustration/SKILL.md +690 -0
- package/skills/skills-codex/paper-illustration-image2/SKILL.md +383 -0
- package/skills/skills-codex/paper-illustration-image2/scripts/paper_illustration_image2.py +255 -0
- package/skills/skills-codex/paper-plan/SKILL.md +278 -0
- package/skills/skills-codex/paper-poster/SKILL.md +19 -0
- package/skills/skills-codex/paper-poster-html/SKILL.md +377 -0
- package/skills/skills-codex/paper-slides/SKILL.md +571 -0
- package/skills/skills-codex/paper-talk/SKILL.md +381 -0
- package/skills/skills-codex/paper-write/SKILL.md +411 -0
- package/skills/skills-codex/paper-write/templates/IEEEtran.bst +2409 -0
- package/skills/skills-codex/paper-write/templates/IEEEtran.cls +6347 -0
- package/skills/skills-codex/paper-write/templates/aaai2026.bst +1493 -0
- package/skills/skills-codex/paper-write/templates/aaai2026.sty +315 -0
- package/skills/skills-codex/paper-write/templates/aaai2026.tex +952 -0
- package/skills/skills-codex/paper-write/templates/acl.sty +312 -0
- package/skills/skills-codex/paper-write/templates/acl2026.tex +377 -0
- package/skills/skills-codex/paper-write/templates/acl_natbib.bst +1940 -0
- package/skills/skills-codex/paper-write/templates/acm.bst +3081 -0
- package/skills/skills-codex/paper-write/templates/acm_mm2026.tex +204 -0
- package/skills/skills-codex/paper-write/templates/acmart.cls +3520 -0
- package/skills/skills-codex/paper-write/templates/cvpr.bst +1448 -0
- package/skills/skills-codex/paper-write/templates/cvpr.sty +508 -0
- package/skills/skills-codex/paper-write/templates/cvpr2026.tex +63 -0
- package/skills/skills-codex/paper-write/templates/iclr2026.tex +84 -0
- package/skills/skills-codex/paper-write/templates/iclr2026_conference.bst +1440 -0
- package/skills/skills-codex/paper-write/templates/iclr2026_conference.sty +246 -0
- package/skills/skills-codex/paper-write/templates/icml2026.sty +767 -0
- package/skills/skills-codex/paper-write/templates/icml2026.tex +662 -0
- package/skills/skills-codex/paper-write/templates/ieee_conference.tex +89 -0
- package/skills/skills-codex/paper-write/templates/ieee_journal.tex +93 -0
- package/skills/skills-codex/paper-write/templates/math_commands.tex +48 -0
- package/skills/skills-codex/paper-write/templates/neurips2026.tex +493 -0
- package/skills/skills-codex/paper-write/templates/neurips_2026.sty +437 -0
- package/skills/skills-codex/paper-writing/SKILL.md +731 -0
- package/skills/skills-codex/patent-novelty-check/SKILL.md +153 -0
- package/skills/skills-codex/patent-pipeline/SKILL.md +344 -0
- package/skills/skills-codex/patent-review/SKILL.md +202 -0
- package/skills/skills-codex/pixel-art/SKILL.md +139 -0
- package/skills/skills-codex/prior-art-search/SKILL.md +146 -0
- package/skills/skills-codex/proof-checker/SKILL.md +554 -0
- package/skills/skills-codex/proof-orchestrator/SKILL.md +260 -0
- package/skills/skills-codex/proof-orchestrator/references/audit-output-contract.md +126 -0
- package/skills/skills-codex/proof-orchestrator/references/deepseek-routing.md +76 -0
- package/skills/skills-codex/proof-orchestrator/references/dispatch-prompts.md +227 -0
- package/skills/skills-codex/proof-orchestrator/references/notation-audit.md +135 -0
- package/skills/skills-codex/proof-orchestrator/references/proof-audit-rubric.md +70 -0
- package/skills/skills-codex/proof-orchestrator/references/stress-tests.md +38 -0
- package/skills/skills-codex/proof-writer/SKILL.md +222 -0
- package/skills/skills-codex/qzcli/SKILL.md +324 -0
- package/skills/skills-codex/rebuttal/SKILL.md +305 -0
- package/skills/skills-codex/render-html/SKILL.md +305 -0
- package/skills/skills-codex/render-html/scripts/__pycache__/render_html.cpython-314.pyc +0 -0
- package/skills/skills-codex/render-html/scripts/render_html.py +909 -0
- package/skills/skills-codex/render-html/scripts/templates/academic.html +342 -0
- package/skills/skills-codex/render-html/scripts/templates/dashboard.html +333 -0
- package/skills/skills-codex/research-lit/SKILL.md +464 -0
- package/skills/skills-codex/research-pipeline/SKILL.md +340 -0
- package/skills/skills-codex/research-refine/SKILL.md +721 -0
- package/skills/skills-codex/research-refine-pipeline/SKILL.md +186 -0
- package/skills/skills-codex/research-review/SKILL.md +135 -0
- package/skills/skills-codex/research-wiki/SKILL.md +421 -0
- package/skills/skills-codex/resubmit-pipeline/SKILL.md +444 -0
- package/skills/skills-codex/result-to-claim/SKILL.md +246 -0
- package/skills/skills-codex/run-experiment/SKILL.md +236 -0
- package/skills/skills-codex/semantic-scholar/SKILL.md +219 -0
- package/skills/skills-codex/serverless-modal/SKILL.md +335 -0
- package/skills/skills-codex/shared-references/acceptance-gate.md +336 -0
- package/skills/skills-codex/shared-references/assurance-contract.md +139 -0
- package/skills/skills-codex/shared-references/capture-antipatterns.md +84 -0
- package/skills/skills-codex/shared-references/citation-discipline.md +452 -0
- package/skills/skills-codex/shared-references/compute-env-contract.md +163 -0
- package/skills/skills-codex/shared-references/effort-contract.md +143 -0
- package/skills/skills-codex/shared-references/evidence-precheck.md +73 -0
- package/skills/skills-codex/shared-references/experiment-integrity.md +49 -0
- package/skills/skills-codex/shared-references/external-cadence.md +334 -0
- package/skills/skills-codex/shared-references/fan-out-pattern.md +375 -0
- package/skills/skills-codex/shared-references/injection-hygiene.md +132 -0
- package/skills/skills-codex/shared-references/integration-contract.md +372 -0
- package/skills/skills-codex/shared-references/output-composition.md +98 -0
- package/skills/skills-codex/shared-references/output-language.md +45 -0
- package/skills/skills-codex/shared-references/output-manifest.md +40 -0
- package/skills/skills-codex/shared-references/output-versioning.md +111 -0
- package/skills/skills-codex/shared-references/patent-format-cn.md +199 -0
- package/skills/skills-codex/shared-references/patent-format-ep.md +173 -0
- package/skills/skills-codex/shared-references/patent-format-us.md +161 -0
- package/skills/skills-codex/shared-references/patent-writing-principles.md +197 -0
- package/skills/skills-codex/shared-references/prior-art-databases.md +141 -0
- package/skills/skills-codex/shared-references/resumable-runs.md +125 -0
- package/skills/skills-codex/shared-references/review-scope-limits.md +81 -0
- package/skills/skills-codex/shared-references/review-tracing.md +144 -0
- package/skills/skills-codex/shared-references/reviewer-independence.md +66 -0
- package/skills/skills-codex/shared-references/reviewer-routing.md +128 -0
- package/skills/skills-codex/shared-references/skill-governance.md +119 -0
- package/skills/skills-codex/shared-references/taste-calibration.md +90 -0
- package/skills/skills-codex/shared-references/venue-checklists.md +73 -0
- package/skills/skills-codex/shared-references/wiki-helper-resolution.md +69 -0
- package/skills/skills-codex/shared-references/writing-principles.md +525 -0
- package/skills/skills-codex/slides-polish/SKILL.md +563 -0
- package/skills/skills-codex/specification-writing/SKILL.md +211 -0
- package/skills/skills-codex/system-profile/SKILL.md +103 -0
- package/skills/skills-codex/training-check/SKILL.md +83 -0
- package/skills/skills-codex/vast-gpu/SKILL.md +394 -0
- package/skills/skills-codex/web-debug-search/SKILL.md +334 -0
- package/skills/skills-codex/wiki-enrich/SKILL.md +255 -0
- package/skills/skills-codex/writing-systems-papers/SKILL.md +184 -0
- package/skills/skills-codex-claude-review/README.md +79 -0
- package/skills/skills-codex-claude-review/README_CN.md +78 -0
- package/skills/skills-codex-claude-review/auto-paper-improvement-loop/SKILL.md +581 -0
- package/skills/skills-codex-claude-review/auto-review-loop/SKILL.md +510 -0
- package/skills/skills-codex-claude-review/novelty-check/SKILL.md +102 -0
- package/skills/skills-codex-claude-review/paper-figure/SKILL.md +319 -0
- package/skills/skills-codex-claude-review/paper-plan/SKILL.md +287 -0
- package/skills/skills-codex-claude-review/paper-write/SKILL.md +420 -0
- package/skills/skills-codex-claude-review/research-refine/SKILL.md +732 -0
- package/skills/skills-codex-claude-review/research-review/SKILL.md +149 -0
- package/skills/skills-codex-gemini-review/README.md +176 -0
- package/skills/skills-codex-gemini-review/README_CN.md +175 -0
- package/skills/skills-codex-gemini-review/auto-paper-improvement-loop/SKILL.md +331 -0
- package/skills/skills-codex-gemini-review/auto-review-loop/SKILL.md +304 -0
- package/skills/skills-codex-gemini-review/grant-proposal/SKILL.md +630 -0
- package/skills/skills-codex-gemini-review/idea-creator/SKILL.md +263 -0
- package/skills/skills-codex-gemini-review/idea-discovery/SKILL.md +275 -0
- package/skills/skills-codex-gemini-review/idea-discovery-robot/SKILL.md +365 -0
- package/skills/skills-codex-gemini-review/novelty-check/SKILL.md +92 -0
- package/skills/skills-codex-gemini-review/paper-figure/SKILL.md +289 -0
- package/skills/skills-codex-gemini-review/paper-plan/SKILL.md +265 -0
- package/skills/skills-codex-gemini-review/paper-poster-html/SKILL.md +102 -0
- package/skills/skills-codex-gemini-review/paper-slides/SKILL.md +582 -0
- package/skills/skills-codex-gemini-review/paper-write/SKILL.md +346 -0
- package/skills/skills-codex-gemini-review/paper-writing/SKILL.md +312 -0
- package/skills/skills-codex-gemini-review/research-refine/SKILL.md +674 -0
- package/skills/skills-codex-gemini-review/research-review/SKILL.md +112 -0
- package/skills/slides-polish/SKILL.md +565 -0
- package/skills/specification-writing/SKILL.md +211 -0
- package/skills/system-profile/SKILL.md +103 -0
- package/skills/training-check/SKILL.md +132 -0
- package/skills/vast-gpu/SKILL.md +394 -0
- package/skills/web-debug-search/SKILL.md +334 -0
- package/skills/wiki-enrich/SKILL.md +257 -0
- package/skills/writing-systems-papers/SKILL.md +184 -0
- package/templates/CLAUDE_MD_TEMPLATE.md +29 -0
- package/templates/EXPERIMENT_LOG_TEMPLATE.md +47 -0
- package/templates/EXPERIMENT_PLAN_TEMPLATE.md +51 -0
- package/templates/EXPERIMENT_PLAN_TEMPLATE_CN.md +53 -0
- package/templates/FINDINGS_TEMPLATE.md +52 -0
- package/templates/IDEA_CANDIDATES_TEMPLATE.md +47 -0
- package/templates/IDEA_CANDIDATES_TEMPLATE_CN.md +47 -0
- package/templates/INVENTION_BRIEF_TEMPLATE.md +87 -0
- package/templates/MANIFEST_TEMPLATE.md +7 -0
- package/templates/NARRATIVE_REPORT_TEMPLATE.md +49 -0
- package/templates/PAPER_PLAN_TEMPLATE.md +47 -0
- package/templates/PATENT_CLAIMS_TEMPLATE.md +78 -0
- package/templates/PATENT_SPECIFICATION_TEMPLATE.md +68 -0
- package/templates/README.md +57 -0
- package/templates/RESEARCH_BRIEF_TEMPLATE.md +35 -0
- package/templates/RESEARCH_BRIEF_TEMPLATE_CN.md +41 -0
- package/templates/RESEARCH_CONTRACT_TEMPLATE.md +60 -0
- package/templates/claude-hooks/corpus_write_guard.json +16 -0
- package/templates/claude-hooks/corpus_write_guard.py +85 -0
- package/templates/claude-hooks/meta_logging.json +74 -0
- package/templates/gitignore-trace.txt +3 -0
- package/tools/__pycache__/check_skills_inventory.cpython-314.pyc +0 -0
- package/tools/arxiv_fetch.py +311 -0
- package/tools/capture_filter.py +126 -0
- package/tools/check_skills_inventory.py +273 -0
- package/tools/convert_skills_to_llm_chat.py +282 -0
- package/tools/copilot_native_evidence.py +818 -0
- package/tools/deepxiv_fetch.py +213 -0
- package/tools/evidence_check.py +212 -0
- package/tools/exa_search.py +425 -0
- package/tools/experiment_queue/README.md +118 -0
- package/tools/experiment_queue/build_manifest.py +44 -0
- package/tools/experiment_queue/queue_manager.py +44 -0
- package/tools/extract_paper_style.py +560 -0
- package/tools/figure_renderer.py +69 -0
- package/tools/forensics_gate.py +669 -0
- package/tools/generate_codex_claude_review_overrides.py +299 -0
- package/tools/idea_discovery_gate.py +256 -0
- package/tools/install_aris.ps1 +1372 -0
- package/tools/install_aris.sh +1370 -0
- package/tools/install_aris_codex.sh +1023 -0
- package/tools/install_aris_copilot.sh +1052 -0
- package/tools/iteration_log.py +143 -0
- package/tools/lint_skills_helpers.sh +84 -0
- package/tools/meta_opt/check_ready.sh +80 -0
- package/tools/meta_opt/log_event.sh +91 -0
- package/tools/meta_opt/trigger_eval.py +280 -0
- package/tools/meta_opt/trigger_evals.sample.json +28 -0
- package/tools/openalex_fetch.py +326 -0
- package/tools/overleaf_audit.sh +104 -0
- package/tools/overleaf_setup.sh +150 -0
- package/tools/paper_illustration_image2.py +62 -0
- package/tools/provenance.py +294 -0
- package/tools/research_wiki.py +1720 -0
- package/tools/review_gate.py +502 -0
- package/tools/run_state.py +399 -0
- package/tools/save_trace.sh +477 -0
- package/tools/semantic_scholar_fetch.py +438 -0
- package/tools/skill-groups.tsv +116 -0
- package/tools/skill_picker.py +238 -0
- package/tools/smart_update.ps1 +521 -0
- package/tools/smart_update.sh +591 -0
- package/tools/smart_update_codex.sh +419 -0
- package/tools/smart_update_copilot.sh +605 -0
- package/tools/threat_scan.py +222 -0
- package/tools/verify_paper_audits.sh +487 -0
- package/tools/verify_papers.py +613 -0
- package/tools/verify_wiki_coverage.sh +176 -0
- 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()
|