dsh-aris-panel 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (442) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +98 -0
  3. package/README_CN.md +87 -0
  4. package/dsh/checkout.patch.yml +38 -0
  5. package/dsh/client.js +634 -0
  6. package/dsh/cordis.patch.yml +44 -0
  7. package/dsh/index.mjs +76 -0
  8. package/dsh/run-status.mjs +182 -0
  9. package/dsh/scope-limits.mjs +50 -0
  10. package/dsh/workbench.mjs +291 -0
  11. package/mcp-servers/claude-review/README.md +93 -0
  12. package/mcp-servers/claude-review/run_with_claude_aws.sh +49 -0
  13. package/mcp-servers/claude-review/server.py +718 -0
  14. package/mcp-servers/codex-image2/README.md +65 -0
  15. package/mcp-servers/codex-image2/server.py +893 -0
  16. package/mcp-servers/feishu-bridge/requirements.txt +1 -0
  17. package/mcp-servers/feishu-bridge/server.py +240 -0
  18. package/mcp-servers/gemini-review/README.md +171 -0
  19. package/mcp-servers/gemini-review/server.py +1856 -0
  20. package/mcp-servers/llm-chat/requirements.txt +1 -0
  21. package/mcp-servers/llm-chat/server.py +664 -0
  22. package/mcp-servers/manual-review/README.md +133 -0
  23. package/mcp-servers/manual-review/server.py +910 -0
  24. package/mcp-servers/manual-review/ui.html +279 -0
  25. package/mcp-servers/minimax-chat/requirements.txt +1 -0
  26. package/mcp-servers/minimax-chat/server.py +381 -0
  27. package/package.json +51 -0
  28. package/skills/ablation-planner/SKILL.md +123 -0
  29. package/skills/alphaxiv/SKILL.md +196 -0
  30. package/skills/analyze-results/SKILL.md +46 -0
  31. package/skills/arxiv/SKILL.md +248 -0
  32. package/skills/auto-paper-improvement-loop/SKILL.md +651 -0
  33. package/skills/auto-review-loop/SKILL.md +1137 -0
  34. package/skills/auto-review-loop-llm/SKILL.md +259 -0
  35. package/skills/auto-review-loop-minimax/SKILL.md +302 -0
  36. package/skills/citation-audit/SKILL.md +502 -0
  37. package/skills/claims-drafting/SKILL.md +227 -0
  38. package/skills/comm-lit-review/SKILL.md +297 -0
  39. package/skills/deepxiv/SKILL.md +263 -0
  40. package/skills/dse-loop/SKILL.md +296 -0
  41. package/skills/embodiment-description/SKILL.md +129 -0
  42. package/skills/exa-search/SKILL.md +205 -0
  43. package/skills/experiment-audit/SKILL.md +311 -0
  44. package/skills/experiment-bridge/SKILL.md +376 -0
  45. package/skills/experiment-plan/SKILL.md +249 -0
  46. package/skills/experiment-queue/SKILL.md +431 -0
  47. package/skills/experiment-queue/scripts/build_manifest.py +142 -0
  48. package/skills/experiment-queue/scripts/queue_manager.py +433 -0
  49. package/skills/feishu-notify/SKILL.md +156 -0
  50. package/skills/figure-description/SKILL.md +138 -0
  51. package/skills/figure-spec/SKILL.md +262 -0
  52. package/skills/figure-spec/scripts/figure_renderer.py +799 -0
  53. package/skills/formula-derivation/SKILL.md +280 -0
  54. package/skills/gemini-search/SKILL.md +231 -0
  55. package/skills/grant-proposal/SKILL.md +698 -0
  56. package/skills/idea-creator/SKILL.md +542 -0
  57. package/skills/idea-discovery/SKILL.md +521 -0
  58. package/skills/idea-discovery-robot/SKILL.md +363 -0
  59. package/skills/integrity-forensics/SKILL.md +284 -0
  60. package/skills/interview-cheatsheet/SKILL.md +245 -0
  61. package/skills/invention-structuring/SKILL.md +188 -0
  62. package/skills/jurisdiction-format/SKILL.md +192 -0
  63. package/skills/kill-argument/SKILL.md +437 -0
  64. package/skills/mermaid-diagram/SKILL.md +419 -0
  65. package/skills/meta-apply/SKILL.md +141 -0
  66. package/skills/meta-optimize/SKILL.md +437 -0
  67. package/skills/monitor-experiment/SKILL.md +140 -0
  68. package/skills/novelty-check/SKILL.md +101 -0
  69. package/skills/openalex/SKILL.md +237 -0
  70. package/skills/overleaf-sync/SKILL.md +220 -0
  71. package/skills/paper-claim-audit/SKILL.md +348 -0
  72. package/skills/paper-compile/SKILL.md +266 -0
  73. package/skills/paper-figure/SKILL.md +312 -0
  74. package/skills/paper-illustration/SKILL.md +736 -0
  75. package/skills/paper-illustration-image2/SKILL.md +391 -0
  76. package/skills/paper-illustration-image2/scripts/paper_illustration_image2.py +255 -0
  77. package/skills/paper-plan/SKILL.md +386 -0
  78. package/skills/paper-poster/SKILL.md +19 -0
  79. package/skills/paper-poster-html/DESIGN_FINAL.md +176 -0
  80. package/skills/paper-poster-html/IMPLEMENTATION_CONVENTIONS.md +161 -0
  81. package/skills/paper-poster-html/LICENSES/posterly-MIT.txt +21 -0
  82. package/skills/paper-poster-html/NOTICE.md +57 -0
  83. package/skills/paper-poster-html/SKILL.md +323 -0
  84. package/skills/paper-poster-html/scripts/_posterly/__init__.py +0 -0
  85. package/skills/paper-poster-html/scripts/_posterly/canvas.py +200 -0
  86. package/skills/paper-poster-html/scripts/_posterly/measure.py +588 -0
  87. package/skills/paper-poster-html/scripts/_posterly/polish.py +498 -0
  88. package/skills/paper-poster-html/scripts/_posterly/preflight.py +489 -0
  89. package/skills/paper-poster-html/scripts/_posterly/render.py +215 -0
  90. package/skills/paper-poster-html/scripts/_posterly/textutil.py +16 -0
  91. package/skills/paper-poster-html/scripts/_posterly/verify_final.py +171 -0
  92. package/skills/paper-poster-html/scripts/asset_check.py +897 -0
  93. package/skills/paper-poster-html/scripts/extract_pdf_figures.py +666 -0
  94. package/skills/paper-poster-html/scripts/poster_check.py +251 -0
  95. package/skills/paper-poster-html/scripts/preprocess_figures.py +238 -0
  96. package/skills/paper-poster-html/scripts/render_preview.py +217 -0
  97. package/skills/paper-poster-html/scripts/run_gates.py +556 -0
  98. package/skills/paper-poster-html/scripts/style_check.py +1324 -0
  99. package/skills/paper-poster-html/templates/COMPONENTS.md +462 -0
  100. package/skills/paper-poster-html/templates/README.md +170 -0
  101. package/skills/paper-poster-html/templates/landscape_4col.html +1032 -0
  102. package/skills/paper-poster-html/templates/landscape_hero.html +1046 -0
  103. package/skills/paper-poster-html/templates/portrait_2col.html +947 -0
  104. package/skills/paper-poster-html/templates/tokens/acl.json +9 -0
  105. package/skills/paper-poster-html/templates/tokens/cvpr.json +9 -0
  106. package/skills/paper-poster-html/templates/tokens/generic.json +9 -0
  107. package/skills/paper-poster-html/templates/tokens/iclr.json +9 -0
  108. package/skills/paper-poster-html/templates/tokens/icml.json +9 -0
  109. package/skills/paper-poster-html/templates/tokens/neurips.json +9 -0
  110. package/skills/paper-slides/SKILL.md +635 -0
  111. package/skills/paper-talk/SKILL.md +381 -0
  112. package/skills/paper-write/SKILL.md +604 -0
  113. package/skills/paper-write/templates/IEEEtran.bst +2409 -0
  114. package/skills/paper-write/templates/IEEEtran.cls +6347 -0
  115. package/skills/paper-write/templates/iclr2026.tex +84 -0
  116. package/skills/paper-write/templates/icml2025.tex +87 -0
  117. package/skills/paper-write/templates/ieee_conference.tex +89 -0
  118. package/skills/paper-write/templates/ieee_journal.tex +93 -0
  119. package/skills/paper-write/templates/math_commands.tex +48 -0
  120. package/skills/paper-write/templates/neurips2025.tex +80 -0
  121. package/skills/paper-writing/SKILL.md +916 -0
  122. package/skills/patent-novelty-check/SKILL.md +153 -0
  123. package/skills/patent-pipeline/SKILL.md +344 -0
  124. package/skills/patent-review/SKILL.md +203 -0
  125. package/skills/pixel-art/SKILL.md +137 -0
  126. package/skills/prior-art-search/SKILL.md +146 -0
  127. package/skills/proof-checker/SKILL.md +866 -0
  128. package/skills/proof-orchestrator/NOTICE.md +24 -0
  129. package/skills/proof-orchestrator/SKILL.md +254 -0
  130. package/skills/proof-orchestrator/references/audit-output-contract.md +126 -0
  131. package/skills/proof-orchestrator/references/deepseek-routing.md +74 -0
  132. package/skills/proof-orchestrator/references/dispatch-prompts.md +227 -0
  133. package/skills/proof-orchestrator/references/notation-audit.md +135 -0
  134. package/skills/proof-orchestrator/references/proof-audit-rubric.md +70 -0
  135. package/skills/proof-orchestrator/references/stress-tests.md +38 -0
  136. package/skills/proof-writer/SKILL.md +223 -0
  137. package/skills/qzcli/SKILL.md +324 -0
  138. package/skills/rebuttal/SKILL.md +376 -0
  139. package/skills/render-html/SKILL.md +316 -0
  140. package/skills/render-html/scripts/render_html.py +1006 -0
  141. package/skills/render-html/scripts/templates/academic.html +703 -0
  142. package/skills/render-html/scripts/templates/dashboard.html +333 -0
  143. package/skills/research-lit/SKILL.md +756 -0
  144. package/skills/research-pipeline/SKILL.md +384 -0
  145. package/skills/research-refine/SKILL.md +770 -0
  146. package/skills/research-refine-pipeline/SKILL.md +186 -0
  147. package/skills/research-review/SKILL.md +198 -0
  148. package/skills/research-wiki/SKILL.md +461 -0
  149. package/skills/resubmit-pipeline/SKILL.md +447 -0
  150. package/skills/result-to-claim/SKILL.md +311 -0
  151. package/skills/run-experiment/SKILL.md +313 -0
  152. package/skills/semantic-scholar/SKILL.md +236 -0
  153. package/skills/serverless-modal/SKILL.md +335 -0
  154. package/skills/shared-references/acceptance-gate.md +324 -0
  155. package/skills/shared-references/assurance-contract.md +248 -0
  156. package/skills/shared-references/capture-antipatterns.md +78 -0
  157. package/skills/shared-references/citation-discipline.md +583 -0
  158. package/skills/shared-references/compute-env-contract.md +163 -0
  159. package/skills/shared-references/effort-contract.md +183 -0
  160. package/skills/shared-references/evidence-precheck.md +65 -0
  161. package/skills/shared-references/experiment-integrity.md +49 -0
  162. package/skills/shared-references/external-cadence.md +326 -0
  163. package/skills/shared-references/fan-out-pattern.md +366 -0
  164. package/skills/shared-references/injection-hygiene.md +127 -0
  165. package/skills/shared-references/integration-contract.md +461 -0
  166. package/skills/shared-references/output-composition.md +93 -0
  167. package/skills/shared-references/output-language.md +45 -0
  168. package/skills/shared-references/output-manifest.md +49 -0
  169. package/skills/shared-references/output-versioning.md +111 -0
  170. package/skills/shared-references/patent-format-cn.md +199 -0
  171. package/skills/shared-references/patent-format-ep.md +173 -0
  172. package/skills/shared-references/patent-format-us.md +161 -0
  173. package/skills/shared-references/patent-writing-principles.md +197 -0
  174. package/skills/shared-references/prior-art-databases.md +141 -0
  175. package/skills/shared-references/resumable-runs.md +109 -0
  176. package/skills/shared-references/review-scope-limits.md +81 -0
  177. package/skills/shared-references/review-tracing.md +391 -0
  178. package/skills/shared-references/reviewer-independence.md +79 -0
  179. package/skills/shared-references/reviewer-routing.md +852 -0
  180. package/skills/shared-references/skill-governance.md +104 -0
  181. package/skills/shared-references/taste-calibration.md +85 -0
  182. package/skills/shared-references/venue-checklists.md +114 -0
  183. package/skills/shared-references/wiki-helper-resolution.md +134 -0
  184. package/skills/shared-references/writing-principles.md +525 -0
  185. package/skills/skills-codex/README.md +102 -0
  186. package/skills/skills-codex/README_CN.md +100 -0
  187. package/skills/skills-codex/ablation-planner/SKILL.md +126 -0
  188. package/skills/skills-codex/alphaxiv/SKILL.md +186 -0
  189. package/skills/skills-codex/analyze-results/SKILL.md +45 -0
  190. package/skills/skills-codex/arxiv/SKILL.md +210 -0
  191. package/skills/skills-codex/auto-paper-improvement-loop/SKILL.md +574 -0
  192. package/skills/skills-codex/auto-review-loop/SKILL.md +500 -0
  193. package/skills/skills-codex/auto-review-loop-llm/SKILL.md +247 -0
  194. package/skills/skills-codex/auto-review-loop-minimax/SKILL.md +290 -0
  195. package/skills/skills-codex/citation-audit/SKILL.md +504 -0
  196. package/skills/skills-codex/claims-drafting/SKILL.md +239 -0
  197. package/skills/skills-codex/comm-lit-review/SKILL.md +299 -0
  198. package/skills/skills-codex/comm-lit-review/references/domain-taxonomy.md +57 -0
  199. package/skills/skills-codex/comm-lit-review/references/output-template.md +37 -0
  200. package/skills/skills-codex/comm-lit-review/references/source-policy.md +99 -0
  201. package/skills/skills-codex/comm-lit-review/references/venue-tiering.md +112 -0
  202. package/skills/skills-codex/deepxiv/SKILL.md +142 -0
  203. package/skills/skills-codex/dse-loop/SKILL.md +285 -0
  204. package/skills/skills-codex/embodiment-description/SKILL.md +129 -0
  205. package/skills/skills-codex/exa-search/SKILL.md +192 -0
  206. package/skills/skills-codex/experiment-audit/SKILL.md +286 -0
  207. package/skills/skills-codex/experiment-bridge/SKILL.md +356 -0
  208. package/skills/skills-codex/experiment-plan/SKILL.md +249 -0
  209. package/skills/skills-codex/experiment-queue/SKILL.md +401 -0
  210. package/skills/skills-codex/feishu-notify/SKILL.md +155 -0
  211. package/skills/skills-codex/figure-description/SKILL.md +138 -0
  212. package/skills/skills-codex/figure-spec/SKILL.md +252 -0
  213. package/skills/skills-codex/formula-derivation/SKILL.md +280 -0
  214. package/skills/skills-codex/gemini-search/SKILL.md +205 -0
  215. package/skills/skills-codex/grant-proposal/SKILL.md +626 -0
  216. package/skills/skills-codex/idea-creator/SKILL.md +405 -0
  217. package/skills/skills-codex/idea-discovery/SKILL.md +475 -0
  218. package/skills/skills-codex/idea-discovery-robot/SKILL.md +362 -0
  219. package/skills/skills-codex/integrity-forensics/SKILL.md +106 -0
  220. package/skills/skills-codex/interview-cheatsheet/SKILL.md +245 -0
  221. package/skills/skills-codex/invention-structuring/SKILL.md +188 -0
  222. package/skills/skills-codex/jurisdiction-format/SKILL.md +192 -0
  223. package/skills/skills-codex/kill-argument/SKILL.md +403 -0
  224. package/skills/skills-codex/mermaid-diagram/SKILL.md +379 -0
  225. package/skills/skills-codex/meta-apply/SKILL.md +154 -0
  226. package/skills/skills-codex/meta-optimize/SKILL.md +348 -0
  227. package/skills/skills-codex/monitor-experiment/SKILL.md +98 -0
  228. package/skills/skills-codex/novelty-check/SKILL.md +89 -0
  229. package/skills/skills-codex/openalex/SKILL.md +228 -0
  230. package/skills/skills-codex/overleaf-sync/SKILL.md +220 -0
  231. package/skills/skills-codex/paper-claim-audit/SKILL.md +350 -0
  232. package/skills/skills-codex/paper-compile/SKILL.md +253 -0
  233. package/skills/skills-codex/paper-figure/SKILL.md +311 -0
  234. package/skills/skills-codex/paper-illustration/SKILL.md +690 -0
  235. package/skills/skills-codex/paper-illustration-image2/SKILL.md +383 -0
  236. package/skills/skills-codex/paper-illustration-image2/scripts/paper_illustration_image2.py +255 -0
  237. package/skills/skills-codex/paper-plan/SKILL.md +278 -0
  238. package/skills/skills-codex/paper-poster/SKILL.md +19 -0
  239. package/skills/skills-codex/paper-poster-html/SKILL.md +377 -0
  240. package/skills/skills-codex/paper-slides/SKILL.md +571 -0
  241. package/skills/skills-codex/paper-talk/SKILL.md +381 -0
  242. package/skills/skills-codex/paper-write/SKILL.md +411 -0
  243. package/skills/skills-codex/paper-write/templates/IEEEtran.bst +2409 -0
  244. package/skills/skills-codex/paper-write/templates/IEEEtran.cls +6347 -0
  245. package/skills/skills-codex/paper-write/templates/aaai2026.bst +1493 -0
  246. package/skills/skills-codex/paper-write/templates/aaai2026.sty +315 -0
  247. package/skills/skills-codex/paper-write/templates/aaai2026.tex +952 -0
  248. package/skills/skills-codex/paper-write/templates/acl.sty +312 -0
  249. package/skills/skills-codex/paper-write/templates/acl2026.tex +377 -0
  250. package/skills/skills-codex/paper-write/templates/acl_natbib.bst +1940 -0
  251. package/skills/skills-codex/paper-write/templates/acm.bst +3081 -0
  252. package/skills/skills-codex/paper-write/templates/acm_mm2026.tex +204 -0
  253. package/skills/skills-codex/paper-write/templates/acmart.cls +3520 -0
  254. package/skills/skills-codex/paper-write/templates/cvpr.bst +1448 -0
  255. package/skills/skills-codex/paper-write/templates/cvpr.sty +508 -0
  256. package/skills/skills-codex/paper-write/templates/cvpr2026.tex +63 -0
  257. package/skills/skills-codex/paper-write/templates/iclr2026.tex +84 -0
  258. package/skills/skills-codex/paper-write/templates/iclr2026_conference.bst +1440 -0
  259. package/skills/skills-codex/paper-write/templates/iclr2026_conference.sty +246 -0
  260. package/skills/skills-codex/paper-write/templates/icml2026.sty +767 -0
  261. package/skills/skills-codex/paper-write/templates/icml2026.tex +662 -0
  262. package/skills/skills-codex/paper-write/templates/ieee_conference.tex +89 -0
  263. package/skills/skills-codex/paper-write/templates/ieee_journal.tex +93 -0
  264. package/skills/skills-codex/paper-write/templates/math_commands.tex +48 -0
  265. package/skills/skills-codex/paper-write/templates/neurips2026.tex +493 -0
  266. package/skills/skills-codex/paper-write/templates/neurips_2026.sty +437 -0
  267. package/skills/skills-codex/paper-writing/SKILL.md +731 -0
  268. package/skills/skills-codex/patent-novelty-check/SKILL.md +153 -0
  269. package/skills/skills-codex/patent-pipeline/SKILL.md +344 -0
  270. package/skills/skills-codex/patent-review/SKILL.md +202 -0
  271. package/skills/skills-codex/pixel-art/SKILL.md +139 -0
  272. package/skills/skills-codex/prior-art-search/SKILL.md +146 -0
  273. package/skills/skills-codex/proof-checker/SKILL.md +554 -0
  274. package/skills/skills-codex/proof-orchestrator/SKILL.md +260 -0
  275. package/skills/skills-codex/proof-orchestrator/references/audit-output-contract.md +126 -0
  276. package/skills/skills-codex/proof-orchestrator/references/deepseek-routing.md +76 -0
  277. package/skills/skills-codex/proof-orchestrator/references/dispatch-prompts.md +227 -0
  278. package/skills/skills-codex/proof-orchestrator/references/notation-audit.md +135 -0
  279. package/skills/skills-codex/proof-orchestrator/references/proof-audit-rubric.md +70 -0
  280. package/skills/skills-codex/proof-orchestrator/references/stress-tests.md +38 -0
  281. package/skills/skills-codex/proof-writer/SKILL.md +222 -0
  282. package/skills/skills-codex/qzcli/SKILL.md +324 -0
  283. package/skills/skills-codex/rebuttal/SKILL.md +305 -0
  284. package/skills/skills-codex/render-html/SKILL.md +305 -0
  285. package/skills/skills-codex/render-html/scripts/__pycache__/render_html.cpython-314.pyc +0 -0
  286. package/skills/skills-codex/render-html/scripts/render_html.py +909 -0
  287. package/skills/skills-codex/render-html/scripts/templates/academic.html +342 -0
  288. package/skills/skills-codex/render-html/scripts/templates/dashboard.html +333 -0
  289. package/skills/skills-codex/research-lit/SKILL.md +464 -0
  290. package/skills/skills-codex/research-pipeline/SKILL.md +340 -0
  291. package/skills/skills-codex/research-refine/SKILL.md +721 -0
  292. package/skills/skills-codex/research-refine-pipeline/SKILL.md +186 -0
  293. package/skills/skills-codex/research-review/SKILL.md +135 -0
  294. package/skills/skills-codex/research-wiki/SKILL.md +421 -0
  295. package/skills/skills-codex/resubmit-pipeline/SKILL.md +444 -0
  296. package/skills/skills-codex/result-to-claim/SKILL.md +246 -0
  297. package/skills/skills-codex/run-experiment/SKILL.md +236 -0
  298. package/skills/skills-codex/semantic-scholar/SKILL.md +219 -0
  299. package/skills/skills-codex/serverless-modal/SKILL.md +335 -0
  300. package/skills/skills-codex/shared-references/acceptance-gate.md +336 -0
  301. package/skills/skills-codex/shared-references/assurance-contract.md +139 -0
  302. package/skills/skills-codex/shared-references/capture-antipatterns.md +84 -0
  303. package/skills/skills-codex/shared-references/citation-discipline.md +452 -0
  304. package/skills/skills-codex/shared-references/compute-env-contract.md +163 -0
  305. package/skills/skills-codex/shared-references/effort-contract.md +143 -0
  306. package/skills/skills-codex/shared-references/evidence-precheck.md +73 -0
  307. package/skills/skills-codex/shared-references/experiment-integrity.md +49 -0
  308. package/skills/skills-codex/shared-references/external-cadence.md +334 -0
  309. package/skills/skills-codex/shared-references/fan-out-pattern.md +375 -0
  310. package/skills/skills-codex/shared-references/injection-hygiene.md +132 -0
  311. package/skills/skills-codex/shared-references/integration-contract.md +372 -0
  312. package/skills/skills-codex/shared-references/output-composition.md +98 -0
  313. package/skills/skills-codex/shared-references/output-language.md +45 -0
  314. package/skills/skills-codex/shared-references/output-manifest.md +40 -0
  315. package/skills/skills-codex/shared-references/output-versioning.md +111 -0
  316. package/skills/skills-codex/shared-references/patent-format-cn.md +199 -0
  317. package/skills/skills-codex/shared-references/patent-format-ep.md +173 -0
  318. package/skills/skills-codex/shared-references/patent-format-us.md +161 -0
  319. package/skills/skills-codex/shared-references/patent-writing-principles.md +197 -0
  320. package/skills/skills-codex/shared-references/prior-art-databases.md +141 -0
  321. package/skills/skills-codex/shared-references/resumable-runs.md +125 -0
  322. package/skills/skills-codex/shared-references/review-scope-limits.md +81 -0
  323. package/skills/skills-codex/shared-references/review-tracing.md +144 -0
  324. package/skills/skills-codex/shared-references/reviewer-independence.md +66 -0
  325. package/skills/skills-codex/shared-references/reviewer-routing.md +128 -0
  326. package/skills/skills-codex/shared-references/skill-governance.md +119 -0
  327. package/skills/skills-codex/shared-references/taste-calibration.md +90 -0
  328. package/skills/skills-codex/shared-references/venue-checklists.md +73 -0
  329. package/skills/skills-codex/shared-references/wiki-helper-resolution.md +69 -0
  330. package/skills/skills-codex/shared-references/writing-principles.md +525 -0
  331. package/skills/skills-codex/slides-polish/SKILL.md +563 -0
  332. package/skills/skills-codex/specification-writing/SKILL.md +211 -0
  333. package/skills/skills-codex/system-profile/SKILL.md +103 -0
  334. package/skills/skills-codex/training-check/SKILL.md +83 -0
  335. package/skills/skills-codex/vast-gpu/SKILL.md +394 -0
  336. package/skills/skills-codex/web-debug-search/SKILL.md +334 -0
  337. package/skills/skills-codex/wiki-enrich/SKILL.md +255 -0
  338. package/skills/skills-codex/writing-systems-papers/SKILL.md +184 -0
  339. package/skills/skills-codex-claude-review/README.md +79 -0
  340. package/skills/skills-codex-claude-review/README_CN.md +78 -0
  341. package/skills/skills-codex-claude-review/auto-paper-improvement-loop/SKILL.md +581 -0
  342. package/skills/skills-codex-claude-review/auto-review-loop/SKILL.md +510 -0
  343. package/skills/skills-codex-claude-review/novelty-check/SKILL.md +102 -0
  344. package/skills/skills-codex-claude-review/paper-figure/SKILL.md +319 -0
  345. package/skills/skills-codex-claude-review/paper-plan/SKILL.md +287 -0
  346. package/skills/skills-codex-claude-review/paper-write/SKILL.md +420 -0
  347. package/skills/skills-codex-claude-review/research-refine/SKILL.md +732 -0
  348. package/skills/skills-codex-claude-review/research-review/SKILL.md +149 -0
  349. package/skills/skills-codex-gemini-review/README.md +176 -0
  350. package/skills/skills-codex-gemini-review/README_CN.md +175 -0
  351. package/skills/skills-codex-gemini-review/auto-paper-improvement-loop/SKILL.md +331 -0
  352. package/skills/skills-codex-gemini-review/auto-review-loop/SKILL.md +304 -0
  353. package/skills/skills-codex-gemini-review/grant-proposal/SKILL.md +630 -0
  354. package/skills/skills-codex-gemini-review/idea-creator/SKILL.md +263 -0
  355. package/skills/skills-codex-gemini-review/idea-discovery/SKILL.md +275 -0
  356. package/skills/skills-codex-gemini-review/idea-discovery-robot/SKILL.md +365 -0
  357. package/skills/skills-codex-gemini-review/novelty-check/SKILL.md +92 -0
  358. package/skills/skills-codex-gemini-review/paper-figure/SKILL.md +289 -0
  359. package/skills/skills-codex-gemini-review/paper-plan/SKILL.md +265 -0
  360. package/skills/skills-codex-gemini-review/paper-poster-html/SKILL.md +102 -0
  361. package/skills/skills-codex-gemini-review/paper-slides/SKILL.md +582 -0
  362. package/skills/skills-codex-gemini-review/paper-write/SKILL.md +346 -0
  363. package/skills/skills-codex-gemini-review/paper-writing/SKILL.md +312 -0
  364. package/skills/skills-codex-gemini-review/research-refine/SKILL.md +674 -0
  365. package/skills/skills-codex-gemini-review/research-review/SKILL.md +112 -0
  366. package/skills/slides-polish/SKILL.md +565 -0
  367. package/skills/specification-writing/SKILL.md +211 -0
  368. package/skills/system-profile/SKILL.md +103 -0
  369. package/skills/training-check/SKILL.md +132 -0
  370. package/skills/vast-gpu/SKILL.md +394 -0
  371. package/skills/web-debug-search/SKILL.md +334 -0
  372. package/skills/wiki-enrich/SKILL.md +257 -0
  373. package/skills/writing-systems-papers/SKILL.md +184 -0
  374. package/templates/CLAUDE_MD_TEMPLATE.md +29 -0
  375. package/templates/EXPERIMENT_LOG_TEMPLATE.md +47 -0
  376. package/templates/EXPERIMENT_PLAN_TEMPLATE.md +51 -0
  377. package/templates/EXPERIMENT_PLAN_TEMPLATE_CN.md +53 -0
  378. package/templates/FINDINGS_TEMPLATE.md +52 -0
  379. package/templates/IDEA_CANDIDATES_TEMPLATE.md +47 -0
  380. package/templates/IDEA_CANDIDATES_TEMPLATE_CN.md +47 -0
  381. package/templates/INVENTION_BRIEF_TEMPLATE.md +87 -0
  382. package/templates/MANIFEST_TEMPLATE.md +7 -0
  383. package/templates/NARRATIVE_REPORT_TEMPLATE.md +49 -0
  384. package/templates/PAPER_PLAN_TEMPLATE.md +47 -0
  385. package/templates/PATENT_CLAIMS_TEMPLATE.md +78 -0
  386. package/templates/PATENT_SPECIFICATION_TEMPLATE.md +68 -0
  387. package/templates/README.md +57 -0
  388. package/templates/RESEARCH_BRIEF_TEMPLATE.md +35 -0
  389. package/templates/RESEARCH_BRIEF_TEMPLATE_CN.md +41 -0
  390. package/templates/RESEARCH_CONTRACT_TEMPLATE.md +60 -0
  391. package/templates/claude-hooks/corpus_write_guard.json +16 -0
  392. package/templates/claude-hooks/corpus_write_guard.py +85 -0
  393. package/templates/claude-hooks/meta_logging.json +74 -0
  394. package/templates/gitignore-trace.txt +3 -0
  395. package/tools/__pycache__/check_skills_inventory.cpython-314.pyc +0 -0
  396. package/tools/arxiv_fetch.py +311 -0
  397. package/tools/capture_filter.py +126 -0
  398. package/tools/check_skills_inventory.py +273 -0
  399. package/tools/convert_skills_to_llm_chat.py +282 -0
  400. package/tools/copilot_native_evidence.py +818 -0
  401. package/tools/deepxiv_fetch.py +213 -0
  402. package/tools/evidence_check.py +212 -0
  403. package/tools/exa_search.py +425 -0
  404. package/tools/experiment_queue/README.md +118 -0
  405. package/tools/experiment_queue/build_manifest.py +44 -0
  406. package/tools/experiment_queue/queue_manager.py +44 -0
  407. package/tools/extract_paper_style.py +560 -0
  408. package/tools/figure_renderer.py +69 -0
  409. package/tools/forensics_gate.py +669 -0
  410. package/tools/generate_codex_claude_review_overrides.py +299 -0
  411. package/tools/idea_discovery_gate.py +256 -0
  412. package/tools/install_aris.ps1 +1372 -0
  413. package/tools/install_aris.sh +1370 -0
  414. package/tools/install_aris_codex.sh +1023 -0
  415. package/tools/install_aris_copilot.sh +1052 -0
  416. package/tools/iteration_log.py +143 -0
  417. package/tools/lint_skills_helpers.sh +84 -0
  418. package/tools/meta_opt/check_ready.sh +80 -0
  419. package/tools/meta_opt/log_event.sh +91 -0
  420. package/tools/meta_opt/trigger_eval.py +280 -0
  421. package/tools/meta_opt/trigger_evals.sample.json +28 -0
  422. package/tools/openalex_fetch.py +326 -0
  423. package/tools/overleaf_audit.sh +104 -0
  424. package/tools/overleaf_setup.sh +150 -0
  425. package/tools/paper_illustration_image2.py +62 -0
  426. package/tools/provenance.py +294 -0
  427. package/tools/research_wiki.py +1720 -0
  428. package/tools/review_gate.py +502 -0
  429. package/tools/run_state.py +399 -0
  430. package/tools/save_trace.sh +477 -0
  431. package/tools/semantic_scholar_fetch.py +438 -0
  432. package/tools/skill-groups.tsv +116 -0
  433. package/tools/skill_picker.py +238 -0
  434. package/tools/smart_update.ps1 +521 -0
  435. package/tools/smart_update.sh +591 -0
  436. package/tools/smart_update_codex.sh +419 -0
  437. package/tools/smart_update_copilot.sh +605 -0
  438. package/tools/threat_scan.py +222 -0
  439. package/tools/verify_paper_audits.sh +487 -0
  440. package/tools/verify_papers.py +613 -0
  441. package/tools/verify_wiki_coverage.sh +176 -0
  442. package/tools/watchdog.py +485 -0
@@ -0,0 +1,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()