dsh-aris-panel 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +98 -0
- package/README_CN.md +87 -0
- package/dsh/checkout.patch.yml +38 -0
- package/dsh/client.js +634 -0
- package/dsh/cordis.patch.yml +44 -0
- package/dsh/index.mjs +76 -0
- package/dsh/run-status.mjs +182 -0
- package/dsh/scope-limits.mjs +50 -0
- package/dsh/workbench.mjs +291 -0
- package/mcp-servers/claude-review/README.md +93 -0
- package/mcp-servers/claude-review/run_with_claude_aws.sh +49 -0
- package/mcp-servers/claude-review/server.py +718 -0
- package/mcp-servers/codex-image2/README.md +65 -0
- package/mcp-servers/codex-image2/server.py +893 -0
- package/mcp-servers/feishu-bridge/requirements.txt +1 -0
- package/mcp-servers/feishu-bridge/server.py +240 -0
- package/mcp-servers/gemini-review/README.md +171 -0
- package/mcp-servers/gemini-review/server.py +1856 -0
- package/mcp-servers/llm-chat/requirements.txt +1 -0
- package/mcp-servers/llm-chat/server.py +664 -0
- package/mcp-servers/manual-review/README.md +133 -0
- package/mcp-servers/manual-review/server.py +910 -0
- package/mcp-servers/manual-review/ui.html +279 -0
- package/mcp-servers/minimax-chat/requirements.txt +1 -0
- package/mcp-servers/minimax-chat/server.py +381 -0
- package/package.json +51 -0
- package/skills/ablation-planner/SKILL.md +123 -0
- package/skills/alphaxiv/SKILL.md +196 -0
- package/skills/analyze-results/SKILL.md +46 -0
- package/skills/arxiv/SKILL.md +248 -0
- package/skills/auto-paper-improvement-loop/SKILL.md +651 -0
- package/skills/auto-review-loop/SKILL.md +1137 -0
- package/skills/auto-review-loop-llm/SKILL.md +259 -0
- package/skills/auto-review-loop-minimax/SKILL.md +302 -0
- package/skills/citation-audit/SKILL.md +502 -0
- package/skills/claims-drafting/SKILL.md +227 -0
- package/skills/comm-lit-review/SKILL.md +297 -0
- package/skills/deepxiv/SKILL.md +263 -0
- package/skills/dse-loop/SKILL.md +296 -0
- package/skills/embodiment-description/SKILL.md +129 -0
- package/skills/exa-search/SKILL.md +205 -0
- package/skills/experiment-audit/SKILL.md +311 -0
- package/skills/experiment-bridge/SKILL.md +376 -0
- package/skills/experiment-plan/SKILL.md +249 -0
- package/skills/experiment-queue/SKILL.md +431 -0
- package/skills/experiment-queue/scripts/build_manifest.py +142 -0
- package/skills/experiment-queue/scripts/queue_manager.py +433 -0
- package/skills/feishu-notify/SKILL.md +156 -0
- package/skills/figure-description/SKILL.md +138 -0
- package/skills/figure-spec/SKILL.md +262 -0
- package/skills/figure-spec/scripts/figure_renderer.py +799 -0
- package/skills/formula-derivation/SKILL.md +280 -0
- package/skills/gemini-search/SKILL.md +231 -0
- package/skills/grant-proposal/SKILL.md +698 -0
- package/skills/idea-creator/SKILL.md +542 -0
- package/skills/idea-discovery/SKILL.md +521 -0
- package/skills/idea-discovery-robot/SKILL.md +363 -0
- package/skills/integrity-forensics/SKILL.md +284 -0
- package/skills/interview-cheatsheet/SKILL.md +245 -0
- package/skills/invention-structuring/SKILL.md +188 -0
- package/skills/jurisdiction-format/SKILL.md +192 -0
- package/skills/kill-argument/SKILL.md +437 -0
- package/skills/mermaid-diagram/SKILL.md +419 -0
- package/skills/meta-apply/SKILL.md +141 -0
- package/skills/meta-optimize/SKILL.md +437 -0
- package/skills/monitor-experiment/SKILL.md +140 -0
- package/skills/novelty-check/SKILL.md +101 -0
- package/skills/openalex/SKILL.md +237 -0
- package/skills/overleaf-sync/SKILL.md +220 -0
- package/skills/paper-claim-audit/SKILL.md +348 -0
- package/skills/paper-compile/SKILL.md +266 -0
- package/skills/paper-figure/SKILL.md +312 -0
- package/skills/paper-illustration/SKILL.md +736 -0
- package/skills/paper-illustration-image2/SKILL.md +391 -0
- package/skills/paper-illustration-image2/scripts/paper_illustration_image2.py +255 -0
- package/skills/paper-plan/SKILL.md +386 -0
- package/skills/paper-poster/SKILL.md +19 -0
- package/skills/paper-poster-html/DESIGN_FINAL.md +176 -0
- package/skills/paper-poster-html/IMPLEMENTATION_CONVENTIONS.md +161 -0
- package/skills/paper-poster-html/LICENSES/posterly-MIT.txt +21 -0
- package/skills/paper-poster-html/NOTICE.md +57 -0
- package/skills/paper-poster-html/SKILL.md +323 -0
- package/skills/paper-poster-html/scripts/_posterly/__init__.py +0 -0
- package/skills/paper-poster-html/scripts/_posterly/canvas.py +200 -0
- package/skills/paper-poster-html/scripts/_posterly/measure.py +588 -0
- package/skills/paper-poster-html/scripts/_posterly/polish.py +498 -0
- package/skills/paper-poster-html/scripts/_posterly/preflight.py +489 -0
- package/skills/paper-poster-html/scripts/_posterly/render.py +215 -0
- package/skills/paper-poster-html/scripts/_posterly/textutil.py +16 -0
- package/skills/paper-poster-html/scripts/_posterly/verify_final.py +171 -0
- package/skills/paper-poster-html/scripts/asset_check.py +897 -0
- package/skills/paper-poster-html/scripts/extract_pdf_figures.py +666 -0
- package/skills/paper-poster-html/scripts/poster_check.py +251 -0
- package/skills/paper-poster-html/scripts/preprocess_figures.py +238 -0
- package/skills/paper-poster-html/scripts/render_preview.py +217 -0
- package/skills/paper-poster-html/scripts/run_gates.py +556 -0
- package/skills/paper-poster-html/scripts/style_check.py +1324 -0
- package/skills/paper-poster-html/templates/COMPONENTS.md +462 -0
- package/skills/paper-poster-html/templates/README.md +170 -0
- package/skills/paper-poster-html/templates/landscape_4col.html +1032 -0
- package/skills/paper-poster-html/templates/landscape_hero.html +1046 -0
- package/skills/paper-poster-html/templates/portrait_2col.html +947 -0
- package/skills/paper-poster-html/templates/tokens/acl.json +9 -0
- package/skills/paper-poster-html/templates/tokens/cvpr.json +9 -0
- package/skills/paper-poster-html/templates/tokens/generic.json +9 -0
- package/skills/paper-poster-html/templates/tokens/iclr.json +9 -0
- package/skills/paper-poster-html/templates/tokens/icml.json +9 -0
- package/skills/paper-poster-html/templates/tokens/neurips.json +9 -0
- package/skills/paper-slides/SKILL.md +635 -0
- package/skills/paper-talk/SKILL.md +381 -0
- package/skills/paper-write/SKILL.md +604 -0
- package/skills/paper-write/templates/IEEEtran.bst +2409 -0
- package/skills/paper-write/templates/IEEEtran.cls +6347 -0
- package/skills/paper-write/templates/iclr2026.tex +84 -0
- package/skills/paper-write/templates/icml2025.tex +87 -0
- package/skills/paper-write/templates/ieee_conference.tex +89 -0
- package/skills/paper-write/templates/ieee_journal.tex +93 -0
- package/skills/paper-write/templates/math_commands.tex +48 -0
- package/skills/paper-write/templates/neurips2025.tex +80 -0
- package/skills/paper-writing/SKILL.md +916 -0
- package/skills/patent-novelty-check/SKILL.md +153 -0
- package/skills/patent-pipeline/SKILL.md +344 -0
- package/skills/patent-review/SKILL.md +203 -0
- package/skills/pixel-art/SKILL.md +137 -0
- package/skills/prior-art-search/SKILL.md +146 -0
- package/skills/proof-checker/SKILL.md +866 -0
- package/skills/proof-orchestrator/NOTICE.md +24 -0
- package/skills/proof-orchestrator/SKILL.md +254 -0
- package/skills/proof-orchestrator/references/audit-output-contract.md +126 -0
- package/skills/proof-orchestrator/references/deepseek-routing.md +74 -0
- package/skills/proof-orchestrator/references/dispatch-prompts.md +227 -0
- package/skills/proof-orchestrator/references/notation-audit.md +135 -0
- package/skills/proof-orchestrator/references/proof-audit-rubric.md +70 -0
- package/skills/proof-orchestrator/references/stress-tests.md +38 -0
- package/skills/proof-writer/SKILL.md +223 -0
- package/skills/qzcli/SKILL.md +324 -0
- package/skills/rebuttal/SKILL.md +376 -0
- package/skills/render-html/SKILL.md +316 -0
- package/skills/render-html/scripts/render_html.py +1006 -0
- package/skills/render-html/scripts/templates/academic.html +703 -0
- package/skills/render-html/scripts/templates/dashboard.html +333 -0
- package/skills/research-lit/SKILL.md +756 -0
- package/skills/research-pipeline/SKILL.md +384 -0
- package/skills/research-refine/SKILL.md +770 -0
- package/skills/research-refine-pipeline/SKILL.md +186 -0
- package/skills/research-review/SKILL.md +198 -0
- package/skills/research-wiki/SKILL.md +461 -0
- package/skills/resubmit-pipeline/SKILL.md +447 -0
- package/skills/result-to-claim/SKILL.md +311 -0
- package/skills/run-experiment/SKILL.md +313 -0
- package/skills/semantic-scholar/SKILL.md +236 -0
- package/skills/serverless-modal/SKILL.md +335 -0
- package/skills/shared-references/acceptance-gate.md +324 -0
- package/skills/shared-references/assurance-contract.md +248 -0
- package/skills/shared-references/capture-antipatterns.md +78 -0
- package/skills/shared-references/citation-discipline.md +583 -0
- package/skills/shared-references/compute-env-contract.md +163 -0
- package/skills/shared-references/effort-contract.md +183 -0
- package/skills/shared-references/evidence-precheck.md +65 -0
- package/skills/shared-references/experiment-integrity.md +49 -0
- package/skills/shared-references/external-cadence.md +326 -0
- package/skills/shared-references/fan-out-pattern.md +366 -0
- package/skills/shared-references/injection-hygiene.md +127 -0
- package/skills/shared-references/integration-contract.md +461 -0
- package/skills/shared-references/output-composition.md +93 -0
- package/skills/shared-references/output-language.md +45 -0
- package/skills/shared-references/output-manifest.md +49 -0
- package/skills/shared-references/output-versioning.md +111 -0
- package/skills/shared-references/patent-format-cn.md +199 -0
- package/skills/shared-references/patent-format-ep.md +173 -0
- package/skills/shared-references/patent-format-us.md +161 -0
- package/skills/shared-references/patent-writing-principles.md +197 -0
- package/skills/shared-references/prior-art-databases.md +141 -0
- package/skills/shared-references/resumable-runs.md +109 -0
- package/skills/shared-references/review-scope-limits.md +81 -0
- package/skills/shared-references/review-tracing.md +391 -0
- package/skills/shared-references/reviewer-independence.md +79 -0
- package/skills/shared-references/reviewer-routing.md +852 -0
- package/skills/shared-references/skill-governance.md +104 -0
- package/skills/shared-references/taste-calibration.md +85 -0
- package/skills/shared-references/venue-checklists.md +114 -0
- package/skills/shared-references/wiki-helper-resolution.md +134 -0
- package/skills/shared-references/writing-principles.md +525 -0
- package/skills/skills-codex/README.md +102 -0
- package/skills/skills-codex/README_CN.md +100 -0
- package/skills/skills-codex/ablation-planner/SKILL.md +126 -0
- package/skills/skills-codex/alphaxiv/SKILL.md +186 -0
- package/skills/skills-codex/analyze-results/SKILL.md +45 -0
- package/skills/skills-codex/arxiv/SKILL.md +210 -0
- package/skills/skills-codex/auto-paper-improvement-loop/SKILL.md +574 -0
- package/skills/skills-codex/auto-review-loop/SKILL.md +500 -0
- package/skills/skills-codex/auto-review-loop-llm/SKILL.md +247 -0
- package/skills/skills-codex/auto-review-loop-minimax/SKILL.md +290 -0
- package/skills/skills-codex/citation-audit/SKILL.md +504 -0
- package/skills/skills-codex/claims-drafting/SKILL.md +239 -0
- package/skills/skills-codex/comm-lit-review/SKILL.md +299 -0
- package/skills/skills-codex/comm-lit-review/references/domain-taxonomy.md +57 -0
- package/skills/skills-codex/comm-lit-review/references/output-template.md +37 -0
- package/skills/skills-codex/comm-lit-review/references/source-policy.md +99 -0
- package/skills/skills-codex/comm-lit-review/references/venue-tiering.md +112 -0
- package/skills/skills-codex/deepxiv/SKILL.md +142 -0
- package/skills/skills-codex/dse-loop/SKILL.md +285 -0
- package/skills/skills-codex/embodiment-description/SKILL.md +129 -0
- package/skills/skills-codex/exa-search/SKILL.md +192 -0
- package/skills/skills-codex/experiment-audit/SKILL.md +286 -0
- package/skills/skills-codex/experiment-bridge/SKILL.md +356 -0
- package/skills/skills-codex/experiment-plan/SKILL.md +249 -0
- package/skills/skills-codex/experiment-queue/SKILL.md +401 -0
- package/skills/skills-codex/feishu-notify/SKILL.md +155 -0
- package/skills/skills-codex/figure-description/SKILL.md +138 -0
- package/skills/skills-codex/figure-spec/SKILL.md +252 -0
- package/skills/skills-codex/formula-derivation/SKILL.md +280 -0
- package/skills/skills-codex/gemini-search/SKILL.md +205 -0
- package/skills/skills-codex/grant-proposal/SKILL.md +626 -0
- package/skills/skills-codex/idea-creator/SKILL.md +405 -0
- package/skills/skills-codex/idea-discovery/SKILL.md +475 -0
- package/skills/skills-codex/idea-discovery-robot/SKILL.md +362 -0
- package/skills/skills-codex/integrity-forensics/SKILL.md +106 -0
- package/skills/skills-codex/interview-cheatsheet/SKILL.md +245 -0
- package/skills/skills-codex/invention-structuring/SKILL.md +188 -0
- package/skills/skills-codex/jurisdiction-format/SKILL.md +192 -0
- package/skills/skills-codex/kill-argument/SKILL.md +403 -0
- package/skills/skills-codex/mermaid-diagram/SKILL.md +379 -0
- package/skills/skills-codex/meta-apply/SKILL.md +154 -0
- package/skills/skills-codex/meta-optimize/SKILL.md +348 -0
- package/skills/skills-codex/monitor-experiment/SKILL.md +98 -0
- package/skills/skills-codex/novelty-check/SKILL.md +89 -0
- package/skills/skills-codex/openalex/SKILL.md +228 -0
- package/skills/skills-codex/overleaf-sync/SKILL.md +220 -0
- package/skills/skills-codex/paper-claim-audit/SKILL.md +350 -0
- package/skills/skills-codex/paper-compile/SKILL.md +253 -0
- package/skills/skills-codex/paper-figure/SKILL.md +311 -0
- package/skills/skills-codex/paper-illustration/SKILL.md +690 -0
- package/skills/skills-codex/paper-illustration-image2/SKILL.md +383 -0
- package/skills/skills-codex/paper-illustration-image2/scripts/paper_illustration_image2.py +255 -0
- package/skills/skills-codex/paper-plan/SKILL.md +278 -0
- package/skills/skills-codex/paper-poster/SKILL.md +19 -0
- package/skills/skills-codex/paper-poster-html/SKILL.md +377 -0
- package/skills/skills-codex/paper-slides/SKILL.md +571 -0
- package/skills/skills-codex/paper-talk/SKILL.md +381 -0
- package/skills/skills-codex/paper-write/SKILL.md +411 -0
- package/skills/skills-codex/paper-write/templates/IEEEtran.bst +2409 -0
- package/skills/skills-codex/paper-write/templates/IEEEtran.cls +6347 -0
- package/skills/skills-codex/paper-write/templates/aaai2026.bst +1493 -0
- package/skills/skills-codex/paper-write/templates/aaai2026.sty +315 -0
- package/skills/skills-codex/paper-write/templates/aaai2026.tex +952 -0
- package/skills/skills-codex/paper-write/templates/acl.sty +312 -0
- package/skills/skills-codex/paper-write/templates/acl2026.tex +377 -0
- package/skills/skills-codex/paper-write/templates/acl_natbib.bst +1940 -0
- package/skills/skills-codex/paper-write/templates/acm.bst +3081 -0
- package/skills/skills-codex/paper-write/templates/acm_mm2026.tex +204 -0
- package/skills/skills-codex/paper-write/templates/acmart.cls +3520 -0
- package/skills/skills-codex/paper-write/templates/cvpr.bst +1448 -0
- package/skills/skills-codex/paper-write/templates/cvpr.sty +508 -0
- package/skills/skills-codex/paper-write/templates/cvpr2026.tex +63 -0
- package/skills/skills-codex/paper-write/templates/iclr2026.tex +84 -0
- package/skills/skills-codex/paper-write/templates/iclr2026_conference.bst +1440 -0
- package/skills/skills-codex/paper-write/templates/iclr2026_conference.sty +246 -0
- package/skills/skills-codex/paper-write/templates/icml2026.sty +767 -0
- package/skills/skills-codex/paper-write/templates/icml2026.tex +662 -0
- package/skills/skills-codex/paper-write/templates/ieee_conference.tex +89 -0
- package/skills/skills-codex/paper-write/templates/ieee_journal.tex +93 -0
- package/skills/skills-codex/paper-write/templates/math_commands.tex +48 -0
- package/skills/skills-codex/paper-write/templates/neurips2026.tex +493 -0
- package/skills/skills-codex/paper-write/templates/neurips_2026.sty +437 -0
- package/skills/skills-codex/paper-writing/SKILL.md +731 -0
- package/skills/skills-codex/patent-novelty-check/SKILL.md +153 -0
- package/skills/skills-codex/patent-pipeline/SKILL.md +344 -0
- package/skills/skills-codex/patent-review/SKILL.md +202 -0
- package/skills/skills-codex/pixel-art/SKILL.md +139 -0
- package/skills/skills-codex/prior-art-search/SKILL.md +146 -0
- package/skills/skills-codex/proof-checker/SKILL.md +554 -0
- package/skills/skills-codex/proof-orchestrator/SKILL.md +260 -0
- package/skills/skills-codex/proof-orchestrator/references/audit-output-contract.md +126 -0
- package/skills/skills-codex/proof-orchestrator/references/deepseek-routing.md +76 -0
- package/skills/skills-codex/proof-orchestrator/references/dispatch-prompts.md +227 -0
- package/skills/skills-codex/proof-orchestrator/references/notation-audit.md +135 -0
- package/skills/skills-codex/proof-orchestrator/references/proof-audit-rubric.md +70 -0
- package/skills/skills-codex/proof-orchestrator/references/stress-tests.md +38 -0
- package/skills/skills-codex/proof-writer/SKILL.md +222 -0
- package/skills/skills-codex/qzcli/SKILL.md +324 -0
- package/skills/skills-codex/rebuttal/SKILL.md +305 -0
- package/skills/skills-codex/render-html/SKILL.md +305 -0
- package/skills/skills-codex/render-html/scripts/__pycache__/render_html.cpython-314.pyc +0 -0
- package/skills/skills-codex/render-html/scripts/render_html.py +909 -0
- package/skills/skills-codex/render-html/scripts/templates/academic.html +342 -0
- package/skills/skills-codex/render-html/scripts/templates/dashboard.html +333 -0
- package/skills/skills-codex/research-lit/SKILL.md +464 -0
- package/skills/skills-codex/research-pipeline/SKILL.md +340 -0
- package/skills/skills-codex/research-refine/SKILL.md +721 -0
- package/skills/skills-codex/research-refine-pipeline/SKILL.md +186 -0
- package/skills/skills-codex/research-review/SKILL.md +135 -0
- package/skills/skills-codex/research-wiki/SKILL.md +421 -0
- package/skills/skills-codex/resubmit-pipeline/SKILL.md +444 -0
- package/skills/skills-codex/result-to-claim/SKILL.md +246 -0
- package/skills/skills-codex/run-experiment/SKILL.md +236 -0
- package/skills/skills-codex/semantic-scholar/SKILL.md +219 -0
- package/skills/skills-codex/serverless-modal/SKILL.md +335 -0
- package/skills/skills-codex/shared-references/acceptance-gate.md +336 -0
- package/skills/skills-codex/shared-references/assurance-contract.md +139 -0
- package/skills/skills-codex/shared-references/capture-antipatterns.md +84 -0
- package/skills/skills-codex/shared-references/citation-discipline.md +452 -0
- package/skills/skills-codex/shared-references/compute-env-contract.md +163 -0
- package/skills/skills-codex/shared-references/effort-contract.md +143 -0
- package/skills/skills-codex/shared-references/evidence-precheck.md +73 -0
- package/skills/skills-codex/shared-references/experiment-integrity.md +49 -0
- package/skills/skills-codex/shared-references/external-cadence.md +334 -0
- package/skills/skills-codex/shared-references/fan-out-pattern.md +375 -0
- package/skills/skills-codex/shared-references/injection-hygiene.md +132 -0
- package/skills/skills-codex/shared-references/integration-contract.md +372 -0
- package/skills/skills-codex/shared-references/output-composition.md +98 -0
- package/skills/skills-codex/shared-references/output-language.md +45 -0
- package/skills/skills-codex/shared-references/output-manifest.md +40 -0
- package/skills/skills-codex/shared-references/output-versioning.md +111 -0
- package/skills/skills-codex/shared-references/patent-format-cn.md +199 -0
- package/skills/skills-codex/shared-references/patent-format-ep.md +173 -0
- package/skills/skills-codex/shared-references/patent-format-us.md +161 -0
- package/skills/skills-codex/shared-references/patent-writing-principles.md +197 -0
- package/skills/skills-codex/shared-references/prior-art-databases.md +141 -0
- package/skills/skills-codex/shared-references/resumable-runs.md +125 -0
- package/skills/skills-codex/shared-references/review-scope-limits.md +81 -0
- package/skills/skills-codex/shared-references/review-tracing.md +144 -0
- package/skills/skills-codex/shared-references/reviewer-independence.md +66 -0
- package/skills/skills-codex/shared-references/reviewer-routing.md +128 -0
- package/skills/skills-codex/shared-references/skill-governance.md +119 -0
- package/skills/skills-codex/shared-references/taste-calibration.md +90 -0
- package/skills/skills-codex/shared-references/venue-checklists.md +73 -0
- package/skills/skills-codex/shared-references/wiki-helper-resolution.md +69 -0
- package/skills/skills-codex/shared-references/writing-principles.md +525 -0
- package/skills/skills-codex/slides-polish/SKILL.md +563 -0
- package/skills/skills-codex/specification-writing/SKILL.md +211 -0
- package/skills/skills-codex/system-profile/SKILL.md +103 -0
- package/skills/skills-codex/training-check/SKILL.md +83 -0
- package/skills/skills-codex/vast-gpu/SKILL.md +394 -0
- package/skills/skills-codex/web-debug-search/SKILL.md +334 -0
- package/skills/skills-codex/wiki-enrich/SKILL.md +255 -0
- package/skills/skills-codex/writing-systems-papers/SKILL.md +184 -0
- package/skills/skills-codex-claude-review/README.md +79 -0
- package/skills/skills-codex-claude-review/README_CN.md +78 -0
- package/skills/skills-codex-claude-review/auto-paper-improvement-loop/SKILL.md +581 -0
- package/skills/skills-codex-claude-review/auto-review-loop/SKILL.md +510 -0
- package/skills/skills-codex-claude-review/novelty-check/SKILL.md +102 -0
- package/skills/skills-codex-claude-review/paper-figure/SKILL.md +319 -0
- package/skills/skills-codex-claude-review/paper-plan/SKILL.md +287 -0
- package/skills/skills-codex-claude-review/paper-write/SKILL.md +420 -0
- package/skills/skills-codex-claude-review/research-refine/SKILL.md +732 -0
- package/skills/skills-codex-claude-review/research-review/SKILL.md +149 -0
- package/skills/skills-codex-gemini-review/README.md +176 -0
- package/skills/skills-codex-gemini-review/README_CN.md +175 -0
- package/skills/skills-codex-gemini-review/auto-paper-improvement-loop/SKILL.md +331 -0
- package/skills/skills-codex-gemini-review/auto-review-loop/SKILL.md +304 -0
- package/skills/skills-codex-gemini-review/grant-proposal/SKILL.md +630 -0
- package/skills/skills-codex-gemini-review/idea-creator/SKILL.md +263 -0
- package/skills/skills-codex-gemini-review/idea-discovery/SKILL.md +275 -0
- package/skills/skills-codex-gemini-review/idea-discovery-robot/SKILL.md +365 -0
- package/skills/skills-codex-gemini-review/novelty-check/SKILL.md +92 -0
- package/skills/skills-codex-gemini-review/paper-figure/SKILL.md +289 -0
- package/skills/skills-codex-gemini-review/paper-plan/SKILL.md +265 -0
- package/skills/skills-codex-gemini-review/paper-poster-html/SKILL.md +102 -0
- package/skills/skills-codex-gemini-review/paper-slides/SKILL.md +582 -0
- package/skills/skills-codex-gemini-review/paper-write/SKILL.md +346 -0
- package/skills/skills-codex-gemini-review/paper-writing/SKILL.md +312 -0
- package/skills/skills-codex-gemini-review/research-refine/SKILL.md +674 -0
- package/skills/skills-codex-gemini-review/research-review/SKILL.md +112 -0
- package/skills/slides-polish/SKILL.md +565 -0
- package/skills/specification-writing/SKILL.md +211 -0
- package/skills/system-profile/SKILL.md +103 -0
- package/skills/training-check/SKILL.md +132 -0
- package/skills/vast-gpu/SKILL.md +394 -0
- package/skills/web-debug-search/SKILL.md +334 -0
- package/skills/wiki-enrich/SKILL.md +257 -0
- package/skills/writing-systems-papers/SKILL.md +184 -0
- package/templates/CLAUDE_MD_TEMPLATE.md +29 -0
- package/templates/EXPERIMENT_LOG_TEMPLATE.md +47 -0
- package/templates/EXPERIMENT_PLAN_TEMPLATE.md +51 -0
- package/templates/EXPERIMENT_PLAN_TEMPLATE_CN.md +53 -0
- package/templates/FINDINGS_TEMPLATE.md +52 -0
- package/templates/IDEA_CANDIDATES_TEMPLATE.md +47 -0
- package/templates/IDEA_CANDIDATES_TEMPLATE_CN.md +47 -0
- package/templates/INVENTION_BRIEF_TEMPLATE.md +87 -0
- package/templates/MANIFEST_TEMPLATE.md +7 -0
- package/templates/NARRATIVE_REPORT_TEMPLATE.md +49 -0
- package/templates/PAPER_PLAN_TEMPLATE.md +47 -0
- package/templates/PATENT_CLAIMS_TEMPLATE.md +78 -0
- package/templates/PATENT_SPECIFICATION_TEMPLATE.md +68 -0
- package/templates/README.md +57 -0
- package/templates/RESEARCH_BRIEF_TEMPLATE.md +35 -0
- package/templates/RESEARCH_BRIEF_TEMPLATE_CN.md +41 -0
- package/templates/RESEARCH_CONTRACT_TEMPLATE.md +60 -0
- package/templates/claude-hooks/corpus_write_guard.json +16 -0
- package/templates/claude-hooks/corpus_write_guard.py +85 -0
- package/templates/claude-hooks/meta_logging.json +74 -0
- package/templates/gitignore-trace.txt +3 -0
- package/tools/__pycache__/check_skills_inventory.cpython-314.pyc +0 -0
- package/tools/arxiv_fetch.py +311 -0
- package/tools/capture_filter.py +126 -0
- package/tools/check_skills_inventory.py +273 -0
- package/tools/convert_skills_to_llm_chat.py +282 -0
- package/tools/copilot_native_evidence.py +818 -0
- package/tools/deepxiv_fetch.py +213 -0
- package/tools/evidence_check.py +212 -0
- package/tools/exa_search.py +425 -0
- package/tools/experiment_queue/README.md +118 -0
- package/tools/experiment_queue/build_manifest.py +44 -0
- package/tools/experiment_queue/queue_manager.py +44 -0
- package/tools/extract_paper_style.py +560 -0
- package/tools/figure_renderer.py +69 -0
- package/tools/forensics_gate.py +669 -0
- package/tools/generate_codex_claude_review_overrides.py +299 -0
- package/tools/idea_discovery_gate.py +256 -0
- package/tools/install_aris.ps1 +1372 -0
- package/tools/install_aris.sh +1370 -0
- package/tools/install_aris_codex.sh +1023 -0
- package/tools/install_aris_copilot.sh +1052 -0
- package/tools/iteration_log.py +143 -0
- package/tools/lint_skills_helpers.sh +84 -0
- package/tools/meta_opt/check_ready.sh +80 -0
- package/tools/meta_opt/log_event.sh +91 -0
- package/tools/meta_opt/trigger_eval.py +280 -0
- package/tools/meta_opt/trigger_evals.sample.json +28 -0
- package/tools/openalex_fetch.py +326 -0
- package/tools/overleaf_audit.sh +104 -0
- package/tools/overleaf_setup.sh +150 -0
- package/tools/paper_illustration_image2.py +62 -0
- package/tools/provenance.py +294 -0
- package/tools/research_wiki.py +1720 -0
- package/tools/review_gate.py +502 -0
- package/tools/run_state.py +399 -0
- package/tools/save_trace.sh +477 -0
- package/tools/semantic_scholar_fetch.py +438 -0
- package/tools/skill-groups.tsv +116 -0
- package/tools/skill_picker.py +238 -0
- package/tools/smart_update.ps1 +521 -0
- package/tools/smart_update.sh +591 -0
- package/tools/smart_update_codex.sh +419 -0
- package/tools/smart_update_copilot.sh +605 -0
- package/tools/threat_scan.py +222 -0
- package/tools/verify_paper_audits.sh +487 -0
- package/tools/verify_papers.py +613 -0
- package/tools/verify_wiki_coverage.sh +176 -0
- package/tools/watchdog.py +485 -0
|
@@ -0,0 +1,1372 @@
|
|
|
1
|
+
#Requires -Version 5.1
|
|
2
|
+
<#
|
|
3
|
+
.SYNOPSIS
|
|
4
|
+
Project-local ARIS skill installation for Windows.
|
|
5
|
+
|
|
6
|
+
.DESCRIPTION
|
|
7
|
+
Creates one flat junction per ARIS skill so Claude Code and Codex can
|
|
8
|
+
discover slash commands at one directory level:
|
|
9
|
+
|
|
10
|
+
Claude: <project>\.claude\skills\<skill-name>
|
|
11
|
+
Codex: <project>\.agents\skills\<skill-name>
|
|
12
|
+
|
|
13
|
+
Managed entries are tracked in .aris manifests. The script never replaces
|
|
14
|
+
real files or user-owned skill directories; conflicts must be resolved
|
|
15
|
+
explicitly.
|
|
16
|
+
|
|
17
|
+
Selective install (catalog: tools\skill-groups.tsv in the ARIS repo):
|
|
18
|
+
-Groups A,B install only these skill groups (see -ListGroups)
|
|
19
|
+
-Skills X,Y additionally install these skills (clears declined mark)
|
|
20
|
+
-Exclude X,Y never install these skills (recorded as declined)
|
|
21
|
+
-All install every upstream skill (legacy default)
|
|
22
|
+
-AddNew reconcile: accept all upstream skills not yet installed
|
|
23
|
+
-SkipNew reconcile: skip new upstream skills without prompting
|
|
24
|
+
-ListGroups print the group catalog and exit
|
|
25
|
+
-Quiet no prompts; non-interactive fallback for every decision
|
|
26
|
+
With no selection flags: a fresh install on a real console walks an
|
|
27
|
+
interactive per-group Y/n/e menu; a fresh install under -Quiet or a
|
|
28
|
+
redirected console installs everything (legacy behavior).
|
|
29
|
+
Reconcile keeps exactly what the manifest says is installed; NEW
|
|
30
|
+
upstream skills need per-skill confirmation (declined ones are
|
|
31
|
+
remembered in .aris\skills-declined.txt and never re-asked).
|
|
32
|
+
|
|
33
|
+
.PARAMETER Groups
|
|
34
|
+
Comma-separated catalog group ids to install on a fresh install (or
|
|
35
|
+
re-enable on reconcile).
|
|
36
|
+
|
|
37
|
+
.PARAMETER Skills
|
|
38
|
+
Comma-separated skill names to install on a fresh install, or re-enable
|
|
39
|
+
(un-decline) on reconcile.
|
|
40
|
+
|
|
41
|
+
.PARAMETER Exclude
|
|
42
|
+
Comma-separated skill names to never install; removed and recorded as
|
|
43
|
+
declined on reconcile.
|
|
44
|
+
|
|
45
|
+
.PARAMETER All
|
|
46
|
+
Install every upstream skill. Cannot be combined with -Groups/-Skills
|
|
47
|
+
(only with -Exclude).
|
|
48
|
+
|
|
49
|
+
.PARAMETER AddNew
|
|
50
|
+
On reconcile, silently accept every new upstream skill not yet installed
|
|
51
|
+
or declined.
|
|
52
|
+
|
|
53
|
+
.PARAMETER SkipNew
|
|
54
|
+
On reconcile, leave new upstream skills uninstalled without prompting or
|
|
55
|
+
recording them as declined (they are asked about again next time).
|
|
56
|
+
|
|
57
|
+
.PARAMETER ListGroups
|
|
58
|
+
Print the skill-group catalog (from tools\skill-groups.tsv) and exit.
|
|
59
|
+
|
|
60
|
+
.PARAMETER Quiet
|
|
61
|
+
Suppress interactive prompts; every decision that would otherwise prompt
|
|
62
|
+
falls back to its non-interactive default (see catalog notes above).
|
|
63
|
+
#>
|
|
64
|
+
|
|
65
|
+
[CmdletBinding()]
|
|
66
|
+
param(
|
|
67
|
+
[Parameter(Position = 0)]
|
|
68
|
+
[string]$ProjectPath = (Get-Location).Path,
|
|
69
|
+
|
|
70
|
+
[ValidateSet('auto', 'claude', 'codex')]
|
|
71
|
+
[string]$Platform = 'auto',
|
|
72
|
+
|
|
73
|
+
[string]$ArisRepo = '',
|
|
74
|
+
|
|
75
|
+
[switch]$DryRun,
|
|
76
|
+
[switch]$NoDoc,
|
|
77
|
+
[switch]$Reconcile,
|
|
78
|
+
[switch]$Uninstall,
|
|
79
|
+
[string[]]$ReplaceLink = @(),
|
|
80
|
+
[switch]$FromOld,
|
|
81
|
+
[ValidateSet('', 'keep-user', 'prefer-upstream')]
|
|
82
|
+
[string]$MigrateCopy = '',
|
|
83
|
+
[switch]$ClearStaleLock,
|
|
84
|
+
|
|
85
|
+
# Selective install (#366 parity with install_aris.sh).
|
|
86
|
+
[string]$Groups = '',
|
|
87
|
+
[string]$Skills = '',
|
|
88
|
+
[string]$Exclude = '',
|
|
89
|
+
[switch]$All,
|
|
90
|
+
[switch]$AddNew,
|
|
91
|
+
[switch]$SkipNew,
|
|
92
|
+
[switch]$ListGroups,
|
|
93
|
+
[switch]$Quiet,
|
|
94
|
+
|
|
95
|
+
# Kept only to preserve CLI recognition. It is intentionally unsafe for
|
|
96
|
+
# this installer and is rejected in favor of per-skill -ReplaceLink.
|
|
97
|
+
[switch]$Force
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
$ErrorActionPreference = 'Stop'
|
|
101
|
+
$Utf8NoBom = New-Object System.Text.UTF8Encoding($false)
|
|
102
|
+
$ManifestVersion = '1'
|
|
103
|
+
$SafeNameRegex = '^[A-Za-z0-9][A-Za-z0-9._-]*$'
|
|
104
|
+
$SupportNames = @('shared-references')
|
|
105
|
+
$CatalogRel = 'tools/skill-groups.tsv'
|
|
106
|
+
$GlobalPointerDir = Join-Path $HOME '.aris'
|
|
107
|
+
$GlobalPointerPath = Join-Path $GlobalPointerDir 'repo'
|
|
108
|
+
$script:LockDir = $null
|
|
109
|
+
$script:LockAcquired = $false
|
|
110
|
+
|
|
111
|
+
function Die {
|
|
112
|
+
param([string]$Message)
|
|
113
|
+
throw $Message
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function Normalize-PathString {
|
|
117
|
+
param([string]$Path)
|
|
118
|
+
return ([System.IO.Path]::GetFullPath($Path)).TrimEnd([char[]]@('\', '/'))
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function Same-Path {
|
|
122
|
+
param([string]$Left, [string]$Right)
|
|
123
|
+
return [System.StringComparer]::OrdinalIgnoreCase.Equals((Normalize-PathString $Left), (Normalize-PathString $Right))
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function Test-PathInside {
|
|
127
|
+
param([string]$Path, [string]$Root)
|
|
128
|
+
$normalizedPath = Normalize-PathString $Path
|
|
129
|
+
$normalizedRoot = Normalize-PathString $Root
|
|
130
|
+
if ([System.StringComparer]::OrdinalIgnoreCase.Equals($normalizedPath, $normalizedRoot)) {
|
|
131
|
+
return $true
|
|
132
|
+
}
|
|
133
|
+
$prefix = $normalizedRoot + [System.IO.Path]::DirectorySeparatorChar
|
|
134
|
+
return $normalizedPath.StartsWith($prefix, [System.StringComparison]::OrdinalIgnoreCase)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function Join-PathSegments {
|
|
138
|
+
param([string]$Root, [string[]]$Segments)
|
|
139
|
+
$path = $Root
|
|
140
|
+
foreach ($segment in $Segments) {
|
|
141
|
+
if ([string]::IsNullOrEmpty($path)) {
|
|
142
|
+
$path = $segment
|
|
143
|
+
} else {
|
|
144
|
+
$path = Join-Path $path $segment
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return $path
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function Get-PathSegments {
|
|
151
|
+
param([string]$Path)
|
|
152
|
+
$full = [System.IO.Path]::GetFullPath($Path)
|
|
153
|
+
$root = [System.IO.Path]::GetPathRoot($full)
|
|
154
|
+
if (-not $root) {
|
|
155
|
+
$root = ''
|
|
156
|
+
}
|
|
157
|
+
$rest = $full.Substring($root.Length).TrimEnd([char[]]@('\', '/'))
|
|
158
|
+
[string[]]$segments = @()
|
|
159
|
+
if ($rest) {
|
|
160
|
+
$segments = @($rest -split '[\\/]' | Where-Object { $_ -ne '' })
|
|
161
|
+
}
|
|
162
|
+
return [pscustomobject]@{
|
|
163
|
+
Root = $root
|
|
164
|
+
Segments = $segments
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function Resolve-ReparseChain {
|
|
169
|
+
param([string]$Path)
|
|
170
|
+
$current = [System.IO.Path]::GetFullPath($Path)
|
|
171
|
+
$seen = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
|
|
172
|
+
for ($depth = 0; $depth -lt 40; $depth++) {
|
|
173
|
+
$normalizedCurrent = Normalize-PathString $current
|
|
174
|
+
if (-not $seen.Add($normalizedCurrent)) {
|
|
175
|
+
Die "reparse point cycle detected while resolving: $Path"
|
|
176
|
+
}
|
|
177
|
+
$parts = Get-PathSegments $current
|
|
178
|
+
$candidate = $parts.Root
|
|
179
|
+
$rewrote = $false
|
|
180
|
+
for ($index = 0; $index -lt $parts.Segments.Count; $index++) {
|
|
181
|
+
if ([string]::IsNullOrEmpty($candidate)) {
|
|
182
|
+
$candidate = $parts.Segments[$index]
|
|
183
|
+
} else {
|
|
184
|
+
$candidate = Join-Path $candidate $parts.Segments[$index]
|
|
185
|
+
}
|
|
186
|
+
$item = Get-PathItem $candidate
|
|
187
|
+
if ($null -eq $item) {
|
|
188
|
+
return $normalizedCurrent
|
|
189
|
+
}
|
|
190
|
+
if (-not (Test-LinkItem $item)) {
|
|
191
|
+
continue
|
|
192
|
+
}
|
|
193
|
+
$target = Get-LinkTarget $candidate
|
|
194
|
+
if (-not $target) {
|
|
195
|
+
return $normalizedCurrent
|
|
196
|
+
}
|
|
197
|
+
[string[]]$remaining = @()
|
|
198
|
+
if (($index + 1) -lt $parts.Segments.Count) {
|
|
199
|
+
$remaining = @($parts.Segments[($index + 1)..($parts.Segments.Count - 1)])
|
|
200
|
+
}
|
|
201
|
+
$current = Join-PathSegments $target $remaining
|
|
202
|
+
$rewrote = $true
|
|
203
|
+
break
|
|
204
|
+
}
|
|
205
|
+
if (-not $rewrote) {
|
|
206
|
+
return $normalizedCurrent
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
Die "reparse point chain too deep while resolving: $Path"
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function Test-ResolvedPathInside {
|
|
213
|
+
param([string]$Path, [string]$Root)
|
|
214
|
+
return Test-PathInside (Resolve-ReparseChain $Path) (Resolve-ReparseChain $Root)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function Read-Text {
|
|
218
|
+
param([string]$Path)
|
|
219
|
+
return [System.IO.File]::ReadAllText($Path, [System.Text.Encoding]::UTF8)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function Write-Text {
|
|
223
|
+
param([string]$Path, [string]$Text)
|
|
224
|
+
[System.IO.File]::WriteAllText($Path, $Text, $Utf8NoBom)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function Get-PathItem {
|
|
228
|
+
param([string]$Path)
|
|
229
|
+
try {
|
|
230
|
+
return Get-Item -LiteralPath $Path -Force -ErrorAction Stop
|
|
231
|
+
} catch {
|
|
232
|
+
return $null
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function Test-LinkItem {
|
|
237
|
+
param($Item)
|
|
238
|
+
return $null -ne $Item -and $Item.LinkType -in @('Junction', 'SymbolicLink')
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function Get-LinkTarget {
|
|
242
|
+
param([string]$Path)
|
|
243
|
+
$item = Get-PathItem $Path
|
|
244
|
+
if (-not (Test-LinkItem $item)) {
|
|
245
|
+
return ''
|
|
246
|
+
}
|
|
247
|
+
$target = $item.Target
|
|
248
|
+
if ($target -is [array]) {
|
|
249
|
+
$target = $target[0]
|
|
250
|
+
}
|
|
251
|
+
if (-not [System.IO.Path]::IsPathRooted([string]$target)) {
|
|
252
|
+
$target = Join-Path (Split-Path -Parent $Path) ([string]$target)
|
|
253
|
+
}
|
|
254
|
+
return Normalize-PathString ([string]$target)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function Join-RelativePath {
|
|
258
|
+
param([string]$Root, [string]$Relative)
|
|
259
|
+
return Join-Path $Root ($Relative.Replace([char]'/', [char][System.IO.Path]::DirectorySeparatorChar))
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function Resolve-ArisRepo {
|
|
263
|
+
if ($ArisRepo) {
|
|
264
|
+
if (-not (Test-Path -LiteralPath (Join-Path $ArisRepo 'skills') -PathType Container)) {
|
|
265
|
+
Die "-ArisRepo path has no skills directory: $ArisRepo"
|
|
266
|
+
}
|
|
267
|
+
return (Resolve-Path -LiteralPath $ArisRepo).ProviderPath
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
$parent = Split-Path -Parent $PSScriptRoot
|
|
271
|
+
if (Test-Path -LiteralPath (Join-Path $parent 'skills') -PathType Container) {
|
|
272
|
+
return (Resolve-Path -LiteralPath $parent).ProviderPath
|
|
273
|
+
}
|
|
274
|
+
if ($env:ARIS_REPO -and (Test-Path -LiteralPath (Join-Path $env:ARIS_REPO 'skills') -PathType Container)) {
|
|
275
|
+
return (Resolve-Path -LiteralPath $env:ARIS_REPO).ProviderPath
|
|
276
|
+
}
|
|
277
|
+
foreach ($candidate in @(
|
|
278
|
+
(Join-Path $env:USERPROFILE 'Auto-claude-code-research-in-sleep'),
|
|
279
|
+
(Join-Path $env:USERPROFILE 'aris_repo'),
|
|
280
|
+
(Join-Path $env:USERPROFILE 'Desktop\Auto-claude-code-research-in-sleep'),
|
|
281
|
+
(Join-Path $env:USERPROFILE '.codex\Auto-claude-code-research-in-sleep'),
|
|
282
|
+
(Join-Path $env:USERPROFILE '.claude\Auto-claude-code-research-in-sleep')
|
|
283
|
+
)) {
|
|
284
|
+
if (Test-Path -LiteralPath (Join-Path $candidate 'skills') -PathType Container) {
|
|
285
|
+
return (Resolve-Path -LiteralPath $candidate).ProviderPath
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
Die 'cannot find ARIS repo. Use -ArisRepo PATH or set ARIS_REPO.'
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function Detect-Platform {
|
|
292
|
+
param([string]$ProjectRoot)
|
|
293
|
+
$hasClaude = (Test-Path -LiteralPath (Join-Path $ProjectRoot 'CLAUDE.md')) -or
|
|
294
|
+
(Test-Path -LiteralPath (Join-Path $ProjectRoot '.claude\skills')) -or
|
|
295
|
+
(Test-Path -LiteralPath (Join-Path $ProjectRoot '.claude\settings.json'))
|
|
296
|
+
$hasCodex = (Test-Path -LiteralPath (Join-Path $ProjectRoot 'AGENTS.md')) -or
|
|
297
|
+
(Test-Path -LiteralPath (Join-Path $ProjectRoot '.agents\skills')) -or
|
|
298
|
+
(Test-Path -LiteralPath (Join-Path $ProjectRoot '.codex\config.toml'))
|
|
299
|
+
if ($hasClaude -and $hasCodex) {
|
|
300
|
+
Die 'project has both Claude and Codex markers; pass -Platform claude or -Platform codex'
|
|
301
|
+
}
|
|
302
|
+
if ($hasClaude) { return 'claude' }
|
|
303
|
+
if ($hasCodex) { return 'codex' }
|
|
304
|
+
Die 'cannot auto-detect platform; pass -Platform claude or -Platform codex'
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function New-Config {
|
|
308
|
+
param([string]$ProjectRoot, [string]$RepoRoot, [string]$SelectedPlatform)
|
|
309
|
+
if ($SelectedPlatform -eq 'claude') {
|
|
310
|
+
return [pscustomobject]@{
|
|
311
|
+
Platform = 'claude'
|
|
312
|
+
RepoRoot = $RepoRoot
|
|
313
|
+
SourceRoot = Join-Path $RepoRoot 'skills'
|
|
314
|
+
SourceRelPrefix = 'skills'
|
|
315
|
+
TargetRel = '.claude\skills'
|
|
316
|
+
TargetRelDisplay = '.claude/skills'
|
|
317
|
+
LegacyNestedRel = '.claude\skills\aris'
|
|
318
|
+
ManifestName = 'installed-skills.txt'
|
|
319
|
+
ManifestPrevName = 'installed-skills.txt.prev'
|
|
320
|
+
DeclinedName = 'skills-declined.txt'
|
|
321
|
+
LockName = '.install.lock.d'
|
|
322
|
+
DocName = 'CLAUDE.md'
|
|
323
|
+
BlockBegin = '<!-- ARIS:BEGIN -->'
|
|
324
|
+
BlockEnd = '<!-- ARIS:END -->'
|
|
325
|
+
Title = 'ARIS Skill Scope'
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return [pscustomobject]@{
|
|
329
|
+
Platform = 'codex'
|
|
330
|
+
RepoRoot = $RepoRoot
|
|
331
|
+
SourceRoot = Join-Path $RepoRoot 'skills\skills-codex'
|
|
332
|
+
SourceRelPrefix = 'skills/skills-codex'
|
|
333
|
+
TargetRel = '.agents\skills'
|
|
334
|
+
TargetRelDisplay = '.agents/skills'
|
|
335
|
+
LegacyNestedRel = '.agents\skills\aris'
|
|
336
|
+
ManifestName = 'installed-skills-codex.txt'
|
|
337
|
+
ManifestPrevName = 'installed-skills-codex.txt.prev'
|
|
338
|
+
DeclinedName = 'skills-declined-codex.txt'
|
|
339
|
+
LockName = '.install-codex.lock.d'
|
|
340
|
+
DocName = 'AGENTS.md'
|
|
341
|
+
BlockBegin = '<!-- ARIS-CODEX:BEGIN -->'
|
|
342
|
+
BlockEnd = '<!-- ARIS-CODEX:END -->'
|
|
343
|
+
Title = 'ARIS Codex Skill Scope'
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function Test-SafeName {
|
|
348
|
+
param([string]$Name)
|
|
349
|
+
return $Name -match $SafeNameRegex
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
# ─── Selective install (#366 parity) ───────────────────────────────────────
|
|
353
|
+
# Catalog = tools/skill-groups.tsv in the ARIS repo, shared across platforms.
|
|
354
|
+
# Two record types (tab-separated):
|
|
355
|
+
# group\t<id>\t<display>\t<description>
|
|
356
|
+
# skill\t<name>\t<group-id>\t<requires: comma list or "->
|
|
357
|
+
# Selection state lives in two project files:
|
|
358
|
+
# .aris/installed-skills[-codex].txt — what IS installed (manifest)
|
|
359
|
+
# .aris/skills-declined[-codex].txt — skills the user explicitly said no
|
|
360
|
+
# to; never re-prompted on reconcile.
|
|
361
|
+
# "Skipped" (via -SkipNew / -Quiet) is NOT declined — those skills are asked
|
|
362
|
+
# about again on the next interactive reconcile.
|
|
363
|
+
|
|
364
|
+
function Get-CommaList {
|
|
365
|
+
# Comma-prefixed returns: PowerShell unrolls IEnumerable return values onto
|
|
366
|
+
# the pipeline, so a 0- or 1-element array collapses to $null or a bare
|
|
367
|
+
# scalar at the call site unless wrapped as the sole pipeline object.
|
|
368
|
+
param([string]$Value)
|
|
369
|
+
if ([string]::IsNullOrWhiteSpace($Value)) { return ,@() }
|
|
370
|
+
return ,@($Value -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ })
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function Test-IsInteractive {
|
|
374
|
+
if ($Quiet) { return $false }
|
|
375
|
+
try { return -not [Console]::IsInputRedirected } catch { return $false }
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function Load-Catalog {
|
|
379
|
+
param([string]$Path)
|
|
380
|
+
$catalog = [pscustomobject]@{
|
|
381
|
+
Path = $Path
|
|
382
|
+
Exists = (Test-Path -LiteralPath $Path -PathType Leaf)
|
|
383
|
+
Groups = New-Object System.Collections.Generic.List[object]
|
|
384
|
+
SkillGroup = @{}
|
|
385
|
+
SkillRequires = @{}
|
|
386
|
+
GroupSkills = @{}
|
|
387
|
+
}
|
|
388
|
+
if (-not $catalog.Exists) { return $catalog }
|
|
389
|
+
foreach ($line in Get-Content -LiteralPath $Path -Encoding UTF8) {
|
|
390
|
+
if (-not $line -or $line.StartsWith('#')) { continue }
|
|
391
|
+
$fields = $line -split "`t"
|
|
392
|
+
if ($fields.Count -lt 4) { continue }
|
|
393
|
+
if ($fields[0] -eq 'group') {
|
|
394
|
+
$gid = $fields[1]
|
|
395
|
+
$catalog.Groups.Add([pscustomobject]@{ Id = $gid; Display = $fields[2]; Desc = $fields[3] })
|
|
396
|
+
if (-not $catalog.GroupSkills.ContainsKey($gid)) {
|
|
397
|
+
$catalog.GroupSkills[$gid] = New-Object System.Collections.Generic.List[string]
|
|
398
|
+
}
|
|
399
|
+
} elseif ($fields[0] -eq 'skill') {
|
|
400
|
+
$name = $fields[1]
|
|
401
|
+
$gid = $fields[2]
|
|
402
|
+
$catalog.SkillGroup[$name] = $gid
|
|
403
|
+
if (-not $catalog.GroupSkills.ContainsKey($gid)) {
|
|
404
|
+
$catalog.GroupSkills[$gid] = New-Object System.Collections.Generic.List[string]
|
|
405
|
+
}
|
|
406
|
+
$catalog.GroupSkills[$gid].Add($name)
|
|
407
|
+
$requires = $fields[3]
|
|
408
|
+
if ($requires -and $requires -ne '-') {
|
|
409
|
+
$catalog.SkillRequires[$name] = Get-CommaList $requires
|
|
410
|
+
} else {
|
|
411
|
+
$catalog.SkillRequires[$name] = @()
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return $catalog
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function Get-CatalogGroupIds { param($Catalog) return ,@($Catalog.Groups | ForEach-Object { $_.Id }) }
|
|
419
|
+
|
|
420
|
+
function Get-CatalogSkillsInGroup {
|
|
421
|
+
param($Catalog, [string]$GroupId)
|
|
422
|
+
if ($Catalog.GroupSkills.ContainsKey($GroupId)) { return ,@($Catalog.GroupSkills[$GroupId]) }
|
|
423
|
+
return ,@()
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function Get-CatalogGroupOf {
|
|
427
|
+
param($Catalog, [string]$Name)
|
|
428
|
+
if ($Catalog.SkillGroup.ContainsKey($Name)) { return $Catalog.SkillGroup[$Name] }
|
|
429
|
+
return $null
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function Get-CatalogRequires {
|
|
433
|
+
param($Catalog, [string]$Name)
|
|
434
|
+
if ($Catalog.SkillRequires.ContainsKey($Name)) { return ,@($Catalog.SkillRequires[$Name]) }
|
|
435
|
+
return ,@()
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function Show-GroupCatalog {
|
|
439
|
+
param($Catalog)
|
|
440
|
+
if (-not $Catalog.Exists) { Die "skill catalog not found: $($Catalog.Path)" }
|
|
441
|
+
Write-Host "Skill groups (from $($Catalog.Path)):"
|
|
442
|
+
foreach ($g in $Catalog.Groups) {
|
|
443
|
+
$groupSkills = Get-CatalogSkillsInGroup $Catalog $g.Id
|
|
444
|
+
Write-Host ''
|
|
445
|
+
Write-Host (" {0,-14} {1} - {2} [{3} skills]" -f $g.Id, $g.Display, $g.Desc, $groupSkills.Count)
|
|
446
|
+
foreach ($name in $groupSkills) { Write-Host " $name" }
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function Read-DeclinedSet {
|
|
451
|
+
param([string]$Path)
|
|
452
|
+
$set = New-Object System.Collections.Generic.HashSet[string]
|
|
453
|
+
if (Test-Path -LiteralPath $Path -PathType Leaf) {
|
|
454
|
+
foreach ($line in Get-Content -LiteralPath $Path -Encoding UTF8) {
|
|
455
|
+
$trimmed = $line.Trim()
|
|
456
|
+
if ($trimmed) { $set.Add($trimmed) | Out-Null }
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
return ,$set
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
# Final declined set = (old declined ∪ new declines ∪ excludes ∪ fresh
|
|
463
|
+
# unselected) minus everything selected. Atomic write, same dir as target.
|
|
464
|
+
function Save-DeclinedSet {
|
|
465
|
+
param(
|
|
466
|
+
[string]$Path,
|
|
467
|
+
[System.Collections.Generic.HashSet[string]]$Declined,
|
|
468
|
+
[System.Collections.Generic.HashSet[string]]$Selected
|
|
469
|
+
)
|
|
470
|
+
if ($DryRun) { return }
|
|
471
|
+
$final = @($Declined | Where-Object { -not $Selected.Contains($_) } | Sort-Object)
|
|
472
|
+
$existed = Test-Path -LiteralPath $Path -PathType Leaf
|
|
473
|
+
if ($final.Count -eq 0 -and -not $existed) { return }
|
|
474
|
+
$dir = Split-Path -Parent $Path
|
|
475
|
+
New-Item -ItemType Directory -Force -Path $dir | Out-Null
|
|
476
|
+
$content = if ($final.Count -gt 0) { ($final -join "`n") + "`n" } else { '' }
|
|
477
|
+
$tmp = "$Path.tmp.$PID"
|
|
478
|
+
Write-Text $tmp $content
|
|
479
|
+
Move-Item -LiteralPath $tmp -Destination $Path -Force
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
# Auto-include hard pipeline deps (catalog `requires` column, transitively).
|
|
483
|
+
# A dep excluded this run is never auto-added — warn instead.
|
|
484
|
+
function Expand-SelectionDeps {
|
|
485
|
+
param(
|
|
486
|
+
$Catalog,
|
|
487
|
+
[System.Collections.Generic.HashSet[string]]$Selected,
|
|
488
|
+
[System.Collections.Generic.HashSet[string]]$ExcludeSet,
|
|
489
|
+
[System.Collections.Generic.HashSet[string]]$UpstreamSkillNames
|
|
490
|
+
)
|
|
491
|
+
if (-not $Catalog.Exists) { return }
|
|
492
|
+
$changed = $true
|
|
493
|
+
while ($changed) {
|
|
494
|
+
$changed = $false
|
|
495
|
+
foreach ($name in @($Selected)) {
|
|
496
|
+
foreach ($dep in (Get-CatalogRequires $Catalog $name)) {
|
|
497
|
+
if ($Selected.Contains($dep)) { continue }
|
|
498
|
+
if ($ExcludeSet.Contains($dep)) {
|
|
499
|
+
Write-Warning "'$name' requires '$dep' but it is excluded -- that pipeline phase will break"
|
|
500
|
+
continue
|
|
501
|
+
}
|
|
502
|
+
if (-not $UpstreamSkillNames.Contains($dep)) { continue }
|
|
503
|
+
$Selected.Add($dep) | Out-Null
|
|
504
|
+
Write-Host " -> auto-including '$dep' (required by '$name')"
|
|
505
|
+
$changed = $true
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
# Validate -Groups/-Skills names against catalog + upstream.
|
|
512
|
+
function Test-SelectionFlags {
|
|
513
|
+
param($Catalog, [string[]]$GroupsList, [string[]]$SkillsList, [System.Collections.Generic.HashSet[string]]$UpstreamSkillNames)
|
|
514
|
+
if ($GroupsList.Count -gt 0) {
|
|
515
|
+
if (-not $Catalog.Exists) { Die "-Groups needs the catalog at $($Catalog.Path) (update your aris-repo clone)" }
|
|
516
|
+
# Get-CatalogGroupIds is already comma-protected (see note above); do
|
|
517
|
+
# not re-wrap with @() here or the result nests into a 1-element array.
|
|
518
|
+
$validIds = Get-CatalogGroupIds $Catalog
|
|
519
|
+
foreach ($g in $GroupsList) {
|
|
520
|
+
if ($validIds -notcontains $g) { Die "unknown group '$g' -- run with -ListGroups to see valid ids" }
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
foreach ($s in $SkillsList) {
|
|
524
|
+
if (-not $UpstreamSkillNames.Contains($s)) { Die "unknown skill '$s' (not an upstream skill)" }
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
# Interactive group menu (fresh install on a real console, no selection flags).
|
|
529
|
+
function Read-InteractiveSelection {
|
|
530
|
+
param($Catalog, [System.Collections.Generic.HashSet[string]]$UpstreamSkillNames, [System.Collections.Generic.HashSet[string]]$Selected)
|
|
531
|
+
Write-Host ''
|
|
532
|
+
Write-Host "Interactive skill selection -- per group: Y=install all, n=skip, e=pick per skill."
|
|
533
|
+
foreach ($g in $Catalog.Groups) {
|
|
534
|
+
# Capture the (comma-protected) catalog result into a real variable
|
|
535
|
+
# first, then filter — piping the function call straight into
|
|
536
|
+
# Where-Object would hand it the whole array as a single item.
|
|
537
|
+
$groupSkillsInCatalog = Get-CatalogSkillsInGroup $Catalog $g.Id
|
|
538
|
+
$groupSkills = @($groupSkillsInCatalog | Where-Object { $UpstreamSkillNames.Contains($_) })
|
|
539
|
+
if ($groupSkills.Count -eq 0) { continue }
|
|
540
|
+
Write-Host ''
|
|
541
|
+
Write-Host "$($g.Display) ($($g.Id)) -- $($g.Desc)"
|
|
542
|
+
foreach ($name in $groupSkills) { Write-Host " $name" }
|
|
543
|
+
$reply = Read-Host "Install group '$($g.Id)' ($($groupSkills.Count) skills)? [Y/n/e]"
|
|
544
|
+
if ($reply -match '^[nN]') {
|
|
545
|
+
continue
|
|
546
|
+
} elseif ($reply -match '^[eE]') {
|
|
547
|
+
foreach ($name in $groupSkills) {
|
|
548
|
+
$r2 = Read-Host " install $name [Y/n]"
|
|
549
|
+
if ($r2 -notmatch '^[nN]') { $Selected.Add($name) | Out-Null }
|
|
550
|
+
}
|
|
551
|
+
} else {
|
|
552
|
+
foreach ($name in $groupSkills) { $Selected.Add($name) | Out-Null }
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
# Upstream skills the catalog doesn't know yet (catalog drift): never drop silently.
|
|
556
|
+
$ungrouped = @($UpstreamSkillNames | Where-Object { -not (Get-CatalogGroupOf $Catalog $_) } | Sort-Object)
|
|
557
|
+
if ($ungrouped.Count -gt 0) {
|
|
558
|
+
Write-Host ''
|
|
559
|
+
Write-Host 'Skills not in the catalog yet:'
|
|
560
|
+
foreach ($name in $ungrouped) { Write-Host " $name" }
|
|
561
|
+
foreach ($name in $ungrouped) {
|
|
562
|
+
$r2 = Read-Host " install $name [Y/n]"
|
|
563
|
+
if ($r2 -notmatch '^[nN]') { $Selected.Add($name) | Out-Null }
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
# Build the selected-skill set for this run.
|
|
569
|
+
function Build-Selection {
|
|
570
|
+
param($Catalog, $Manifest, [System.Collections.Generic.HashSet[string]]$UpstreamSkillNames, [string]$DeclinedPath, [bool]$IsFresh)
|
|
571
|
+
|
|
572
|
+
$declinedCandidates = Read-DeclinedSet $DeclinedPath
|
|
573
|
+
$excludeList = Get-CommaList $Exclude
|
|
574
|
+
$excludeSet = New-Object System.Collections.Generic.HashSet[string]
|
|
575
|
+
foreach ($e in $excludeList) {
|
|
576
|
+
$excludeSet.Add($e) | Out-Null
|
|
577
|
+
$declinedCandidates.Add($e) | Out-Null
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
$groupsList = Get-CommaList $Groups
|
|
581
|
+
$skillsList = Get-CommaList $Skills
|
|
582
|
+
Test-SelectionFlags $Catalog $groupsList $skillsList $UpstreamSkillNames
|
|
583
|
+
|
|
584
|
+
$selected = New-Object System.Collections.Generic.HashSet[string]
|
|
585
|
+
$hasSelectionFlags = ($groupsList.Count -gt 0 -or $skillsList.Count -gt 0)
|
|
586
|
+
|
|
587
|
+
if ($IsFresh) {
|
|
588
|
+
$subsetChoice = $false
|
|
589
|
+
if ($All -or (-not $hasSelectionFlags -and -not (Test-IsInteractive))) {
|
|
590
|
+
foreach ($n in $UpstreamSkillNames) { $selected.Add($n) | Out-Null }
|
|
591
|
+
} elseif ($hasSelectionFlags) {
|
|
592
|
+
$subsetChoice = $true
|
|
593
|
+
foreach ($g in $groupsList) {
|
|
594
|
+
foreach ($n in (Get-CatalogSkillsInGroup $Catalog $g)) {
|
|
595
|
+
if ($UpstreamSkillNames.Contains($n)) { $selected.Add($n) | Out-Null }
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
foreach ($n in $skillsList) { $selected.Add($n) | Out-Null }
|
|
599
|
+
} elseif ($Catalog.Exists) {
|
|
600
|
+
$subsetChoice = $true
|
|
601
|
+
Read-InteractiveSelection $Catalog $UpstreamSkillNames $selected
|
|
602
|
+
} else {
|
|
603
|
+
Write-Warning "catalog missing at $($Catalog.Path) -- falling back to full install"
|
|
604
|
+
foreach ($n in $UpstreamSkillNames) { $selected.Add($n) | Out-Null }
|
|
605
|
+
}
|
|
606
|
+
# Explicit subset choice ⇒ remember the rest as declined (won't re-ask).
|
|
607
|
+
if ($subsetChoice) {
|
|
608
|
+
foreach ($n in $UpstreamSkillNames) {
|
|
609
|
+
if (-not $selected.Contains($n)) { $declinedCandidates.Add($n) | Out-Null }
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
} else {
|
|
613
|
+
# Reconcile: installed set = manifest ∩ upstream (auto-detected).
|
|
614
|
+
foreach ($entry in $Manifest.Entries) {
|
|
615
|
+
if ($entry.Kind -eq 'skill' -and $UpstreamSkillNames.Contains($entry.Name)) {
|
|
616
|
+
$selected.Add($entry.Name) | Out-Null
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
# Flag-based additions re-enable previously declined skills.
|
|
620
|
+
foreach ($g in $groupsList) {
|
|
621
|
+
foreach ($n in (Get-CatalogSkillsInGroup $Catalog $g)) {
|
|
622
|
+
if ($UpstreamSkillNames.Contains($n)) { $selected.Add($n) | Out-Null }
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
foreach ($n in $skillsList) { $selected.Add($n) | Out-Null }
|
|
626
|
+
|
|
627
|
+
# NEW upstream skills: not installed, not declined, not just selected.
|
|
628
|
+
$newSkills = @($UpstreamSkillNames | Where-Object { -not $selected.Contains($_) -and -not $declinedCandidates.Contains($_) } | Sort-Object)
|
|
629
|
+
if ($newSkills.Count -gt 0) {
|
|
630
|
+
if ($All -or $AddNew) {
|
|
631
|
+
foreach ($n in $newSkills) { $selected.Add($n) | Out-Null }
|
|
632
|
+
Write-Host "-> adding $($newSkills.Count) new upstream skill(s) (-All/-AddNew)"
|
|
633
|
+
} elseif ($SkipNew -or -not (Test-IsInteractive)) {
|
|
634
|
+
# warn (not Write-Host): must stay visible under -Quiet — silently
|
|
635
|
+
# missing new skills is exactly the failure mode this feature fixes.
|
|
636
|
+
Write-Warning "new upstream skills NOT installed: $($newSkills -join ',')"
|
|
637
|
+
Write-Warning ' (rerun interactively to be asked, or pass -AddNew / -Skills NAME)'
|
|
638
|
+
} else {
|
|
639
|
+
Write-Host ''
|
|
640
|
+
Write-Host 'New skills appeared upstream since your last install:'
|
|
641
|
+
foreach ($name in $newSkills) {
|
|
642
|
+
$grp = Get-CatalogGroupOf $Catalog $name
|
|
643
|
+
if (-not $grp) { $grp = '?' }
|
|
644
|
+
$reply = Read-Host " install new skill $name (group: $grp) [y/N]"
|
|
645
|
+
if ($reply -match '^[yY]') { $selected.Add($name) | Out-Null }
|
|
646
|
+
else { $declinedCandidates.Add($name) | Out-Null }
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
# Excludes beat every other source (manifest, groups, deps, new skills):
|
|
653
|
+
# prune before dep expansion so an excluded pipeline doesn't drag deps in,
|
|
654
|
+
# and Expand-SelectionDeps itself refuses to re-add excluded names.
|
|
655
|
+
foreach ($e in $excludeSet) { $selected.Remove($e) | Out-Null }
|
|
656
|
+
Expand-SelectionDeps $Catalog $selected $excludeSet $UpstreamSkillNames
|
|
657
|
+
|
|
658
|
+
if ($selected.Count -eq 0) {
|
|
659
|
+
Die 'selection is empty -- nothing to install (use -All or -Groups/-Skills)'
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
return [pscustomobject]@{ Selected = $selected; DeclinedCandidates = $declinedCandidates }
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
# Layer-4 helper resolution: a global pointer file lets globally/copy-installed
|
|
666
|
+
# skills find $ArisRepo\tools without a per-project install.
|
|
667
|
+
function Ensure-GlobalPointer {
|
|
668
|
+
param([string]$RepoRoot)
|
|
669
|
+
if ($DryRun) { return }
|
|
670
|
+
try {
|
|
671
|
+
New-Item -ItemType Directory -Force -Path $GlobalPointerDir -ErrorAction Stop | Out-Null
|
|
672
|
+
} catch {
|
|
673
|
+
Write-Warning "cannot create $GlobalPointerDir -- skipping global pointer"
|
|
674
|
+
return
|
|
675
|
+
}
|
|
676
|
+
$current = ''
|
|
677
|
+
if (Test-Path -LiteralPath $GlobalPointerPath -PathType Leaf) {
|
|
678
|
+
$current = (Read-Text $GlobalPointerPath).Trim()
|
|
679
|
+
}
|
|
680
|
+
if ($current -eq $RepoRoot) { return }
|
|
681
|
+
$tmp = "$GlobalPointerPath.tmp.$PID"
|
|
682
|
+
Write-Text $tmp ($RepoRoot + "`n")
|
|
683
|
+
Move-Item -LiteralPath $tmp -Destination $GlobalPointerPath -Force
|
|
684
|
+
Write-Host " + global pointer $GlobalPointerPath -> $RepoRoot"
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function Build-Inventory {
|
|
688
|
+
param($Config)
|
|
689
|
+
if (-not (Test-Path -LiteralPath $Config.SourceRoot -PathType Container)) {
|
|
690
|
+
Die "source skills directory does not exist: $($Config.SourceRoot)"
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
$entries = New-Object System.Collections.Generic.List[object]
|
|
694
|
+
$resolvedRepoRoot = Resolve-ReparseChain $Config.RepoRoot
|
|
695
|
+
foreach ($dir in Get-ChildItem -LiteralPath $Config.SourceRoot -Directory | Sort-Object Name) {
|
|
696
|
+
$name = $dir.Name
|
|
697
|
+
if (-not (Test-SafeName $name)) {
|
|
698
|
+
Write-Warning "skipping unsafe upstream name: $name"
|
|
699
|
+
continue
|
|
700
|
+
}
|
|
701
|
+
$resolved = Resolve-ReparseChain $dir.FullName
|
|
702
|
+
if (-not (Test-PathInside $resolved $resolvedRepoRoot)) {
|
|
703
|
+
Write-Warning "skipping upstream link leading outside ARIS repo: $name -> $resolved"
|
|
704
|
+
continue
|
|
705
|
+
}
|
|
706
|
+
$kind = $null
|
|
707
|
+
if ($SupportNames -contains $name) {
|
|
708
|
+
$kind = 'support'
|
|
709
|
+
} elseif (Test-Path -LiteralPath (Join-Path $dir.FullName 'SKILL.md') -PathType Leaf) {
|
|
710
|
+
$kind = 'skill'
|
|
711
|
+
} else {
|
|
712
|
+
continue
|
|
713
|
+
}
|
|
714
|
+
$sourceRel = "$($Config.SourceRelPrefix)/$name"
|
|
715
|
+
$targetRel = ($Config.TargetRelDisplay + '/' + $name)
|
|
716
|
+
$entries.Add([pscustomobject]@{
|
|
717
|
+
Kind = $kind
|
|
718
|
+
Name = $name
|
|
719
|
+
SourceRel = $sourceRel
|
|
720
|
+
TargetRel = $targetRel
|
|
721
|
+
ExpectedTarget = (Normalize-PathString $dir.FullName)
|
|
722
|
+
})
|
|
723
|
+
}
|
|
724
|
+
if ($entries.Count -eq 0) {
|
|
725
|
+
Die "upstream inventory is empty: $($Config.SourceRoot)"
|
|
726
|
+
}
|
|
727
|
+
return $entries.ToArray()
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
function Load-Manifest {
|
|
731
|
+
param([string]$Path)
|
|
732
|
+
$result = [pscustomobject]@{
|
|
733
|
+
Headers = @{}
|
|
734
|
+
Entries = @()
|
|
735
|
+
ByName = @{}
|
|
736
|
+
}
|
|
737
|
+
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
|
|
738
|
+
return $result
|
|
739
|
+
}
|
|
740
|
+
$lines = Get-Content -LiteralPath $Path -Encoding UTF8
|
|
741
|
+
$inBody = $false
|
|
742
|
+
$entries = New-Object System.Collections.Generic.List[object]
|
|
743
|
+
foreach ($line in $lines) {
|
|
744
|
+
if ($line -eq 'kind name source_rel target_rel mode') {
|
|
745
|
+
$inBody = $true
|
|
746
|
+
continue
|
|
747
|
+
}
|
|
748
|
+
if (-not $inBody) {
|
|
749
|
+
$parts = $line -split "`t", 2
|
|
750
|
+
if ($parts.Count -eq 2) {
|
|
751
|
+
$result.Headers[$parts[0]] = $parts[1]
|
|
752
|
+
}
|
|
753
|
+
continue
|
|
754
|
+
}
|
|
755
|
+
$fields = $line -split "`t"
|
|
756
|
+
if ($fields.Count -ne 5) { continue }
|
|
757
|
+
$entry = [pscustomobject]@{
|
|
758
|
+
Kind = $fields[0]
|
|
759
|
+
Name = $fields[1]
|
|
760
|
+
SourceRel = $fields[2]
|
|
761
|
+
TargetRel = $fields[3]
|
|
762
|
+
Mode = $fields[4]
|
|
763
|
+
}
|
|
764
|
+
$entries.Add($entry)
|
|
765
|
+
$result.ByName[$entry.Name] = $entry
|
|
766
|
+
}
|
|
767
|
+
if ($result.Headers.ContainsKey('version') -and $result.Headers['version'] -ne $ManifestVersion) {
|
|
768
|
+
Die "manifest version mismatch in $Path"
|
|
769
|
+
}
|
|
770
|
+
$result.Entries = $entries.ToArray()
|
|
771
|
+
return $result
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
function Test-NameInReplaceList {
|
|
775
|
+
param([string]$Name)
|
|
776
|
+
return $ReplaceLink -contains $Name
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
function Compute-Plan {
|
|
780
|
+
param($Inventory, $Manifest, $Config, [string]$ProjectRoot, [string]$ManifestPath)
|
|
781
|
+
$plan = New-Object System.Collections.Generic.List[object]
|
|
782
|
+
$targetRoot = Join-Path $ProjectRoot $Config.TargetRel
|
|
783
|
+
|
|
784
|
+
foreach ($entry in $Inventory) {
|
|
785
|
+
$targetPath = Join-Path $targetRoot $entry.Name
|
|
786
|
+
$item = Get-PathItem $targetPath
|
|
787
|
+
$inManifest = $Manifest.ByName.ContainsKey($entry.Name)
|
|
788
|
+
|
|
789
|
+
if ($null -eq $item) {
|
|
790
|
+
$action = 'CREATE'
|
|
791
|
+
$extra = ''
|
|
792
|
+
} elseif (Test-LinkItem $item) {
|
|
793
|
+
$currentTarget = Get-LinkTarget $targetPath
|
|
794
|
+
if (Same-Path $currentTarget $entry.ExpectedTarget) {
|
|
795
|
+
$action = $(if ($inManifest) { 'REUSE' } else { 'ADOPT' })
|
|
796
|
+
$extra = ''
|
|
797
|
+
} elseif (($inManifest -or (Test-NameInReplaceList $entry.Name)) -and (Test-ResolvedPathInside $currentTarget $Config.RepoRoot)) {
|
|
798
|
+
$action = 'UPDATE_TARGET'
|
|
799
|
+
$extra = $currentTarget
|
|
800
|
+
} else {
|
|
801
|
+
$action = 'CONFLICT'
|
|
802
|
+
$extra = "link_to:$currentTarget"
|
|
803
|
+
}
|
|
804
|
+
} else {
|
|
805
|
+
$action = 'CONFLICT'
|
|
806
|
+
$extra = 'real_path'
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
$plan.Add([pscustomobject]@{
|
|
810
|
+
Action = $action
|
|
811
|
+
Kind = $entry.Kind
|
|
812
|
+
Name = $entry.Name
|
|
813
|
+
SourceRel = $entry.SourceRel
|
|
814
|
+
TargetRel = $entry.TargetRel
|
|
815
|
+
ExpectedTarget = $entry.ExpectedTarget
|
|
816
|
+
TargetPath = $targetPath
|
|
817
|
+
Extra = $extra
|
|
818
|
+
})
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
$inventoryNames = @{}
|
|
822
|
+
foreach ($entry in $Inventory) {
|
|
823
|
+
$inventoryNames[$entry.Name] = $true
|
|
824
|
+
}
|
|
825
|
+
$recordedRepo = ''
|
|
826
|
+
if ($Manifest.Headers.ContainsKey('repo_root')) {
|
|
827
|
+
$recordedRepo = $Manifest.Headers['repo_root']
|
|
828
|
+
}
|
|
829
|
+
foreach ($entry in $Manifest.Entries) {
|
|
830
|
+
if ($inventoryNames.ContainsKey($entry.Name)) {
|
|
831
|
+
continue
|
|
832
|
+
}
|
|
833
|
+
if (-not $recordedRepo) {
|
|
834
|
+
Die "manifest missing repo_root: $ManifestPath"
|
|
835
|
+
}
|
|
836
|
+
$plan.Add([pscustomobject]@{
|
|
837
|
+
Action = 'REMOVE'
|
|
838
|
+
Kind = $entry.Kind
|
|
839
|
+
Name = $entry.Name
|
|
840
|
+
SourceRel = $entry.SourceRel
|
|
841
|
+
TargetRel = $entry.TargetRel
|
|
842
|
+
ExpectedTarget = (Join-RelativePath $recordedRepo $entry.SourceRel)
|
|
843
|
+
TargetPath = (Join-RelativePath $ProjectRoot $entry.TargetRel)
|
|
844
|
+
Extra = (Join-RelativePath $recordedRepo $entry.SourceRel)
|
|
845
|
+
})
|
|
846
|
+
}
|
|
847
|
+
return $plan.ToArray()
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
function Print-Plan {
|
|
851
|
+
param($Plan, [string]$Mode)
|
|
852
|
+
Write-Host ''
|
|
853
|
+
Write-Host "ARIS Windows Install Plan"
|
|
854
|
+
Write-Host " Mode: $Mode"
|
|
855
|
+
foreach ($action in @('CREATE', 'ADOPT', 'UPDATE_TARGET', 'REUSE', 'REMOVE', 'CONFLICT')) {
|
|
856
|
+
$count = @($Plan | Where-Object { $_.Action -eq $action }).Count
|
|
857
|
+
Write-Host (" {0}: {1}" -f $action, $count)
|
|
858
|
+
}
|
|
859
|
+
$conflicts = @($Plan | Where-Object { $_.Action -eq 'CONFLICT' })
|
|
860
|
+
if ($conflicts.Count -gt 0) {
|
|
861
|
+
Write-Host ''
|
|
862
|
+
Write-Host 'CONFLICT entries:'
|
|
863
|
+
foreach ($item in $conflicts) {
|
|
864
|
+
Write-Host " - $($item.Name): $($item.Extra)"
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
function Check-NoSymlinkedParents {
|
|
870
|
+
param([string[]]$Paths)
|
|
871
|
+
foreach ($path in $Paths) {
|
|
872
|
+
$item = Get-PathItem $path
|
|
873
|
+
if (Test-LinkItem $item) {
|
|
874
|
+
Die "$path is a link; refusing to mutate linked parent directories"
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
function Test-ActiveLock {
|
|
880
|
+
param([string]$LockPath)
|
|
881
|
+
$pidPath = Join-Path $LockPath 'owner.pid'
|
|
882
|
+
$hostPath = Join-Path $LockPath 'owner.host'
|
|
883
|
+
if (-not (Test-Path -LiteralPath $pidPath) -or -not (Test-Path -LiteralPath $hostPath)) {
|
|
884
|
+
return $false
|
|
885
|
+
}
|
|
886
|
+
$ownerPid = (Get-Content -LiteralPath $pidPath -ErrorAction SilentlyContinue | Select-Object -First 1)
|
|
887
|
+
$ownerHost = (Get-Content -LiteralPath $hostPath -ErrorAction SilentlyContinue | Select-Object -First 1)
|
|
888
|
+
if ($ownerHost -ne [Environment]::MachineName) {
|
|
889
|
+
return $false
|
|
890
|
+
}
|
|
891
|
+
$pidNumber = 0
|
|
892
|
+
if (-not [int]::TryParse($ownerPid, [ref]$pidNumber)) {
|
|
893
|
+
return $false
|
|
894
|
+
}
|
|
895
|
+
return $null -ne (Get-Process -Id $pidNumber -ErrorAction SilentlyContinue)
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
function Acquire-Lock {
|
|
899
|
+
param([string]$ArisDir, [string]$LockPath)
|
|
900
|
+
if ($DryRun) { return }
|
|
901
|
+
New-Item -ItemType Directory -Force -Path $ArisDir | Out-Null
|
|
902
|
+
try {
|
|
903
|
+
New-Item -ItemType Directory -Path $LockPath -ErrorAction Stop | Out-Null
|
|
904
|
+
} catch {
|
|
905
|
+
if (-not $ClearStaleLock) {
|
|
906
|
+
Die "installer lock exists at $LockPath; rerun with -ClearStaleLock if no install is running"
|
|
907
|
+
}
|
|
908
|
+
if (Test-ActiveLock $LockPath) {
|
|
909
|
+
Die "installer lock at $LockPath belongs to a running process"
|
|
910
|
+
}
|
|
911
|
+
Remove-Item -LiteralPath $LockPath -Recurse -Force
|
|
912
|
+
New-Item -ItemType Directory -Path $LockPath -ErrorAction Stop | Out-Null
|
|
913
|
+
}
|
|
914
|
+
Write-Text (Join-Path $LockPath 'owner.pid') "$PID`n"
|
|
915
|
+
Write-Text (Join-Path $LockPath 'owner.host') "$([Environment]::MachineName)`n"
|
|
916
|
+
Write-Text (Join-Path $LockPath 'owner.json') "{`"host`":`"$([Environment]::MachineName)`",`"pid`":$PID,`"tool`":`"install_aris.ps1`"}`n"
|
|
917
|
+
$script:LockDir = $LockPath
|
|
918
|
+
$script:LockAcquired = $true
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
function Release-Lock {
|
|
922
|
+
if (-not $script:LockAcquired -or -not $script:LockDir) { return }
|
|
923
|
+
Remove-Item -LiteralPath $script:LockDir -Recurse -Force -ErrorAction SilentlyContinue
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
function New-Junction {
|
|
927
|
+
param([string]$Path, [string]$Target)
|
|
928
|
+
New-Item -ItemType Junction -Path $Path -Target $Target | Out-Null
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
function Remove-LinkPath {
|
|
932
|
+
param([string]$Path)
|
|
933
|
+
[System.IO.Directory]::Delete($Path, $false)
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
function Get-LegacyState {
|
|
937
|
+
param($Config, [string]$ProjectRoot)
|
|
938
|
+
$path = Join-Path $ProjectRoot $Config.LegacyNestedRel
|
|
939
|
+
$item = Get-PathItem $path
|
|
940
|
+
if ($null -eq $item) {
|
|
941
|
+
return [pscustomobject]@{ Kind = 'none'; Path = $path; Target = '' }
|
|
942
|
+
}
|
|
943
|
+
if (Test-LinkItem $item) {
|
|
944
|
+
$target = Get-LinkTarget $path
|
|
945
|
+
if (Same-Path $target $Config.SourceRoot) {
|
|
946
|
+
return [pscustomobject]@{ Kind = 'link_to_repo'; Path = $path; Target = $target }
|
|
947
|
+
}
|
|
948
|
+
return [pscustomobject]@{ Kind = 'link_to_other'; Path = $path; Target = $target }
|
|
949
|
+
}
|
|
950
|
+
if ($item.PSIsContainer) {
|
|
951
|
+
return [pscustomobject]@{ Kind = 'real_dir'; Path = $path; Target = '' }
|
|
952
|
+
}
|
|
953
|
+
return [pscustomobject]@{ Kind = 'real_file'; Path = $path; Target = '' }
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
function Assert-LegacyMigrationAllowed {
|
|
957
|
+
param($Legacy)
|
|
958
|
+
if ($Legacy.Kind -eq 'none') { return }
|
|
959
|
+
if (-not $FromOld) {
|
|
960
|
+
Die "legacy nested install detected at $($Legacy.Path); rerun with -FromOld to migrate"
|
|
961
|
+
}
|
|
962
|
+
switch ($Legacy.Kind) {
|
|
963
|
+
'link_to_repo' { return }
|
|
964
|
+
'link_to_other' { Die "legacy nested link points outside expected ARIS source: $($Legacy.Path) -> $($Legacy.Target)" }
|
|
965
|
+
'real_file' { Die "legacy nested path is a real file; move it manually before installing: $($Legacy.Path)" }
|
|
966
|
+
'real_dir' {
|
|
967
|
+
if (-not $MigrateCopy) {
|
|
968
|
+
Die "legacy nested copy detected at $($Legacy.Path); pass -MigrateCopy keep-user or -MigrateCopy prefer-upstream"
|
|
969
|
+
}
|
|
970
|
+
return
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
function Apply-LegacyMigration {
|
|
976
|
+
param($Legacy, [string]$ArisDir)
|
|
977
|
+
if ($Legacy.Kind -eq 'none') { return }
|
|
978
|
+
if ($Legacy.Kind -eq 'link_to_repo') {
|
|
979
|
+
if ($DryRun) {
|
|
980
|
+
Write-Host " (dry-run) remove legacy nested link $($Legacy.Path)"
|
|
981
|
+
} else {
|
|
982
|
+
Remove-LinkPath $Legacy.Path
|
|
983
|
+
Write-Host " - legacy nested link"
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
function Archive-LegacyCopy {
|
|
989
|
+
param($Legacy, [string]$ArisDir)
|
|
990
|
+
if ($Legacy.Kind -ne 'real_dir' -or $MigrateCopy -ne 'prefer-upstream') { return }
|
|
991
|
+
if ($DryRun) {
|
|
992
|
+
Write-Host " (dry-run) archive legacy nested copy $($Legacy.Path)"
|
|
993
|
+
return
|
|
994
|
+
}
|
|
995
|
+
New-Item -ItemType Directory -Force -Path $ArisDir | Out-Null
|
|
996
|
+
$stamp = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssZ')
|
|
997
|
+
$archive = Join-Path $ArisDir "legacy-copy-backup-$stamp"
|
|
998
|
+
Move-Item -LiteralPath $Legacy.Path -Destination $archive
|
|
999
|
+
Write-Host " - archived legacy nested copy to $archive"
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
function Ensure-ToolsJunction {
|
|
1003
|
+
param([string]$ArisDir, [string]$RepoRoot)
|
|
1004
|
+
$linkPath = Join-Path $ArisDir 'tools'
|
|
1005
|
+
$expectedTarget = Join-Path $RepoRoot 'tools'
|
|
1006
|
+
if (-not (Test-Path -LiteralPath $expectedTarget -PathType Container)) {
|
|
1007
|
+
Write-Warning "ARIS tools directory not found: $expectedTarget"
|
|
1008
|
+
return
|
|
1009
|
+
}
|
|
1010
|
+
$item = Get-PathItem $linkPath
|
|
1011
|
+
if (Test-LinkItem $item) {
|
|
1012
|
+
$currentTarget = Get-LinkTarget $linkPath
|
|
1013
|
+
if (Same-Path $currentTarget $expectedTarget) { return }
|
|
1014
|
+
Write-Warning ".aris\tools already points to $currentTarget; leaving it unchanged"
|
|
1015
|
+
return
|
|
1016
|
+
}
|
|
1017
|
+
if ($null -ne $item) {
|
|
1018
|
+
Write-Warning ".aris\tools already exists as a real path; leaving it unchanged"
|
|
1019
|
+
return
|
|
1020
|
+
}
|
|
1021
|
+
if ($DryRun) {
|
|
1022
|
+
Write-Host " (dry-run) junction $linkPath -> $expectedTarget"
|
|
1023
|
+
return
|
|
1024
|
+
}
|
|
1025
|
+
New-Item -ItemType Directory -Force -Path $ArisDir | Out-Null
|
|
1026
|
+
New-Junction $linkPath $expectedTarget
|
|
1027
|
+
Write-Host " + .aris\tools"
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
function Remove-ToolsJunction {
|
|
1031
|
+
param([string]$ArisDir, [string]$RepoRoot, [string]$CurrentManifestName = '')
|
|
1032
|
+
$linkPath = Join-Path $ArisDir 'tools'
|
|
1033
|
+
$expectedTarget = Join-Path $RepoRoot 'tools'
|
|
1034
|
+
$item = Get-PathItem $linkPath
|
|
1035
|
+
if (-not (Test-LinkItem $item)) { return }
|
|
1036
|
+
$currentTarget = Get-LinkTarget $linkPath
|
|
1037
|
+
if (-not (Same-Path $currentTarget $expectedTarget)) { return }
|
|
1038
|
+
foreach ($manifestName in @('installed-skills.txt', 'installed-skills-codex.txt')) {
|
|
1039
|
+
if ($manifestName -eq $CurrentManifestName) { continue }
|
|
1040
|
+
$otherManifestPath = Join-Path $ArisDir $manifestName
|
|
1041
|
+
if (-not (Test-Path -LiteralPath $otherManifestPath -PathType Leaf)) { continue }
|
|
1042
|
+
$otherManifest = Load-Manifest $otherManifestPath
|
|
1043
|
+
if ($otherManifest.Headers.ContainsKey('repo_root') -and (Same-Path $otherManifest.Headers['repo_root'] $RepoRoot)) {
|
|
1044
|
+
Write-Host " = .aris\tools (kept; $manifestName still uses this ARIS repo)"
|
|
1045
|
+
return
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
if ($DryRun) {
|
|
1049
|
+
Write-Host " (dry-run) remove $linkPath"
|
|
1050
|
+
} else {
|
|
1051
|
+
Remove-LinkPath $linkPath
|
|
1052
|
+
Write-Host " - .aris\tools"
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
function Apply-Plan {
|
|
1057
|
+
param($Plan, [string]$RepoRoot)
|
|
1058
|
+
foreach ($entry in $Plan) {
|
|
1059
|
+
switch ($entry.Action) {
|
|
1060
|
+
'REUSE' { continue }
|
|
1061
|
+
'ADOPT' { continue }
|
|
1062
|
+
'CREATE' {
|
|
1063
|
+
if ($DryRun) {
|
|
1064
|
+
Write-Host " (dry-run) junction $($entry.TargetPath) -> $($entry.ExpectedTarget)"
|
|
1065
|
+
continue
|
|
1066
|
+
}
|
|
1067
|
+
if (Get-PathItem $entry.TargetPath) {
|
|
1068
|
+
Die "path appeared during install: $($entry.TargetPath)"
|
|
1069
|
+
}
|
|
1070
|
+
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $entry.TargetPath) | Out-Null
|
|
1071
|
+
New-Junction $entry.TargetPath $entry.ExpectedTarget
|
|
1072
|
+
Write-Host " + $($entry.Name)"
|
|
1073
|
+
}
|
|
1074
|
+
'UPDATE_TARGET' {
|
|
1075
|
+
if ($DryRun) {
|
|
1076
|
+
Write-Host " (dry-run) relink $($entry.TargetPath) -> $($entry.ExpectedTarget)"
|
|
1077
|
+
continue
|
|
1078
|
+
}
|
|
1079
|
+
$currentTarget = Get-LinkTarget $entry.TargetPath
|
|
1080
|
+
if (-not (Same-Path $currentTarget $entry.Extra)) {
|
|
1081
|
+
Die "link target changed during install for $($entry.Name)"
|
|
1082
|
+
}
|
|
1083
|
+
if (-not (Test-ResolvedPathInside $currentTarget $RepoRoot)) {
|
|
1084
|
+
Die "refusing to relink $($entry.Name); current target is outside ARIS repo: $currentTarget"
|
|
1085
|
+
}
|
|
1086
|
+
Remove-LinkPath $entry.TargetPath
|
|
1087
|
+
New-Junction $entry.TargetPath $entry.ExpectedTarget
|
|
1088
|
+
Write-Host " > $($entry.Name)"
|
|
1089
|
+
}
|
|
1090
|
+
'REMOVE' {
|
|
1091
|
+
$item = Get-PathItem $entry.TargetPath
|
|
1092
|
+
if ($null -eq $item) {
|
|
1093
|
+
Write-Host " - $($entry.Name) (already absent)"
|
|
1094
|
+
continue
|
|
1095
|
+
}
|
|
1096
|
+
if (-not (Test-LinkItem $item)) {
|
|
1097
|
+
Write-Warning "skipping $($entry.Name); target is no longer a junction/symlink"
|
|
1098
|
+
continue
|
|
1099
|
+
}
|
|
1100
|
+
$currentTarget = Get-LinkTarget $entry.TargetPath
|
|
1101
|
+
if (-not (Same-Path $currentTarget $entry.Extra)) {
|
|
1102
|
+
Write-Warning "skipping $($entry.Name); target changed to $currentTarget"
|
|
1103
|
+
continue
|
|
1104
|
+
}
|
|
1105
|
+
if ($DryRun) {
|
|
1106
|
+
Write-Host " (dry-run) remove $($entry.TargetPath)"
|
|
1107
|
+
} else {
|
|
1108
|
+
Remove-LinkPath $entry.TargetPath
|
|
1109
|
+
Write-Host " - $($entry.Name)"
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
'CONFLICT' {
|
|
1113
|
+
Die "conflict reached apply phase for $($entry.Name)"
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
function New-ManifestContent {
|
|
1120
|
+
param($Plan, [string]$RepoRoot, [string]$ProjectRoot, [string]$PlatformName)
|
|
1121
|
+
$lines = New-Object System.Collections.Generic.List[string]
|
|
1122
|
+
$lines.Add("version`t$ManifestVersion")
|
|
1123
|
+
$lines.Add("repo_root`t$RepoRoot")
|
|
1124
|
+
$lines.Add("project_root`t$ProjectRoot")
|
|
1125
|
+
$lines.Add("platform`t$PlatformName")
|
|
1126
|
+
$lines.Add("generated`t$((Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ'))")
|
|
1127
|
+
$lines.Add("kind`tname`tsource_rel`ttarget_rel`tmode")
|
|
1128
|
+
foreach ($entry in $Plan | Where-Object { $_.Action -in @('REUSE', 'ADOPT', 'CREATE', 'UPDATE_TARGET') } | Sort-Object Name) {
|
|
1129
|
+
$lines.Add("$($entry.Kind)`t$($entry.Name)`t$($entry.SourceRel)`t$($entry.TargetRel)`tjunction")
|
|
1130
|
+
}
|
|
1131
|
+
return ($lines -join "`n") + "`n"
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
function Commit-Manifest {
|
|
1135
|
+
param([string]$ManifestPath, [string]$ManifestPrevPath, [string]$Content)
|
|
1136
|
+
if ($DryRun) {
|
|
1137
|
+
Write-Host " (dry-run) would commit manifest $ManifestPath"
|
|
1138
|
+
return
|
|
1139
|
+
}
|
|
1140
|
+
$manifestDir = Split-Path -Parent $ManifestPath
|
|
1141
|
+
New-Item -ItemType Directory -Force -Path $manifestDir | Out-Null
|
|
1142
|
+
$tmp = "$ManifestPath.tmp.$PID"
|
|
1143
|
+
Write-Text $tmp $Content
|
|
1144
|
+
if (Test-Path -LiteralPath $ManifestPath -PathType Leaf) {
|
|
1145
|
+
Copy-Item -LiteralPath $ManifestPath -Destination "$ManifestPrevPath.tmp" -Force
|
|
1146
|
+
Move-Item -LiteralPath "$ManifestPrevPath.tmp" -Destination $ManifestPrevPath -Force
|
|
1147
|
+
}
|
|
1148
|
+
Move-Item -LiteralPath $tmp -Destination $ManifestPath -Force
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
function Update-ManagedDoc {
|
|
1152
|
+
param($Config, [string]$DocPath, [string]$RepoRoot, [string]$ProjectRoot, [int]$Count)
|
|
1153
|
+
if ($NoDoc) { return }
|
|
1154
|
+
$reconcileCommand = "powershell -NoProfile -ExecutionPolicy Bypass -File `"$RepoRoot\tools\install_aris.ps1`" `"$ProjectRoot`" -Platform $($Config.Platform) -Reconcile"
|
|
1155
|
+
$block = @"
|
|
1156
|
+
$($Config.BlockBegin)
|
|
1157
|
+
## $($Config.Title)
|
|
1158
|
+
ARIS skills installed in this project: $Count entries.
|
|
1159
|
+
Manifest: ``.aris/$($Config.ManifestName)``
|
|
1160
|
+
ARIS repo root: ``$RepoRoot``
|
|
1161
|
+
Project skill path: ``$($Config.TargetRelDisplay)/<skill-name>``
|
|
1162
|
+
For ARIS workflows, prefer the project-local skills under ``$($Config.TargetRelDisplay)/``.
|
|
1163
|
+
Do not edit or delete junctioned skills in place; update upstream or rerun:
|
|
1164
|
+
``$reconcileCommand``
|
|
1165
|
+
$($Config.BlockEnd)
|
|
1166
|
+
"@
|
|
1167
|
+
$original = ''
|
|
1168
|
+
if (Test-Path -LiteralPath $DocPath -PathType Leaf) {
|
|
1169
|
+
$original = Read-Text $DocPath
|
|
1170
|
+
}
|
|
1171
|
+
$newContent = $null
|
|
1172
|
+
if ($original.Contains($Config.BlockBegin)) {
|
|
1173
|
+
$pattern = [regex]::Escape($Config.BlockBegin) + '.*?' + [regex]::Escape($Config.BlockEnd)
|
|
1174
|
+
$newContent = [regex]::Replace(
|
|
1175
|
+
$original,
|
|
1176
|
+
$pattern,
|
|
1177
|
+
[System.Text.RegularExpressions.MatchEvaluator]{ param($m) $block },
|
|
1178
|
+
[System.Text.RegularExpressions.RegexOptions]::Singleline
|
|
1179
|
+
)
|
|
1180
|
+
} else {
|
|
1181
|
+
$separator = ''
|
|
1182
|
+
if ($original.Length -gt 0 -and -not $original.EndsWith("`n")) {
|
|
1183
|
+
$separator = "`n"
|
|
1184
|
+
}
|
|
1185
|
+
$newContent = $original + $separator + $block + "`n"
|
|
1186
|
+
}
|
|
1187
|
+
Write-Text $DocPath $newContent
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
function Remove-ManagedDocBlock {
|
|
1191
|
+
param($Config, [string]$DocPath)
|
|
1192
|
+
if ($NoDoc -or -not (Test-Path -LiteralPath $DocPath -PathType Leaf)) { return }
|
|
1193
|
+
if ($DryRun) {
|
|
1194
|
+
Write-Host " (dry-run) would remove managed block from $DocPath"
|
|
1195
|
+
return
|
|
1196
|
+
}
|
|
1197
|
+
$original = Read-Text $DocPath
|
|
1198
|
+
if (-not $original.Contains($Config.BlockBegin)) { return }
|
|
1199
|
+
$pattern = "`r?`n?" + [regex]::Escape($Config.BlockBegin) + '.*?' + [regex]::Escape($Config.BlockEnd) + "`r?`n?"
|
|
1200
|
+
$newContent = [regex]::Replace(
|
|
1201
|
+
$original,
|
|
1202
|
+
$pattern,
|
|
1203
|
+
[System.Text.RegularExpressions.MatchEvaluator]{ param($m) "`n" },
|
|
1204
|
+
[System.Text.RegularExpressions.RegexOptions]::Singleline
|
|
1205
|
+
).TrimStart("`r", "`n")
|
|
1206
|
+
Write-Text $DocPath $newContent
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
function Do-Uninstall {
|
|
1210
|
+
param($Config, [string]$ProjectRoot, [string]$ManifestPath, [string]$ManifestPrevPath, [string]$DocPath)
|
|
1211
|
+
if (-not (Test-Path -LiteralPath $ManifestPath -PathType Leaf)) {
|
|
1212
|
+
Die "no manifest at $ManifestPath; nothing to uninstall"
|
|
1213
|
+
}
|
|
1214
|
+
$manifest = Load-Manifest $ManifestPath
|
|
1215
|
+
if (-not $manifest.Headers.ContainsKey('repo_root')) {
|
|
1216
|
+
Die "manifest missing repo_root: $ManifestPath"
|
|
1217
|
+
}
|
|
1218
|
+
$recordedRepo = $manifest.Headers['repo_root']
|
|
1219
|
+
Write-Host ''
|
|
1220
|
+
Write-Host 'Uninstall plan:'
|
|
1221
|
+
foreach ($entry in $manifest.Entries) {
|
|
1222
|
+
Write-Host " - $($entry.Name) ($($entry.Kind))"
|
|
1223
|
+
}
|
|
1224
|
+
foreach ($entry in $manifest.Entries) {
|
|
1225
|
+
$targetPath = Join-RelativePath $ProjectRoot $entry.TargetRel
|
|
1226
|
+
$expectedTarget = Join-RelativePath $recordedRepo $entry.SourceRel
|
|
1227
|
+
$item = Get-PathItem $targetPath
|
|
1228
|
+
if ($null -eq $item) { continue }
|
|
1229
|
+
if (-not (Test-LinkItem $item)) {
|
|
1230
|
+
Write-Warning "skipping $($entry.Name); target path is not a junction/symlink"
|
|
1231
|
+
continue
|
|
1232
|
+
}
|
|
1233
|
+
$currentTarget = Get-LinkTarget $targetPath
|
|
1234
|
+
if (Same-Path $currentTarget $expectedTarget) {
|
|
1235
|
+
if ($DryRun) {
|
|
1236
|
+
Write-Host " (dry-run) remove $targetPath"
|
|
1237
|
+
} else {
|
|
1238
|
+
Remove-LinkPath $targetPath
|
|
1239
|
+
Write-Host " - removed $($entry.Name)"
|
|
1240
|
+
}
|
|
1241
|
+
} else {
|
|
1242
|
+
Write-Warning "skipping $($entry.Name); target changed to $currentTarget"
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
Remove-ToolsJunction (Split-Path -Parent $ManifestPath) $recordedRepo $Config.ManifestName
|
|
1246
|
+
if (-not $DryRun) {
|
|
1247
|
+
Move-Item -LiteralPath $ManifestPath -Destination $ManifestPrevPath -Force
|
|
1248
|
+
}
|
|
1249
|
+
Remove-ManagedDocBlock $Config $DocPath
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
function Invoke-Main {
|
|
1253
|
+
if ($Force) {
|
|
1254
|
+
Die '-Force is no longer supported. Use -ReplaceLink NAME for a specific existing junction/symlink; real files are never overwritten.'
|
|
1255
|
+
}
|
|
1256
|
+
if ($Reconcile -and $Uninstall) {
|
|
1257
|
+
Die '-Reconcile and -Uninstall are mutually exclusive'
|
|
1258
|
+
}
|
|
1259
|
+
if ($All -and (Get-CommaList $Groups).Count + (Get-CommaList $Skills).Count -gt 0) {
|
|
1260
|
+
Die '-All cannot be combined with -Groups/-Skills (only -Exclude)'
|
|
1261
|
+
}
|
|
1262
|
+
if (-not (Test-Path -LiteralPath $ProjectPath -PathType Container)) {
|
|
1263
|
+
Die "project path does not exist: $ProjectPath"
|
|
1264
|
+
}
|
|
1265
|
+
$projectRoot = (Resolve-Path -LiteralPath $ProjectPath).ProviderPath
|
|
1266
|
+
$repoRoot = Resolve-ArisRepo
|
|
1267
|
+
$catalog = Load-Catalog (Join-RelativePath $repoRoot $CatalogRel)
|
|
1268
|
+
if ($ListGroups) {
|
|
1269
|
+
Show-GroupCatalog $catalog
|
|
1270
|
+
return
|
|
1271
|
+
}
|
|
1272
|
+
$selectedPlatform = $Platform
|
|
1273
|
+
if ($selectedPlatform -eq 'auto') {
|
|
1274
|
+
$selectedPlatform = Detect-Platform $projectRoot
|
|
1275
|
+
}
|
|
1276
|
+
$config = New-Config $projectRoot $repoRoot $selectedPlatform
|
|
1277
|
+
$arisDir = Join-Path $projectRoot '.aris'
|
|
1278
|
+
$manifestPath = Join-Path $arisDir $config.ManifestName
|
|
1279
|
+
$manifestPrevPath = Join-Path $arisDir $config.ManifestPrevName
|
|
1280
|
+
$docPath = Join-Path $projectRoot $config.DocName
|
|
1281
|
+
$targetRoot = Join-Path $projectRoot $config.TargetRel
|
|
1282
|
+
$lockPath = Join-Path $arisDir $config.LockName
|
|
1283
|
+
$mode = $(if ($DryRun) { 'DRY-RUN' } elseif ($Uninstall) { 'UNINSTALL' } elseif ($Reconcile) { 'RECONCILE' } else { 'APPLY' })
|
|
1284
|
+
|
|
1285
|
+
Write-Host ''
|
|
1286
|
+
Write-Host 'ARIS Project Install'
|
|
1287
|
+
Write-Host " Project: $projectRoot"
|
|
1288
|
+
Write-Host " Platform: $selectedPlatform"
|
|
1289
|
+
Write-Host " Repo: $repoRoot"
|
|
1290
|
+
Write-Host " Target: $targetRoot"
|
|
1291
|
+
Write-Host " Mode: $mode"
|
|
1292
|
+
|
|
1293
|
+
Check-NoSymlinkedParents @($arisDir, (Split-Path -Parent $targetRoot), $targetRoot)
|
|
1294
|
+
|
|
1295
|
+
if ($Uninstall) {
|
|
1296
|
+
if (-not $DryRun) { Acquire-Lock $arisDir $lockPath }
|
|
1297
|
+
Do-Uninstall $config $projectRoot $manifestPath $manifestPrevPath $docPath
|
|
1298
|
+
return
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
$legacy = Get-LegacyState $config $projectRoot
|
|
1302
|
+
Assert-LegacyMigrationAllowed $legacy
|
|
1303
|
+
|
|
1304
|
+
if ($Reconcile -and -not (Test-Path -LiteralPath $manifestPath -PathType Leaf)) {
|
|
1305
|
+
Die "-Reconcile requires existing manifest; none found at $manifestPath"
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
$inventory = Build-Inventory $config
|
|
1309
|
+
$manifest = Load-Manifest $manifestPath
|
|
1310
|
+
|
|
1311
|
+
# Selective install (#366 parity): build the selected set, then plan
|
|
1312
|
+
# against that filtered subset (support entries always pass through).
|
|
1313
|
+
$upstreamSkillNames = New-Object System.Collections.Generic.HashSet[string]
|
|
1314
|
+
foreach ($entry in $inventory) {
|
|
1315
|
+
if ($entry.Kind -eq 'skill') { $upstreamSkillNames.Add($entry.Name) | Out-Null }
|
|
1316
|
+
}
|
|
1317
|
+
$isFresh = -not (Test-Path -LiteralPath $manifestPath -PathType Leaf)
|
|
1318
|
+
$declinedPath = Join-Path $arisDir $config.DeclinedName
|
|
1319
|
+
$selection = Build-Selection $catalog $manifest $upstreamSkillNames $declinedPath $isFresh
|
|
1320
|
+
$selectedInventory = @($inventory | Where-Object { $_.Kind -eq 'support' -or $selection.Selected.Contains($_.Name) })
|
|
1321
|
+
Write-Host ''
|
|
1322
|
+
Write-Host "Selection: $($selection.Selected.Count) of $($upstreamSkillNames.Count) upstream skills"
|
|
1323
|
+
|
|
1324
|
+
$plan = Compute-Plan $selectedInventory $manifest $config $projectRoot $manifestPath
|
|
1325
|
+
Print-Plan $plan $mode
|
|
1326
|
+
|
|
1327
|
+
$conflicts = @($plan | Where-Object { $_.Action -eq 'CONFLICT' })
|
|
1328
|
+
if ($conflicts.Count -gt 0) {
|
|
1329
|
+
Die "CONFLICT: $($conflicts.Count) existing path(s) must be resolved before install"
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
if ($DryRun) {
|
|
1333
|
+
Apply-LegacyMigration $legacy $arisDir
|
|
1334
|
+
Archive-LegacyCopy $legacy $arisDir
|
|
1335
|
+
Ensure-ToolsJunction $arisDir $repoRoot
|
|
1336
|
+
Write-Host ''
|
|
1337
|
+
Write-Host '(dry-run) no changes made'
|
|
1338
|
+
return
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
Acquire-Lock $arisDir $lockPath
|
|
1342
|
+
Apply-LegacyMigration $legacy $arisDir
|
|
1343
|
+
New-Item -ItemType Directory -Force -Path $targetRoot | Out-Null
|
|
1344
|
+
Write-Host ''
|
|
1345
|
+
Write-Host 'Applying:'
|
|
1346
|
+
Apply-Plan $plan $repoRoot
|
|
1347
|
+
$manifestContent = New-ManifestContent $plan $repoRoot $projectRoot $selectedPlatform
|
|
1348
|
+
Commit-Manifest $manifestPath $manifestPrevPath $manifestContent
|
|
1349
|
+
Ensure-ToolsJunction $arisDir $repoRoot
|
|
1350
|
+
Archive-LegacyCopy $legacy $arisDir
|
|
1351
|
+
|
|
1352
|
+
# #366: persist declined skills + global repo pointer (both best-effort,
|
|
1353
|
+
# after manifest commit for the same reason as the tools junction above).
|
|
1354
|
+
Save-DeclinedSet $declinedPath $selection.DeclinedCandidates $selection.Selected
|
|
1355
|
+
Ensure-GlobalPointer $repoRoot
|
|
1356
|
+
|
|
1357
|
+
$managedCount = @($plan | Where-Object { $_.Action -in @('REUSE', 'ADOPT', 'CREATE', 'UPDATE_TARGET') }).Count
|
|
1358
|
+
Update-ManagedDoc $config $docPath $repoRoot $projectRoot $managedCount
|
|
1359
|
+
Write-Host ''
|
|
1360
|
+
Write-Host "Install complete. Managed entries: $managedCount"
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
$exitCode = 0
|
|
1364
|
+
try {
|
|
1365
|
+
Invoke-Main
|
|
1366
|
+
} catch {
|
|
1367
|
+
Write-Error $_.Exception.Message
|
|
1368
|
+
$exitCode = 1
|
|
1369
|
+
} finally {
|
|
1370
|
+
Release-Lock
|
|
1371
|
+
}
|
|
1372
|
+
exit $exitCode
|