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,1052 @@
1
+ #!/usr/bin/env bash
2
+ # install_aris_copilot.sh -- Project-local ARIS skill installation for GitHub Copilot CLI.
3
+ #
4
+ # This installer manages a flat layout using mainline skills:
5
+ # <project>/.github/skills/<skill-name> -> <aris-repo>/skills/<skill-name>
6
+ #
7
+ # Unlike the Codex installer, Copilot CLI natively supports SKILL.md with
8
+ # allowed-tools and MCP tool calls, so mainline skills work directly --
9
+ # no separate skill mirror is needed.
10
+ #
11
+ # Managed entries are tracked in:
12
+ # <project>/.aris/installed-skills-copilot.txt
13
+ #
14
+ # Usage:
15
+ # bash tools/install_aris_copilot.sh [project_path] [options]
16
+ #
17
+ # Actions (mutually exclusive, default: auto):
18
+ # default install if no manifest, else reconcile
19
+ # --reconcile explicit reconcile; refuse if no manifest
20
+ # --uninstall remove only entries in manifest; delete manifest
21
+ #
22
+ # Selection (catalog: tools/skill-groups.tsv in the aris-repo):
23
+ # --groups A,B install only these skill groups (see --list-groups)
24
+ # --skills X,Y additionally install these skills (clears declined mark)
25
+ # --exclude X,Y never install these skills (recorded as declined)
26
+ # --all install every upstream skill (legacy default)
27
+ # --add-new reconcile: accept all upstream skills not yet installed
28
+ # --skip-new reconcile: skip new upstream skills without prompting
29
+ # --list-groups print the group catalog and exit
30
+ # With no selection flags: fresh install on a TTY opens a full-screen
31
+ # checkbox picker (Space toggles a skill / a whole group, Enter confirms;
32
+ # ARIS_NO_PICKER=1 or missing python3/curses falls back to per-group
33
+ # Y/n/e prompts); fresh install with --quiet/no TTY installs everything (old behavior).
34
+ # Reconcile keeps exactly what the manifest says is installed; NEW upstream
35
+ # skills need per-skill confirmation (declined ones are remembered in
36
+ # .aris/skills-declined-copilot.txt and never re-asked).
37
+ #
38
+ # Options:
39
+ # --aris-repo PATH override aris-repo discovery
40
+ # --dry-run show plan, no writes
41
+ # --quiet no prompts; abort on any condition that would prompt
42
+ # --no-doc skip AGENTS.md managed block update
43
+ # --replace-link NAME replace a conflicting symlink for NAME (repeatable)
44
+ # --clear-stale-lock clear a stale installer lock
45
+ #
46
+ # Safety rules enforced:
47
+ # S1 Never delete a path that is not a symlink.
48
+ # S2 Never delete a symlink whose target is outside the configured aris-repo.
49
+ # S3 Never delete a symlink not listed in the manifest.
50
+ # S4 Never overwrite an existing path during CREATE -- abort by default.
51
+ # S5 Manifest write is atomic (temp + rename in same dir).
52
+ # S6 Concurrent runs in same project serialize via mkdir lockdir.
53
+ # S7 Crash mid-apply leaves the previous manifest intact; rerun adopts.
54
+ # S8 Uninstall revalidates each managed symlink's target before removing.
55
+ # S9 If .aris/, .github/, or .github/skills/ is itself a symlink, abort.
56
+ # S10 Reject upstream entries that are symlinks to outside aris-repo.
57
+ # S11 Revalidate exact target match (lstat + readlink) before every mutation.
58
+ # S12 Temp files live in the same directory as the destination.
59
+ # S13 Skill names must match ^[A-Za-z0-9][A-Za-z0-9._-]*$ (slug regex).
60
+
61
+ set -euo pipefail
62
+
63
+ # --- Constants ---
64
+ MANIFEST_VERSION="1"
65
+ MANIFEST_NAME="installed-skills-copilot.txt"
66
+ MANIFEST_PREV_NAME="installed-skills-copilot.txt.prev"
67
+ DECLINED_NAME="skills-declined-copilot.txt"
68
+ CATALOG_REL="tools/skill-groups.tsv"
69
+ GLOBAL_POINTER="$HOME/.aris/repo"
70
+ ARIS_DIR_NAME=".aris"
71
+ LOCK_DIR_NAME=".install-copilot.lock.d"
72
+ SKILLS_REL=".github/skills"
73
+ AGENTS_REL=".github/agents"
74
+ DOC_FILE_NAME="AGENTS.md"
75
+ BLOCK_BEGIN="<!-- ARIS-COPILOT:BEGIN -->"
76
+ BLOCK_END="<!-- ARIS-COPILOT:END -->"
77
+ SAFE_NAME_REGEX='^[A-Za-z0-9][A-Za-z0-9._-]*$'
78
+
79
+ # Directories to skip when scanning upstream skills/ as installable skills.
80
+ # shared-references is handled separately as a "support" entry.
81
+ # This pattern MUST stay in sync with smart_update_copilot.sh SKIP_DIRS_PATTERN.
82
+ SKIP_DIRS="skills-codex|skills-codex-claude-review|skills-codex-gemini-review|shared-references"
83
+
84
+ PROJECT_PATH=""
85
+ ARIS_REPO_OVERRIDE=""
86
+ ACTION="auto"
87
+ DRY_RUN=false
88
+ QUIET=false
89
+ NO_DOC=false
90
+ CLEAR_STALE_LOCK=false
91
+ REPLACE_LINK_NAMES=()
92
+ SELECT_GROUPS="" # comma list from --groups
93
+ SELECT_SKILLS="" # comma list from --skills
94
+ EXCLUDE_SKILLS="" # comma list from --exclude
95
+ SELECT_ALL=false
96
+ NEW_POLICY="" # "" (prompt) | add | skip
97
+ LIST_GROUPS=false
98
+
99
+ usage() { sed -n '2,59p' "$0" | sed 's/^# \?//'; }
100
+
101
+ while [[ $# -gt 0 ]]; do
102
+ case "$1" in
103
+ --reconcile) ACTION="reconcile"; shift ;;
104
+ --uninstall) ACTION="uninstall"; shift ;;
105
+ --aris-repo) ARIS_REPO_OVERRIDE="${2:?--aris-repo requires path}"; shift 2 ;;
106
+ --dry-run) DRY_RUN=true; shift ;;
107
+ --quiet) QUIET=true; shift ;;
108
+ --no-doc) NO_DOC=true; shift ;;
109
+ --replace-link) REPLACE_LINK_NAMES+=("${2:?--replace-link requires NAME}"); shift 2 ;;
110
+ --clear-stale-lock) CLEAR_STALE_LOCK=true; shift ;;
111
+ --groups) SELECT_GROUPS="${SELECT_GROUPS:+$SELECT_GROUPS,}${2:?--groups requires A,B,...}"; shift 2 ;;
112
+ --skills) SELECT_SKILLS="${SELECT_SKILLS:+$SELECT_SKILLS,}${2:?--skills requires X,Y,...}"; shift 2 ;;
113
+ --exclude) EXCLUDE_SKILLS="${EXCLUDE_SKILLS:+$EXCLUDE_SKILLS,}${2:?--exclude requires X,Y,...}"; shift 2 ;;
114
+ --all) SELECT_ALL=true; shift ;;
115
+ --add-new) NEW_POLICY="add"; shift ;;
116
+ --skip-new) NEW_POLICY="skip"; shift ;;
117
+ --list-groups) LIST_GROUPS=true; shift ;;
118
+ -h|--help) usage; exit 0 ;;
119
+ --*) echo "Unknown option: $1" >&2; exit 2 ;;
120
+ *)
121
+ if [[ -z "$PROJECT_PATH" ]]; then
122
+ PROJECT_PATH="$1"
123
+ else
124
+ echo "Error: unexpected positional argument: $1" >&2
125
+ exit 2
126
+ fi
127
+ shift
128
+ ;;
129
+ esac
130
+ done
131
+
132
+ if $SELECT_ALL && [[ -n "$SELECT_GROUPS$SELECT_SKILLS" ]]; then
133
+ echo "Error: --all cannot be combined with --groups/--skills (only --exclude)" >&2; exit 2
134
+ fi
135
+
136
+ log() { $QUIET && return 0; echo "$@"; }
137
+ warn() { echo "warning: $*" >&2; }
138
+ die() { echo "error: $*" >&2; exit 1; }
139
+ prompt() { $QUIET && return 0; printf "%s " "$1" >&2; read -r REPLY; [[ "$REPLY" =~ ^[Yy]$ ]]; }
140
+ abs_path() { ( cd "$1" 2>/dev/null && pwd ) || return 1; }
141
+ is_safe_name() { [[ "$1" =~ $SAFE_NAME_REGEX ]]; }
142
+ is_symlink() { [[ -L "$1" ]]; }
143
+ name_in_replace_allowlist() {
144
+ local needle="$1"
145
+ local item
146
+ # bash 3.2: empty-array "${ARR[@]}" trips `set -u`; no --replace-link = empty allowlist
147
+ [[ ${#REPLACE_LINK_NAMES[@]} -gt 0 ]] || return 1
148
+ for item in "${REPLACE_LINK_NAMES[@]}"; do
149
+ [[ "$item" == "$needle" ]] && return 0
150
+ done
151
+ return 1
152
+ }
153
+
154
+ read_link_target() {
155
+ if command -v greadlink >/dev/null 2>&1; then greadlink "$1"
156
+ else readlink "$1"; fi
157
+ }
158
+
159
+ canonicalize() {
160
+ if command -v greadlink >/dev/null 2>&1; then greadlink -f "$1" 2>/dev/null || true
161
+ elif readlink -f "$1" 2>/dev/null; then :
162
+ else
163
+ local d f
164
+ if [[ -d "$1" ]]; then
165
+ ( cd "$1" && pwd )
166
+ else
167
+ d="$(dirname "$1")"
168
+ f="$(basename "$1")"
169
+ ( cd "$d" 2>/dev/null && echo "$(pwd)/$f" )
170
+ fi
171
+ fi
172
+ }
173
+
174
+ resolve_aris_repo() {
175
+ local p
176
+ if [[ -n "$ARIS_REPO_OVERRIDE" ]]; then
177
+ p="$(abs_path "$ARIS_REPO_OVERRIDE")" || die "--aris-repo path not found: $ARIS_REPO_OVERRIDE"
178
+ else
179
+ local script_dir parent
180
+ script_dir="$(cd "$(dirname "$0")" && pwd)"
181
+ parent="$(cd "$script_dir/.." && pwd)"
182
+ if [[ -d "$parent/skills" && -f "$parent/AGENT_GUIDE.md" ]]; then
183
+ p="$parent"
184
+ elif [[ -n "${ARIS_REPO:-}" && -d "$ARIS_REPO/skills" ]]; then
185
+ p="$(abs_path "$ARIS_REPO")"
186
+ else
187
+ local guess
188
+ for guess in \
189
+ "$HOME/Desktop/Auto-claude-code-research-in-sleep" \
190
+ "$HOME/Auto-claude-code-research-in-sleep" \
191
+ "$HOME/aris_repo" \
192
+ "$HOME/.copilot/Auto-claude-code-research-in-sleep"; do
193
+ if [[ -d "$guess/skills" && -f "$guess/AGENT_GUIDE.md" ]]; then
194
+ p="$(abs_path "$guess")"
195
+ break
196
+ fi
197
+ done
198
+ fi
199
+ fi
200
+ [[ -n "${p:-}" ]] || die "cannot find ARIS repo with skills/. Use --aris-repo PATH."
201
+ [[ -d "$p/skills" ]] || die "repo missing skills/ directory: $p"
202
+ echo "$p"
203
+ }
204
+
205
+ build_upstream_inventory() {
206
+ local repo="$1" out="$2"
207
+ local skills_dir="$repo/skills"
208
+ local d name agents_dir agent_name base_name f
209
+ : > "$out"
210
+
211
+ for d in "$skills_dir"/*/; do
212
+ [[ -d "$d" ]] || continue
213
+ name="$(basename "$d")"
214
+ # Skip Codex-specific packages and shared-references (support dir handled separately)
215
+ if [[ "$name" =~ ^($SKIP_DIRS)$ ]]; then
216
+ continue
217
+ fi
218
+ is_safe_name "$name" || { warn "skipping unsafe upstream name: $name"; continue; }
219
+ if [[ -f "$d/SKILL.md" ]]; then
220
+ printf "skill|%s|skills/%s\n" "$name" "$name" >> "$out"
221
+ fi
222
+ done
223
+
224
+ # Include shared-references as a support directory
225
+ if [[ -d "$skills_dir/shared-references" ]]; then
226
+ printf "support|shared-references|skills/shared-references\n" >> "$out"
227
+ fi
228
+
229
+ # Include agent profiles from .github/agents/
230
+ agents_dir="$repo/.github/agents"
231
+ if [[ -d "$agents_dir" ]]; then
232
+ if [[ -L "$agents_dir" ]]; then
233
+ warn "skipping symlinked upstream agents directory: $agents_dir"
234
+ else
235
+ for f in "$agents_dir"/*.agent.md; do
236
+ [[ -f "$f" ]] || continue
237
+ # Resolve symlink and verify it's within the expected directory
238
+ local resolved; resolved="$(canonicalize "$f")"
239
+ local agents_canon; agents_canon="$(canonicalize "$agents_dir")"
240
+ [[ "$resolved" == "$agents_canon"/* ]] || { warn "skipping external symlink: $f -> $resolved"; continue; }
241
+ agent_name="$(basename "$f")"
242
+ base_name="${agent_name%.agent.md}"
243
+ is_safe_name "$base_name" || { warn "skipping unsafe agent name: $agent_name"; continue; }
244
+ printf "agent|%s|.github/agents/%s\n" "$agent_name" "$agent_name" >> "$out"
245
+ done
246
+ fi
247
+ fi
248
+
249
+ [[ -s "$out" ]] || die "upstream inventory empty"
250
+ sort -t'|' -k2,2 -o "$out" "$out"
251
+ }
252
+
253
+ # --- Selective install (#366, ported from install_aris.sh) ---
254
+ # Upstream rows are 3-field (kind|name|source_rel); catalog skill names match
255
+ # these mainline skill names 1:1.
256
+
257
+ catalog_ok() { [[ -n "${CATALOG_PATH:-}" && -f "$CATALOG_PATH" ]]; }
258
+ catalog_groups() { awk -F'\t' '$1=="group"{print $2 "\t" $3 "\t" $4}' "$CATALOG_PATH"; }
259
+ catalog_group_of() { awk -F'\t' -v s="$1" '$1=="skill" && $2==s {print $3; exit}' "$CATALOG_PATH"; }
260
+ catalog_desc_of() { awk -F'\t' -v s="$1" '$1=="skill" && $2==s {print (NF>=5?$5:""); exit}' "$CATALOG_PATH"; }
261
+ catalog_requires() { awk -F'\t' -v s="$1" '$1=="skill" && $2==s && $4!="-" {print $4; exit}' "$CATALOG_PATH" | tr ',' '\n'; }
262
+ catalog_skills_in_group() { awk -F'\t' -v g="$1" '$1=="skill" && $3==g {print $2}' "$CATALOG_PATH"; }
263
+ catalog_has_skill() { awk -F'\t' -v s="$1" '$1=="skill" && $2==s {found=1; exit} END{exit !found}' "$CATALOG_PATH"; }
264
+
265
+ in_file() { grep -qxF "$1" "$2" 2>/dev/null; }
266
+ upstream_has_skill() { grep -q "^skill|$1|" "$2"; }
267
+
268
+ print_group_catalog() {
269
+ catalog_ok || die "skill catalog not found: ${CATALOG_PATH:-<unset>}"
270
+ echo "Skill groups (from $CATALOG_PATH):"
271
+ local gid display desc n
272
+ while IFS=$'\t' read -r gid display desc; do
273
+ n=$(catalog_skills_in_group "$gid" | wc -l | tr -d ' ')
274
+ printf "\n %-14s %s — %s [%s skills]\n" "$gid" "$display" "$desc" "$n"
275
+ catalog_skills_in_group "$gid" | sed 's/^/ /'
276
+ done < <(catalog_groups)
277
+ }
278
+
279
+ load_declined() { # $1 = out file
280
+ : > "$1"
281
+ [[ -f "$DECLINED_PATH" ]] || return 0
282
+ grep -v '^[[:space:]]*$' "$DECLINED_PATH" >> "$1" || true
283
+ }
284
+
285
+ save_declined() { # $1 = candidates file, $2 = selected file
286
+ $DRY_RUN && return 0
287
+ mkdir -p "$PROJECT_ARIS_DIR"
288
+ local tmp="$DECLINED_PATH.tmp.$$"
289
+ sort -u "$1" | grep -v '^$' | grep -vxF -f "$2" > "$tmp" || true
290
+ if [[ -s "$tmp" || -f "$DECLINED_PATH" ]]; then
291
+ mv -f "$tmp" "$DECLINED_PATH"
292
+ else
293
+ rm -f "$tmp"
294
+ fi
295
+ }
296
+
297
+ # Auto-include hard pipeline deps (catalog `requires` column, transitively).
298
+ expand_deps() { # $1 = selected file, $2 = excludes file, $3 = upstream file
299
+ catalog_ok || return 0
300
+ local changed=1 name dep
301
+ while (( changed )); do
302
+ changed=0
303
+ for name in $(cat "$1"); do
304
+ for dep in $(catalog_requires "$name"); do
305
+ in_file "$dep" "$1" && continue
306
+ if in_file "$dep" "$2"; then
307
+ warn "'$name' requires '$dep' but it is excluded — that pipeline phase will break"
308
+ continue
309
+ fi
310
+ upstream_has_skill "$dep" "$3" || continue
311
+ echo "$dep" >> "$1"
312
+ log " ↳ auto-including '$dep' (required by '$name')"
313
+ changed=1
314
+ done
315
+ done
316
+ done
317
+ }
318
+
319
+ # Interactive selection (fresh install on a TTY, no selection flags).
320
+ # Preferred UI: full-screen checkbox picker (tools/skill_picker.py, curses) —
321
+ # Space toggles a skill / a whole group, Enter confirms, q aborts. Falls back
322
+ # to the classic per-group Y/n/e prompts when python3/curses/tty are
323
+ # unavailable or ARIS_NO_PICKER=1 is set.
324
+ interactive_select() { # $1 = upstream file, $2 = out (selected) file
325
+ if [[ "${ARIS_NO_PICKER:-0}" != "1" ]] && command -v python3 >/dev/null 2>&1 \
326
+ && [[ -f "$ARIS_REPO/tools/skill_picker.py" ]]; then
327
+ local avail_f rc=0
328
+ avail_f="$(mktemp -t aris-avail.XXXX)"
329
+ grep '^skill|' "$1" | cut -d'|' -f2 > "$avail_f" || true
330
+ python3 "$ARIS_REPO/tools/skill_picker.py" \
331
+ --catalog "$CATALOG_PATH" --available "$avail_f" --out "$2" \
332
+ </dev/tty >/dev/tty || rc=$?
333
+ rm -f "$avail_f"
334
+ if [[ $rc -eq 0 ]]; then return 0
335
+ elif [[ $rc -eq 1 ]]; then die "interactive selection aborted"
336
+ fi
337
+ # rc=2 (or unexpected): picker unusable — fall through to prompts.
338
+ warn "checkbox picker unavailable — using per-group prompts"
339
+ fi
340
+ log ""
341
+ log "Interactive skill selection — per group: Y=install all, n=skip, e=pick per skill."
342
+ local gid display desc n reply r2 name glist
343
+ glist="$(mktemp -t aris-copilot-glist.XXXX)"
344
+ while IFS=$'\t' read -r gid display desc; do
345
+ catalog_skills_in_group "$gid" | while read -r name; do
346
+ upstream_has_skill "$name" "$1" && echo "$name"
347
+ done > "$glist" || true
348
+ n=$(wc -l < "$glist" | tr -d ' ')
349
+ (( n == 0 )) && continue
350
+ printf "\n%s (%s) — %s\n" "$display" "$gid" "$desc" >&2
351
+ sed 's/^/ /' "$glist" >&2
352
+ printf "Install group '%s' (%s skills)? [Y/n/e] " "$gid" "$n" >&2
353
+ read -r reply </dev/tty
354
+ case "$reply" in
355
+ [nN]*) : ;;
356
+ [eE]*)
357
+ while read -r name; do
358
+ printf " install %-30s [Y/n] " "$name" >&2
359
+ read -r r2 </dev/tty
360
+ [[ "$r2" =~ ^[nN] ]] || echo "$name" >> "$2"
361
+ done < "$glist"
362
+ ;;
363
+ *) cat "$glist" >> "$2" ;;
364
+ esac
365
+ done < <(catalog_groups)
366
+ # Upstream skills the catalog doesn't know yet (catalog drift): never drop silently.
367
+ local ungrouped; ungrouped="$(mktemp -t aris-copilot-ungrouped.XXXX)"
368
+ grep '^skill|' "$1" | cut -d'|' -f2 | while read -r name; do
369
+ catalog_has_skill "$name" || echo "$name"
370
+ done > "$ungrouped" || true
371
+ if [[ -s "$ungrouped" ]]; then
372
+ printf "\nSkills not in the catalog yet:\n" >&2
373
+ sed 's/^/ /' "$ungrouped" >&2
374
+ while read -r name; do
375
+ printf " install %-30s [Y/n] " "$name" >&2
376
+ read -r r2 </dev/tty
377
+ [[ "$r2" =~ ^[nN] ]] || echo "$name" >> "$2"
378
+ done < "$ungrouped"
379
+ fi
380
+ rm -f "$glist" "$ungrouped"
381
+ }
382
+
383
+ # Validate --groups/--skills names against catalog + upstream.
384
+ validate_selection_flags() { # $1 = upstream file
385
+ local g s
386
+ if [[ -n "$SELECT_GROUPS" ]]; then
387
+ catalog_ok || die "--groups needs the catalog at $CATALOG_PATH (update your aris-repo clone)"
388
+ for g in $(echo "$SELECT_GROUPS" | tr ',' ' '); do
389
+ catalog_groups | cut -f1 | grep -qxF "$g" \
390
+ || die "unknown group '$g' — run with --list-groups to see valid ids"
391
+ done
392
+ fi
393
+ for s in $(echo "$SELECT_SKILLS" | tr ',' ' '); do
394
+ upstream_has_skill "$s" "$1" || die "unknown skill '$s' (not an upstream skill)"
395
+ done
396
+ }
397
+
398
+ # Build the selected-skill set (one name per line in $3).
399
+ build_selection() { # $1 = upstream file, $2 = declined-candidates out file, $3 = selected out file
400
+ local upstream="$1" declined_out="$2" out="$3"
401
+ : > "$out"
402
+ load_declined "$declined_out"
403
+
404
+ local excl; excl="$(mktemp -t aris-copilot-excl.XXXX)"
405
+ echo "$EXCLUDE_SKILLS" | tr ',' '\n' | grep -v '^$' > "$excl" || true
406
+ cat "$excl" >> "$declined_out"
407
+
408
+ validate_selection_flags "$upstream"
409
+
410
+ local has_selection_flags=false
411
+ [[ -n "$SELECT_GROUPS$SELECT_SKILLS" ]] && has_selection_flags=true
412
+ local fresh=true
413
+ [[ -f "$MANIFEST_PATH" ]] && fresh=false
414
+
415
+ local name g subset_choice=false
416
+ if $fresh; then
417
+ if $SELECT_ALL || { ! $has_selection_flags && { $QUIET || [[ ! -t 0 ]]; }; }; then
418
+ grep '^skill|' "$upstream" | cut -d'|' -f2 > "$out"
419
+ elif $has_selection_flags; then
420
+ subset_choice=true
421
+ for g in $(echo "$SELECT_GROUPS" | tr ',' ' '); do
422
+ catalog_skills_in_group "$g" | while read -r name; do
423
+ upstream_has_skill "$name" "$upstream" && echo "$name"
424
+ done >> "$out" || true
425
+ done
426
+ echo "$SELECT_SKILLS" | tr ',' '\n' | grep -v '^$' >> "$out" || true
427
+ elif catalog_ok; then
428
+ subset_choice=true
429
+ interactive_select "$upstream" "$out"
430
+ else
431
+ warn "catalog missing at $CATALOG_PATH — falling back to full install"
432
+ grep '^skill|' "$upstream" | cut -d'|' -f2 > "$out"
433
+ fi
434
+ # Explicit subset choice ⇒ remember the rest as declined (won't re-ask).
435
+ if $subset_choice; then
436
+ grep '^skill|' "$upstream" | cut -d'|' -f2 | grep -vxF -f "$out" >> "$declined_out" 2>/dev/null || true
437
+ fi
438
+ else
439
+ # Reconcile: installed set = manifest ∩ upstream (auto-detected).
440
+ manifest_names "$MANIFEST_DATA" | while read -r name; do
441
+ [[ "$(manifest_kind_of "$MANIFEST_DATA" "$name")" == "skill" ]] || continue
442
+ upstream_has_skill "$name" "$upstream" && echo "$name"
443
+ done >> "$out" || true
444
+ # Flag-based additions re-enable previously declined skills.
445
+ for g in $(echo "$SELECT_GROUPS" | tr ',' ' '); do
446
+ catalog_skills_in_group "$g" | while read -r name; do
447
+ upstream_has_skill "$name" "$upstream" && echo "$name"
448
+ done >> "$out" || true
449
+ done
450
+ echo "$SELECT_SKILLS" | tr ',' '\n' | grep -v '^$' >> "$out" || true
451
+ # NEW upstream skills: not installed, not declined, not just selected.
452
+ local new_file; new_file="$(mktemp -t aris-copilot-new.XXXX)"
453
+ grep '^skill|' "$upstream" | cut -d'|' -f2 | while read -r name; do
454
+ in_file "$name" "$out" && continue
455
+ in_file "$name" "$declined_out" && continue
456
+ echo "$name"
457
+ done > "$new_file" || true
458
+ if [[ -s "$new_file" ]]; then
459
+ if $SELECT_ALL || [[ "$NEW_POLICY" == "add" ]]; then
460
+ cat "$new_file" >> "$out"
461
+ log "→ adding $(wc -l < "$new_file" | tr -d ' ') new upstream skill(s) (--all/--add-new)"
462
+ elif [[ "$NEW_POLICY" == "skip" ]] || $QUIET || [[ ! -t 0 ]]; then
463
+ log ""
464
+ log "New upstream skills NOT installed (rerun without --skip-new/--quiet to be asked,"
465
+ log "or pass --add-new / --skills NAME):"
466
+ sed 's/^/ /' "$new_file" | while read -r l; do log "$l"; done
467
+ else
468
+ log ""
469
+ log "New skills appeared upstream since your last install:"
470
+ local reply grp sdesc
471
+ while read -r name; do
472
+ grp="$(catalog_group_of "$name")"
473
+ sdesc="$(catalog_desc_of "$name")"
474
+ printf " install new skill %-30s (group: %s)%s [y/N] " "$name" "${grp:-?}" "${sdesc:+ — $sdesc}" >&2
475
+ read -r reply </dev/tty
476
+ if [[ "$reply" =~ ^[yY] ]]; then echo "$name" >> "$out"
477
+ else echo "$name" >> "$declined_out"
478
+ fi
479
+ done < "$new_file"
480
+ fi
481
+ fi
482
+ rm -f "$new_file"
483
+ fi
484
+
485
+ # Excludes beat every other source (manifest, groups, deps, new skills).
486
+ local pruned
487
+ if [[ -s "$excl" ]]; then
488
+ pruned="$(mktemp -t aris-copilot-pruned.XXXX)"
489
+ grep -vxF -f "$excl" "$out" > "$pruned" || true
490
+ mv -f "$pruned" "$out"
491
+ fi
492
+ expand_deps "$out" "$excl" "$upstream"
493
+ sort -u -o "$out" "$out"
494
+ rm -f "$excl"
495
+ [[ -s "$out" ]] || die "selection is empty — nothing to install (use --all or --groups/--skills)"
496
+ }
497
+
498
+ # Keep support entries + selected skills only (upstream rows stay 3-field).
499
+ filter_upstream_by_selection() { # $1 = upstream file, $2 = selected file, $3 = out
500
+ awk -F'|' -v sel="$2" '
501
+ BEGIN { while ((getline line < sel) > 0) picked[line]=1 }
502
+ $1=="support" { print; next }
503
+ $1=="agent" { print; next }
504
+ $1=="skill" && picked[$2] { print }
505
+ ' "$1" > "$3"
506
+ }
507
+
508
+ # Layer-4 helper resolution (#366): a global pointer file lets globally/copy-
509
+ # installed skills find $ARIS_REPO/tools without a per-project install.
510
+ ensure_global_pointer() {
511
+ $DRY_RUN && return 0
512
+ mkdir -p "$(dirname "$GLOBAL_POINTER")" 2>/dev/null || { warn "cannot create $(dirname "$GLOBAL_POINTER") — skipping global pointer"; return 0; }
513
+ local cur=""
514
+ [[ -f "$GLOBAL_POINTER" ]] && cur="$(cat "$GLOBAL_POINTER" 2>/dev/null || true)"
515
+ [[ "$cur" == "$ARIS_REPO" ]] && return 0
516
+ printf '%s\n' "$ARIS_REPO" > "$GLOBAL_POINTER.tmp.$$" && mv -f "$GLOBAL_POINTER.tmp.$$" "$GLOBAL_POINTER"
517
+ log " + global pointer $GLOBAL_POINTER -> $ARIS_REPO"
518
+ }
519
+
520
+ load_manifest() {
521
+ local path="$1" out="$2"
522
+ : > "$out"
523
+ [[ -f "$path" ]] || return 0
524
+ local ver
525
+ ver="$(awk -F'\t' '$1=="version"{print $2}' "$path" | head -1)"
526
+ [[ "$ver" == "$MANIFEST_VERSION" ]] || die "manifest version mismatch (got: ${ver:-none}, expected: $MANIFEST_VERSION)"
527
+ awk -F'\t' '
528
+ BEGIN { in_body=0 }
529
+ /^kind\tname\tsource_rel\ttarget_rel\tmode$/ { in_body=1; next }
530
+ in_body && NF==5 { print }
531
+ ' "$path" > "$out"
532
+ }
533
+
534
+ manifest_lookup_target() { awk -F'\t' -v n="$2" '$2==n {print $4; exit}' "$1"; }
535
+ manifest_lookup_source() { awk -F'\t' -v n="$2" '$2==n {print $3; exit}' "$1"; }
536
+ manifest_repo_root() { awk -F'\t' '$1=="repo_root" {print $2; exit}' "$1"; }
537
+ manifest_names() { awk -F'\t' '{print $2}' "$1"; }
538
+ manifest_kind_of() { awk -F'\t' -v n="$2" '$2==n {print $1; exit}' "$1"; }
539
+
540
+ PROJECT_PATH="${PROJECT_PATH:-$(pwd)}"
541
+ [[ -d "$PROJECT_PATH" ]] || die "project path does not exist: $PROJECT_PATH"
542
+ PROJECT_PATH="$(abs_path "$PROJECT_PATH")"
543
+ ARIS_REPO="$(resolve_aris_repo)"
544
+ PROJECT_SKILLS_DIR="$PROJECT_PATH/$SKILLS_REL"
545
+ PROJECT_AGENTS_DIR="$PROJECT_PATH/$AGENTS_REL"
546
+ PROJECT_ARIS_DIR="$PROJECT_PATH/$ARIS_DIR_NAME"
547
+ MANIFEST_PATH="$PROJECT_ARIS_DIR/$MANIFEST_NAME"
548
+ MANIFEST_PREV="$PROJECT_ARIS_DIR/$MANIFEST_PREV_NAME"
549
+ LOCK_DIR="$PROJECT_ARIS_DIR/$LOCK_DIR_NAME"
550
+ DOC_FILE="$PROJECT_PATH/$DOC_FILE_NAME"
551
+ LEGACY_NESTED="$PROJECT_PATH/.github/skills/aris"
552
+ CATALOG_PATH="$ARIS_REPO/$CATALOG_REL"
553
+ DECLINED_PATH="$PROJECT_ARIS_DIR/$DECLINED_NAME"
554
+
555
+ if $LIST_GROUPS; then
556
+ print_group_catalog
557
+ exit 0
558
+ fi
559
+
560
+ check_no_symlinked_parents() {
561
+ local p
562
+ for p in "$PROJECT_ARIS_DIR" "$PROJECT_PATH/.github" "$PROJECT_SKILLS_DIR" "$PROJECT_AGENTS_DIR"; do
563
+ if is_symlink "$p"; then
564
+ die "$p is a symlink; refusing to mutate symlinked parent directories"
565
+ fi
566
+ done
567
+ }
568
+
569
+ check_legacy_nested_install() {
570
+ if [[ -e "$LEGACY_NESTED" || -L "$LEGACY_NESTED" ]]; then
571
+ die "legacy nested install detected at $LEGACY_NESTED. Remove it before using the flat .github/skills/<name> layout."
572
+ fi
573
+ }
574
+
575
+ write_lock_metadata() {
576
+ cat > "$LOCK_DIR/owner.json" <<EOF
577
+ {"host":"$(hostname)","pid":$$,"started_at":"$(date -u +%Y-%m-%dT%H:%M:%SZ)","tool":"install_aris_copilot.sh"}
578
+ EOF
579
+ echo "$$" > "$LOCK_DIR/owner.pid"
580
+ echo "$(hostname)" > "$LOCK_DIR/owner.host"
581
+ }
582
+
583
+ release_lock() {
584
+ [[ -d "$LOCK_DIR" ]] || return 0
585
+ if [[ -f "$LOCK_DIR/owner.pid" && -f "$LOCK_DIR/owner.host" ]]; then
586
+ local pid host
587
+ pid="$(cat "$LOCK_DIR/owner.pid" 2>/dev/null || echo "")"
588
+ host="$(cat "$LOCK_DIR/owner.host" 2>/dev/null || echo "")"
589
+ if [[ "$pid" == "$$" && "$host" == "$(hostname)" ]]; then
590
+ rm -rf "$LOCK_DIR"
591
+ fi
592
+ fi
593
+ }
594
+
595
+ acquire_lock() {
596
+ mkdir -p "$PROJECT_ARIS_DIR"
597
+ if mkdir "$LOCK_DIR" 2>/dev/null; then
598
+ write_lock_metadata
599
+ trap release_lock EXIT INT TERM
600
+ return 0
601
+ fi
602
+ if $CLEAR_STALE_LOCK; then
603
+ warn "removing stale lock: $LOCK_DIR"
604
+ rm -rf "$LOCK_DIR"
605
+ mkdir "$LOCK_DIR" || die "cannot acquire lock after stale clear"
606
+ write_lock_metadata
607
+ trap release_lock EXIT INT TERM
608
+ return 0
609
+ fi
610
+ local owner=""
611
+ [[ -f "$LOCK_DIR/owner.json" ]] && owner="$(cat "$LOCK_DIR/owner.json")"
612
+ die "another install_aris_copilot.sh appears to be running (lock: $LOCK_DIR, owner: $owner)"
613
+ }
614
+
615
+ compute_plan() {
616
+ local upstream_file="$1" manifest_data="$2" out="$3"
617
+ local kind name source_rel target_path expected_target current_target in_manifest
618
+ : > "$out"
619
+
620
+ while IFS='|' read -r kind name source_rel; do
621
+ [[ -z "$name" ]] && continue
622
+ if [[ "$kind" == "agent" ]]; then
623
+ target_path="$PROJECT_AGENTS_DIR/$name"
624
+ else
625
+ target_path="$PROJECT_SKILLS_DIR/$name"
626
+ fi
627
+ expected_target="$ARIS_REPO/$source_rel"
628
+ in_manifest=false
629
+ [[ -n "$(manifest_lookup_target "$manifest_data" "$name")" ]] && in_manifest=true
630
+
631
+ if [[ -L "$target_path" ]]; then
632
+ current_target="$(read_link_target "$target_path")"
633
+ [[ "$current_target" != /* ]] && current_target="$(canonicalize "$(dirname "$target_path")/$current_target")"
634
+ if [[ "$current_target" == "$expected_target" ]]; then
635
+ if $in_manifest; then
636
+ printf "REUSE|%s|%s|%s|\n" "$kind" "$name" "$source_rel" >> "$out"
637
+ else
638
+ printf "ADOPT|%s|%s|%s|\n" "$kind" "$name" "$source_rel" >> "$out"
639
+ fi
640
+ elif $in_manifest || name_in_replace_allowlist "$name"; then
641
+ printf "UPDATE_TARGET|%s|%s|%s|%s\n" "$kind" "$name" "$source_rel" "$current_target" >> "$out"
642
+ else
643
+ printf "CONFLICT|%s|%s|%s|symlink_to:%s\n" "$kind" "$name" "$source_rel" "$current_target" >> "$out"
644
+ fi
645
+ elif [[ -e "$target_path" ]]; then
646
+ printf "CONFLICT|%s|%s|%s|real_path\n" "$kind" "$name" "$source_rel" >> "$out"
647
+ else
648
+ printf "CREATE|%s|%s|%s|\n" "$kind" "$name" "$source_rel" >> "$out"
649
+ fi
650
+ done < "$upstream_file"
651
+
652
+ # Detect removals: entries in manifest but not in upstream
653
+ local recorded_repo_root
654
+ recorded_repo_root="$(manifest_repo_root "$MANIFEST_PATH" 2>/dev/null || true)"
655
+ local mkind mname msource mtarget mmode
656
+ while IFS=$'\t' read -r mkind mname msource mtarget mmode; do
657
+ [[ -z "$mname" ]] && continue
658
+ if awk -F'|' -v n="$mname" '$2==n {found=1} END{exit found?0:1}' "$upstream_file"; then
659
+ continue
660
+ fi
661
+ [[ -n "$recorded_repo_root" ]] || die "manifest missing repo_root: $MANIFEST_PATH"
662
+ printf "REMOVE|%s|%s|%s|%s/%s\n" "$mkind" "$mname" "$msource" "$recorded_repo_root" "$msource" >> "$out"
663
+ done < "$manifest_data"
664
+ }
665
+
666
+ print_plan() {
667
+ local plan="$1"
668
+ local action
669
+ log ""
670
+ log "Plan summary:"
671
+ for action in CREATE ADOPT UPDATE_TARGET REUSE REMOVE CONFLICT; do
672
+ log " $action: $(grep -c "^$action|" "$plan" || true)"
673
+ done
674
+ if grep -q '^CONFLICT|' "$plan"; then
675
+ log ""
676
+ log "Conflicts:"
677
+ while IFS='|' read -r _ kind name _source extra; do
678
+ log " - $name ($kind): $extra"
679
+ done < <(grep '^CONFLICT|' "$plan")
680
+ fi
681
+ }
682
+
683
+ write_manifest_tmp() {
684
+ local plan="$1" out="$2"
685
+ {
686
+ printf "version\t%s\n" "$MANIFEST_VERSION"
687
+ printf "repo_root\t%s\n" "$ARIS_REPO"
688
+ printf "project_root\t%s\n" "$PROJECT_PATH"
689
+ printf "generated\t%s\n" "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
690
+ printf "installer\tinstall_aris_copilot.sh\n"
691
+ printf "kind\tname\tsource_rel\ttarget_rel\tmode\n"
692
+ awk -F'|' '$1=="REUSE"||$1=="ADOPT"||$1=="CREATE"||$1=="UPDATE_TARGET"{print}' "$plan" \
693
+ | while IFS='|' read -r _ kind name source_rel _extra; do
694
+ if [[ "$kind" == "agent" ]]; then
695
+ printf "%s\t%s\t%s\t%s/%s\tsymlink\n" "$kind" "$name" "$source_rel" "$AGENTS_REL" "$name"
696
+ else
697
+ printf "%s\t%s\t%s\t%s/%s\tsymlink\n" "$kind" "$name" "$source_rel" "$SKILLS_REL" "$name"
698
+ fi
699
+ done
700
+ } > "$out"
701
+ }
702
+
703
+ apply_plan() {
704
+ local plan="$1"
705
+ local action kind name source_rel extra target_path expected_target current_target
706
+ mkdir -p "$PROJECT_SKILLS_DIR"
707
+ mkdir -p "$PROJECT_AGENTS_DIR"
708
+ while IFS='|' read -r action kind name source_rel extra; do
709
+ [[ -z "$name" ]] && continue
710
+ if [[ "$kind" == "agent" ]]; then
711
+ target_path="$PROJECT_AGENTS_DIR/$name"
712
+ else
713
+ target_path="$PROJECT_SKILLS_DIR/$name"
714
+ fi
715
+ expected_target="$ARIS_REPO/$source_rel"
716
+ case "$action" in
717
+ REUSE|ADOPT)
718
+ :
719
+ ;;
720
+ CREATE)
721
+ if [[ -e "$target_path" || -L "$target_path" ]]; then
722
+ die "path appeared during install: $target_path"
723
+ fi
724
+ if $DRY_RUN; then
725
+ log " (dry-run) ln -s $expected_target $target_path"
726
+ else
727
+ ln -s "$expected_target" "$target_path"
728
+ log " + $name"
729
+ fi
730
+ ;;
731
+ UPDATE_TARGET)
732
+ if [[ -L "$target_path" ]]; then
733
+ current_target="$(read_link_target "$target_path")"
734
+ [[ "$current_target" != /* ]] && current_target="$(canonicalize "$(dirname "$target_path")/$current_target")"
735
+ else
736
+ current_target=""
737
+ fi
738
+ if [[ "$current_target" != "$extra" ]]; then
739
+ die "symlink target changed during install for $name (expected: $extra, got: ${current_target:-missing})"
740
+ fi
741
+ if $DRY_RUN; then
742
+ log " (dry-run) relink $target_path -> $expected_target"
743
+ else
744
+ rm -f "$target_path"
745
+ ln -s "$expected_target" "$target_path"
746
+ log " ~ $name"
747
+ fi
748
+ ;;
749
+ REMOVE)
750
+ [[ -n "$extra" ]] || die "remove action missing recorded target for $name"
751
+ if [[ -L "$target_path" ]]; then
752
+ current_target="$(read_link_target "$target_path")"
753
+ [[ "$current_target" != /* ]] && current_target="$(canonicalize "$(dirname "$target_path")/$current_target")"
754
+ if [[ "$current_target" == "$extra" ]]; then
755
+ if $DRY_RUN; then
756
+ log " (dry-run) rm $target_path"
757
+ else
758
+ rm -f "$target_path"
759
+ log " - $name"
760
+ fi
761
+ else
762
+ die "refusing to remove $name; target changed during reconcile (expected: $extra, got: $current_target)"
763
+ fi
764
+ elif [[ -e "$target_path" ]]; then
765
+ die "refusing to remove $name; target path is no longer a symlink"
766
+ else
767
+ log " - $name (already removed)"
768
+ fi
769
+ ;;
770
+ CONFLICT)
771
+ die "conflict reached apply phase for $name"
772
+ ;;
773
+ esac
774
+ done < "$plan"
775
+ }
776
+
777
+ commit_manifest() {
778
+ local manifest_tmp="$1"
779
+ if $DRY_RUN; then
780
+ log " (dry-run) would commit manifest"
781
+ return 0
782
+ fi
783
+ mkdir -p "$PROJECT_ARIS_DIR"
784
+ if [[ -f "$MANIFEST_PATH" ]]; then
785
+ cp -p "$MANIFEST_PATH" "$MANIFEST_PREV.tmp"
786
+ mv -f "$MANIFEST_PREV.tmp" "$MANIFEST_PREV"
787
+ fi
788
+ mv -f "$manifest_tmp" "$MANIFEST_PATH"
789
+ }
790
+
791
+ update_agents_doc() {
792
+ local installed_names_file="$1"
793
+ $NO_DOC && return 0
794
+ local original=""
795
+ [[ -f "$DOC_FILE" ]] && original="$(cat "$DOC_FILE")"
796
+ local count new_block new_content tmp current
797
+ count="$(wc -l < "$installed_names_file" | tr -d ' ')"
798
+ local repo_lookup_cmd
799
+ repo_lookup_cmd="ARIS_REPO=\$(awk -F'\\t' '\$1==\"repo_root\"{print \$2; exit}' \"$PROJECT_PATH/$ARIS_DIR_NAME/$MANIFEST_NAME\")"
800
+ new_block="$BLOCK_BEGIN
801
+ ## ARIS Copilot CLI Skill Scope
802
+ ARIS mainline skills installed in this project for GitHub Copilot CLI.
803
+ Managed entries: $count
804
+ Manifest: \`$ARIS_DIR_NAME/$MANIFEST_NAME\`
805
+ ARIS repo root: \`$ARIS_REPO\`
806
+ Project skill path: \`$SKILLS_REL/<skill-name>\`
807
+ For ARIS workflows, prefer the project-local skills under \`$SKILLS_REL/\`.
808
+ When a skill needs ARIS helper scripts, resolve the repo root from the manifest or set it explicitly:
809
+ \`$repo_lookup_cmd\`
810
+ Do not edit or delete symlinked skills in place; update upstream or rerun:
811
+ \`bash $ARIS_REPO/tools/install_aris_copilot.sh \"$PROJECT_PATH\" --reconcile\`
812
+ For copied installs, use:
813
+ \`bash $ARIS_REPO/tools/smart_update_copilot.sh --project \"$PROJECT_PATH\"\`
814
+ $BLOCK_END"
815
+
816
+ if printf '%s' "$original" | grep -qF "$BLOCK_BEGIN"; then
817
+ new_content="$(python3 - "$DOC_FILE" "$BLOCK_BEGIN" "$BLOCK_END" "$new_block" <<'PYEOF'
818
+ import pathlib
819
+ import re
820
+ import sys
821
+
822
+ path, begin, end, body = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]
823
+ text = pathlib.Path(path).read_text() if pathlib.Path(path).exists() else ""
824
+ pattern = re.compile(re.escape(begin) + r".*?" + re.escape(end), re.DOTALL)
825
+ matches = pattern.findall(text)
826
+ if len(matches) > 1:
827
+ sys.stderr.write("ARIS-COPILOT:WARN multiple managed blocks found; skipping update\n")
828
+ sys.stdout.write(text)
829
+ else:
830
+ sys.stdout.write(pattern.sub(body, text))
831
+ PYEOF
832
+ )" || { warn "AGENTS.md update failed; continuing"; return 0; }
833
+ else
834
+ new_content="$original"
835
+ [[ -n "$new_content" && "${new_content: -1}" != $'\n' ]] && new_content="${new_content}"$'\n'
836
+ new_content="${new_content}${new_block}"$'\n'
837
+ fi
838
+
839
+ if $DRY_RUN; then
840
+ log " (dry-run) would update AGENTS.md managed block"
841
+ return 0
842
+ fi
843
+
844
+ tmp="$DOC_FILE.aris-copilot-tmp.$$"
845
+ printf '%s' "$new_content" > "$tmp"
846
+ current=""
847
+ [[ -f "$DOC_FILE" ]] && current="$(cat "$DOC_FILE")"
848
+ if [[ "$current" != "$original" ]]; then
849
+ rm -f "$tmp"
850
+ warn "AGENTS.md changed during install; skipping managed block update"
851
+ return 0
852
+ fi
853
+ mv -f "$tmp" "$DOC_FILE"
854
+ log " updated AGENTS.md"
855
+ }
856
+
857
+ remove_agents_doc_block() {
858
+ $NO_DOC && return 0
859
+ [[ -f "$DOC_FILE" ]] || return 0
860
+
861
+ local original new_content tmp current
862
+ original="$(cat "$DOC_FILE")"
863
+ if ! printf '%s' "$original" | grep -qF "$BLOCK_BEGIN"; then
864
+ return 0
865
+ fi
866
+
867
+ new_content="$(python3 - "$DOC_FILE" "$BLOCK_BEGIN" "$BLOCK_END" <<'PYEOF'
868
+ import pathlib
869
+ import re
870
+ import sys
871
+
872
+ path, begin, end = sys.argv[1], sys.argv[2], sys.argv[3]
873
+ text = pathlib.Path(path).read_text()
874
+ pattern = re.compile(r"\n?" + re.escape(begin) + r".*?" + re.escape(end) + r"\n?", re.DOTALL)
875
+ matches = pattern.findall(text)
876
+ if len(matches) > 1:
877
+ sys.stderr.write("ARIS-COPILOT:WARN multiple managed blocks found; skipping removal\n")
878
+ sys.stdout.write(text)
879
+ else:
880
+ updated = pattern.sub("\n", text)
881
+ sys.stdout.write(updated.lstrip("\n"))
882
+ PYEOF
883
+ )" || { warn "AGENTS.md managed block removal failed; continuing"; return 0; }
884
+
885
+ if $DRY_RUN; then
886
+ log " (dry-run) would remove AGENTS.md managed block"
887
+ return 0
888
+ fi
889
+
890
+ tmp="$DOC_FILE.aris-copilot-tmp.$$"
891
+ printf '%s' "$new_content" > "$tmp"
892
+ current="$(cat "$DOC_FILE")"
893
+ if [[ "$current" != "$original" ]]; then
894
+ rm -f "$tmp"
895
+ warn "AGENTS.md changed during uninstall; skipping managed block removal"
896
+ return 0
897
+ fi
898
+ mv -f "$tmp" "$DOC_FILE"
899
+ log " removed AGENTS.md managed block"
900
+ }
901
+
902
+ do_uninstall() {
903
+ [[ -f "$MANIFEST_PATH" ]] || die "no manifest at $MANIFEST_PATH; nothing to uninstall"
904
+ local manifest_data
905
+ manifest_data="$(mktemp -t aris-copilot-manifest.XXXX)"
906
+ load_manifest "$MANIFEST_PATH" "$manifest_data"
907
+ log ""
908
+ log "Uninstall plan:"
909
+ while IFS=$'\t' read -r kind name _source _target _mode; do
910
+ [[ -z "$name" ]] && continue
911
+ log " - $name ($kind)"
912
+ done < "$manifest_data"
913
+ if ! $DRY_RUN; then
914
+ prompt "Proceed? [y/N]" || { log "aborted"; exit 0; }
915
+ fi
916
+ local kind name source_rel target_rel mode target_path expected_target current_target
917
+ local recorded_repo_root
918
+ recorded_repo_root="$(manifest_repo_root "$MANIFEST_PATH")"
919
+ [[ -n "$recorded_repo_root" ]] || die "manifest missing repo_root: $MANIFEST_PATH"
920
+ while IFS=$'\t' read -r kind name source_rel target_rel mode; do
921
+ [[ -z "$name" ]] && continue
922
+ target_path="$PROJECT_PATH/$target_rel"
923
+ expected_target="$recorded_repo_root/$source_rel"
924
+ if [[ -L "$target_path" ]]; then
925
+ current_target="$(read_link_target "$target_path")"
926
+ [[ "$current_target" != /* ]] && current_target="$(canonicalize "$(dirname "$target_path")/$current_target")"
927
+ if [[ "$current_target" == "$expected_target" ]]; then
928
+ if $DRY_RUN; then
929
+ log " (dry-run) rm $target_path"
930
+ else
931
+ rm -f "$target_path"
932
+ log " - removed $name"
933
+ fi
934
+ else
935
+ warn "skipping $name during uninstall; target changed to $current_target"
936
+ fi
937
+ else
938
+ warn "skipping $name during uninstall; not a symlink"
939
+ fi
940
+ done < "$manifest_data"
941
+ rm -f "$manifest_data"
942
+ if ! $DRY_RUN; then
943
+ mv -f "$MANIFEST_PATH" "$MANIFEST_PREV"
944
+ log " uninstalled (manifest preserved as $MANIFEST_PREV)"
945
+ fi
946
+ remove_agents_doc_block
947
+ }
948
+
949
+ # --- Main ---
950
+ log ""
951
+ log "ARIS Copilot CLI Project Install"
952
+ log " Project: $PROJECT_PATH"
953
+ log " Repo: $ARIS_REPO"
954
+ log " Target: $SKILLS_REL/"
955
+ log " Action: $ACTION$($DRY_RUN && echo ' (dry-run)')"
956
+ log ""
957
+
958
+ check_no_symlinked_parents
959
+ check_legacy_nested_install
960
+ if ! $DRY_RUN; then
961
+ acquire_lock
962
+ fi
963
+
964
+ if [[ "$ACTION" == "uninstall" ]]; then
965
+ do_uninstall
966
+ exit 0
967
+ fi
968
+
969
+ if [[ "$ACTION" == "reconcile" && ! -f "$MANIFEST_PATH" ]]; then
970
+ die "--reconcile requires existing manifest; none found at $MANIFEST_PATH"
971
+ fi
972
+
973
+ UPSTREAM_FILE="$(mktemp -t aris-copilot-upstream.XXXX)"
974
+ build_upstream_inventory "$ARIS_REPO" "$UPSTREAM_FILE"
975
+
976
+ MANIFEST_DATA="$(mktemp -t aris-copilot-manifest.XXXX)"
977
+ load_manifest "$MANIFEST_PATH" "$MANIFEST_DATA"
978
+
979
+ # Selective install (#366): build the selected set, then plan against it.
980
+ SELECTED_FILE="$(mktemp -t aris-copilot-selected.XXXX)"
981
+ DECLINED_CANDIDATES="$(mktemp -t aris-copilot-declined.XXXX)"
982
+ build_selection "$UPSTREAM_FILE" "$DECLINED_CANDIDATES" "$SELECTED_FILE"
983
+ SELECTED_UPSTREAM="$(mktemp -t aris-copilot-upstream-sel.XXXX)"
984
+ filter_upstream_by_selection "$UPSTREAM_FILE" "$SELECTED_FILE" "$SELECTED_UPSTREAM"
985
+ N_SELECTED=$(grep -c '^skill|' "$SELECTED_UPSTREAM" || true)
986
+ N_UPSTREAM=$(grep -c '^skill|' "$UPSTREAM_FILE" || true)
987
+ log ""
988
+ log "Selection: $N_SELECTED of $N_UPSTREAM upstream skills"
989
+
990
+ PLAN_FILE="$(mktemp -t aris-copilot-plan.XXXX)"
991
+ compute_plan "$SELECTED_UPSTREAM" "$MANIFEST_DATA" "$PLAN_FILE"
992
+ print_plan "$PLAN_FILE"
993
+
994
+ if grep -q '^CONFLICT|' "$PLAN_FILE"; then
995
+ die "aborting due to unresolved conflicts. Use --replace-link NAME for a symlink you want to replace."
996
+ fi
997
+
998
+ if $DRY_RUN; then
999
+ log ""
1000
+ log "(dry-run) no changes made"
1001
+ rm -f "$UPSTREAM_FILE" "$MANIFEST_DATA" "$PLAN_FILE" "$SELECTED_FILE" "$SELECTED_UPSTREAM" "$DECLINED_CANDIDATES"
1002
+ exit 0
1003
+ fi
1004
+
1005
+ N_CHANGES="$(awk -F'|' '$1=="CREATE"||$1=="UPDATE_TARGET"||$1=="REMOVE"{n++} END{print n+0}' "$PLAN_FILE")"
1006
+ if (( N_CHANGES > 0 )); then
1007
+ prompt "Apply these $N_CHANGES changes? [y/N]" || { log "aborted"; exit 0; }
1008
+ fi
1009
+
1010
+ MANIFEST_TMP="$MANIFEST_PATH.tmp.$$"
1011
+ write_manifest_tmp "$PLAN_FILE" "$MANIFEST_TMP"
1012
+ log ""
1013
+ log "Applying:"
1014
+ apply_plan "$PLAN_FILE"
1015
+ commit_manifest "$MANIFEST_TMP"
1016
+
1017
+ # #366: persist declined skills + global repo pointer (best-effort, after
1018
+ # manifest commit for the same reason as install_aris.sh).
1019
+ save_declined "$DECLINED_CANDIDATES" "$SELECTED_FILE"
1020
+ ensure_global_pointer
1021
+
1022
+ INSTALLED_NAMES="$(mktemp -t aris-copilot-names.XXXX)"
1023
+ awk -F'|' '$1=="REUSE"||$1=="ADOPT"||$1=="CREATE"||$1=="UPDATE_TARGET"{print $3}' "$PLAN_FILE" > "$INSTALLED_NAMES"
1024
+ update_agents_doc "$INSTALLED_NAMES"
1025
+
1026
+ # Post-install verification
1027
+ if ! $DRY_RUN; then
1028
+ local_bad=0
1029
+ while IFS=$'\t' read -r kind name source_rel target_rel mode; do
1030
+ [[ -z "$name" ]] && continue
1031
+ target_path="$PROJECT_PATH/$target_rel"
1032
+ expected_target="$ARIS_REPO/$source_rel"
1033
+ if [[ ! -L "$target_path" ]]; then
1034
+ warn "verify: missing symlink $target_path"
1035
+ local_bad=$((local_bad + 1))
1036
+ continue
1037
+ fi
1038
+ current_target="$(read_link_target "$target_path")"
1039
+ [[ "$current_target" != /* ]] && current_target="$(canonicalize "$(dirname "$target_path")/$current_target")"
1040
+ if [[ "$current_target" != "$expected_target" ]]; then
1041
+ warn "verify: wrong target for $target_path -> $current_target"
1042
+ local_bad=$((local_bad + 1))
1043
+ fi
1044
+ done < <(awk -F'\t' '
1045
+ BEGIN { in_body=0 }
1046
+ /^kind\tname\tsource_rel\ttarget_rel\tmode$/ { in_body=1; next }
1047
+ in_body && NF==5 { print }
1048
+ ' "$MANIFEST_PATH")
1049
+ (( local_bad == 0 )) && log "" && log "Copilot CLI install complete. $N_CHANGES changes applied."
1050
+ fi
1051
+
1052
+ rm -f "$UPSTREAM_FILE" "$MANIFEST_DATA" "$PLAN_FILE" "$INSTALLED_NAMES" "$SELECTED_FILE" "$SELECTED_UPSTREAM" "$DECLINED_CANDIDATES"