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,718 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Claude review MCP server for Codex-first ARIS workflows.
|
|
3
|
+
|
|
4
|
+
This server exposes a narrow review-only interface over Claude Code CLI so
|
|
5
|
+
Codex can remain the executor while Claude acts as the external reviewer.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
import shutil
|
|
13
|
+
import subprocess
|
|
14
|
+
import sys
|
|
15
|
+
import time
|
|
16
|
+
import traceback
|
|
17
|
+
import uuid
|
|
18
|
+
from datetime import datetime, timezone
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _configure_stdio_for_mcp() -> None:
|
|
24
|
+
"""Re-bind sys.stdout/sys.stdin as raw binary streams for the MCP protocol.
|
|
25
|
+
|
|
26
|
+
Called from main() rather than module import so that unit tests can
|
|
27
|
+
safely `importlib.exec_module(server.py)` without globally clobbering
|
|
28
|
+
the test process's text-mode stdio (which breaks subsequent print()
|
|
29
|
+
calls with TypeError).
|
|
30
|
+
"""
|
|
31
|
+
sys.stdout = os.fdopen(sys.stdout.fileno(), "wb", buffering=0)
|
|
32
|
+
sys.stdin = os.fdopen(sys.stdin.fileno(), "rb", buffering=0)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
SERVER_NAME = os.environ.get("CLAUDE_REVIEW_SERVER_NAME", "claude-review")
|
|
36
|
+
CLAUDE_BIN = os.environ.get("CLAUDE_BIN", "claude")
|
|
37
|
+
DEFAULT_MODEL = os.environ.get("CLAUDE_REVIEW_MODEL", "")
|
|
38
|
+
DEFAULT_SYSTEM = os.environ.get("CLAUDE_REVIEW_SYSTEM", "")
|
|
39
|
+
DEFAULT_TOOLS = os.environ.get("CLAUDE_REVIEW_TOOLS", "")
|
|
40
|
+
DEFAULT_TIMEOUT_SEC = int(os.environ.get("CLAUDE_REVIEW_TIMEOUT_SEC", "600"))
|
|
41
|
+
_default_debug_dir = os.environ.get("TEMP", "/tmp") if sys.platform == "win32" else "/tmp"
|
|
42
|
+
DEBUG_LOG = Path(os.environ.get("CLAUDE_REVIEW_DEBUG_LOG", f"{_default_debug_dir}/{SERVER_NAME}-mcp-debug.log"))
|
|
43
|
+
STATE_DIR = Path(
|
|
44
|
+
os.environ.get(
|
|
45
|
+
"CLAUDE_REVIEW_STATE_DIR",
|
|
46
|
+
str(Path.home() / ".codex" / "state" / SERVER_NAME),
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
JOBS_DIR = STATE_DIR / "jobs"
|
|
50
|
+
|
|
51
|
+
_use_ndjson = False
|
|
52
|
+
TERMINAL_JOB_STATES = {"completed", "failed"}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def debug_log(message: str) -> None:
|
|
56
|
+
try:
|
|
57
|
+
DEBUG_LOG.parent.mkdir(parents=True, exist_ok=True)
|
|
58
|
+
with DEBUG_LOG.open("a", encoding="utf-8") as fh:
|
|
59
|
+
fh.write(f"{message}\n")
|
|
60
|
+
except OSError:
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def send_response(response: dict[str, Any]) -> None:
|
|
65
|
+
global _use_ndjson
|
|
66
|
+
|
|
67
|
+
payload = json.dumps(response, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
|
|
68
|
+
debug_log(f"SEND {payload.decode('utf-8', errors='replace')}")
|
|
69
|
+
if _use_ndjson:
|
|
70
|
+
sys.stdout.write(payload + b"\n")
|
|
71
|
+
else:
|
|
72
|
+
header = f"Content-Length: {len(payload)}\r\n\r\n".encode("utf-8")
|
|
73
|
+
sys.stdout.write(header + payload)
|
|
74
|
+
sys.stdout.flush()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def read_message() -> dict[str, Any] | None:
|
|
78
|
+
global _use_ndjson
|
|
79
|
+
|
|
80
|
+
line = sys.stdin.readline()
|
|
81
|
+
if not line:
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
line_text = line.decode("utf-8").rstrip("\r\n")
|
|
85
|
+
if line_text.lower().startswith("content-length:"):
|
|
86
|
+
try:
|
|
87
|
+
content_length = int(line_text.split(":", 1)[1].strip())
|
|
88
|
+
except ValueError:
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
while True:
|
|
92
|
+
header_line = sys.stdin.readline()
|
|
93
|
+
if not header_line:
|
|
94
|
+
return None
|
|
95
|
+
if header_line in {b"\r\n", b"\n"}:
|
|
96
|
+
break
|
|
97
|
+
|
|
98
|
+
body = sys.stdin.read(content_length)
|
|
99
|
+
try:
|
|
100
|
+
return json.loads(body.decode("utf-8"))
|
|
101
|
+
except json.JSONDecodeError:
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
if line_text.startswith("{") or line_text.startswith("["):
|
|
105
|
+
_use_ndjson = True
|
|
106
|
+
try:
|
|
107
|
+
return json.loads(line_text)
|
|
108
|
+
except json.JSONDecodeError:
|
|
109
|
+
return None
|
|
110
|
+
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def find_claude_bin() -> str | None:
|
|
115
|
+
if Path(CLAUDE_BIN).is_file():
|
|
116
|
+
return CLAUDE_BIN
|
|
117
|
+
return shutil.which(CLAUDE_BIN)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def parse_claude_json(raw_stdout: str) -> tuple[dict[str, Any] | None, str | None]:
|
|
121
|
+
stripped = raw_stdout.strip()
|
|
122
|
+
if not stripped:
|
|
123
|
+
return None, "Claude CLI returned empty output"
|
|
124
|
+
|
|
125
|
+
# CLI 2.x: try whole stdout as a single JSON value first.
|
|
126
|
+
# Handles both compact one-line arrays and pretty-printed multi-line arrays.
|
|
127
|
+
try:
|
|
128
|
+
payload = json.loads(stripped)
|
|
129
|
+
except json.JSONDecodeError:
|
|
130
|
+
payload = None
|
|
131
|
+
|
|
132
|
+
if isinstance(payload, dict):
|
|
133
|
+
return payload, None
|
|
134
|
+
if isinstance(payload, list):
|
|
135
|
+
# claude CLI 2.x emits a JSON array of event objects under --output-format json
|
|
136
|
+
# (system init -> assistant -> rate_limit_event -> result). Only the terminal
|
|
137
|
+
# "result" event carries the fields run_claude_review consumes downstream
|
|
138
|
+
# (result text, session_id, duration_ms, stop_reason). Falling back to any
|
|
139
|
+
# other dict (e.g. rate_limit_event) would surface as a "successful parse"
|
|
140
|
+
# producing an empty review — strictly worse than a clear error.
|
|
141
|
+
for item in reversed(payload):
|
|
142
|
+
if isinstance(item, dict) and item.get("type") == "result":
|
|
143
|
+
return item, None
|
|
144
|
+
return None, "Claude CLI returned a JSON array without a 'result' event"
|
|
145
|
+
|
|
146
|
+
# Legacy CLI 1.x: NDJSON stream of dicts, walk lines in reverse for the
|
|
147
|
+
# last useful payload. Same array-vs-dict policy as above so a CLI 2.x
|
|
148
|
+
# JSON-array line surrounded by non-JSON noise (wrapper warnings, nvm/asdf
|
|
149
|
+
# banners, future CLI debug prints) still surfaces the result event
|
|
150
|
+
# instead of being silently dropped.
|
|
151
|
+
saw_array_without_result = False
|
|
152
|
+
for candidate in reversed(stripped.splitlines()):
|
|
153
|
+
candidate = candidate.strip()
|
|
154
|
+
if not candidate:
|
|
155
|
+
continue
|
|
156
|
+
try:
|
|
157
|
+
line_payload = json.loads(candidate)
|
|
158
|
+
except json.JSONDecodeError:
|
|
159
|
+
continue
|
|
160
|
+
if isinstance(line_payload, dict):
|
|
161
|
+
if saw_array_without_result and line_payload.get("type") != "result":
|
|
162
|
+
continue
|
|
163
|
+
return line_payload, None
|
|
164
|
+
if isinstance(line_payload, list):
|
|
165
|
+
for item in reversed(line_payload):
|
|
166
|
+
if isinstance(item, dict) and item.get("type") == "result":
|
|
167
|
+
return item, None
|
|
168
|
+
# fall through: this line was an array without a result event,
|
|
169
|
+
# but earlier lines might still carry one — keep scanning.
|
|
170
|
+
saw_array_without_result = True
|
|
171
|
+
|
|
172
|
+
if saw_array_without_result:
|
|
173
|
+
return None, "Claude CLI returned a JSON array without a 'result' event"
|
|
174
|
+
return None, "Claude CLI did not return JSON output"
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def utc_now() -> str:
|
|
178
|
+
return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def write_json(path: Path, payload: dict[str, Any]) -> None:
|
|
182
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
183
|
+
temp_path = path.with_suffix(path.suffix + ".tmp")
|
|
184
|
+
temp_path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
185
|
+
temp_path.replace(path)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def read_json(path: Path) -> dict[str, Any]:
|
|
189
|
+
return json.loads(path.read_text(encoding="utf-8"))
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def job_state_path(job_id: str) -> Path:
|
|
193
|
+
return JOBS_DIR / f"{job_id}.json"
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def is_pid_alive(pid: int | None) -> bool:
|
|
197
|
+
if not pid or pid <= 0:
|
|
198
|
+
return False
|
|
199
|
+
if sys.platform == "win32":
|
|
200
|
+
import ctypes
|
|
201
|
+
kernel32 = ctypes.windll.kernel32
|
|
202
|
+
SYNCHRONIZE = 0x00100000
|
|
203
|
+
handle = kernel32.OpenProcess(SYNCHRONIZE, False, pid)
|
|
204
|
+
if handle:
|
|
205
|
+
kernel32.CloseHandle(handle)
|
|
206
|
+
return True
|
|
207
|
+
return False
|
|
208
|
+
try:
|
|
209
|
+
os.kill(pid, 0)
|
|
210
|
+
except OSError:
|
|
211
|
+
return False
|
|
212
|
+
return True
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def serialize_job(job: dict[str, Any]) -> dict[str, Any]:
|
|
216
|
+
result = job.get("result") or {}
|
|
217
|
+
return {
|
|
218
|
+
"jobId": job.get("jobId"),
|
|
219
|
+
"status": job.get("status"),
|
|
220
|
+
"done": job.get("status") in TERMINAL_JOB_STATES,
|
|
221
|
+
"threadId": result.get("threadId"),
|
|
222
|
+
"response": result.get("response"),
|
|
223
|
+
"model": result.get("model"),
|
|
224
|
+
"duration_ms": result.get("duration_ms"),
|
|
225
|
+
"stop_reason": result.get("stop_reason"),
|
|
226
|
+
"error": job.get("error"),
|
|
227
|
+
"createdAt": job.get("createdAt"),
|
|
228
|
+
"startedAt": job.get("startedAt"),
|
|
229
|
+
"completedAt": job.get("completedAt"),
|
|
230
|
+
"updatedAt": job.get("updatedAt"),
|
|
231
|
+
"resumeHint": "Call review_status with this jobId until done=true.",
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def build_command(
|
|
236
|
+
prompt: str,
|
|
237
|
+
*,
|
|
238
|
+
session_id: str | None = None,
|
|
239
|
+
model: str | None = None,
|
|
240
|
+
system: str | None = None,
|
|
241
|
+
tools: str | None = None,
|
|
242
|
+
) -> list[str]:
|
|
243
|
+
bin_path = find_claude_bin()
|
|
244
|
+
if not bin_path:
|
|
245
|
+
raise FileNotFoundError(f"Claude CLI not found: {CLAUDE_BIN}")
|
|
246
|
+
|
|
247
|
+
cmd = [bin_path, "-p", prompt, "--output-format", "json", "--permission-mode", "plan"]
|
|
248
|
+
|
|
249
|
+
if session_id:
|
|
250
|
+
cmd.extend(["--resume", session_id])
|
|
251
|
+
|
|
252
|
+
selected_model = model or DEFAULT_MODEL
|
|
253
|
+
if selected_model:
|
|
254
|
+
cmd.extend(["--model", selected_model])
|
|
255
|
+
|
|
256
|
+
selected_system = system or DEFAULT_SYSTEM
|
|
257
|
+
if selected_system:
|
|
258
|
+
cmd.extend(["--system-prompt", selected_system])
|
|
259
|
+
|
|
260
|
+
selected_tools = DEFAULT_TOOLS if tools is None else tools
|
|
261
|
+
cmd.extend(["--tools", selected_tools])
|
|
262
|
+
return cmd
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def run_claude_review(
|
|
266
|
+
prompt: str,
|
|
267
|
+
*,
|
|
268
|
+
session_id: str | None = None,
|
|
269
|
+
model: str | None = None,
|
|
270
|
+
system: str | None = None,
|
|
271
|
+
tools: str | None = None,
|
|
272
|
+
) -> tuple[dict[str, Any] | None, str | None]:
|
|
273
|
+
try:
|
|
274
|
+
cmd = build_command(
|
|
275
|
+
prompt,
|
|
276
|
+
session_id=session_id,
|
|
277
|
+
model=model,
|
|
278
|
+
system=system,
|
|
279
|
+
tools=tools,
|
|
280
|
+
)
|
|
281
|
+
except FileNotFoundError as exc:
|
|
282
|
+
return None, str(exc)
|
|
283
|
+
|
|
284
|
+
debug_log(f"RUN {' '.join(cmd)}")
|
|
285
|
+
try:
|
|
286
|
+
result = subprocess.run(
|
|
287
|
+
cmd,
|
|
288
|
+
capture_output=True,
|
|
289
|
+
text=True,
|
|
290
|
+
encoding='utf-8',
|
|
291
|
+
errors='replace',
|
|
292
|
+
stdin=subprocess.DEVNULL,
|
|
293
|
+
timeout=DEFAULT_TIMEOUT_SEC,
|
|
294
|
+
check=False,
|
|
295
|
+
)
|
|
296
|
+
except subprocess.TimeoutExpired:
|
|
297
|
+
return None, f"Claude review timed out after {DEFAULT_TIMEOUT_SEC} seconds"
|
|
298
|
+
|
|
299
|
+
payload, parse_error = parse_claude_json(result.stdout or "")
|
|
300
|
+
if parse_error:
|
|
301
|
+
stderr = (result.stderr or "").strip()
|
|
302
|
+
message = parse_error if not stderr else f"{parse_error}. stderr: {stderr}"
|
|
303
|
+
return None, message
|
|
304
|
+
|
|
305
|
+
if payload is None:
|
|
306
|
+
return None, "Failed to parse Claude CLI output"
|
|
307
|
+
if result.returncode != 0 or payload.get("is_error"):
|
|
308
|
+
# Claude CLI 2.x emits structured error events without `result` / `error`
|
|
309
|
+
# fields — the diagnostic text lives in an `errors` list (e.g.
|
|
310
|
+
# subtype="error_max_budget_usd", errors=["Reached maximum budget ..."]).
|
|
311
|
+
# Surface it explicitly; otherwise the user sees only the generic
|
|
312
|
+
# "Claude review failed" and loses the actionable message.
|
|
313
|
+
errors_list = payload.get("errors")
|
|
314
|
+
if isinstance(errors_list, list) and errors_list:
|
|
315
|
+
errors_text = "; ".join(str(e) for e in errors_list)
|
|
316
|
+
elif isinstance(errors_list, str):
|
|
317
|
+
errors_text = errors_list
|
|
318
|
+
else:
|
|
319
|
+
errors_text = ""
|
|
320
|
+
message = str(
|
|
321
|
+
payload.get("result")
|
|
322
|
+
or payload.get("error")
|
|
323
|
+
or errors_text
|
|
324
|
+
or (result.stderr or "").strip()
|
|
325
|
+
or "Claude review failed"
|
|
326
|
+
)
|
|
327
|
+
return None, message
|
|
328
|
+
|
|
329
|
+
thread_id = payload.get("session_id")
|
|
330
|
+
response_text = str(payload.get("result", "")).strip()
|
|
331
|
+
model_name = payload.get("model", "") or model or DEFAULT_MODEL
|
|
332
|
+
return {
|
|
333
|
+
"threadId": thread_id,
|
|
334
|
+
"response": response_text,
|
|
335
|
+
"model": model_name,
|
|
336
|
+
"duration_ms": payload.get("duration_ms"),
|
|
337
|
+
"stop_reason": payload.get("stop_reason"),
|
|
338
|
+
}, None
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def start_async_review(
|
|
342
|
+
prompt: str,
|
|
343
|
+
*,
|
|
344
|
+
session_id: str | None = None,
|
|
345
|
+
model: str | None = None,
|
|
346
|
+
system: str | None = None,
|
|
347
|
+
tools: str | None = None,
|
|
348
|
+
) -> tuple[dict[str, Any] | None, str | None]:
|
|
349
|
+
job_id = uuid.uuid4().hex
|
|
350
|
+
created_at = utc_now()
|
|
351
|
+
job = {
|
|
352
|
+
"jobId": job_id,
|
|
353
|
+
"status": "queued",
|
|
354
|
+
"createdAt": created_at,
|
|
355
|
+
"startedAt": None,
|
|
356
|
+
"completedAt": None,
|
|
357
|
+
"updatedAt": created_at,
|
|
358
|
+
"error": None,
|
|
359
|
+
"result": None,
|
|
360
|
+
"workerPid": None,
|
|
361
|
+
"request": {
|
|
362
|
+
"prompt": prompt,
|
|
363
|
+
"threadId": session_id,
|
|
364
|
+
"model": model,
|
|
365
|
+
"system": system,
|
|
366
|
+
"tools": tools,
|
|
367
|
+
},
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
job_path = job_state_path(job_id)
|
|
371
|
+
write_json(job_path, job)
|
|
372
|
+
|
|
373
|
+
worker_out_path = JOBS_DIR / f"{job_id}.worker.out.log"
|
|
374
|
+
worker_err_path = JOBS_DIR / f"{job_id}.worker.err.log"
|
|
375
|
+
stdout_fh = None
|
|
376
|
+
stderr_fh = None
|
|
377
|
+
try:
|
|
378
|
+
stdout_fh = open(worker_out_path, "w")
|
|
379
|
+
stderr_fh = open(worker_err_path, "w")
|
|
380
|
+
popen_kwargs: dict[str, Any] = {
|
|
381
|
+
"stdin": subprocess.DEVNULL,
|
|
382
|
+
"stdout": stdout_fh,
|
|
383
|
+
"stderr": stderr_fh,
|
|
384
|
+
"cwd": os.getcwd(),
|
|
385
|
+
}
|
|
386
|
+
if sys.platform == "win32":
|
|
387
|
+
popen_kwargs["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP
|
|
388
|
+
else:
|
|
389
|
+
popen_kwargs["close_fds"] = True
|
|
390
|
+
popen_kwargs["start_new_session"] = True
|
|
391
|
+
|
|
392
|
+
worker = subprocess.Popen(
|
|
393
|
+
[sys.executable, str(Path(__file__).resolve()), "--run-job", job_id],
|
|
394
|
+
**popen_kwargs,
|
|
395
|
+
)
|
|
396
|
+
except OSError as exc:
|
|
397
|
+
job["status"] = "failed"
|
|
398
|
+
job["completedAt"] = utc_now()
|
|
399
|
+
job["updatedAt"] = job["completedAt"]
|
|
400
|
+
job["error"] = f"Failed to launch background review worker: {exc}"
|
|
401
|
+
write_json(job_path, job)
|
|
402
|
+
return None, job["error"]
|
|
403
|
+
finally:
|
|
404
|
+
if stdout_fh:
|
|
405
|
+
stdout_fh.close()
|
|
406
|
+
if stderr_fh:
|
|
407
|
+
stderr_fh.close()
|
|
408
|
+
|
|
409
|
+
job["workerPid"] = worker.pid
|
|
410
|
+
job["updatedAt"] = utc_now()
|
|
411
|
+
write_json(job_path, job)
|
|
412
|
+
debug_log(f"JOB_START job_id={job_id} worker_pid={worker.pid}")
|
|
413
|
+
return serialize_job(job), None
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def get_review_status(job_id: str, *, wait_seconds: int = 0) -> tuple[dict[str, Any] | None, str | None]:
|
|
417
|
+
job_path = job_state_path(job_id)
|
|
418
|
+
if not job_path.exists():
|
|
419
|
+
return None, f"Unknown jobId: {job_id}"
|
|
420
|
+
|
|
421
|
+
deadline = time.monotonic() + max(wait_seconds, 0)
|
|
422
|
+
while True:
|
|
423
|
+
job = read_json(job_path)
|
|
424
|
+
if job.get("status") in {"queued", "running"} and not is_pid_alive(job.get("workerPid")):
|
|
425
|
+
job["status"] = "failed"
|
|
426
|
+
job["error"] = "Background review worker exited before writing a final result"
|
|
427
|
+
job["completedAt"] = utc_now()
|
|
428
|
+
job["updatedAt"] = job["completedAt"]
|
|
429
|
+
write_json(job_path, job)
|
|
430
|
+
if job.get("status") in TERMINAL_JOB_STATES:
|
|
431
|
+
return serialize_job(job), None
|
|
432
|
+
if time.monotonic() >= deadline:
|
|
433
|
+
return serialize_job(job), None
|
|
434
|
+
time.sleep(min(0.5, max(deadline - time.monotonic(), 0.0)))
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
def run_async_job(job_id: str) -> int:
|
|
438
|
+
job_path = job_state_path(job_id)
|
|
439
|
+
if not job_path.exists():
|
|
440
|
+
debug_log(f"JOB_MISSING job_id={job_id}")
|
|
441
|
+
return 1
|
|
442
|
+
|
|
443
|
+
job = read_json(job_path)
|
|
444
|
+
job["status"] = "running"
|
|
445
|
+
job["startedAt"] = utc_now()
|
|
446
|
+
job["updatedAt"] = job["startedAt"]
|
|
447
|
+
job["workerPid"] = os.getpid()
|
|
448
|
+
write_json(job_path, job)
|
|
449
|
+
debug_log(f"JOB_RUNNING job_id={job_id} worker_pid={os.getpid()}")
|
|
450
|
+
|
|
451
|
+
request = job.get("request") or {}
|
|
452
|
+
try:
|
|
453
|
+
payload, error = run_claude_review(
|
|
454
|
+
str(request.get("prompt", "")),
|
|
455
|
+
session_id=request.get("threadId"),
|
|
456
|
+
model=request.get("model"),
|
|
457
|
+
system=request.get("system"),
|
|
458
|
+
tools=request.get("tools"),
|
|
459
|
+
)
|
|
460
|
+
except Exception as exc:
|
|
461
|
+
payload = None
|
|
462
|
+
error = f"Background review crashed: {exc}"
|
|
463
|
+
debug_log(traceback.format_exc())
|
|
464
|
+
|
|
465
|
+
finished_at = utc_now()
|
|
466
|
+
job = read_json(job_path)
|
|
467
|
+
job["updatedAt"] = finished_at
|
|
468
|
+
job["completedAt"] = finished_at
|
|
469
|
+
if error:
|
|
470
|
+
job["status"] = "failed"
|
|
471
|
+
job["error"] = error
|
|
472
|
+
job["result"] = None
|
|
473
|
+
debug_log(f"JOB_FAILED job_id={job_id} error={error}")
|
|
474
|
+
write_json(job_path, job)
|
|
475
|
+
return 1
|
|
476
|
+
|
|
477
|
+
job["status"] = "completed"
|
|
478
|
+
job["error"] = None
|
|
479
|
+
job["result"] = payload
|
|
480
|
+
debug_log(f"JOB_COMPLETED job_id={job_id} thread_id={(payload or {}).get('threadId')}")
|
|
481
|
+
write_json(job_path, job)
|
|
482
|
+
return 0
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def tool_success(request_id: Any, payload: dict[str, Any]) -> dict[str, Any]:
|
|
486
|
+
return {
|
|
487
|
+
"jsonrpc": "2.0",
|
|
488
|
+
"id": request_id,
|
|
489
|
+
"result": {
|
|
490
|
+
"content": [{"type": "text", "text": json.dumps(payload, ensure_ascii=False)}],
|
|
491
|
+
},
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def tool_error(request_id: Any, message: str) -> dict[str, Any]:
|
|
496
|
+
return {
|
|
497
|
+
"jsonrpc": "2.0",
|
|
498
|
+
"id": request_id,
|
|
499
|
+
"result": {
|
|
500
|
+
"content": [{"type": "text", "text": json.dumps({"error": message}, ensure_ascii=False)}],
|
|
501
|
+
"isError": True,
|
|
502
|
+
},
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
def handle_request(request: dict[str, Any]) -> dict[str, Any] | None:
|
|
507
|
+
request_id = request.get("id")
|
|
508
|
+
method = request.get("method", "")
|
|
509
|
+
params = request.get("params", {})
|
|
510
|
+
debug_log(f"REQUEST id={request_id!r} method={method} params={json.dumps(params, ensure_ascii=False)}")
|
|
511
|
+
|
|
512
|
+
if request_id is None:
|
|
513
|
+
if method in {"notifications/initialized", "initialized"}:
|
|
514
|
+
return None
|
|
515
|
+
return None
|
|
516
|
+
|
|
517
|
+
if method == "initialize":
|
|
518
|
+
return {
|
|
519
|
+
"jsonrpc": "2.0",
|
|
520
|
+
"id": request_id,
|
|
521
|
+
"result": {
|
|
522
|
+
"protocolVersion": "2024-11-05",
|
|
523
|
+
"capabilities": {"tools": {}},
|
|
524
|
+
"serverInfo": {"name": SERVER_NAME, "version": "1.0.0"},
|
|
525
|
+
},
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if method == "ping":
|
|
529
|
+
return {"jsonrpc": "2.0", "id": request_id, "result": {}}
|
|
530
|
+
|
|
531
|
+
if method == "resources/list":
|
|
532
|
+
return {"jsonrpc": "2.0", "id": request_id, "result": {"resources": []}}
|
|
533
|
+
|
|
534
|
+
if method == "resources/templates/list":
|
|
535
|
+
return {"jsonrpc": "2.0", "id": request_id, "result": {"resourceTemplates": []}}
|
|
536
|
+
|
|
537
|
+
if method in {"notifications/initialized", "initialized"}:
|
|
538
|
+
return {"jsonrpc": "2.0", "id": request_id, "result": {}}
|
|
539
|
+
|
|
540
|
+
if method == "tools/list":
|
|
541
|
+
return {
|
|
542
|
+
"jsonrpc": "2.0",
|
|
543
|
+
"id": request_id,
|
|
544
|
+
"result": {
|
|
545
|
+
"tools": [
|
|
546
|
+
{
|
|
547
|
+
"name": "review",
|
|
548
|
+
"description": "Run a fresh Claude Code review and return JSON containing threadId and response.",
|
|
549
|
+
"inputSchema": {
|
|
550
|
+
"type": "object",
|
|
551
|
+
"properties": {
|
|
552
|
+
"prompt": {"type": "string", "description": "Reviewer prompt"},
|
|
553
|
+
"system": {"type": "string", "description": "Optional system prompt"},
|
|
554
|
+
"model": {"type": "string", "description": "Optional Claude model override"},
|
|
555
|
+
"tools": {"type": "string", "description": "Optional Claude tools override, empty string disables tools"},
|
|
556
|
+
},
|
|
557
|
+
"required": ["prompt"],
|
|
558
|
+
},
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
"name": "review_reply",
|
|
562
|
+
"description": "Continue a previous Claude Code review session using threadId/session_id.",
|
|
563
|
+
"inputSchema": {
|
|
564
|
+
"type": "object",
|
|
565
|
+
"properties": {
|
|
566
|
+
"threadId": {"type": "string", "description": "Claude session id from a previous review call"},
|
|
567
|
+
"thread_id": {"type": "string", "description": "Alias of threadId"},
|
|
568
|
+
"prompt": {"type": "string", "description": "Follow-up reviewer prompt"},
|
|
569
|
+
"system": {"type": "string", "description": "Optional system prompt override"},
|
|
570
|
+
"model": {"type": "string", "description": "Optional Claude model override"},
|
|
571
|
+
"tools": {"type": "string", "description": "Optional Claude tools override, empty string disables tools"},
|
|
572
|
+
},
|
|
573
|
+
"required": ["prompt"],
|
|
574
|
+
},
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
"name": "review_start",
|
|
578
|
+
"description": "Start a background Claude Code review job and return a resumable jobId immediately.",
|
|
579
|
+
"inputSchema": {
|
|
580
|
+
"type": "object",
|
|
581
|
+
"properties": {
|
|
582
|
+
"prompt": {"type": "string", "description": "Reviewer prompt"},
|
|
583
|
+
"system": {"type": "string", "description": "Optional system prompt"},
|
|
584
|
+
"model": {"type": "string", "description": "Optional Claude model override"},
|
|
585
|
+
"tools": {"type": "string", "description": "Optional Claude tools override, empty string disables tools"},
|
|
586
|
+
},
|
|
587
|
+
"required": ["prompt"],
|
|
588
|
+
},
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
"name": "review_reply_start",
|
|
592
|
+
"description": "Start a background follow-up review job in an existing Claude thread and return a resumable jobId immediately.",
|
|
593
|
+
"inputSchema": {
|
|
594
|
+
"type": "object",
|
|
595
|
+
"properties": {
|
|
596
|
+
"threadId": {"type": "string", "description": "Claude session id from a previous review call"},
|
|
597
|
+
"thread_id": {"type": "string", "description": "Alias of threadId"},
|
|
598
|
+
"prompt": {"type": "string", "description": "Follow-up reviewer prompt"},
|
|
599
|
+
"system": {"type": "string", "description": "Optional system prompt override"},
|
|
600
|
+
"model": {"type": "string", "description": "Optional Claude model override"},
|
|
601
|
+
"tools": {"type": "string", "description": "Optional Claude tools override, empty string disables tools"},
|
|
602
|
+
},
|
|
603
|
+
"required": ["prompt"],
|
|
604
|
+
},
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
"name": "review_status",
|
|
608
|
+
"description": "Check whether a background review job has finished and fetch the final result when available.",
|
|
609
|
+
"inputSchema": {
|
|
610
|
+
"type": "object",
|
|
611
|
+
"properties": {
|
|
612
|
+
"jobId": {"type": "string", "description": "Background review job id"},
|
|
613
|
+
"job_id": {"type": "string", "description": "Alias of jobId"},
|
|
614
|
+
"waitSeconds": {"type": "integer", "description": "Optional bounded wait before returning status"},
|
|
615
|
+
},
|
|
616
|
+
"required": ["jobId"],
|
|
617
|
+
},
|
|
618
|
+
},
|
|
619
|
+
]
|
|
620
|
+
},
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
if method == "tools/call":
|
|
624
|
+
name = params.get("name", "")
|
|
625
|
+
args = params.get("arguments", {}) or {}
|
|
626
|
+
|
|
627
|
+
if name == "review":
|
|
628
|
+
payload, error = run_claude_review(
|
|
629
|
+
str(args.get("prompt", "")),
|
|
630
|
+
model=args.get("model"),
|
|
631
|
+
system=args.get("system"),
|
|
632
|
+
tools=args.get("tools"),
|
|
633
|
+
)
|
|
634
|
+
return tool_error(request_id, error) if error else tool_success(request_id, payload or {})
|
|
635
|
+
|
|
636
|
+
if name == "review_reply":
|
|
637
|
+
thread_id = args.get("threadId") or args.get("thread_id")
|
|
638
|
+
if not thread_id:
|
|
639
|
+
return tool_error(request_id, "threadId or thread_id is required")
|
|
640
|
+
payload, error = run_claude_review(
|
|
641
|
+
str(args.get("prompt", "")),
|
|
642
|
+
session_id=str(thread_id),
|
|
643
|
+
model=args.get("model"),
|
|
644
|
+
system=args.get("system"),
|
|
645
|
+
tools=args.get("tools"),
|
|
646
|
+
)
|
|
647
|
+
return tool_error(request_id, error) if error else tool_success(request_id, payload or {})
|
|
648
|
+
|
|
649
|
+
if name == "review_start":
|
|
650
|
+
payload, error = start_async_review(
|
|
651
|
+
str(args.get("prompt", "")),
|
|
652
|
+
model=args.get("model"),
|
|
653
|
+
system=args.get("system"),
|
|
654
|
+
tools=args.get("tools"),
|
|
655
|
+
)
|
|
656
|
+
return tool_error(request_id, error) if error else tool_success(request_id, payload or {})
|
|
657
|
+
|
|
658
|
+
if name == "review_reply_start":
|
|
659
|
+
thread_id = args.get("threadId") or args.get("thread_id")
|
|
660
|
+
if not thread_id:
|
|
661
|
+
return tool_error(request_id, "threadId or thread_id is required")
|
|
662
|
+
payload, error = start_async_review(
|
|
663
|
+
str(args.get("prompt", "")),
|
|
664
|
+
session_id=str(thread_id),
|
|
665
|
+
model=args.get("model"),
|
|
666
|
+
system=args.get("system"),
|
|
667
|
+
tools=args.get("tools"),
|
|
668
|
+
)
|
|
669
|
+
return tool_error(request_id, error) if error else tool_success(request_id, payload or {})
|
|
670
|
+
|
|
671
|
+
if name == "review_status":
|
|
672
|
+
job_id = args.get("jobId") or args.get("job_id")
|
|
673
|
+
if not job_id:
|
|
674
|
+
return tool_error(request_id, "jobId or job_id is required")
|
|
675
|
+
wait_seconds_raw = args.get("waitSeconds", 0)
|
|
676
|
+
try:
|
|
677
|
+
wait_seconds = int(wait_seconds_raw)
|
|
678
|
+
except (TypeError, ValueError):
|
|
679
|
+
return tool_error(request_id, "waitSeconds must be an integer")
|
|
680
|
+
payload, error = get_review_status(str(job_id), wait_seconds=max(wait_seconds, 0))
|
|
681
|
+
return tool_error(request_id, error) if error else tool_success(request_id, payload or {})
|
|
682
|
+
|
|
683
|
+
return {
|
|
684
|
+
"jsonrpc": "2.0",
|
|
685
|
+
"id": request_id,
|
|
686
|
+
"error": {"code": -32601, "message": f"Unknown tool: {name}"},
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
return {
|
|
690
|
+
"jsonrpc": "2.0",
|
|
691
|
+
"id": request_id,
|
|
692
|
+
"error": {"code": -32601, "message": f"Unknown method: {method}"},
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
def main() -> None:
|
|
697
|
+
_configure_stdio_for_mcp()
|
|
698
|
+
|
|
699
|
+
if len(sys.argv) == 3 and sys.argv[1] == "--run-job":
|
|
700
|
+
raise SystemExit(run_async_job(sys.argv[2]))
|
|
701
|
+
|
|
702
|
+
debug_log(f"=== {SERVER_NAME} starting ===")
|
|
703
|
+
while True:
|
|
704
|
+
try:
|
|
705
|
+
request = read_message()
|
|
706
|
+
if request is None:
|
|
707
|
+
debug_log("EOF")
|
|
708
|
+
break
|
|
709
|
+
response = handle_request(request)
|
|
710
|
+
if response is not None:
|
|
711
|
+
send_response(response)
|
|
712
|
+
except Exception:
|
|
713
|
+
debug_log(traceback.format_exc())
|
|
714
|
+
break
|
|
715
|
+
|
|
716
|
+
|
|
717
|
+
if __name__ == "__main__":
|
|
718
|
+
main()
|