@iowarp/clio-coder 0.4.6 → 0.4.7
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/.claude-plugin/marketplace.json +283 -0
- package/CHANGELOG.md +50 -0
- package/CONTRIBUTING.md +102 -29
- package/NOTICE +8 -0
- package/README.md +37 -5
- package/dist/{acp-UVEVWSDL.js → acp-W3Y3TRHD.js} +12 -12
- package/dist/{agents-VWY37DTK.js → agents-J4ICNLQ3.js} +52 -52
- package/dist/assets/codewiki.json +1 -1
- package/dist/assets/tui-notices/get-east-asian-width-LICENSE +9 -0
- package/dist/assets/tui-notices/marked-LICENSE +44 -0
- package/dist/assets/tui-notices/pi-tui-LICENSE +21 -0
- package/dist/{auth-PCV2XQDV.js → auth-X2UJUMOC.js} +24 -24
- package/dist/{builtins-PD652MTK.js → builtins-XLXWWSRF.js} +6 -6
- package/dist/{chunk-DZK63TZJ.js → chunk-24S3DN22.js} +336 -612
- package/dist/{chunk-NTK3WTHM.js → chunk-24TOGHPO.js} +2 -2
- package/dist/{chunk-M6TAYK66.js → chunk-2O2LVQQK.js} +11 -4
- package/dist/{chunk-OQM5IVJY.js → chunk-34XHRVKG.js} +3 -3
- package/dist/{chunk-LY4N4TTL.js → chunk-3LXJOICS.js} +2 -2
- package/dist/{chunk-3BJGMT2D.js → chunk-3XE4NRLF.js} +1 -1
- package/dist/{chunk-N2OKBREN.js → chunk-4MFBBY2Z.js} +3 -3
- package/dist/{chunk-5VTJP36Q.js → chunk-4N2RZ4TO.js} +2 -2
- package/dist/{chunk-WNGF5AJX.js → chunk-4QKAITQT.js} +3 -3
- package/dist/chunk-4UHVS5T7.js +207 -0
- package/dist/{chunk-GLYBJ4RQ.js → chunk-5AJPUR72.js} +6 -6
- package/dist/{chunk-5CY4Q6QI.js → chunk-5EQO5NWP.js} +105 -3
- package/dist/{chunk-6HAO6X5B.js → chunk-65BGHOUR.js} +8 -5
- package/dist/{chunk-6VRRJT7L.js → chunk-6CA2BHOR.js} +2 -2
- package/dist/{chunk-HL26ADLH.js → chunk-6CB2IL4N.js} +2 -1
- package/dist/{chunk-E7RF4KBO.js → chunk-6LRLOJHQ.js} +3 -3
- package/dist/{chunk-HXHFDHB6.js → chunk-6TAATXXH.js} +6 -6
- package/dist/{chunk-P6M7RKMI.js → chunk-6YHPAAZR.js} +3 -3
- package/dist/{chunk-5QRX7SLS.js → chunk-7EEGBKV4.js} +7 -7
- package/dist/{chunk-BS4GAMUB.js → chunk-A67NR5AT.js} +2 -2
- package/dist/{chunk-WJIMKGD6.js → chunk-BAU36233.js} +8 -8
- package/dist/{chunk-PZN2LQY6.js → chunk-BO4XV5VX.js} +4 -4
- package/dist/{chunk-I2K2NCZQ.js → chunk-BQQ5MXUC.js} +6 -6
- package/dist/{chunk-X3BI7HTV.js → chunk-C3G5PNUO.js} +21 -21
- package/dist/{chunk-SRZOW5L2.js → chunk-C6GEXX4L.js} +1 -1
- package/dist/{chunk-6RYDAVQT.js → chunk-CE64C5K5.js} +219 -64
- package/dist/chunk-CP3HIR5H.js +2119 -0
- package/dist/{chunk-BBV5TKE6.js → chunk-CPXGJI2A.js} +2 -2
- package/dist/{chunk-IGLVVNEG.js → chunk-CULVKYTK.js} +4 -4
- package/dist/{chunk-DXLUVUD4.js → chunk-D2T66PDV.js} +5 -5
- package/dist/{chunk-NF3BK5WR.js → chunk-DACXIVVF.js} +5 -5
- package/dist/{chunk-IKRWLEDA.js → chunk-DLIYANB7.js} +20 -15
- package/dist/{chunk-RGZNQ5CT.js → chunk-DO7M3LSX.js} +9 -9
- package/dist/{chunk-2QPI3TR3.js → chunk-DVJL77SO.js} +2 -2
- package/dist/{chunk-ODUTYZIU.js → chunk-EUEXGXXR.js} +5 -5
- package/dist/{chunk-WKUYQBCG.js → chunk-EUZRMP4Y.js} +18 -16
- package/dist/{chunk-JMPL7NUB.js → chunk-G4LGUTOE.js} +4 -4
- package/dist/{chunk-CBSKDFWL.js → chunk-GEVQNST4.js} +2 -2
- package/dist/{chunk-NNWJYUNO.js → chunk-GFQ7P2UF.js} +7 -7
- package/dist/{chunk-SXQKNKRA.js → chunk-GI5RRMAH.js} +14 -14
- package/dist/{chunk-HPTJF6DG.js → chunk-GVZFCJHE.js} +336 -83
- package/dist/{chunk-ACCKVSHU.js → chunk-GXIWHXDB.js} +3 -3
- package/dist/{chunk-LUL5Z66S.js → chunk-HOX4YZ2K.js} +6 -5
- package/dist/chunk-HRAKUPIH.js +57 -0
- package/dist/{chunk-F5Z7EWMN.js → chunk-IJMW64DX.js} +50 -50
- package/dist/{chunk-QNWRHOMF.js → chunk-IMOD2YPV.js} +16 -9
- package/dist/{chunk-HZ2T2YED.js → chunk-IQCNVLO6.js} +28 -8
- package/dist/{chunk-HZFBVU5X.js → chunk-ISNCCX42.js} +8 -8
- package/dist/{chunk-7JSSKXG2.js → chunk-IZYTT3CD.js} +7 -3
- package/dist/chunk-KKEYGFX4.js +915 -0
- package/dist/{chunk-HHVJNVUJ.js → chunk-KS7ETA3P.js} +2 -2
- package/dist/{chunk-BLTEAUCB.js → chunk-L3Z5ANCV.js} +7 -7
- package/dist/{chunk-4VMV6OS6.js → chunk-LBFQMYS3.js} +2 -2
- package/dist/{chunk-URBGEOH3.js → chunk-M6Z2V6YU.js} +5 -5
- package/dist/{chunk-BT7HGDOX.js → chunk-MBJO5VNZ.js} +12 -12
- package/dist/chunk-MNQN3X6Y.js +595 -0
- package/dist/{chunk-3QKXHCGS.js → chunk-MZHXENL7.js} +5 -5
- package/dist/chunk-MZNWZUJM.js +1801 -0
- package/dist/{chunk-7YJWEF2L.js → chunk-N37QC5FV.js} +5 -5
- package/dist/{chunk-TJFDL26W.js → chunk-NCCQ6DVS.js} +7 -7
- package/dist/{chunk-P5JMBPDJ.js → chunk-NO7J45X6.js} +162 -47
- package/dist/chunk-OH3YYA3B.js +15 -0
- package/dist/{chunk-22PQ4TX4.js → chunk-P4DL2LM5.js} +16 -77
- package/dist/{chunk-LIN2K3EL.js → chunk-PFLOULP3.js} +4 -4
- package/dist/{chunk-QXFBABFJ.js → chunk-QKWL7SYE.js} +11 -11
- package/dist/chunk-RFCKPS2V.js +26 -0
- package/dist/{chunk-ZS43Y2FT.js → chunk-RTLNFZMD.js} +12 -4
- package/dist/{chunk-U7H5WRG5.js → chunk-RXFPB32E.js} +8 -6
- package/dist/{chunk-Q7NNUHQV.js → chunk-SEDI5K5B.js} +16 -16
- package/dist/{chunk-WV6TDAPN.js → chunk-SELG4SUR.js} +7 -7
- package/dist/{chunk-3WBALRQH.js → chunk-SER7RRAE.js} +27 -832
- package/dist/{chunk-K2HL3GBL.js → chunk-T4TY5GG3.js} +165 -15
- package/dist/chunk-T7QQBNLH.js +691 -0
- package/dist/{chunk-6OII3LOK.js → chunk-TLTGZTXI.js} +3 -3
- package/dist/chunk-TZ6UVP2H.js +637 -0
- package/dist/{chunk-3JLFK5MW.js → chunk-TZHN5ITX.js} +13 -41
- package/dist/{chunk-VANN3GDP.js → chunk-U3TPUXOQ.js} +399 -234
- package/dist/{chunk-VHMX7SXF.js → chunk-UA3W4FGC.js} +6 -6
- package/dist/{chunk-3O2ZAUGQ.js → chunk-UA6W4XFJ.js} +2 -2
- package/dist/chunk-UWD3JD5X.js +709 -0
- package/dist/chunk-V4ZKOKRB.js +13433 -0
- package/dist/{chunk-NC4KV7EL.js → chunk-VCRVEDUC.js} +5 -5
- package/dist/{chunk-4DU2TVI4.js → chunk-VMATUZB6.js} +12 -10
- package/dist/{chunk-J2FYEQ5L.js → chunk-VTYHHQ57.js} +2 -2
- package/dist/chunk-WB7POC6M.js +891 -0
- package/dist/{chunk-3HQVBIKD.js → chunk-WBVKKTUT.js} +2 -2
- package/dist/{chunk-EHOSNVVM.js → chunk-WLHX3MM5.js} +6 -2
- package/dist/{chunk-24T2YZ7A.js → chunk-WN552PKN.js} +553 -298
- package/dist/{chunk-4U4TKZFR.js → chunk-WT5TRMO4.js} +51 -26
- package/dist/{chunk-XWMSPJYN.js → chunk-WURGVOZJ.js} +56 -12
- package/dist/{chunk-7WUZEB2M.js → chunk-XR3XJN2M.js} +2 -2
- package/dist/chunk-XW2UZICE.js +372 -0
- package/dist/{chunk-2AKYFREM.js → chunk-XZ6UFQ52.js} +11 -11
- package/dist/chunk-Y3JU5RPK.js +8 -0
- package/dist/chunk-YKRJTYNP.js +1143 -0
- package/dist/{chunk-PWWSIB2A.js → chunk-ZGIGW4ZG.js} +5 -5
- package/dist/cli/index.js +34 -36
- package/dist/{clio-QQCMTUZE.js → clio-L2P7S2R2.js} +9 -9
- package/dist/{code-nav-GUPM6766.js → code-nav-B5LNX4KU.js} +25 -25
- package/dist/{components-TNIKXCBH.js → components-63MMQKUT.js} +6 -6
- package/dist/{config-IIM7YOD3.js → config-YXARKGTX.js} +70 -67
- package/dist/{configure-4ZM5HWJX.js → configure-LMVQJ46J.js} +47 -35
- package/dist/{context-OL5P3HEP.js → context-7HJEWTTP.js} +87 -44
- package/dist/{context-44BZLJ3J.js → context-XSZKKFBW.js} +14 -14
- package/dist/{context-AIDVQKMW.js → context-YG2LZDYZ.js} +60 -58
- package/dist/{context-clear-OQV76MYP.js → context-clear-AC5NXHM2.js} +57 -55
- package/dist/{context-index-HP2JFUWA.js → context-index-KKTPRV5G.js} +3 -3
- package/dist/{context-map-INTXD54D.js → context-map-T7ROR5D5.js} +4 -4
- package/dist/{context-working-set-ZUW3EGLS.js → context-working-set-F4Q4J3NS.js} +12 -10
- package/dist/{detail-UXPWRANG.js → detail-H73BEF2N.js} +58 -56
- package/dist/{dispatch-runner-P3ZW6G77.js → dispatch-runner-3RY5FSJC.js} +74 -72
- package/dist/{doctor-JJVGPZWT.js → doctor-EQYJOGUG.js} +44 -30
- package/dist/{eval-V2RTN3UH.js → eval-4P7O6CCE.js} +109 -46
- package/dist/{evidence-35PYWGSO.js → evidence-D6GUSYJW.js} +57 -55
- package/dist/{evidence-CIGGKI6J.js → evidence-O76QF7TZ.js} +62 -59
- package/dist/{evolve-33SM3NJP.js → evolve-H47BCETS.js} +58 -56
- package/dist/{extensions-YVRHX6ZI.js → extensions-NNL3ZOCU.js} +98 -20
- package/dist/{fleet-LFO7SIPC.js → fleet-KMNS6NOM.js} +106 -99
- package/dist/{fleet-commands-RLK72GU3.js → fleet-commands-DOWSYCBX.js} +14 -14
- package/dist/{fleet-decisions-3SBSCTCW.js → fleet-decisions-S3PTZV2D.js} +9 -9
- package/dist/{fleet-graph-JZIBCGMW.js → fleet-graph-F2DE2TME.js} +26 -24
- package/dist/{fleet-inspect-2L5YGGZJ.js → fleet-inspect-EN2Q6IVB.js} +60 -57
- package/dist/{fleet-preflight-QRSHIIDE.js → fleet-preflight-FRLETDD3.js} +3 -3
- package/dist/{fleet-validate-LIAVKDUU.js → fleet-validate-B2XDLS57.js} +28 -29
- package/dist/{fleet-verify-JIK3OQZR.js → fleet-verify-4JKUWGJ7.js} +57 -55
- package/dist/{fleet-view-IWHYGQKK.js → fleet-view-BHJFW26B.js} +60 -57
- package/dist/{init-NJEDOTQW.js → init-XAT5N5YM.js} +79 -74
- package/dist/interop-FVECHFKG.js +246 -0
- package/dist/{inventory-74UL7UHU.js → inventory-7KFQGLCV.js} +60 -57
- package/dist/library-NY2XOT75.js +492 -0
- package/dist/library-QAPAO23R.js +306 -0
- package/dist/library-import-5Y6MAS3R.js +183 -0
- package/dist/library-inventory-JT6HWITQ.js +56 -0
- package/dist/library-validation-EQUH25DC.js +42 -0
- package/dist/{memory-I76I5VUH.js → memory-DYVTTCP6.js} +58 -56
- package/dist/{models-D4YDIQZQ.js → models-MASFJ6Y2.js} +48 -36
- package/dist/{monitor-NKVD55HX.js → monitor-YMP4NXJJ.js} +64 -61
- package/dist/{orchestrator-I6OATVPK.js → orchestrator-AFQRRTEI.js} +3685 -1841
- package/dist/{panes-7BTYH3XQ.js → panes-D3Y2KFG5.js} +4 -4
- package/dist/{panes-WPOMEMRH.js → panes-NICTHQCV.js} +7 -7
- package/dist/{paths-J3KN7UAO.js → paths-PSFAUIGO.js} +5 -5
- package/dist/{preload-EJAWJEIO.js → preload-5Q7VXRFJ.js} +57 -55
- package/dist/{reset-4XMQKDNZ.js → reset-7NKZK7JN.js} +9 -9
- package/dist/{resources-TVYVXK3Q.js → resources-NILSIZAT.js} +94 -38
- package/dist/{run-OWQNNSJU.js → run-BPZGEBZV.js} +121 -105
- package/dist/{share-NPZZMUXG.js → share-EZ6ERBMH.js} +31 -28
- package/dist/skills-BTVVNYVM.js +100 -0
- package/dist/{skills-eval-J5TCKSJA.js → skills-eval-UY34MNK7.js} +110 -72
- package/dist/{skills-inventory-7II4DM57.js → skills-inventory-JDHSIA7I.js} +30 -25
- package/dist/{slash-commands-MDSDSWYF.js → slash-commands-K4FNQ2AT.js} +49 -46
- package/dist/{steer-S7D7IKPU.js → steer-2B3EQGFH.js} +4 -4
- package/dist/{support-5VAEC32W.js → support-L2SOL3IE.js} +7 -7
- package/dist/{targets-KILZEOCE.js → targets-44VKGFLN.js} +64 -58
- package/dist/{tasks-R6P6OV22.js → tasks-3KODKFU7.js} +12 -12
- package/dist/{terminal-lease-LZCG2KN3.js → terminal-lease-LXA7MMPO.js} +32 -19
- package/dist/{tools-6OCQ6SVB.js → tools-3UB7TEVR.js} +7 -7
- package/dist/{trace-JHLXWUMN.js → trace-TAMIU4OJ.js} +5 -4
- package/dist/{tui-primitives-RCY2HN7G.js → tui-primitives-A6MUW32V.js} +3 -2
- package/dist/{uninstall-FXYMFFRT.js → uninstall-QYVRPEM4.js} +9 -9
- package/dist/{upgrade-D2OZH6IQ.js → upgrade-7PMKM2JH.js} +15 -16
- package/dist/{usage-PIASNUS4.js → usage-6EQV7EQK.js} +70 -67
- package/dist/{verifiers-LTHUY2PX.js → verifiers-JLITWSHU.js} +15 -15
- package/dist/{verify-HYF7KBVE.js → verify-T7NAEVIG.js} +13 -13
- package/dist/{web-fetch-MQN4FH3X.js → web-fetch-W4TNW4Q7.js} +4 -4
- package/dist/{wiki-generate-RK7JQ5NP.js → wiki-generate-T2TJQJUA.js} +81 -76
- package/dist/{with-panes-OFTNAR4Y.js → with-panes-CHFC7HGI.js} +4 -4
- package/dist/worker/entry.js +283 -76
- package/docs/README.md +43 -0
- package/docs/architecture/architecture.md +8 -7
- package/docs/architecture/artifact-versions.md +3 -3
- package/docs/architecture/context-engine.md +35 -1
- package/docs/architecture/library.md +32 -0
- package/docs/architecture/middleware-and-components.md +1 -1
- package/docs/architecture/model-catalog.md +21 -1
- package/docs/architecture/pi-boundary.md +18 -0
- package/docs/architecture/prompt-envelope-and-tools.md +5 -5
- package/docs/architecture/safety-model.md +21 -2
- package/docs/architecture/session-lifecycle.md +12 -1
- package/docs/architecture/tui-design.md +108 -9
- package/docs/architecture/worker-context.md +106 -0
- package/docs/guide/authoring-plugins.md +160 -0
- package/docs/guide/built-in-agents.md +5 -5
- package/docs/guide/commands-and-modes.md +161 -56
- package/docs/guide/configuration-and-targets.md +1 -1
- package/docs/guide/configuration-reference.md +27 -34
- package/docs/guide/environment-variables.md +2 -2
- package/docs/guide/exit-codes-and-output.md +1 -1
- package/docs/guide/extensions-and-sharing.md +93 -53
- package/docs/guide/fleet-dispatch.md +31 -4
- package/docs/guide/glossary.md +1 -1
- package/docs/guide/harness-extensions.md +291 -0
- package/docs/guide/installation-and-lifecycle.md +9 -9
- package/docs/guide/interop.md +243 -0
- package/docs/guide/panes-and-files.md +3 -3
- package/docs/guide/plugins.md +80 -0
- package/docs/guide/resource-library.md +111 -45
- package/docs/guide/skills-marketplace.md +35 -72
- package/docs/guide/tool-usage.md +19 -7
- package/docs/guide/troubleshooting.md +1 -1
- package/docs/process/development-pipeline.md +14 -14
- package/docs/process/documentation-coverage.md +28 -1
- package/docs/process/documentation-guide.md +15 -7
- package/docs/process/eval-runner.md +8 -3
- package/docs/process/performance-methodology.md +1 -1
- package/docs/process/release-cut-checklist.md +4 -0
- package/docs/process/scientific-validation.md +23 -0
- package/evals/scalar-decision-machinery.ts +1 -1
- package/examples/extensions/README.md +67 -0
- package/examples/extensions/lab-status/clio-coder-extension.json +14 -0
- package/examples/extensions/lab-status/extension.mjs +68 -0
- package/examples/extensions/lab-status/jobs.synthetic.json +9 -0
- package/examples/extensions/measurements/clio-coder-extension.json +28 -0
- package/examples/extensions/measurements/summarize.cjs +23 -0
- package/library/README.md +109 -0
- package/library/_authoring/templates/agent/README.md +28 -0
- package/library/_authoring/templates/agent/agents/benchmark-scout.md +30 -0
- package/library/_authoring/templates/agent/plugin.json +23 -0
- package/library/_authoring/templates/fleet/README.md +29 -0
- package/library/_authoring/templates/fleet/fleets/pipeline-review.md +31 -0
- package/library/_authoring/templates/fleet/plugin.json +23 -0
- package/library/_authoring/templates/plugin/README.md +37 -0
- package/library/_authoring/templates/plugin/agents/data-curator.md +31 -0
- package/library/_authoring/templates/plugin/assets/curation-guide.md +22 -0
- package/library/_authoring/templates/plugin/plugin.json +42 -0
- package/library/_authoring/templates/plugin/prompts/inspect-dataset.md +15 -0
- package/library/_authoring/templates/plugin/skills/dataset-curation/SKILL.md +36 -0
- package/library/_authoring/templates/plugin/skills/dataset-curation/evals.md +17 -0
- package/library/_authoring/templates/prompt/README.md +28 -0
- package/library/_authoring/templates/prompt/plugin.json +23 -0
- package/library/_authoring/templates/prompt/prompts/paper-summary.md +26 -0
- package/library/_authoring/templates/skill/README.md +24 -0
- package/library/_authoring/templates/skill/SKILL.md +39 -0
- package/library/_authoring/templates/skill/evals.md +30 -0
- package/library/_authoring/templates/skill/plugin.json +23 -0
- package/library/agents/README.md +7 -0
- package/library/fleets/README.md +7 -0
- package/library/plugins/materio/README.md +137 -0
- package/library/plugins/materio/ai.iowarp.clio/agents/materio-lab-definer.md +208 -0
- package/library/plugins/materio/ai.iowarp.clio/agents/materio-literature-reviewer.md +308 -0
- package/library/plugins/materio/ai.iowarp.clio/agents/materio-research-explorer.md +249 -0
- package/library/plugins/materio/ai.iowarp.clio/agents/materio-task-executor.md +416 -0
- package/library/plugins/materio/ai.iowarp.clio/agents/materio-task-verifier.md +60 -0
- package/library/plugins/materio/ai.iowarp.clio/agents/materio-workflow-planner.md +238 -0
- package/library/plugins/materio/ai.iowarp.clio/fleets/materio-execute-task.md +50 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/add-task.md +66 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/archive-task.md +65 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/checkpoint.md +26 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/define-research-tasks.md +230 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/define-virtual-lab.md +181 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/execute-task.md +292 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/help.md +129 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/identify-research.md +206 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/literature-review.md +201 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/pause-research.md +12 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/progress.md +105 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/remove-task.md +63 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/resume-research.md +64 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/settings.md +16 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/status.md +92 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/upload-data.md +104 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/wtfp.md +145 -0
- package/library/plugins/materio/ai.iowarp.clio/references/execution.md +56 -0
- package/library/plugins/materio/ai.iowarp.portability/provenance.json +9 -0
- package/library/plugins/materio/assets/actions/add-task.md +86 -0
- package/library/plugins/materio/assets/actions/archive-task.md +84 -0
- package/library/plugins/materio/assets/actions/checkpoint.md +47 -0
- package/library/plugins/materio/assets/actions/define-research-tasks.md +228 -0
- package/library/plugins/materio/assets/actions/define-virtual-lab.md +179 -0
- package/library/plugins/materio/assets/actions/execute-task.md +290 -0
- package/library/plugins/materio/assets/actions/help.md +140 -0
- package/library/plugins/materio/assets/actions/identify-research.md +204 -0
- package/library/plugins/materio/assets/actions/literature-review.md +199 -0
- package/library/plugins/materio/assets/actions/pause-research.md +34 -0
- package/library/plugins/materio/assets/actions/progress.md +125 -0
- package/library/plugins/materio/assets/actions/remove-task.md +82 -0
- package/library/plugins/materio/assets/actions/resume-research.md +84 -0
- package/library/plugins/materio/assets/actions/settings.md +38 -0
- package/library/plugins/materio/assets/actions/status.md +112 -0
- package/library/plugins/materio/assets/actions/upload-data.md +123 -0
- package/library/plugins/materio/assets/actions/wtfp.md +179 -0
- package/library/plugins/materio/assets/references/research-domains.md +134 -0
- package/library/plugins/materio/assets/references/research-policy.md +77 -0
- package/library/plugins/materio/assets/references/traditional-workflows.md +146 -0
- package/library/plugins/materio/assets/scripts/check_physics.py +271 -0
- package/library/plugins/materio/assets/scripts/check_scripts.py +248 -0
- package/library/plugins/materio/assets/scripts/project_plugin.py +165 -0
- package/library/plugins/materio/assets/scripts/research_state.py +400 -0
- package/library/plugins/materio/assets/scripts/verify_citations.py +616 -0
- package/library/plugins/materio/assets/templates/RESEARCH.md +39 -0
- package/library/plugins/materio/assets/templates/VIRTUAL-LAB.md +120 -0
- package/library/plugins/materio/assets/templates/WORKFLOW.md +42 -0
- package/library/plugins/materio/assets/templates/config.json +5 -0
- package/library/plugins/materio/evals/scripts.yaml +29 -0
- package/library/plugins/materio/plugin.json +530 -0
- package/library/plugins/materio/skills/materio-lab-definer/SKILL.md +74 -0
- package/library/plugins/materio/skills/materio-literature-reviewer/SKILL.md +64 -0
- package/library/plugins/materio/skills/materio-research-explorer/SKILL.md +74 -0
- package/library/plugins/materio/skills/materio-task-executor/SKILL.md +214 -0
- package/library/plugins/materio/skills/materio-task-verifier/SKILL.md +46 -0
- package/library/plugins/materio/skills/materio-workflow-planner/SKILL.md +79 -0
- package/library/plugins/materio/tests/test_check_physics.py +139 -0
- package/library/plugins/materio/tests/test_check_scripts.py +140 -0
- package/library/plugins/materio/tests/test_research_state.py +291 -0
- package/library/plugins/materio/tests/test_verify_citations.py +337 -0
- package/library/prompts/README.md +7 -0
- package/library/registry.yaml +921 -0
- package/library/skills/README.md +120 -0
- package/{skills → library/skills}/coding/ast-grep/SKILL.md +1 -1
- package/{skills → library/skills}/coding/ast-grep/evals.md +1 -1
- package/library/skills/coding/ast-grep/plugin.json +23 -0
- package/{skills → library/skills}/coding/coding-standards/SKILL.md +1 -1
- package/{skills → library/skills}/coding/coding-standards/evals.md +1 -1
- package/library/skills/coding/coding-standards/plugin.json +23 -0
- package/{skills → library/skills}/coding/prototype/SKILL.md +1 -1
- package/{skills → library/skills}/coding/prototype/evals.md +1 -1
- package/library/skills/coding/prototype/plugin.json +23 -0
- package/{skills → library/skills}/coding/tdd/SKILL.md +1 -1
- package/{skills → library/skills}/coding/tdd/evals.md +1 -1
- package/library/skills/coding/tdd/plugin.json +23 -0
- package/{skills → library/skills}/context/context-handoff/SKILL.md +1 -1
- package/{skills → library/skills}/context/context-handoff/evals.md +1 -1
- package/library/skills/context/context-handoff/plugin.json +23 -0
- package/{skills → library/skills}/context/context-prime/SKILL.md +1 -1
- package/{skills → library/skills}/context/context-prime/evals.md +1 -1
- package/library/skills/context/context-prime/plugin.json +23 -0
- package/{skills → library/skills}/git/branch-closeout/SKILL.md +1 -1
- package/library/skills/git/branch-closeout/plugin.json +23 -0
- package/{skills → library/skills}/git/file-ticket/SKILL.md +1 -1
- package/library/skills/git/file-ticket/plugin.json +23 -0
- package/{skills → library/skills}/git/fix-issue/SKILL.md +1 -1
- package/library/skills/git/fix-issue/plugin.json +23 -0
- package/{skills → library/skills}/git/resolve-merge-conflicts/SKILL.md +1 -1
- package/{skills → library/skills}/git/resolve-merge-conflicts/evals.md +1 -1
- package/library/skills/git/resolve-merge-conflicts/plugin.json +23 -0
- package/{skills → library/skills}/git/ship/SKILL.md +1 -1
- package/library/skills/git/ship/plugin.json +23 -0
- package/{skills → library/skills}/git/worktree-create/SKILL.md +1 -1
- package/library/skills/git/worktree-create/plugin.json +23 -0
- package/{skills → library/skills}/git/worktree-merge/SKILL.md +1 -1
- package/library/skills/git/worktree-merge/plugin.json +23 -0
- package/{skills → library/skills}/meta/clio-coder-dev/SKILL.md +1 -1
- package/{skills → library/skills}/meta/clio-coder-dev/evals.md +1 -1
- package/library/skills/meta/clio-coder-dev/plugin.json +23 -0
- package/{skills → library/skills}/meta/clio-coder-test/SKILL.md +1 -1
- package/{skills → library/skills}/meta/clio-coder-test/evals.md +1 -1
- package/library/skills/meta/clio-coder-test/plugin.json +23 -0
- package/{skills → library/skills}/meta/credentials/SKILL.md +1 -1
- package/{skills → library/skills}/meta/credentials/evals.md +1 -1
- package/library/skills/meta/credentials/plugin.json +23 -0
- package/library/skills/meta/find-skills/SKILL.md +85 -0
- package/library/skills/meta/find-skills/evals.md +65 -0
- package/library/skills/meta/find-skills/plugin.json +23 -0
- package/{skills → library/skills}/meta/herdr/SKILL.md +1 -1
- package/{skills → library/skills}/meta/herdr/evals.md +1 -1
- package/library/skills/meta/herdr/plugin.json +23 -0
- package/{skills → library/skills}/meta/skill-craft/SKILL.md +23 -9
- package/{skills → library/skills}/meta/skill-craft/evals.md +11 -3
- package/library/skills/meta/skill-craft/plugin.json +23 -0
- package/{skills → library/skills}/planning/archify/SKILL.md +13 -13
- package/library/skills/planning/archify/plugin.json +23 -0
- package/{skills → library/skills}/planning/architecture/SKILL.md +1 -1
- package/{skills → library/skills}/planning/architecture/evals.md +1 -1
- package/library/skills/planning/architecture/plugin.json +23 -0
- package/{skills → library/skills}/planning/backlog/SKILL.md +1 -1
- package/{skills → library/skills}/planning/backlog/evals.md +1 -1
- package/library/skills/planning/backlog/plugin.json +23 -0
- package/{skills → library/skills}/planning/prd/SKILL.md +1 -1
- package/{skills → library/skills}/planning/prd/evals.md +1 -1
- package/library/skills/planning/prd/plugin.json +23 -0
- package/{skills → library/skills}/planning/product-intent/SKILL.md +1 -1
- package/{skills → library/skills}/planning/product-intent/evals.md +1 -1
- package/library/skills/planning/product-intent/plugin.json +23 -0
- package/{skills → library/skills}/planning/tech-spec/SKILL.md +1 -1
- package/{skills → library/skills}/planning/tech-spec/evals.md +1 -1
- package/library/skills/planning/tech-spec/plugin.json +23 -0
- package/{skills → library/skills}/registry.yaml +7 -7
- package/{skills → library/skills}/remote.yaml +1 -1
- package/{skills → library/skills}/research/arxiv-literature/SKILL.md +1 -1
- package/{skills → library/skills}/research/arxiv-literature/evals.md +1 -1
- package/library/skills/research/arxiv-literature/plugin.json +23 -0
- package/{skills → library/skills}/research/experiment-protocol/SKILL.md +1 -1
- package/{skills → library/skills}/research/experiment-protocol/evals.md +1 -1
- package/library/skills/research/experiment-protocol/plugin.json +23 -0
- package/{skills → library/skills}/research/scientific-debugging/SKILL.md +1 -1
- package/{skills → library/skills}/research/scientific-debugging/evals.md +1 -1
- package/library/skills/research/scientific-debugging/plugin.json +23 -0
- package/{skills → library/skills}/research/scientific-modernization/SKILL.md +1 -1
- package/{skills → library/skills}/research/scientific-modernization/evals.md +1 -1
- package/library/skills/research/scientific-modernization/plugin.json +23 -0
- package/{skills → library/skills}/skill-marketplace.json +37 -37
- package/{skills → library/skills}/workflow/cut-it/SKILL.md +1 -1
- package/{skills → library/skills}/workflow/cut-it/evals.md +1 -1
- package/library/skills/workflow/cut-it/plugin.json +23 -0
- package/{skills → library/skills}/workflow/design-council/SKILL.md +1 -1
- package/{skills → library/skills}/workflow/design-council/evals.md +1 -1
- package/library/skills/workflow/design-council/plugin.json +23 -0
- package/{skills → library/skills}/workflow/grill-me/SKILL.md +1 -1
- package/{skills → library/skills}/workflow/grill-me/evals.md +1 -1
- package/library/skills/workflow/grill-me/plugin.json +23 -0
- package/{skills → library/skills}/workflow/workflow-distiller/SKILL.md +14 -11
- package/{skills → library/skills}/workflow/workflow-distiller/evals.md +14 -6
- package/library/skills/workflow/workflow-distiller/plugin.json +23 -0
- package/package.json +12 -5
- package/src/cli/agents.ts +1 -1
- package/src/cli/config-inspect.ts +1 -1
- package/src/cli/configure.ts +2 -2
- package/src/cli/eval.ts +40 -6
- package/src/cli/extensions.ts +74 -8
- package/src/cli/fleet-preflight.ts +2 -0
- package/src/cli/fleet.ts +2 -0
- package/src/cli/index.ts +1 -3
- package/src/cli/interop-inspect.ts +61 -33
- package/src/cli/interop.ts +79 -9
- package/src/cli/library-import.ts +139 -0
- package/src/cli/library.ts +418 -120
- package/src/cli/run.ts +26 -9
- package/src/cli/skills-eval.ts +43 -6
- package/src/cli/skills-inventory.ts +6 -2
- package/src/cli/skills.ts +44 -491
- package/src/core/bus-events.ts +5 -0
- package/src/core/defaults.ts +1 -1
- package/src/core/recent-models.ts +2 -2
- package/src/core/session-routing.ts +3 -3
- package/src/core/state-file-lock.ts +1 -1
- package/src/core/tool-names.ts +5 -0
- package/src/domains/agents/extension.ts +42 -6
- package/src/domains/agents/fleet-commands.ts +92 -1
- package/src/domains/agents/fleet-contract.ts +87 -19
- package/src/domains/agents/recipe.ts +5 -4
- package/src/domains/agents/registry.ts +80 -24
- package/src/domains/config/keybindings.ts +155 -95
- package/src/domains/context/worker/contract.ts +91 -0
- package/src/domains/context/worker/pressure.ts +120 -0
- package/src/domains/context/worker/recall.ts +62 -0
- package/src/domains/context/worker/select.ts +164 -0
- package/src/domains/context/worker/snapshot.ts +35 -0
- package/src/domains/context/worker/store.ts +16 -0
- package/src/domains/context/working-set/replay/synthetic.ts +2 -1
- package/src/domains/dispatch/active-route-planner.ts +2 -0
- package/src/domains/dispatch/backoff.ts +1 -0
- package/src/domains/dispatch/code-step.ts +12 -2
- package/src/domains/dispatch/contract.ts +3 -0
- package/src/domains/dispatch/execution-plan.ts +2 -0
- package/src/domains/dispatch/extension.ts +109 -27
- package/src/domains/dispatch/fleet-plan.ts +10 -0
- package/src/domains/dispatch/fleet-run.ts +1 -0
- package/src/domains/dispatch/orphan-recovery.ts +1 -0
- package/src/domains/dispatch/receipt-integrity.ts +4 -0
- package/src/domains/dispatch/state.ts +1 -0
- package/src/domains/dispatch/types.ts +5 -0
- package/src/domains/dispatch/validation.ts +12 -0
- package/src/domains/dispatch/workspace-checkpoint.ts +1 -1
- package/src/domains/evidence/failure-attribution.ts +1 -0
- package/src/domains/evidence/trust-status.ts +22 -3
- package/src/domains/extensions/command-schema.ts +145 -0
- package/src/domains/extensions/command-tools.ts +15 -0
- package/src/domains/extensions/compatibility.ts +7 -0
- package/src/domains/extensions/contract.ts +0 -4
- package/src/domains/extensions/discovery.ts +69 -205
- package/src/domains/extensions/extension.ts +0 -4
- package/src/domains/extensions/index.ts +1 -5
- package/src/domains/extensions/manager.ts +1 -6
- package/src/domains/extensions/operator-commands.ts +29 -0
- package/src/domains/extensions/operator-runtime.ts +483 -0
- package/src/domains/extensions/public-api.ts +53 -0
- package/src/domains/extensions/runtime-child.mjs +114 -0
- package/src/domains/extensions/runtime-process.ts +306 -0
- package/src/domains/extensions/runtime-schema.ts +146 -0
- package/src/domains/extensions/{resources.ts → snapshot-access.ts} +1 -17
- package/src/domains/extensions/snapshot.ts +4 -32
- package/src/domains/extensions/state.ts +5 -83
- package/src/domains/extensions/types.ts +33 -34
- package/src/domains/interop/adopt.ts +546 -0
- package/src/domains/interop/detect.ts +50 -12
- package/src/domains/interop/foreign.ts +554 -0
- package/src/domains/interop/import.ts +277 -0
- package/src/domains/interop/index.ts +24 -0
- package/src/domains/interop/install.ts +6 -0
- package/src/domains/interop/inventory.ts +351 -0
- package/src/domains/interop/projection.ts +360 -0
- package/src/domains/interop/registry.ts +106 -3
- package/src/domains/interop/types.ts +51 -0
- package/src/domains/lifecycle/migrations/index.ts +0 -2
- package/src/domains/lifecycle/naming-resources.ts +1 -1
- package/src/domains/middleware/marketplace-offer.ts +1 -1
- package/src/domains/mux/contract.ts +1 -1
- package/src/domains/plugins/catalog.ts +204 -0
- package/src/domains/plugins/contract.ts +24 -0
- package/src/domains/plugins/discovery.ts +316 -0
- package/src/domains/plugins/extension.ts +40 -0
- package/src/domains/plugins/index.ts +77 -0
- package/src/domains/plugins/integrity.ts +6 -0
- package/src/domains/plugins/manifest.ts +3 -0
- package/src/domains/plugins/resources.ts +102 -0
- package/src/domains/plugins/state.ts +753 -0
- package/src/domains/plugins/types.ts +217 -0
- package/src/domains/prompts/extension.ts +9 -2
- package/src/domains/prompts/fragments/operating/skills.md +10 -10
- package/src/domains/resources/common-loader.ts +9 -6
- package/src/domains/resources/index.ts +67 -0
- package/src/domains/resources/library-actions.ts +836 -0
- package/src/domains/resources/library-evals.ts +38 -0
- package/src/domains/resources/library-inventory.ts +1048 -0
- package/src/domains/resources/library-types.ts +50 -0
- package/src/domains/resources/library-validation.ts +634 -0
- package/src/domains/resources/library.ts +437 -156
- package/src/domains/resources/package-references.ts +66 -0
- package/src/domains/resources/prompts/loader.ts +75 -41
- package/src/domains/resources/skills/install.ts +2 -2
- package/src/domains/resources/skills/loader.ts +77 -22
- package/src/domains/resources/skills/marketplace.ts +48 -57
- package/src/domains/resources/skills/promotion.ts +2 -2
- package/src/domains/resources/skills/provenance-pin.ts +17 -4
- package/src/domains/safety/action-classifier.ts +2 -2
- package/src/domains/safety/default-path-policy.ts +3 -1
- package/src/domains/safety/policy-engine.ts +1 -1
- package/src/domains/safety/protected-artifacts.ts +21 -5
- package/src/domains/safety/skill-authority.ts +5 -2
- package/src/domains/share/archive.ts +1 -1
- package/src/engine/apis/openai-completions.ts +52 -2
- package/src/engine/notices/pi-tui-LICENSE +21 -0
- package/src/engine/tui.ts +3 -0
- package/src/engine/worker-runtime.ts +58 -14
- package/src/engine/worker-tools.ts +5 -0
- package/src/entry/orchestrator.ts +26 -5
- package/src/entry/plugin-reload.ts +18 -0
- package/src/interactive/application-controller.ts +67 -21
- package/src/interactive/chat-loop.ts +32 -5
- package/src/interactive/chat-panel.ts +5 -1
- package/src/interactive/clio-editor.ts +53 -21
- package/src/interactive/command-output.ts +56 -1
- package/src/interactive/editor-submit.ts +65 -28
- package/src/interactive/fleet-run-preview.ts +2 -0
- package/src/interactive/follow-up-queue-panel.ts +3 -3
- package/src/interactive/footer/dashboard.ts +4 -1
- package/src/interactive/footer/notifications.ts +2 -2
- package/src/interactive/footer/widgets.ts +2 -2
- package/src/interactive/interactive-application.ts +209 -21
- package/src/interactive/interactive-input-runtime.ts +254 -7
- package/src/interactive/interactive-presentation.ts +41 -12
- package/src/interactive/interactive-slash-runtime.ts +37 -8
- package/src/interactive/keybinding-manager.ts +156 -79
- package/src/interactive/keyboard-owner.ts +37 -0
- package/src/interactive/leader-key.ts +49 -102
- package/src/interactive/leader-menu.ts +45 -0
- package/src/interactive/overlay-ask-user-lifecycle.ts +9 -1
- package/src/interactive/overlay-frame.ts +15 -0
- package/src/interactive/overlay-key-routing.ts +46 -47
- package/src/interactive/overlay-lifecycle.ts +30 -15
- package/src/interactive/overlay-resource-openers.ts +56 -46
- package/src/interactive/overlays/ask-user.ts +630 -556
- package/src/interactive/overlays/extension-panel.ts +70 -0
- package/src/interactive/overlays/extensions.ts +28 -5
- package/src/interactive/overlays/fleet-run-approval.ts +7 -3
- package/src/interactive/overlays/help-reference.ts +89 -40
- package/src/interactive/overlays/interop.ts +122 -3
- package/src/interactive/overlays/keybinding-detail.ts +2 -2
- package/src/interactive/overlays/library-lifecycle.ts +90 -0
- package/src/interactive/overlays/library-model.ts +574 -0
- package/src/interactive/overlays/library-review.ts +557 -0
- package/src/interactive/overlays/library-tabs.ts +3 -15
- package/src/interactive/overlays/library.ts +602 -0
- package/src/interactive/overlays/list-overlay.ts +218 -39
- package/src/interactive/overlays/model-selector.ts +28 -25
- package/src/interactive/overlays/session-selector.ts +9 -3
- package/src/interactive/overlays/settings.ts +25 -18
- package/src/interactive/overlays/tree-selector.ts +16 -11
- package/src/interactive/permission-hint.ts +13 -0
- package/src/interactive/permission-overlay.ts +77 -28
- package/src/interactive/prompt-source-label.ts +21 -0
- package/src/interactive/renderers/reference-card.ts +35 -0
- package/src/interactive/renderers/worker-entry.ts +35 -3
- package/src/interactive/slash-autocomplete.ts +99 -1
- package/src/interactive/slash-commands.ts +390 -67
- package/src/interactive/slash-spec.ts +0 -1
- package/src/interactive/terminal-lease.ts +44 -12
- package/src/interactive/turn-queues.ts +8 -7
- package/src/interactive/turn-runtime.ts +1 -1
- package/src/interactive/view/view-overlay.ts +19 -14
- package/src/interactive/welcome-dashboard.ts +525 -408
- package/src/tools/bootstrap.ts +8 -0
- package/src/tools/builtin-tool-catalog.ts +7 -2
- package/src/tools/context/index.ts +58 -5
- package/src/tools/context/library.ts +465 -0
- package/src/tools/context/surface.ts +20 -6
- package/src/tools/core-bootstrap.ts +3 -0
- package/src/tools/dispatch-admission.ts +24 -1
- package/src/tools/dispatch-arguments.ts +8 -0
- package/src/tools/dispatch-plan.ts +32 -0
- package/src/tools/dispatch-schema.ts +31 -1
- package/src/tools/dispatch-types.ts +3 -0
- package/src/tools/dispatch.ts +1 -1
- package/src/tools/harness-extensions.ts +169 -0
- package/src/tools/observation.ts +4 -0
- package/src/tools/policy.ts +7 -1
- package/src/tools/registry.ts +19 -1
- package/src/tools/worker-evidence.ts +5 -0
- package/src/worker/context-seed.ts +192 -0
- package/src/worker/entry.ts +1 -0
- package/src/worker/spec-contract.ts +8 -1
- package/dist/chunk-3UW3KU4A.js +0 -21
- package/dist/chunk-536HXRFE.js +0 -1257
- package/dist/chunk-B3MOMLJ4.js +0 -433
- package/dist/chunk-CXQ37WJH.js +0 -45
- package/dist/chunk-KTFSFCVN.js +0 -46
- package/dist/chunk-QCOV5DFY.js +0 -911
- package/dist/chunk-QRFXEO6Y.js +0 -17
- package/dist/chunk-VAHLPXPY.js +0 -399
- package/dist/interop-RVCAYECG.js +0 -115
- package/dist/library-CTKZA7K3.js +0 -226
- package/dist/skills-OXFEAWMT.js +0 -510
- package/skills/README.md +0 -418
- package/skills/meta/find-skills/SKILL.md +0 -78
- package/skills/meta/find-skills/evals.md +0 -47
- package/src/domains/extensions/resource-path.ts +0 -27
- package/src/domains/lifecycle/migrations/2026-09-01-extension-install-digests.ts +0 -27
- package/src/interactive/overlays/agents.ts +0 -70
- package/src/interactive/overlays/library-install-confirm.ts +0 -152
- package/src/interactive/overlays/prompts.ts +0 -89
- package/src/interactive/overlays/skills-hub.ts +0 -615
- /package/{skills → library/skills}/coding/ast-grep/references/rule_reference.md +0 -0
- /package/{skills → library/skills}/coding/prototype/references/LOGIC.md +0 -0
- /package/{skills → library/skills}/coding/prototype/references/UI.md +0 -0
- /package/{skills → library/skills}/coding/tdd/references/mocking.md +0 -0
- /package/{skills → library/skills}/coding/tdd/references/tests.md +0 -0
- /package/{skills → library/skills}/context/context-handoff/scripts/new-handoff.sh +0 -0
- /package/{skills → library/skills}/git/branch-closeout/evals.md +0 -0
- /package/{skills → library/skills}/git/branch-closeout/references/closeout-checklist.md +0 -0
- /package/{skills → library/skills}/git/file-ticket/assets/issue-template.md +0 -0
- /package/{skills → library/skills}/git/file-ticket/evals.md +0 -0
- /package/{skills → library/skills}/git/file-ticket/references/issue-discovery.md +0 -0
- /package/{skills → library/skills}/git/fix-issue/evals.md +0 -0
- /package/{skills → library/skills}/git/fix-issue/references/diagnosis-and-rca.md +0 -0
- /package/{skills → library/skills}/git/resolve-merge-conflicts/references/conflict-matrix.md +0 -0
- /package/{skills → library/skills}/git/ship/assets/pr-template.md +0 -0
- /package/{skills → library/skills}/git/ship/evals.md +0 -0
- /package/{skills → library/skills}/git/ship/references/remote-and-branch-policy.md +0 -0
- /package/{skills → library/skills}/git/worktree-create/evals.md +0 -0
- /package/{skills → library/skills}/git/worktree-create/references/worktree-setup.md +0 -0
- /package/{skills → library/skills}/git/worktree-merge/evals.md +0 -0
- /package/{skills → library/skills}/git/worktree-merge/references/merge-strategies.md +0 -0
- /package/{skills → library/skills}/meta/clio-coder-test/references/harness.md +0 -0
- /package/{skills → library/skills}/meta/clio-coder-test/references/test-map.md +0 -0
- /package/{skills → library/skills}/planning/archify/evals.md +0 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Generate a peer host package without claiming equivalent fleet enforcement."""
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import shutil
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
ACTION_ROLES = {
|
|
11
|
+
"identify-research": "research-explorer", "literature-review": "literature-reviewer",
|
|
12
|
+
"define-virtual-lab": "lab-definer", "define-research-tasks": "workflow-planner",
|
|
13
|
+
"execute-task": "task-executor",
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def render_peer_guides(target, output, name):
|
|
18
|
+
"""Bind logical action/skill requests to capabilities actually exported."""
|
|
19
|
+
actions = sorted(path.stem for path in (output / "assets/actions").glob("*.md"))
|
|
20
|
+
invocations = {}
|
|
21
|
+
for action in actions:
|
|
22
|
+
skill = name + "-" + ACTION_ROLES.get(action, "research-explorer")
|
|
23
|
+
invocations[action] = ({"kind": "skill", "name": skill, "request": action}
|
|
24
|
+
if target == "codex" else {"kind": "command", "name": f"/{name}:{action}"})
|
|
25
|
+
paths = list((output / "assets/actions").glob("*.md")) + list((output / "skills").glob("*/SKILL.md"))
|
|
26
|
+
for path in paths:
|
|
27
|
+
text = path.read_text()
|
|
28
|
+
if target in {"claude", "gemini"}:
|
|
29
|
+
for action in actions:
|
|
30
|
+
skill = name + "-" + ACTION_ROLES.get(action, "research-explorer")
|
|
31
|
+
text = text.replace(f"Run {action} using the {skill} skill", f"/{name}:{action}")
|
|
32
|
+
text = text.replace("this standard package registers no slash commands",
|
|
33
|
+
"this native package registers the command routes shown below")
|
|
34
|
+
text = text.replace("actual skill invocation vocabulary", "native command vocabulary")
|
|
35
|
+
if path.parent.name == "actions":
|
|
36
|
+
role = ACTION_ROLES.get(path.stem)
|
|
37
|
+
if target == "claude" and role:
|
|
38
|
+
binding = (f"\n## Native role binding\n\nThe interactive orchestrator dispatches the registered agent `{name}:{role}`\n"
|
|
39
|
+
f"for the `{name}-{role}` scientific skill. After a checkpoint, dispatch that\n"
|
|
40
|
+
"same registered agent again with the complete transcript, prior outputs and\n"
|
|
41
|
+
"actual researcher answer. Skill names identify instructions, not agent IDs.\n"
|
|
42
|
+
"A worker returns the checkpoint and does not dispatch or interview.\n")
|
|
43
|
+
if role == "task-executor":
|
|
44
|
+
binding += (f"For the separate read-only artifact review, dispatch the registered agent `{name}:task-verifier`.\n"
|
|
45
|
+
"Give it no write grant and preserve any host grounding limitations.\n")
|
|
46
|
+
elif target in {"codex", "gemini"}:
|
|
47
|
+
binding = ("\n## Host role execution\n\nThis export registers no named agents. Apply the role's installed skill in the\n"
|
|
48
|
+
"main conversation, or give that skill and complete context to a generic worker\n"
|
|
49
|
+
"only when the host supports delegation. The interactive assistant owns every\n"
|
|
50
|
+
"researcher question and resumes the role with the actual answer.\n")
|
|
51
|
+
else:
|
|
52
|
+
binding = ""
|
|
53
|
+
text += binding
|
|
54
|
+
path.write_text(text)
|
|
55
|
+
return invocations
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def project(target, output, source=None):
|
|
59
|
+
source = Path(source or Path(__file__).resolve().parents[2]).resolve(strict=True)
|
|
60
|
+
output = Path(output).absolute()
|
|
61
|
+
if output.exists() or output.is_symlink():
|
|
62
|
+
raise ValueError("projection output must not already exist")
|
|
63
|
+
output = output.resolve()
|
|
64
|
+
if source == output or source in output.parents:
|
|
65
|
+
raise ValueError("projection output must be outside the canonical bundle")
|
|
66
|
+
manifest = json.loads((source / "plugin.json").read_text())
|
|
67
|
+
output.mkdir(parents=True)
|
|
68
|
+
try:
|
|
69
|
+
for directory in ("skills", "assets"):
|
|
70
|
+
for path in (source / directory).rglob("*"):
|
|
71
|
+
if path.is_symlink():
|
|
72
|
+
raise ValueError(f"projection refuses symbolic link: {path}")
|
|
73
|
+
shutil.copytree(source / directory, output / directory,
|
|
74
|
+
ignore=shutil.ignore_patterns("__pycache__", "*.pyc"))
|
|
75
|
+
metadata = {key: manifest[key] for key in ("name", "version", "description", "author", "repository", "keywords") if key in manifest}
|
|
76
|
+
report = {"target": target, "skills": 6, "commands": 0, "agents": 0,
|
|
77
|
+
"fleet": "instructions only; no native write-scope enforcement claimed",
|
|
78
|
+
"scripts": "local Python helpers; no automatic execution",
|
|
79
|
+
"validation": "generated structure; run target-native validation before publication"}
|
|
80
|
+
report["invocations"] = render_peer_guides(target, output, metadata["name"])
|
|
81
|
+
report["agent_bindings"] = {}
|
|
82
|
+
if target == "codex":
|
|
83
|
+
# Codex recognizes the current portable standard. Do not fabricate a
|
|
84
|
+
# separate proprietary manifest or pretend Clio recipes are native.
|
|
85
|
+
portable = {"$schema": manifest["$schema"], **metadata}
|
|
86
|
+
(output / "plugin.json").write_text(json.dumps(portable, indent=2) + "\n")
|
|
87
|
+
elif target == "claude":
|
|
88
|
+
directory = output / ".claude-plugin"
|
|
89
|
+
directory.mkdir()
|
|
90
|
+
(directory / "plugin.json").write_text(json.dumps(metadata, indent=2) + "\n")
|
|
91
|
+
commands = output / "commands"
|
|
92
|
+
agents = output / "agents"
|
|
93
|
+
commands.mkdir()
|
|
94
|
+
agents.mkdir()
|
|
95
|
+
roles = ACTION_ROLES
|
|
96
|
+
for guide in sorted((output / "assets/actions").glob("*.md")):
|
|
97
|
+
assignment = (f"Delegate this scientific role to the registered agent `{metadata['name']}:{roles[guide.stem]}`.\n"
|
|
98
|
+
if guide.stem in roles else "This action is owned by the interactive orchestrator.\n")
|
|
99
|
+
text = (f"---\ndescription: Materials characterization {guide.stem} workflow.\n---\n\n"
|
|
100
|
+
f"Read ${{CLAUDE_PLUGIN_ROOT}}/assets/actions/{guide.name} and follow that workflow.\n"
|
|
101
|
+
+ assignment +
|
|
102
|
+
"Use AskUserQuestion for the interactive interview. If delegating, use the registered\n"
|
|
103
|
+
"materio role agent and include full context; workers return\n"
|
|
104
|
+
"checkpoint questions to this orchestrator. Use Bash only for approved local helper\n"
|
|
105
|
+
"calls; bind PACKAGE_ROOT to ${CLAUDE_PLUGIN_ROOT} in each call.\n"
|
|
106
|
+
"Arguments: $ARGUMENTS\n")
|
|
107
|
+
(commands / guide.name).write_text(text)
|
|
108
|
+
report["commands"] += 1
|
|
109
|
+
for skill in sorted((output / "skills").glob("*/SKILL.md")):
|
|
110
|
+
role = skill.parent.name.removeprefix("materio-")
|
|
111
|
+
tools = "Read, Glob, Grep" if role == "task-verifier" else "Read, Glob, Grep, Write, Edit, Bash, WebFetch"
|
|
112
|
+
text = (f"---\nname: {role}\ndescription: Materials characterization {role} role.\n"
|
|
113
|
+
f"tools: {tools}\n---\n\n"
|
|
114
|
+
f"Read ${{CLAUDE_PLUGIN_ROOT}}/skills/{skill.parent.name}/SKILL.md.\n"
|
|
115
|
+
"Apply its scientific contract within the caller's approved output paths.\n"
|
|
116
|
+
"Return structured complete, blocked, or needs_input with checkpoint kind\n"
|
|
117
|
+
"decision, human-action, or human-verify. Do not interview the researcher.\n"
|
|
118
|
+
"The caller owns state reconciliation, optional recording and paper gates.\n")
|
|
119
|
+
(agents / (role + ".md")).write_text(text)
|
|
120
|
+
report["agents"] += 1
|
|
121
|
+
report["agent_bindings"][role] = metadata["name"] + ":" + role
|
|
122
|
+
elif target == "gemini":
|
|
123
|
+
(output / "gemini-extension.json").write_text(json.dumps({
|
|
124
|
+
"name": metadata["name"], "version": metadata["version"],
|
|
125
|
+
"description": metadata["description"], "contextFileName": "GEMINI.md"
|
|
126
|
+
}, indent=2) + "\n")
|
|
127
|
+
(output / "GEMINI.md").write_text(
|
|
128
|
+
"# Materials characterization\n\nUse the installed materio skills and their relative action-guide links.\n"
|
|
129
|
+
"The interactive assistant owns researcher questions and state changes. Roles may\n"
|
|
130
|
+
"run inline when delegation is unavailable. No native fleet boundary is claimed.\n")
|
|
131
|
+
directory = output / "commands/materio"
|
|
132
|
+
directory.mkdir(parents=True)
|
|
133
|
+
for guide in sorted((output / "assets/actions").glob("*.md")):
|
|
134
|
+
text = (f"Use the installed materio skills to perform {guide.stem}. "
|
|
135
|
+
f"Read their relative link to assets/actions/{guide.name} and follow its contract. "
|
|
136
|
+
"Use the host conversation tool for researcher interviews. Delegate only with "
|
|
137
|
+
"available capabilities; otherwise perform the role inline. Arguments: {{args}}")
|
|
138
|
+
(directory / (guide.stem + ".toml")).write_text(
|
|
139
|
+
"description = " + json.dumps(f"Materials characterization {guide.stem}") + "\n"
|
|
140
|
+
"prompt = " + json.dumps(text) + "\n")
|
|
141
|
+
report["commands"] += 1
|
|
142
|
+
else:
|
|
143
|
+
raise ValueError(f"unsupported peer target: {target}")
|
|
144
|
+
(output / "capability-report.json").write_text(json.dumps(report, indent=2) + "\n")
|
|
145
|
+
return report
|
|
146
|
+
except Exception:
|
|
147
|
+
shutil.rmtree(output)
|
|
148
|
+
raise
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def main():
|
|
152
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
153
|
+
parser.add_argument("--target", choices=["codex", "claude", "gemini"], required=True)
|
|
154
|
+
parser.add_argument("--output", required=True)
|
|
155
|
+
args = parser.parse_args()
|
|
156
|
+
try:
|
|
157
|
+
print(json.dumps(project(args.target, args.output), indent=2))
|
|
158
|
+
return 0
|
|
159
|
+
except (OSError, ValueError) as error:
|
|
160
|
+
print(str(error), file=sys.stderr)
|
|
161
|
+
return 2
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
if __name__ == "__main__":
|
|
165
|
+
sys.exit(main())
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Deterministic local research state operations. Python 3.10+, stdlib only.
|
|
3
|
+
|
|
4
|
+
This helper never initializes git, tags a checkpoint, or writes paper state.
|
|
5
|
+
It does not run experiments or generated analysis programs.
|
|
6
|
+
"""
|
|
7
|
+
import argparse
|
|
8
|
+
import hashlib
|
|
9
|
+
import io
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
import re
|
|
14
|
+
import shutil
|
|
15
|
+
import subprocess
|
|
16
|
+
import sys
|
|
17
|
+
import tarfile
|
|
18
|
+
import tempfile
|
|
19
|
+
from datetime import datetime, timezone
|
|
20
|
+
from uuid import uuid4
|
|
21
|
+
|
|
22
|
+
DEFAULTS = {"web_search": False, "auto_checkpoint": True, "commit_research": False}
|
|
23
|
+
PRESERVED = {"data", "checkpoints"}
|
|
24
|
+
ARCHIVE_MANIFEST = ".snapshot-manifest.json"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def unique_object(pairs):
|
|
28
|
+
result = {}
|
|
29
|
+
for key, value in pairs:
|
|
30
|
+
if key in result:
|
|
31
|
+
raise ValueError(f"duplicate JSON key: {key}")
|
|
32
|
+
result[key] = value
|
|
33
|
+
return result
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def research_root(project):
|
|
37
|
+
project = Path(project).resolve(strict=True)
|
|
38
|
+
root = project / ".research"
|
|
39
|
+
if root.is_symlink():
|
|
40
|
+
raise ValueError(".research must be a real directory, not a symlink")
|
|
41
|
+
return root
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def config(root):
|
|
45
|
+
path = root / "config.json"
|
|
46
|
+
result = dict(DEFAULTS)
|
|
47
|
+
if path.is_symlink():
|
|
48
|
+
raise ValueError("config.json must not be a symlink")
|
|
49
|
+
if path.exists():
|
|
50
|
+
value = json.loads(path.read_text(), object_pairs_hook=unique_object)
|
|
51
|
+
if not isinstance(value, dict):
|
|
52
|
+
raise ValueError("research config must be a JSON object")
|
|
53
|
+
for key in DEFAULTS:
|
|
54
|
+
if key in value and type(value[key]) is not bool:
|
|
55
|
+
raise ValueError(f"{key} must be a JSON boolean")
|
|
56
|
+
result.update(value)
|
|
57
|
+
return result
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def contained(root, relative, allow_missing=False):
|
|
61
|
+
relative = Path(relative)
|
|
62
|
+
if relative.is_absolute() or ".." in relative.parts or not relative.parts:
|
|
63
|
+
raise ValueError(f"invalid relative research path: {relative}")
|
|
64
|
+
path = root
|
|
65
|
+
for part in relative.parts:
|
|
66
|
+
path = path / part
|
|
67
|
+
if path.is_symlink():
|
|
68
|
+
raise ValueError(f"symlink is not permitted: {path}")
|
|
69
|
+
if not allow_missing and not path.exists():
|
|
70
|
+
raise ValueError(f"missing research path: {path}")
|
|
71
|
+
return path
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def task_number(value):
|
|
75
|
+
if not re.fullmatch(r"[0-9]+", str(value)) or int(value) < 1:
|
|
76
|
+
raise ValueError("task must be a positive decimal integer")
|
|
77
|
+
return f"{int(value):02d}"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def workflow(root):
|
|
81
|
+
path = root / "WORKFLOW.md"
|
|
82
|
+
if not path.exists():
|
|
83
|
+
return [], []
|
|
84
|
+
text = contained(root, "WORKFLOW.md").read_text()
|
|
85
|
+
active_text, _, archive_text = text.partition("## Archived Tasks")
|
|
86
|
+
def parse(text):
|
|
87
|
+
matches = list(re.finditer(r"^### Task ([0-9]+):[^\n]*", text, re.M))
|
|
88
|
+
return [(task_number(m.group(1)), text[m.start():matches[i + 1].start() if i + 1 < len(matches) else len(text)]) for i, m in enumerate(matches)]
|
|
89
|
+
active, archived = parse(active_text), parse(archive_text)
|
|
90
|
+
ids = [number for number, _ in active + archived]
|
|
91
|
+
if len(set(ids)) != len(ids):
|
|
92
|
+
raise ValueError("duplicate task identity in WORKFLOW.md")
|
|
93
|
+
return active, archived
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def next_task(root):
|
|
97
|
+
active, archived = workflow(root)
|
|
98
|
+
ids = [int(number) for number, _ in active + archived]
|
|
99
|
+
tasks = root / "tasks"
|
|
100
|
+
if tasks.exists():
|
|
101
|
+
contained(root, "tasks")
|
|
102
|
+
for path in tasks.iterdir():
|
|
103
|
+
m = re.fullmatch(r"task-([0-9]+)", path.name)
|
|
104
|
+
if m:
|
|
105
|
+
ids.append(int(m.group(1)))
|
|
106
|
+
return task_number(max(ids, default=0) + 1)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def validate_workflow(root):
|
|
110
|
+
active, archived = workflow(root)
|
|
111
|
+
ids = {number for number, _ in active}
|
|
112
|
+
archived_ids = {number for number, _ in archived}
|
|
113
|
+
graph = {}
|
|
114
|
+
for number, block in active:
|
|
115
|
+
match = re.search(r"^[-*] \*\*Dependencies\*\*:\s*(.*)$", block, re.M)
|
|
116
|
+
refs = [task_number(value) for value in re.findall(r"Task\s+([0-9]+)", match.group(1) if match else "")]
|
|
117
|
+
for ref in refs:
|
|
118
|
+
if ref not in ids:
|
|
119
|
+
kind = "archived" if ref in archived_ids else "missing"
|
|
120
|
+
raise ValueError(f"Task {number} depends on {kind} Task {ref}")
|
|
121
|
+
graph[number] = refs
|
|
122
|
+
visiting, visited = set(), set()
|
|
123
|
+
def visit(number):
|
|
124
|
+
if number in visiting:
|
|
125
|
+
raise ValueError(f"dependency cycle at Task {number}")
|
|
126
|
+
if number in visited:
|
|
127
|
+
return
|
|
128
|
+
visiting.add(number)
|
|
129
|
+
for ref in graph[number]:
|
|
130
|
+
visit(ref)
|
|
131
|
+
visiting.remove(number)
|
|
132
|
+
visited.add(number)
|
|
133
|
+
for number in graph:
|
|
134
|
+
visit(number)
|
|
135
|
+
return {"active": list(graph), "archived": sorted(archived_ids), "dependencies": graph}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def next_ready_task(root):
|
|
139
|
+
"""Choose a pending/in-progress task by workflow order and completed inputs."""
|
|
140
|
+
graph = validate_workflow(root)["dependencies"]
|
|
141
|
+
active, _ = workflow(root)
|
|
142
|
+
statuses = {}
|
|
143
|
+
for number, block in active:
|
|
144
|
+
match = re.search(r"^[-*] \*\*Status\*\*:\s*(.*)$", block, re.M)
|
|
145
|
+
status = re.sub(r"^[^a-zA-Z]+", "", match.group(1) if match else "").lower()
|
|
146
|
+
statuses[number] = status.split()[0] if status else "unspecified"
|
|
147
|
+
blocked = []
|
|
148
|
+
for number, _ in active:
|
|
149
|
+
if statuses[number] not in {"pending", "in-progress"}:
|
|
150
|
+
continue
|
|
151
|
+
unsettled = [dependency for dependency in graph[number] if statuses[dependency] != "complete"]
|
|
152
|
+
if not unsettled:
|
|
153
|
+
return {"id": number, "status": statuses[number], "blocked": blocked}
|
|
154
|
+
blocked.append({"id": number, "dependencies": unsettled})
|
|
155
|
+
return {"id": None, "reason": "no dependency-ready pending task", "blocked": blocked}
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def snapshot_files(root):
|
|
159
|
+
files = {}
|
|
160
|
+
for base, directories, names in os.walk(root, followlinks=False):
|
|
161
|
+
base = Path(base)
|
|
162
|
+
if base == root:
|
|
163
|
+
directories[:] = [name for name in directories if name not in PRESERVED]
|
|
164
|
+
for name in directories:
|
|
165
|
+
if (base / name).is_symlink():
|
|
166
|
+
raise ValueError(f"snapshot refuses symlink: {base / name}")
|
|
167
|
+
for name in names:
|
|
168
|
+
path = base / name
|
|
169
|
+
relative = path.relative_to(root).as_posix()
|
|
170
|
+
if base == root and name in PRESERVED:
|
|
171
|
+
continue
|
|
172
|
+
if path == root / ARCHIVE_MANIFEST:
|
|
173
|
+
raise ValueError(f"reserved snapshot filename: {ARCHIVE_MANIFEST}")
|
|
174
|
+
if path.is_symlink() or not path.is_file():
|
|
175
|
+
raise ValueError(f"snapshot needs regular files: {path}")
|
|
176
|
+
files[relative] = path.read_bytes()
|
|
177
|
+
return files
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def checkpoint_save(root, label):
|
|
181
|
+
if not re.fullmatch(r"[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}", label):
|
|
182
|
+
raise ValueError("checkpoint label must be 1–64 path-safe characters")
|
|
183
|
+
if not root.is_dir():
|
|
184
|
+
raise ValueError("research state is not initialized")
|
|
185
|
+
directory = contained(root, "checkpoints", allow_missing=True)
|
|
186
|
+
directory.mkdir(exist_ok=True)
|
|
187
|
+
files = snapshot_files(root)
|
|
188
|
+
directories = []
|
|
189
|
+
for base, names, _ in os.walk(root, followlinks=False):
|
|
190
|
+
if Path(base) == root:
|
|
191
|
+
names[:] = [name for name in names if name not in PRESERVED]
|
|
192
|
+
directories.extend((Path(base) / name).relative_to(root).as_posix() for name in names)
|
|
193
|
+
modes = {name: (root / name).stat().st_mode & 0o777 for name in files}
|
|
194
|
+
manifest = {"version": 1, "files": {name: hashlib.sha256(data).hexdigest() for name, data in files.items()},
|
|
195
|
+
"directories": sorted(directories), "modes": modes}
|
|
196
|
+
name = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%S.%fZ") + "-" + label + "-" + uuid4().hex[:8] + ".tgz"
|
|
197
|
+
destination = directory / name
|
|
198
|
+
created = False
|
|
199
|
+
try:
|
|
200
|
+
with destination.open("xb") as output:
|
|
201
|
+
created = True
|
|
202
|
+
with tarfile.open(fileobj=output, mode="w:gz") as archive:
|
|
203
|
+
for relative, data in sorted({**files, ARCHIVE_MANIFEST: json.dumps(manifest).encode()}.items()):
|
|
204
|
+
member = tarfile.TarInfo(relative)
|
|
205
|
+
member.size, member.mode = len(data), 0o600
|
|
206
|
+
archive.addfile(member, io.BytesIO(data))
|
|
207
|
+
except Exception:
|
|
208
|
+
if created:
|
|
209
|
+
destination.unlink(missing_ok=True)
|
|
210
|
+
raise
|
|
211
|
+
return {"checkpoint": name, "files": len(files), "excluded": sorted(PRESERVED)}
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def checkpoint_read(root, name):
|
|
215
|
+
if not re.fullmatch(r"[a-zA-Z0-9][a-zA-Z0-9._-]*\.tgz", name):
|
|
216
|
+
raise ValueError("checkpoint must be a filename listed by checkpoint list")
|
|
217
|
+
path = contained(root, "checkpoints/" + name)
|
|
218
|
+
files = {}
|
|
219
|
+
with tarfile.open(path, "r:gz") as archive:
|
|
220
|
+
for member in archive:
|
|
221
|
+
relative = Path(member.name)
|
|
222
|
+
if member.name in files or relative.is_absolute() or ".." in relative.parts or not relative.parts:
|
|
223
|
+
raise ValueError(f"invalid or duplicate archive member: {member.name}")
|
|
224
|
+
if relative.parts[0] in PRESERVED or not member.isfile():
|
|
225
|
+
raise ValueError(f"archive member is not permitted: {member.name}")
|
|
226
|
+
if relative.as_posix() != member.name:
|
|
227
|
+
raise ValueError(f"noncanonical archive member: {member.name}")
|
|
228
|
+
files[member.name] = archive.extractfile(member).read()
|
|
229
|
+
if ARCHIVE_MANIFEST not in files:
|
|
230
|
+
raise ValueError("checkpoint has no integrity manifest")
|
|
231
|
+
manifest = json.loads(files.pop(ARCHIVE_MANIFEST), object_pairs_hook=unique_object)
|
|
232
|
+
observed = {key: hashlib.sha256(value).hexdigest() for key, value in files.items()}
|
|
233
|
+
if not isinstance(manifest, dict) or manifest.get("version") != 1 or manifest.get("files") != observed:
|
|
234
|
+
raise ValueError("checkpoint content integrity mismatch")
|
|
235
|
+
directories = manifest.get("directories", [])
|
|
236
|
+
modes = manifest.get("modes", {})
|
|
237
|
+
if not isinstance(directories, list) or not isinstance(modes, dict):
|
|
238
|
+
raise ValueError("invalid snapshot directory or mode inventory")
|
|
239
|
+
for directory in directories:
|
|
240
|
+
if not isinstance(directory, str):
|
|
241
|
+
raise ValueError("invalid snapshot directory")
|
|
242
|
+
relative = Path(directory)
|
|
243
|
+
if relative.is_absolute() or ".." in relative.parts or not relative.parts or relative.parts[0] in PRESERVED or relative.as_posix() != directory:
|
|
244
|
+
raise ValueError(f"invalid snapshot directory: {directory}")
|
|
245
|
+
if any(name not in files or type(mode) is not int or mode < 0 or mode > 0o777 for name, mode in modes.items()):
|
|
246
|
+
raise ValueError("invalid snapshot mode")
|
|
247
|
+
return files, directories, modes
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def checkpoint_restore(root, name, confirmed):
|
|
251
|
+
if not confirmed:
|
|
252
|
+
raise ValueError("restore requires --confirmed after researcher confirmation")
|
|
253
|
+
archive_name = name
|
|
254
|
+
files, directories, modes = checkpoint_read(root, name)
|
|
255
|
+
# Validate and stage before touching current state. Data and checkpoints are
|
|
256
|
+
# moved, never traversed or copied, and rollback restores the original root.
|
|
257
|
+
stage = Path(tempfile.mkdtemp(prefix=".research-restore-", dir=root.parent))
|
|
258
|
+
backup = Path(tempfile.mkdtemp(prefix=".research-previous-", dir=root.parent))
|
|
259
|
+
backup.rmdir()
|
|
260
|
+
moved = []
|
|
261
|
+
swapped = False
|
|
262
|
+
try:
|
|
263
|
+
for directory in directories:
|
|
264
|
+
(stage / directory).mkdir(parents=True, exist_ok=True)
|
|
265
|
+
for relative, data in files.items():
|
|
266
|
+
path = stage / relative
|
|
267
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
268
|
+
path.write_bytes(data)
|
|
269
|
+
path.chmod(modes.get(relative, 0o600))
|
|
270
|
+
root.rename(backup)
|
|
271
|
+
swapped = True
|
|
272
|
+
for name in sorted(PRESERVED):
|
|
273
|
+
if (backup / name).exists() or (backup / name).is_symlink():
|
|
274
|
+
(backup / name).rename(stage / name)
|
|
275
|
+
moved.append(name)
|
|
276
|
+
stage.rename(root)
|
|
277
|
+
except Exception:
|
|
278
|
+
if swapped:
|
|
279
|
+
for name in reversed(moved):
|
|
280
|
+
(stage / name).rename(backup / name)
|
|
281
|
+
backup.rename(root)
|
|
282
|
+
raise
|
|
283
|
+
finally:
|
|
284
|
+
if stage.exists():
|
|
285
|
+
shutil.rmtree(stage)
|
|
286
|
+
shutil.rmtree(backup)
|
|
287
|
+
return {"restored": archive_name, "files": len(files), "preserved": sorted(PRESERVED)}
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def record(root, message, paths):
|
|
291
|
+
if not config(root)["commit_research"]:
|
|
292
|
+
return {"recorded": False, "reason": "commit_research is false"}
|
|
293
|
+
project = root.parent
|
|
294
|
+
def git(*argv, check=True):
|
|
295
|
+
return subprocess.run(["git", "-C", str(project), *argv], text=True, capture_output=True, check=check)
|
|
296
|
+
git("rev-parse", "--show-toplevel")
|
|
297
|
+
if git("diff", "--cached", "--name-only").stdout.strip():
|
|
298
|
+
raise ValueError("optional record refused: unrelated or preexisting staged changes")
|
|
299
|
+
selected = []
|
|
300
|
+
for value in paths:
|
|
301
|
+
relative = Path(value)
|
|
302
|
+
if not relative.parts or relative.parts[0] != ".research":
|
|
303
|
+
raise ValueError("optional record accepts only explicit .research files")
|
|
304
|
+
path = contained(root, Path(*relative.parts[1:]))
|
|
305
|
+
if not path.is_file():
|
|
306
|
+
raise ValueError("record needs named files, not directory staging")
|
|
307
|
+
selected.append(relative.as_posix())
|
|
308
|
+
if not selected:
|
|
309
|
+
return {"recorded": False, "reason": "no changed files supplied"}
|
|
310
|
+
git("add", "--", *selected)
|
|
311
|
+
if git("diff", "--cached", "--quiet", check=False).returncode == 0:
|
|
312
|
+
return {"recorded": False, "reason": "no staged changes"}
|
|
313
|
+
result = git("commit", "-m", message)
|
|
314
|
+
return {"recorded": True, "files": selected, "detail": result.stdout.strip()}
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def register_copy(root, source):
|
|
318
|
+
source = Path(source).resolve(strict=True)
|
|
319
|
+
if not source.is_file():
|
|
320
|
+
raise ValueError("data registration needs an actual file")
|
|
321
|
+
directory = contained(root, "data", allow_missing=True)
|
|
322
|
+
directory.mkdir(exist_ok=True)
|
|
323
|
+
target = directory / source.name
|
|
324
|
+
data = source.read_bytes()
|
|
325
|
+
digest = hashlib.sha256(data).hexdigest()
|
|
326
|
+
if target.exists() or target.is_symlink():
|
|
327
|
+
if target.is_file() and not target.is_symlink() and target.read_bytes() == data:
|
|
328
|
+
return {"path": str(target), "source": str(source), "sha256": digest, "copied": False}
|
|
329
|
+
raise ValueError(f"data filename collision: {target.name}; choose a distinct name")
|
|
330
|
+
with target.open("xb") as output:
|
|
331
|
+
output.write(data)
|
|
332
|
+
return {"path": str(target), "source": str(source), "sha256": digest, "copied": True}
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def main(argv=None):
|
|
336
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
337
|
+
parser.add_argument("--project", default=".")
|
|
338
|
+
sub = parser.add_subparsers(dest="action", required=True)
|
|
339
|
+
sub.add_parser("init")
|
|
340
|
+
cfg = sub.add_parser("config")
|
|
341
|
+
cfg.add_argument("--key", choices=sorted(DEFAULTS))
|
|
342
|
+
sub.add_parser("next-task-id")
|
|
343
|
+
sub.add_parser("next-task")
|
|
344
|
+
sub.add_parser("validate-workflow")
|
|
345
|
+
sub.add_parser("task-dirs")
|
|
346
|
+
checkpoint = sub.add_parser("checkpoint")
|
|
347
|
+
checkpoint.add_argument("operation", choices=["save", "restore", "list"])
|
|
348
|
+
checkpoint.add_argument("name", nargs="?", default="manual")
|
|
349
|
+
checkpoint.add_argument("--confirmed", action="store_true")
|
|
350
|
+
rec = sub.add_parser("record")
|
|
351
|
+
rec.add_argument("--message", required=True)
|
|
352
|
+
rec.add_argument("--files", nargs="+", required=True)
|
|
353
|
+
copy = sub.add_parser("copy-data")
|
|
354
|
+
copy.add_argument("source")
|
|
355
|
+
args = parser.parse_args(argv)
|
|
356
|
+
try:
|
|
357
|
+
root = research_root(args.project)
|
|
358
|
+
if args.action == "init":
|
|
359
|
+
config(root)
|
|
360
|
+
root.mkdir(exist_ok=True)
|
|
361
|
+
for name in ("tasks", "data", "checkpoints"):
|
|
362
|
+
contained(root, name, allow_missing=True).mkdir(exist_ok=True)
|
|
363
|
+
if not (root / "config.json").exists():
|
|
364
|
+
with (root / "config.json").open("x") as output:
|
|
365
|
+
output.write(json.dumps(DEFAULTS, indent=2) + "\n")
|
|
366
|
+
result = {"initialized": True, "config": config(root)}
|
|
367
|
+
elif args.action == "config":
|
|
368
|
+
result = config(root)
|
|
369
|
+
if args.key:
|
|
370
|
+
result = result[args.key]
|
|
371
|
+
elif args.action == "next-task-id":
|
|
372
|
+
result = {"id": next_task(root)}
|
|
373
|
+
elif args.action == "next-task":
|
|
374
|
+
result = next_ready_task(root)
|
|
375
|
+
elif args.action in ("validate-workflow", "task-dirs"):
|
|
376
|
+
result = validate_workflow(root)
|
|
377
|
+
if args.action == "task-dirs":
|
|
378
|
+
for number in result["active"]:
|
|
379
|
+
contained(root, f"tasks/task-{number}", allow_missing=True).mkdir(parents=True, exist_ok=True)
|
|
380
|
+
elif args.action == "checkpoint":
|
|
381
|
+
if args.operation == "save":
|
|
382
|
+
result = checkpoint_save(root, args.name)
|
|
383
|
+
elif args.operation == "restore":
|
|
384
|
+
result = checkpoint_restore(root, args.name, args.confirmed)
|
|
385
|
+
else:
|
|
386
|
+
directory = contained(root, "checkpoints", allow_missing=True)
|
|
387
|
+
result = {"checkpoints": sorted(p.name for p in directory.glob("*.tgz") if p.is_file() and not p.is_symlink())}
|
|
388
|
+
elif args.action == "record":
|
|
389
|
+
result = record(root, args.message, args.files)
|
|
390
|
+
else:
|
|
391
|
+
result = register_copy(root, args.source)
|
|
392
|
+
print(json.dumps(result, indent=2))
|
|
393
|
+
return 0
|
|
394
|
+
except (ValueError, OSError, json.JSONDecodeError, tarfile.TarError, subprocess.CalledProcessError) as error:
|
|
395
|
+
print(json.dumps({"error": str(error)}), file=sys.stderr)
|
|
396
|
+
return 2
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
if __name__ == "__main__":
|
|
400
|
+
sys.exit(main())
|