@mudssky/trellis 0.6.16-mudssky.5
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/AGENT_INSTALL.md +100 -0
- package/COPYRIGHT +15 -0
- package/INTEGRATION.md +140 -0
- package/LICENSE +235 -0
- package/README.md +125 -0
- package/bin/trellis.js +3 -0
- package/corresponding-source.tar.gz +0 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +304 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/commands/ablate.d.ts +14 -0
- package/dist/commands/ablate.d.ts.map +1 -0
- package/dist/commands/ablate.js +308 -0
- package/dist/commands/ablate.js.map +1 -0
- package/dist/commands/channel/adapters/claude.d.ts +53 -0
- package/dist/commands/channel/adapters/claude.d.ts.map +1 -0
- package/dist/commands/channel/adapters/claude.js +225 -0
- package/dist/commands/channel/adapters/claude.js.map +1 -0
- package/dist/commands/channel/adapters/codex.d.ts +93 -0
- package/dist/commands/channel/adapters/codex.d.ts.map +1 -0
- package/dist/commands/channel/adapters/codex.js +573 -0
- package/dist/commands/channel/adapters/codex.js.map +1 -0
- package/dist/commands/channel/adapters/index.d.ts +94 -0
- package/dist/commands/channel/adapters/index.d.ts.map +1 -0
- package/dist/commands/channel/adapters/index.js +116 -0
- package/dist/commands/channel/adapters/index.js.map +1 -0
- package/dist/commands/channel/adapters/types.d.ts +33 -0
- package/dist/commands/channel/adapters/types.d.ts.map +1 -0
- package/dist/commands/channel/adapters/types.js +2 -0
- package/dist/commands/channel/adapters/types.js.map +1 -0
- package/dist/commands/channel/agent-loader.d.ts +32 -0
- package/dist/commands/channel/agent-loader.d.ts.map +1 -0
- package/dist/commands/channel/agent-loader.js +158 -0
- package/dist/commands/channel/agent-loader.js.map +1 -0
- package/dist/commands/channel/context-loader.d.ts +26 -0
- package/dist/commands/channel/context-loader.d.ts.map +1 -0
- package/dist/commands/channel/context-loader.js +311 -0
- package/dist/commands/channel/context-loader.js.map +1 -0
- package/dist/commands/channel/context-trust.d.ts +31 -0
- package/dist/commands/channel/context-trust.d.ts.map +1 -0
- package/dist/commands/channel/context-trust.js +139 -0
- package/dist/commands/channel/context-trust.js.map +1 -0
- package/dist/commands/channel/context.d.ts +16 -0
- package/dist/commands/channel/context.d.ts.map +1 -0
- package/dist/commands/channel/context.js +83 -0
- package/dist/commands/channel/context.js.map +1 -0
- package/dist/commands/channel/create.d.ts +27 -0
- package/dist/commands/channel/create.d.ts.map +1 -0
- package/dist/commands/channel/create.js +39 -0
- package/dist/commands/channel/create.js.map +1 -0
- package/dist/commands/channel/dev-parse-trace.d.ts +14 -0
- package/dist/commands/channel/dev-parse-trace.d.ts.map +1 -0
- package/dist/commands/channel/dev-parse-trace.js +70 -0
- package/dist/commands/channel/dev-parse-trace.js.map +1 -0
- package/dist/commands/channel/guard.d.ts +150 -0
- package/dist/commands/channel/guard.d.ts.map +1 -0
- package/dist/commands/channel/guard.js +480 -0
- package/dist/commands/channel/guard.js.map +1 -0
- package/dist/commands/channel/index.d.ts +3 -0
- package/dist/commands/channel/index.d.ts.map +1 -0
- package/dist/commands/channel/index.js +535 -0
- package/dist/commands/channel/index.js.map +1 -0
- package/dist/commands/channel/interrupt.d.ts +10 -0
- package/dist/commands/channel/interrupt.d.ts.map +1 -0
- package/dist/commands/channel/interrupt.js +22 -0
- package/dist/commands/channel/interrupt.js.map +1 -0
- package/dist/commands/channel/kill.d.ts +7 -0
- package/dist/commands/channel/kill.d.ts.map +1 -0
- package/dist/commands/channel/kill.js +130 -0
- package/dist/commands/channel/kill.js.map +1 -0
- package/dist/commands/channel/list.d.ts +17 -0
- package/dist/commands/channel/list.d.ts.map +1 -0
- package/dist/commands/channel/list.js +233 -0
- package/dist/commands/channel/list.js.map +1 -0
- package/dist/commands/channel/messages.d.ts +15 -0
- package/dist/commands/channel/messages.d.ts.map +1 -0
- package/dist/commands/channel/messages.js +245 -0
- package/dist/commands/channel/messages.js.map +1 -0
- package/dist/commands/channel/rm.d.ts +27 -0
- package/dist/commands/channel/rm.d.ts.map +1 -0
- package/dist/commands/channel/rm.js +216 -0
- package/dist/commands/channel/rm.js.map +1 -0
- package/dist/commands/channel/run.d.ts +30 -0
- package/dist/commands/channel/run.d.ts.map +1 -0
- package/dist/commands/channel/run.js +130 -0
- package/dist/commands/channel/run.js.map +1 -0
- package/dist/commands/channel/send.d.ts +11 -0
- package/dist/commands/channel/send.d.ts.map +1 -0
- package/dist/commands/channel/send.js +24 -0
- package/dist/commands/channel/send.js.map +1 -0
- package/dist/commands/channel/spawn.d.ts +43 -0
- package/dist/commands/channel/spawn.d.ts.map +1 -0
- package/dist/commands/channel/spawn.js +247 -0
- package/dist/commands/channel/spawn.js.map +1 -0
- package/dist/commands/channel/store/events.d.ts +22 -0
- package/dist/commands/channel/store/events.d.ts.map +1 -0
- package/dist/commands/channel/store/events.js +43 -0
- package/dist/commands/channel/store/events.js.map +1 -0
- package/dist/commands/channel/store/filter.d.ts +3 -0
- package/dist/commands/channel/store/filter.d.ts.map +1 -0
- package/dist/commands/channel/store/filter.js +2 -0
- package/dist/commands/channel/store/filter.js.map +1 -0
- package/dist/commands/channel/store/lock.d.ts +23 -0
- package/dist/commands/channel/store/lock.d.ts.map +1 -0
- package/dist/commands/channel/store/lock.js +99 -0
- package/dist/commands/channel/store/lock.js.map +1 -0
- package/dist/commands/channel/store/paths.d.ts +77 -0
- package/dist/commands/channel/store/paths.d.ts.map +1 -0
- package/dist/commands/channel/store/paths.js +272 -0
- package/dist/commands/channel/store/paths.js.map +1 -0
- package/dist/commands/channel/store/schema.d.ts +27 -0
- package/dist/commands/channel/store/schema.d.ts.map +1 -0
- package/dist/commands/channel/store/schema.js +34 -0
- package/dist/commands/channel/store/schema.js.map +1 -0
- package/dist/commands/channel/store/thread-state.d.ts +5 -0
- package/dist/commands/channel/store/thread-state.d.ts.map +1 -0
- package/dist/commands/channel/store/thread-state.js +16 -0
- package/dist/commands/channel/store/thread-state.js.map +1 -0
- package/dist/commands/channel/store/watch.d.ts +19 -0
- package/dist/commands/channel/store/watch.d.ts.map +1 -0
- package/dist/commands/channel/store/watch.js +153 -0
- package/dist/commands/channel/store/watch.js.map +1 -0
- package/dist/commands/channel/supervisor/idle.d.ts +45 -0
- package/dist/commands/channel/supervisor/idle.d.ts.map +1 -0
- package/dist/commands/channel/supervisor/idle.js +70 -0
- package/dist/commands/channel/supervisor/idle.js.map +1 -0
- package/dist/commands/channel/supervisor/inbox.d.ts +30 -0
- package/dist/commands/channel/supervisor/inbox.d.ts.map +1 -0
- package/dist/commands/channel/supervisor/inbox.js +160 -0
- package/dist/commands/channel/supervisor/inbox.js.map +1 -0
- package/dist/commands/channel/supervisor/shutdown.d.ts +68 -0
- package/dist/commands/channel/supervisor/shutdown.d.ts.map +1 -0
- package/dist/commands/channel/supervisor/shutdown.js +146 -0
- package/dist/commands/channel/supervisor/shutdown.js.map +1 -0
- package/dist/commands/channel/supervisor/stdout.d.ts +71 -0
- package/dist/commands/channel/supervisor/stdout.d.ts.map +1 -0
- package/dist/commands/channel/supervisor/stdout.js +229 -0
- package/dist/commands/channel/supervisor/stdout.js.map +1 -0
- package/dist/commands/channel/supervisor/turns.d.ts +31 -0
- package/dist/commands/channel/supervisor/turns.d.ts.map +1 -0
- package/dist/commands/channel/supervisor/turns.js +45 -0
- package/dist/commands/channel/supervisor/turns.js.map +1 -0
- package/dist/commands/channel/supervisor/warning.d.ts +48 -0
- package/dist/commands/channel/supervisor/warning.d.ts.map +1 -0
- package/dist/commands/channel/supervisor/warning.js +77 -0
- package/dist/commands/channel/supervisor/warning.js.map +1 -0
- package/dist/commands/channel/supervisor.d.ts +91 -0
- package/dist/commands/channel/supervisor.d.ts.map +1 -0
- package/dist/commands/channel/supervisor.js +463 -0
- package/dist/commands/channel/supervisor.js.map +1 -0
- package/dist/commands/channel/text-body.d.ts +13 -0
- package/dist/commands/channel/text-body.d.ts.map +1 -0
- package/dist/commands/channel/text-body.js +47 -0
- package/dist/commands/channel/text-body.js.map +1 -0
- package/dist/commands/channel/threads.d.ts +39 -0
- package/dist/commands/channel/threads.d.ts.map +1 -0
- package/dist/commands/channel/threads.js +107 -0
- package/dist/commands/channel/threads.js.map +1 -0
- package/dist/commands/channel/title.d.ts +12 -0
- package/dist/commands/channel/title.d.ts.map +1 -0
- package/dist/commands/channel/title.js +24 -0
- package/dist/commands/channel/title.js.map +1 -0
- package/dist/commands/channel/wait.d.ts +17 -0
- package/dist/commands/channel/wait.d.ts.map +1 -0
- package/dist/commands/channel/wait.js +75 -0
- package/dist/commands/channel/wait.js.map +1 -0
- package/dist/commands/init.d.ts +71 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +1631 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/integration.d.ts +23 -0
- package/dist/commands/integration.d.ts.map +1 -0
- package/dist/commands/integration.js +373 -0
- package/dist/commands/integration.js.map +1 -0
- package/dist/commands/mem.d.ts +30 -0
- package/dist/commands/mem.d.ts.map +1 -0
- package/dist/commands/mem.js +424 -0
- package/dist/commands/mem.js.map +1 -0
- package/dist/commands/uninstall.d.ts +36 -0
- package/dist/commands/uninstall.d.ts.map +1 -0
- package/dist/commands/uninstall.js +205 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/update.d.ts +104 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +2184 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/upgrade.d.ts +28 -0
- package/dist/commands/upgrade.d.ts.map +1 -0
- package/dist/commands/upgrade.js +86 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/commands/workflow.d.ts +35 -0
- package/dist/commands/workflow.d.ts.map +1 -0
- package/dist/commands/workflow.js +233 -0
- package/dist/commands/workflow.js.map +1 -0
- package/dist/configurators/antigravity.d.ts +7 -0
- package/dist/configurators/antigravity.d.ts.map +1 -0
- package/dist/configurators/antigravity.js +11 -0
- package/dist/configurators/antigravity.js.map +1 -0
- package/dist/configurators/claude.d.ts +23 -0
- package/dist/configurators/claude.d.ts.map +1 -0
- package/dist/configurators/claude.js +124 -0
- package/dist/configurators/claude.js.map +1 -0
- package/dist/configurators/codebuddy.d.ts +10 -0
- package/dist/configurators/codebuddy.d.ts.map +1 -0
- package/dist/configurators/codebuddy.js +24 -0
- package/dist/configurators/codebuddy.js.map +1 -0
- package/dist/configurators/codex.d.ts +57 -0
- package/dist/configurators/codex.d.ts.map +1 -0
- package/dist/configurators/codex.js +170 -0
- package/dist/configurators/codex.js.map +1 -0
- package/dist/configurators/copilot.d.ts +14 -0
- package/dist/configurators/copilot.d.ts.map +1 -0
- package/dist/configurators/copilot.js +46 -0
- package/dist/configurators/copilot.js.map +1 -0
- package/dist/configurators/cursor.d.ts +10 -0
- package/dist/configurators/cursor.d.ts.map +1 -0
- package/dist/configurators/cursor.js +23 -0
- package/dist/configurators/cursor.js.map +1 -0
- package/dist/configurators/devin.d.ts +8 -0
- package/dist/configurators/devin.d.ts.map +1 -0
- package/dist/configurators/devin.js +12 -0
- package/dist/configurators/devin.js.map +1 -0
- package/dist/configurators/droid.d.ts +10 -0
- package/dist/configurators/droid.d.ts.map +1 -0
- package/dist/configurators/droid.js +24 -0
- package/dist/configurators/droid.js.map +1 -0
- package/dist/configurators/dsh.d.ts +29 -0
- package/dist/configurators/dsh.d.ts.map +1 -0
- package/dist/configurators/dsh.js +64 -0
- package/dist/configurators/dsh.js.map +1 -0
- package/dist/configurators/gemini.d.ts +18 -0
- package/dist/configurators/gemini.d.ts.map +1 -0
- package/dist/configurators/gemini.js +39 -0
- package/dist/configurators/gemini.js.map +1 -0
- package/dist/configurators/grok.d.ts +17 -0
- package/dist/configurators/grok.d.ts.map +1 -0
- package/dist/configurators/grok.js +38 -0
- package/dist/configurators/grok.js.map +1 -0
- package/dist/configurators/index.d.ts +68 -0
- package/dist/configurators/index.d.ts.map +1 -0
- package/dist/configurators/index.js +191 -0
- package/dist/configurators/index.js.map +1 -0
- package/dist/configurators/kilo.d.ts +7 -0
- package/dist/configurators/kilo.d.ts.map +1 -0
- package/dist/configurators/kilo.js +11 -0
- package/dist/configurators/kilo.js.map +1 -0
- package/dist/configurators/kimi.d.ts +26 -0
- package/dist/configurators/kimi.d.ts.map +1 -0
- package/dist/configurators/kimi.js +74 -0
- package/dist/configurators/kimi.js.map +1 -0
- package/dist/configurators/kiro.d.ts +11 -0
- package/dist/configurators/kiro.d.ts.map +1 -0
- package/dist/configurators/kiro.js +30 -0
- package/dist/configurators/kiro.js.map +1 -0
- package/dist/configurators/omp.d.ts +6 -0
- package/dist/configurators/omp.d.ts.map +1 -0
- package/dist/configurators/omp.js +28 -0
- package/dist/configurators/omp.js.map +1 -0
- package/dist/configurators/opencode.d.ts +5 -0
- package/dist/configurators/opencode.d.ts.map +1 -0
- package/dist/configurators/opencode.js +81 -0
- package/dist/configurators/opencode.js.map +1 -0
- package/dist/configurators/pi.d.ts +5 -0
- package/dist/configurators/pi.d.ts.map +1 -0
- package/dist/configurators/pi.js +39 -0
- package/dist/configurators/pi.js.map +1 -0
- package/dist/configurators/qoder.d.ts +12 -0
- package/dist/configurators/qoder.d.ts.map +1 -0
- package/dist/configurators/qoder.js +30 -0
- package/dist/configurators/qoder.js.map +1 -0
- package/dist/configurators/reasonix.d.ts +17 -0
- package/dist/configurators/reasonix.d.ts.map +1 -0
- package/dist/configurators/reasonix.js +37 -0
- package/dist/configurators/reasonix.js.map +1 -0
- package/dist/configurators/shared.d.ts +187 -0
- package/dist/configurators/shared.d.ts.map +1 -0
- package/dist/configurators/shared.js +589 -0
- package/dist/configurators/shared.js.map +1 -0
- package/dist/configurators/snow.d.ts +25 -0
- package/dist/configurators/snow.d.ts.map +1 -0
- package/dist/configurators/snow.js +69 -0
- package/dist/configurators/snow.js.map +1 -0
- package/dist/configurators/trae.d.ts +16 -0
- package/dist/configurators/trae.d.ts.map +1 -0
- package/dist/configurators/trae.js +34 -0
- package/dist/configurators/trae.js.map +1 -0
- package/dist/configurators/workflow.d.ts +53 -0
- package/dist/configurators/workflow.d.ts.map +1 -0
- package/dist/configurators/workflow.js +212 -0
- package/dist/configurators/workflow.js.map +1 -0
- package/dist/configurators/zcode.d.ts +25 -0
- package/dist/configurators/zcode.d.ts.map +1 -0
- package/dist/configurators/zcode.js +67 -0
- package/dist/configurators/zcode.js.map +1 -0
- package/dist/constants/paths.d.ts +74 -0
- package/dist/constants/paths.d.ts.map +1 -0
- package/dist/constants/paths.js +83 -0
- package/dist/constants/paths.js.map +1 -0
- package/dist/constants/version.d.ts +12 -0
- package/dist/constants/version.d.ts.map +1 -0
- package/dist/constants/version.js +18 -0
- package/dist/constants/version.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations/index.d.ts +62 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +190 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/migrations/manifests/0.1.9.json +30 -0
- package/dist/migrations/manifests/0.2.0.json +49 -0
- package/dist/migrations/manifests/0.2.12.json +9 -0
- package/dist/migrations/manifests/0.2.13.json +9 -0
- package/dist/migrations/manifests/0.2.14.json +175 -0
- package/dist/migrations/manifests/0.2.15.json +33 -0
- package/dist/migrations/manifests/0.3.0-beta.0.json +297 -0
- package/dist/migrations/manifests/0.3.0-beta.1.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.10.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.11.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.12.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.13.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.14.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.15.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.16.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.2.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.3.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.4.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.5.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.6.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.7.json +11 -0
- package/dist/migrations/manifests/0.3.0-beta.8.json +9 -0
- package/dist/migrations/manifests/0.3.0-beta.9.json +9 -0
- package/dist/migrations/manifests/0.3.0-rc.0.json +9 -0
- package/dist/migrations/manifests/0.3.0-rc.1.json +9 -0
- package/dist/migrations/manifests/0.3.0-rc.2.json +9 -0
- package/dist/migrations/manifests/0.3.0-rc.3.json +9 -0
- package/dist/migrations/manifests/0.3.0-rc.4.json +9 -0
- package/dist/migrations/manifests/0.3.0-rc.5.json +9 -0
- package/dist/migrations/manifests/0.3.0-rc.6.json +9 -0
- package/dist/migrations/manifests/0.3.0.json +11 -0
- package/dist/migrations/manifests/0.3.1.json +9 -0
- package/dist/migrations/manifests/0.3.10.json +9 -0
- package/dist/migrations/manifests/0.3.2.json +9 -0
- package/dist/migrations/manifests/0.3.3.json +9 -0
- package/dist/migrations/manifests/0.3.4.json +21 -0
- package/dist/migrations/manifests/0.3.5.json +9 -0
- package/dist/migrations/manifests/0.3.6.json +9 -0
- package/dist/migrations/manifests/0.3.7.json +9 -0
- package/dist/migrations/manifests/0.3.8.json +9 -0
- package/dist/migrations/manifests/0.3.9.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.1.json +228 -0
- package/dist/migrations/manifests/0.4.0-beta.10.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.2.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.3.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.4.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.5.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.6.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.7.json +9 -0
- package/dist/migrations/manifests/0.4.0-beta.8.json +34 -0
- package/dist/migrations/manifests/0.4.0-beta.9.json +9 -0
- package/dist/migrations/manifests/0.4.0-rc.0.json +9 -0
- package/dist/migrations/manifests/0.4.0-rc.1.json +9 -0
- package/dist/migrations/manifests/0.4.0.json +9 -0
- package/dist/migrations/manifests/0.5.0-beta.0.json +1646 -0
- package/dist/migrations/manifests/0.5.0-beta.1.json +9 -0
- package/dist/migrations/manifests/0.5.0-beta.10.json +9 -0
- package/dist/migrations/manifests/0.5.0-beta.11.json +9 -0
- package/dist/migrations/manifests/0.5.0-beta.12.json +9 -0
- package/dist/migrations/manifests/0.5.0-beta.13.json +9 -0
- package/dist/migrations/manifests/0.5.0-beta.14.json +9 -0
- package/dist/migrations/manifests/0.5.0-beta.15.json +116 -0
- package/dist/migrations/manifests/0.5.0-beta.16.json +9 -0
- package/dist/migrations/manifests/0.5.0-beta.17.json +9 -0
- package/dist/migrations/manifests/0.5.0-beta.18.json +9 -0
- package/dist/migrations/manifests/0.5.0-beta.19.json +9 -0
- package/dist/migrations/manifests/0.5.0-beta.2.json +9 -0
- package/dist/migrations/manifests/0.5.0-beta.3.json +9 -0
- package/dist/migrations/manifests/0.5.0-beta.4.json +9 -0
- package/dist/migrations/manifests/0.5.0-beta.5.json +222 -0
- package/dist/migrations/manifests/0.5.0-beta.6.json +9 -0
- package/dist/migrations/manifests/0.5.0-beta.7.json +9 -0
- package/dist/migrations/manifests/0.5.0-beta.8.json +9 -0
- package/dist/migrations/manifests/0.5.0-beta.9.json +48 -0
- package/dist/migrations/manifests/0.5.0-rc.0.json +9 -0
- package/dist/migrations/manifests/0.5.0-rc.1.json +9 -0
- package/dist/migrations/manifests/0.5.0-rc.2.json +9 -0
- package/dist/migrations/manifests/0.5.0-rc.3.json +9 -0
- package/dist/migrations/manifests/0.5.0-rc.4.json +9 -0
- package/dist/migrations/manifests/0.5.0-rc.5.json +9 -0
- package/dist/migrations/manifests/0.5.0-rc.6.json +9 -0
- package/dist/migrations/manifests/0.5.0-rc.7.json +9 -0
- package/dist/migrations/manifests/0.5.0.json +9 -0
- package/dist/migrations/manifests/0.5.1.json +9 -0
- package/dist/migrations/manifests/0.5.10.json +9 -0
- package/dist/migrations/manifests/0.5.11.json +16 -0
- package/dist/migrations/manifests/0.5.12.json +9 -0
- package/dist/migrations/manifests/0.5.13.json +9 -0
- package/dist/migrations/manifests/0.5.14.json +9 -0
- package/dist/migrations/manifests/0.5.15.json +9 -0
- package/dist/migrations/manifests/0.5.16.json +9 -0
- package/dist/migrations/manifests/0.5.17.json +9 -0
- package/dist/migrations/manifests/0.5.18.json +9 -0
- package/dist/migrations/manifests/0.5.19.json +9 -0
- package/dist/migrations/manifests/0.5.2.json +9 -0
- package/dist/migrations/manifests/0.5.3.json +9 -0
- package/dist/migrations/manifests/0.5.4.json +9 -0
- package/dist/migrations/manifests/0.5.5.json +9 -0
- package/dist/migrations/manifests/0.5.6.json +9 -0
- package/dist/migrations/manifests/0.5.7.json +16 -0
- package/dist/migrations/manifests/0.5.8.json +9 -0
- package/dist/migrations/manifests/0.5.9.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.0.json +16 -0
- package/dist/migrations/manifests/0.6.0-beta.1.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.10.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.11.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.12.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.13.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.14.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.15.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.16.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.17.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.18.json +16 -0
- package/dist/migrations/manifests/0.6.0-beta.19.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.2.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.20.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.21.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.22.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.23.json +88 -0
- package/dist/migrations/manifests/0.6.0-beta.3.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.4.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.5.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.6.json +16 -0
- package/dist/migrations/manifests/0.6.0-beta.7.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.8.json +9 -0
- package/dist/migrations/manifests/0.6.0-beta.9.json +9 -0
- package/dist/migrations/manifests/0.6.0-rc.0.json +9 -0
- package/dist/migrations/manifests/0.6.0.json +1 -0
- package/dist/migrations/manifests/0.6.1.json +9 -0
- package/dist/migrations/manifests/0.6.10.json +9 -0
- package/dist/migrations/manifests/0.6.11.json +9 -0
- package/dist/migrations/manifests/0.6.12.json +9 -0
- package/dist/migrations/manifests/0.6.13.json +9 -0
- package/dist/migrations/manifests/0.6.14.json +9 -0
- package/dist/migrations/manifests/0.6.15.json +9 -0
- package/dist/migrations/manifests/0.6.16.json +9 -0
- package/dist/migrations/manifests/0.6.2.json +9 -0
- package/dist/migrations/manifests/0.6.3.json +24 -0
- package/dist/migrations/manifests/0.6.4.json +9 -0
- package/dist/migrations/manifests/0.6.5.json +9 -0
- package/dist/migrations/manifests/0.6.6.json +17 -0
- package/dist/migrations/manifests/0.6.7.json +9 -0
- package/dist/migrations/manifests/0.6.8.json +17 -0
- package/dist/migrations/manifests/0.6.9.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.0.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.1.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.2.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.3.json +9 -0
- package/dist/templates/claude/agents/trellis-check.md +115 -0
- package/dist/templates/claude/agents/trellis-implement.md +110 -0
- package/dist/templates/claude/agents/trellis-research.md +137 -0
- package/dist/templates/claude/hooks/statusline.py +332 -0
- package/dist/templates/claude/index.d.ts +31 -0
- package/dist/templates/claude/index.d.ts.map +1 -0
- package/dist/templates/claude/index.js +58 -0
- package/dist/templates/claude/index.js.map +1 -0
- package/dist/templates/claude/settings.json +73 -0
- package/dist/templates/codebuddy/agents/trellis-check.md +115 -0
- package/dist/templates/codebuddy/agents/trellis-implement.md +110 -0
- package/dist/templates/codebuddy/agents/trellis-research.md +137 -0
- package/dist/templates/codebuddy/index.d.ts +15 -0
- package/dist/templates/codebuddy/index.d.ts.map +1 -0
- package/dist/templates/codebuddy/index.js +15 -0
- package/dist/templates/codebuddy/index.js.map +1 -0
- package/dist/templates/codebuddy/settings.json +69 -0
- package/dist/templates/codex/agents/trellis-check.toml +56 -0
- package/dist/templates/codex/agents/trellis-implement.toml +37 -0
- package/dist/templates/codex/agents/trellis-research.toml +78 -0
- package/dist/templates/codex/config.toml +39 -0
- package/dist/templates/codex/hooks/session-start.py +552 -0
- package/dist/templates/codex/hooks.json +27 -0
- package/dist/templates/codex/index.d.ts +35 -0
- package/dist/templates/codex/index.d.ts.map +1 -0
- package/dist/templates/codex/index.js +68 -0
- package/dist/templates/codex/index.js.map +1 -0
- package/dist/templates/command-renderer.d.ts +2 -0
- package/dist/templates/command-renderer.d.ts.map +1 -0
- package/dist/templates/command-renderer.js +13 -0
- package/dist/templates/command-renderer.js.map +1 -0
- package/dist/templates/common/bundled-skills/trellis-channel/SKILL.md +67 -0
- package/dist/templates/common/bundled-skills/trellis-channel/references/command-reference.md +480 -0
- package/dist/templates/common/bundled-skills/trellis-channel/references/forum.md +233 -0
- package/dist/templates/common/bundled-skills/trellis-channel/references/progress-debugging.md +226 -0
- package/dist/templates/common/bundled-skills/trellis-channel/references/workers.md +276 -0
- package/dist/templates/common/bundled-skills/trellis-channel/references/workflows.md +128 -0
- package/dist/templates/common/bundled-skills/trellis-meta/SKILL.md +85 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/add-project-local-conventions.md +83 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-agents.md +56 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-context-loading.md +84 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-hooks.md +57 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-skills-or-commands.md +123 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-spec-structure.md +83 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-task-lifecycle.md +90 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-workflow.md +65 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/overview.md +55 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/bundled-skills.md +159 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/context-injection.md +68 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/generated-files.md +80 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/multi-agent-channel.md +69 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/overview.md +51 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/spec-system.md +102 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/task-system.md +130 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/workflow.md +75 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/workspace-memory.md +71 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/agents.md +83 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/hooks-and-settings.md +72 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/overview.md +59 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/platform-map.md +112 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/skills-and-commands.md +86 -0
- package/dist/templates/common/bundled-skills/trellis-session-insight/SKILL.md +81 -0
- package/dist/templates/common/bundled-skills/trellis-session-insight/references/cli-quick-reference.md +65 -0
- package/dist/templates/common/bundled-skills/trellis-session-insight/references/triggering-patterns.md +93 -0
- package/dist/templates/common/bundled-skills/trellis-spec-bootstrap/SKILL.md +41 -0
- package/dist/templates/common/bundled-skills/trellis-spec-bootstrap/references/mcp-setup.md +90 -0
- package/dist/templates/common/bundled-skills/trellis-spec-bootstrap/references/repository-analysis.md +59 -0
- package/dist/templates/common/bundled-skills/trellis-spec-bootstrap/references/spec-task-planning.md +61 -0
- package/dist/templates/common/bundled-skills/trellis-spec-bootstrap/references/spec-writing.md +70 -0
- package/dist/templates/common/commands/continue.md +56 -0
- package/dist/templates/common/commands/finish-work.md +66 -0
- package/dist/templates/common/commands/start.md +59 -0
- package/dist/templates/common/index.d.ts +48 -0
- package/dist/templates/common/index.d.ts.map +1 -0
- package/dist/templates/common/index.js +105 -0
- package/dist/templates/common/index.js.map +1 -0
- package/dist/templates/common/skills/before-dev.md +46 -0
- package/dist/templates/common/skills/brainstorm.md +195 -0
- package/dist/templates/common/skills/break-loop.md +184 -0
- package/dist/templates/common/skills/check.md +106 -0
- package/dist/templates/common/skills/update-spec.md +351 -0
- package/dist/templates/copilot/copilot-instructions.md +11 -0
- package/dist/templates/copilot/hooks/session-start.py +548 -0
- package/dist/templates/copilot/hooks.json +19 -0
- package/dist/templates/copilot/index.d.ts +28 -0
- package/dist/templates/copilot/index.d.ts.map +1 -0
- package/dist/templates/copilot/index.js +62 -0
- package/dist/templates/copilot/index.js.map +1 -0
- package/dist/templates/copilot/prompts/before-dev.prompt.md +39 -0
- package/dist/templates/copilot/prompts/brainstorm.prompt.md +156 -0
- package/dist/templates/copilot/prompts/break-loop.prompt.md +129 -0
- package/dist/templates/copilot/prompts/check-cross-layer.prompt.md +170 -0
- package/dist/templates/copilot/prompts/check.prompt.md +110 -0
- package/dist/templates/copilot/prompts/create-command.prompt.md +116 -0
- package/dist/templates/copilot/prompts/finish-work.prompt.md +99 -0
- package/dist/templates/copilot/prompts/integrate-skill.prompt.md +223 -0
- package/dist/templates/copilot/prompts/onboard.prompt.md +362 -0
- package/dist/templates/copilot/prompts/parallel.prompt.md +204 -0
- package/dist/templates/copilot/prompts/record-session.prompt.md +66 -0
- package/dist/templates/copilot/prompts/start.prompt.md +63 -0
- package/dist/templates/copilot/prompts/update-spec.prompt.md +358 -0
- package/dist/templates/cursor/agents/trellis-check.md +114 -0
- package/dist/templates/cursor/agents/trellis-implement.md +109 -0
- package/dist/templates/cursor/agents/trellis-research.md +136 -0
- package/dist/templates/cursor/hooks.json +24 -0
- package/dist/templates/cursor/index.d.ts +13 -0
- package/dist/templates/cursor/index.d.ts.map +1 -0
- package/dist/templates/cursor/index.js +13 -0
- package/dist/templates/cursor/index.js.map +1 -0
- package/dist/templates/droid/droids/trellis-check.md +107 -0
- package/dist/templates/droid/droids/trellis-implement.md +102 -0
- package/dist/templates/droid/droids/trellis-research.md +137 -0
- package/dist/templates/droid/index.d.ts +15 -0
- package/dist/templates/droid/index.d.ts.map +1 -0
- package/dist/templates/droid/index.js +15 -0
- package/dist/templates/droid/index.js.map +1 -0
- package/dist/templates/droid/settings.json +69 -0
- package/dist/templates/dsh/DSH.md +56 -0
- package/dist/templates/dsh/index.d.ts +21 -0
- package/dist/templates/dsh/index.d.ts.map +1 -0
- package/dist/templates/dsh/index.js +25 -0
- package/dist/templates/dsh/index.js.map +1 -0
- package/dist/templates/extract.d.ts +40 -0
- package/dist/templates/extract.d.ts.map +1 -0
- package/dist/templates/extract.js +113 -0
- package/dist/templates/extract.js.map +1 -0
- package/dist/templates/gemini/agents/trellis-check.md +107 -0
- package/dist/templates/gemini/agents/trellis-implement.md +102 -0
- package/dist/templates/gemini/agents/trellis-research.md +136 -0
- package/dist/templates/gemini/index.d.ts +13 -0
- package/dist/templates/gemini/index.d.ts.map +1 -0
- package/dist/templates/gemini/index.js +13 -0
- package/dist/templates/gemini/index.js.map +1 -0
- package/dist/templates/gemini/settings.json +40 -0
- package/dist/templates/grok/agents/trellis-check.md +105 -0
- package/dist/templates/grok/agents/trellis-implement.md +116 -0
- package/dist/templates/grok/agents/trellis-research.md +146 -0
- package/dist/templates/grok/index.d.ts +16 -0
- package/dist/templates/grok/index.d.ts.map +1 -0
- package/dist/templates/grok/index.js +19 -0
- package/dist/templates/grok/index.js.map +1 -0
- package/dist/templates/kimi/agents/trellis-check.md +103 -0
- package/dist/templates/kimi/agents/trellis-implement.md +115 -0
- package/dist/templates/kimi/agents/trellis-research.md +144 -0
- package/dist/templates/kimi/index.d.ts +20 -0
- package/dist/templates/kimi/index.d.ts.map +1 -0
- package/dist/templates/kimi/index.js +23 -0
- package/dist/templates/kimi/index.js.map +1 -0
- package/dist/templates/kiro/agents/trellis-check.json +26 -0
- package/dist/templates/kiro/agents/trellis-implement.json +26 -0
- package/dist/templates/kiro/agents/trellis-research.json +30 -0
- package/dist/templates/kiro/agents/trellis.json +34 -0
- package/dist/templates/kiro/hooks/trellis-workflow-state.kiro.hook +14 -0
- package/dist/templates/kiro/index.d.ts +32 -0
- package/dist/templates/kiro/index.d.ts.map +1 -0
- package/dist/templates/kiro/index.js +28 -0
- package/dist/templates/kiro/index.js.map +1 -0
- package/dist/templates/markdown/agents.md +21 -0
- package/dist/templates/markdown/gitignore.txt +15 -0
- package/dist/templates/markdown/index.d.ts +27 -0
- package/dist/templates/markdown/index.d.ts.map +1 -0
- package/dist/templates/markdown/index.js +53 -0
- package/dist/templates/markdown/index.js.map +1 -0
- package/dist/templates/markdown/spec/backend/database-guidelines.md.txt +51 -0
- package/dist/templates/markdown/spec/backend/directory-structure.md.txt +54 -0
- package/dist/templates/markdown/spec/backend/error-handling.md.txt +51 -0
- package/dist/templates/markdown/spec/backend/index.md.txt +38 -0
- package/dist/templates/markdown/spec/backend/logging-guidelines.md.txt +51 -0
- package/dist/templates/markdown/spec/backend/quality-guidelines.md.txt +51 -0
- package/dist/templates/markdown/spec/frontend/component-guidelines.md.txt +59 -0
- package/dist/templates/markdown/spec/frontend/directory-structure.md.txt +54 -0
- package/dist/templates/markdown/spec/frontend/hook-guidelines.md.txt +51 -0
- package/dist/templates/markdown/spec/frontend/index.md.txt +39 -0
- package/dist/templates/markdown/spec/frontend/quality-guidelines.md.txt +51 -0
- package/dist/templates/markdown/spec/frontend/state-management.md.txt +51 -0
- package/dist/templates/markdown/spec/frontend/type-safety.md.txt +51 -0
- package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md.txt +223 -0
- package/dist/templates/markdown/spec/guides/cross-layer-thinking-guide.md.txt +327 -0
- package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +633 -0
- package/dist/templates/markdown/spec/guides/index.md.txt +97 -0
- package/dist/templates/markdown/workspace-index.md +125 -0
- package/dist/templates/markdown/worktree.yaml.txt +58 -0
- package/dist/templates/omp/agents/trellis-check.md +41 -0
- package/dist/templates/omp/agents/trellis-implement.md +45 -0
- package/dist/templates/omp/agents/trellis-research.md +30 -0
- package/dist/templates/omp/extensions/trellis/index.ts.txt +1783 -0
- package/dist/templates/omp/index.d.ts +4 -0
- package/dist/templates/omp/index.d.ts.map +1 -0
- package/dist/templates/omp/index.js +9 -0
- package/dist/templates/omp/index.js.map +1 -0
- package/dist/templates/opencode/agents/trellis-check.md +122 -0
- package/dist/templates/opencode/agents/trellis-implement.md +118 -0
- package/dist/templates/opencode/agents/trellis-research.md +145 -0
- package/dist/templates/opencode/lib/context-visibility.js +148 -0
- package/dist/templates/opencode/lib/session-utils.js +527 -0
- package/dist/templates/opencode/lib/trellis-context.js +627 -0
- package/dist/templates/opencode/package.json +5 -0
- package/dist/templates/opencode/plugins/inject-subagent-context.js +569 -0
- package/dist/templates/opencode/plugins/inject-workflow-state.js +240 -0
- package/dist/templates/opencode/plugins/session-start.js +65 -0
- package/dist/templates/pi/agents/trellis-check.md +37 -0
- package/dist/templates/pi/agents/trellis-implement.md +42 -0
- package/dist/templates/pi/agents/trellis-research.md +25 -0
- package/dist/templates/pi/extensions/trellis/index.ts.txt +2149 -0
- package/dist/templates/pi/index.d.ts +5 -0
- package/dist/templates/pi/index.d.ts.map +1 -0
- package/dist/templates/pi/index.js +12 -0
- package/dist/templates/pi/index.js.map +1 -0
- package/dist/templates/pi/settings.json +9 -0
- package/dist/templates/qoder/agents/trellis-check.md +108 -0
- package/dist/templates/qoder/agents/trellis-implement.md +103 -0
- package/dist/templates/qoder/agents/trellis-research.md +137 -0
- package/dist/templates/qoder/index.d.ts +15 -0
- package/dist/templates/qoder/index.d.ts.map +1 -0
- package/dist/templates/qoder/index.js +15 -0
- package/dist/templates/qoder/index.js.map +1 -0
- package/dist/templates/qoder/settings.json +59 -0
- package/dist/templates/reasonix/agents/trellis-check.md +36 -0
- package/dist/templates/reasonix/agents/trellis-implement.md +41 -0
- package/dist/templates/reasonix/index.d.ts +13 -0
- package/dist/templates/reasonix/index.d.ts.map +1 -0
- package/dist/templates/reasonix/index.js +16 -0
- package/dist/templates/reasonix/index.js.map +1 -0
- package/dist/templates/shared-hooks/index.d.ts +77 -0
- package/dist/templates/shared-hooks/index.d.ts.map +1 -0
- package/dist/templates/shared-hooks/index.js +141 -0
- package/dist/templates/shared-hooks/index.js.map +1 -0
- package/dist/templates/shared-hooks/inject-shell-session-context.py +292 -0
- package/dist/templates/shared-hooks/inject-subagent-context.py +1212 -0
- package/dist/templates/shared-hooks/inject-workflow-state.py +488 -0
- package/dist/templates/shared-hooks/session-start.py +949 -0
- package/dist/templates/snow/SNOW.md +86 -0
- package/dist/templates/snow/agents/trellis-check.md +135 -0
- package/dist/templates/snow/agents/trellis-implement.md +153 -0
- package/dist/templates/snow/agents/trellis-research.md +114 -0
- package/dist/templates/snow/hooks/beforeSubAgentStart.json +15 -0
- package/dist/templates/snow/hooks/onSessionStart.json +15 -0
- package/dist/templates/snow/hooks/onUserMessage.json +15 -0
- package/dist/templates/snow/hooks/write-trellis-context.py +644 -0
- package/dist/templates/snow/index.d.ts +26 -0
- package/dist/templates/snow/index.d.ts.map +1 -0
- package/dist/templates/snow/index.js +36 -0
- package/dist/templates/snow/index.js.map +1 -0
- package/dist/templates/template-utils.d.ts +26 -0
- package/dist/templates/template-utils.d.ts.map +1 -0
- package/dist/templates/template-utils.js +65 -0
- package/dist/templates/template-utils.js.map +1 -0
- package/dist/templates/trae/agents/trellis-check.md +108 -0
- package/dist/templates/trae/agents/trellis-implement.md +103 -0
- package/dist/templates/trae/agents/trellis-research.md +137 -0
- package/dist/templates/trae/hooks.json +39 -0
- package/dist/templates/trae/index.d.ts +15 -0
- package/dist/templates/trae/index.d.ts.map +1 -0
- package/dist/templates/trae/index.js +15 -0
- package/dist/templates/trae/index.js.map +1 -0
- package/dist/templates/trellis/agents/check.md +70 -0
- package/dist/templates/trellis/agents/implement.md +71 -0
- package/dist/templates/trellis/config.yaml +177 -0
- package/dist/templates/trellis/gitattributes.txt +9 -0
- package/dist/templates/trellis/gitignore.txt +32 -0
- package/dist/templates/trellis/index.d.ts +67 -0
- package/dist/templates/trellis/index.d.ts.map +1 -0
- package/dist/templates/trellis/index.js +134 -0
- package/dist/templates/trellis/index.js.map +1 -0
- package/dist/templates/trellis/scripts/__init__.py +5 -0
- package/dist/templates/trellis/scripts/add_session.py +1545 -0
- package/dist/templates/trellis/scripts/common/__init__.py +94 -0
- package/dist/templates/trellis/scripts/common/active_task.py +857 -0
- package/dist/templates/trellis/scripts/common/cli_adapter.py +950 -0
- package/dist/templates/trellis/scripts/common/config.py +482 -0
- package/dist/templates/trellis/scripts/common/developer.py +192 -0
- package/dist/templates/trellis/scripts/common/git.py +203 -0
- package/dist/templates/trellis/scripts/common/git_context.py +106 -0
- package/dist/templates/trellis/scripts/common/io.py +160 -0
- package/dist/templates/trellis/scripts/common/log.py +45 -0
- package/dist/templates/trellis/scripts/common/packages_context.py +238 -0
- package/dist/templates/trellis/scripts/common/paths.py +543 -0
- package/dist/templates/trellis/scripts/common/safe_commit.py +322 -0
- package/dist/templates/trellis/scripts/common/session_context.py +892 -0
- package/dist/templates/trellis/scripts/common/task_context.py +470 -0
- package/dist/templates/trellis/scripts/common/task_queue.py +188 -0
- package/dist/templates/trellis/scripts/common/task_store.py +1883 -0
- package/dist/templates/trellis/scripts/common/task_utils.py +498 -0
- package/dist/templates/trellis/scripts/common/tasks.py +122 -0
- package/dist/templates/trellis/scripts/common/trellis_config.py +257 -0
- package/dist/templates/trellis/scripts/common/types.py +110 -0
- package/dist/templates/trellis/scripts/common/workflow_phase.py +242 -0
- package/dist/templates/trellis/scripts/get_context.py +16 -0
- package/dist/templates/trellis/scripts/get_developer.py +26 -0
- package/dist/templates/trellis/scripts/hooks/linear_sync.py +243 -0
- package/dist/templates/trellis/scripts/init_developer.py +51 -0
- package/dist/templates/trellis/scripts/task.py +811 -0
- package/dist/templates/trellis/tasks/.gitkeep +0 -0
- package/dist/templates/trellis/workflow.md +721 -0
- package/dist/templates/zcode/agents/trellis-check.md +109 -0
- package/dist/templates/zcode/agents/trellis-implement.md +111 -0
- package/dist/templates/zcode/agents/trellis-research.md +133 -0
- package/dist/templates/zcode/config.json +52 -0
- package/dist/templates/zcode/index.d.ts +25 -0
- package/dist/templates/zcode/index.d.ts.map +1 -0
- package/dist/templates/zcode/index.js +29 -0
- package/dist/templates/zcode/index.js.map +1 -0
- package/dist/types/ai-tools.d.ts +105 -0
- package/dist/types/ai-tools.d.ts.map +1 -0
- package/dist/types/ai-tools.js +502 -0
- package/dist/types/ai-tools.js.map +1 -0
- package/dist/types/migration.d.ts +125 -0
- package/dist/types/migration.d.ts.map +1 -0
- package/dist/types/migration.js +8 -0
- package/dist/types/migration.js.map +1 -0
- package/dist/utils/ablation-store.d.ts +123 -0
- package/dist/utils/ablation-store.d.ts.map +1 -0
- package/dist/utils/ablation-store.js +749 -0
- package/dist/utils/ablation-store.js.map +1 -0
- package/dist/utils/agent-refs.d.ts +31 -0
- package/dist/utils/agent-refs.d.ts.map +1 -0
- package/dist/utils/agent-refs.js +63 -0
- package/dist/utils/agent-refs.js.map +1 -0
- package/dist/utils/atomic-write.d.ts +12 -0
- package/dist/utils/atomic-write.d.ts.map +1 -0
- package/dist/utils/atomic-write.js +30 -0
- package/dist/utils/atomic-write.js.map +1 -0
- package/dist/utils/compare-versions.d.ts +12 -0
- package/dist/utils/compare-versions.d.ts.map +1 -0
- package/dist/utils/compare-versions.js +86 -0
- package/dist/utils/compare-versions.js.map +1 -0
- package/dist/utils/cwd-guard.d.ts +38 -0
- package/dist/utils/cwd-guard.d.ts.map +1 -0
- package/dist/utils/cwd-guard.js +63 -0
- package/dist/utils/cwd-guard.js.map +1 -0
- package/dist/utils/file-writer.d.ts +36 -0
- package/dist/utils/file-writer.d.ts.map +1 -0
- package/dist/utils/file-writer.js +204 -0
- package/dist/utils/file-writer.js.map +1 -0
- package/dist/utils/integration.d.ts +96 -0
- package/dist/utils/integration.d.ts.map +1 -0
- package/dist/utils/integration.js +1549 -0
- package/dist/utils/integration.js.map +1 -0
- package/dist/utils/managed-paths.d.ts +5 -0
- package/dist/utils/managed-paths.d.ts.map +1 -0
- package/dist/utils/managed-paths.js +51 -0
- package/dist/utils/managed-paths.js.map +1 -0
- package/dist/utils/managed-removal.d.ts +74 -0
- package/dist/utils/managed-removal.d.ts.map +1 -0
- package/dist/utils/managed-removal.js +270 -0
- package/dist/utils/managed-removal.js.map +1 -0
- package/dist/utils/manifest-prune.d.ts +61 -0
- package/dist/utils/manifest-prune.d.ts.map +1 -0
- package/dist/utils/manifest-prune.js +135 -0
- package/dist/utils/manifest-prune.js.map +1 -0
- package/dist/utils/posix.d.ts +13 -0
- package/dist/utils/posix.d.ts.map +1 -0
- package/dist/utils/posix.js +15 -0
- package/dist/utils/posix.js.map +1 -0
- package/dist/utils/project-detector.d.ts +46 -0
- package/dist/utils/project-detector.d.ts.map +1 -0
- package/dist/utils/project-detector.js +666 -0
- package/dist/utils/project-detector.js.map +1 -0
- package/dist/utils/proxy.d.ts +25 -0
- package/dist/utils/proxy.d.ts.map +1 -0
- package/dist/utils/proxy.js +60 -0
- package/dist/utils/proxy.js.map +1 -0
- package/dist/utils/registry-config.d.ts +7 -0
- package/dist/utils/registry-config.d.ts.map +1 -0
- package/dist/utils/registry-config.js +173 -0
- package/dist/utils/registry-config.js.map +1 -0
- package/dist/utils/task-json.d.ts +13 -0
- package/dist/utils/task-json.d.ts.map +1 -0
- package/dist/utils/task-json.js +12 -0
- package/dist/utils/task-json.js.map +1 -0
- package/dist/utils/template-fetcher.d.ts +161 -0
- package/dist/utils/template-fetcher.d.ts.map +1 -0
- package/dist/utils/template-fetcher.js +1020 -0
- package/dist/utils/template-fetcher.js.map +1 -0
- package/dist/utils/template-hash.d.ts +127 -0
- package/dist/utils/template-hash.d.ts.map +1 -0
- package/dist/utils/template-hash.js +335 -0
- package/dist/utils/template-hash.js.map +1 -0
- package/dist/utils/uninstall-scrubbers.d.ts +66 -0
- package/dist/utils/uninstall-scrubbers.d.ts.map +1 -0
- package/dist/utils/uninstall-scrubbers.js +417 -0
- package/dist/utils/uninstall-scrubbers.js.map +1 -0
- package/dist/utils/workflow-resolver.d.ts +86 -0
- package/dist/utils/workflow-resolver.d.ts.map +1 -0
- package/dist/utils/workflow-resolver.js +266 -0
- package/dist/utils/workflow-resolver.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,1549 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { AI_TOOLS } from "../types/ai-tools.js";
|
|
5
|
+
import { PLATFORM_IDS, collectPlatformTemplates, getConfiguredPlatforms, } from "../configurators/index.js";
|
|
6
|
+
import { agentsMdContent } from "../templates/markdown/index.js";
|
|
7
|
+
import { TRELLIS_BLOCK_END, TRELLIS_BLOCK_START } from "./managed-paths.js";
|
|
8
|
+
import { assertSafeManagedPath, lstatIfPresent, validateManagedRelativePath, } from "./managed-removal.js";
|
|
9
|
+
import { fingerprintPath, fingerprintsEqual, withAblationProjectLock, } from "./ablation-store.js";
|
|
10
|
+
import { writeFileAtomic } from "./atomic-write.js";
|
|
11
|
+
import { loadHashes } from "./template-hash.js";
|
|
12
|
+
const SUPPORTED_PLATFORMS = new Set([
|
|
13
|
+
"pi",
|
|
14
|
+
"omp",
|
|
15
|
+
"codex",
|
|
16
|
+
"claude-code",
|
|
17
|
+
"zcode",
|
|
18
|
+
]);
|
|
19
|
+
const STATE_DIR = ".trellis/.integration";
|
|
20
|
+
const STATE_PATH = `${STATE_DIR}/state.json`;
|
|
21
|
+
const PARKED_ROOT = `${STATE_DIR}/parked`;
|
|
22
|
+
const MANIFEST_PATH = ".trellis/.template-hashes.json";
|
|
23
|
+
function integrationPath(projectRoot, relativePath, allowLeafSymlink = false) {
|
|
24
|
+
const absolute = assertSafeManagedPath(projectRoot, relativePath);
|
|
25
|
+
const segments = relativePath.split("/");
|
|
26
|
+
let current = projectRoot;
|
|
27
|
+
for (const [index, segment] of segments.entries()) {
|
|
28
|
+
current = path.join(current, segment);
|
|
29
|
+
if (allowLeafSymlink && index === segments.length - 1)
|
|
30
|
+
break;
|
|
31
|
+
if (lstatIfPresent(current)?.isSymbolicLink()) {
|
|
32
|
+
throw new Error(`Integration paths must not traverse symlinks: ${relativePath}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return absolute;
|
|
36
|
+
}
|
|
37
|
+
function ownedTemplates(projectRoot, id) {
|
|
38
|
+
integrationPath(projectRoot, MANIFEST_PATH);
|
|
39
|
+
const hashes = loadHashes(projectRoot);
|
|
40
|
+
return new Map([...(collectPlatformTemplates(id) ?? new Map())].filter(([relativePath]) => Object.hasOwn(hashes, relativePath)));
|
|
41
|
+
}
|
|
42
|
+
function sha256(content) {
|
|
43
|
+
return createHash("sha256").update(content).digest("hex");
|
|
44
|
+
}
|
|
45
|
+
function defaultConfig() {
|
|
46
|
+
return { enabled: true, skills: true, hooks: true, platforms: {} };
|
|
47
|
+
}
|
|
48
|
+
function cloneConfig(config) {
|
|
49
|
+
return {
|
|
50
|
+
enabled: config.enabled,
|
|
51
|
+
skills: config.skills,
|
|
52
|
+
hooks: config.hooks,
|
|
53
|
+
platforms: Object.fromEntries(Object.entries(config.platforms).map(([id, value]) => [id, { ...value }])),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function parseBoolean(raw, key) {
|
|
57
|
+
const value = raw
|
|
58
|
+
.trim()
|
|
59
|
+
.replace(/\s+#.*$/, "")
|
|
60
|
+
.trim();
|
|
61
|
+
if (value === "true")
|
|
62
|
+
return true;
|
|
63
|
+
if (value === "false")
|
|
64
|
+
return false;
|
|
65
|
+
throw new Error(`Invalid integration boolean for ${key}: ${JSON.stringify(value)}`);
|
|
66
|
+
}
|
|
67
|
+
function indentation(line) {
|
|
68
|
+
return line.length - line.trimStart().length;
|
|
69
|
+
}
|
|
70
|
+
function parseIntegrationConfig(content) {
|
|
71
|
+
const config = defaultConfig();
|
|
72
|
+
const lines = content.split(/\r?\n/);
|
|
73
|
+
let integrationIndent = null;
|
|
74
|
+
let childIndent = null;
|
|
75
|
+
let platformsIndent = null;
|
|
76
|
+
let platformIndent = null;
|
|
77
|
+
let platform = null;
|
|
78
|
+
for (const line of lines) {
|
|
79
|
+
const trimmed = line.trim();
|
|
80
|
+
if (trimmed.length === 0 || trimmed.startsWith("#"))
|
|
81
|
+
continue;
|
|
82
|
+
const indent = indentation(line);
|
|
83
|
+
if (integrationIndent === null) {
|
|
84
|
+
if (/^integration:\s*(?:#.*)?$/.test(trimmed)) {
|
|
85
|
+
integrationIndent = indent;
|
|
86
|
+
}
|
|
87
|
+
else if (/^integration\s*:/.test(trimmed)) {
|
|
88
|
+
throw new Error(`Unsupported integration mapping syntax: ${trimmed}`);
|
|
89
|
+
}
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (indent <= integrationIndent)
|
|
93
|
+
break;
|
|
94
|
+
childIndent ??= indent;
|
|
95
|
+
if (indent === childIndent) {
|
|
96
|
+
platform = null;
|
|
97
|
+
platformIndent = null;
|
|
98
|
+
if (/^platforms:\s*(?:#.*)?$/.test(trimmed)) {
|
|
99
|
+
platformsIndent = indent;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
platformsIndent = null;
|
|
103
|
+
const match = trimmed.match(/^(enabled|skills|hooks):\s*(.*)$/);
|
|
104
|
+
if (!match)
|
|
105
|
+
throw new Error(`Invalid integration entry: ${trimmed}`);
|
|
106
|
+
const key = match[1];
|
|
107
|
+
config[key] = parseBoolean(match[2] ?? "", `integration.${key}`);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (platformsIndent === null || indent <= platformsIndent)
|
|
111
|
+
continue;
|
|
112
|
+
if (platformIndent === null || indent === platformIndent) {
|
|
113
|
+
const match = trimmed.match(/^([^:#][^:]*):\s*(?:#.*)?$/);
|
|
114
|
+
if (!match) {
|
|
115
|
+
throw new Error(`Invalid integration platform entry: ${trimmed}`);
|
|
116
|
+
}
|
|
117
|
+
platformIndent = indent;
|
|
118
|
+
platform = match[1]?.trim() ?? null;
|
|
119
|
+
if (!platform || platform.includes("/") || platform.includes("\\")) {
|
|
120
|
+
throw new Error(`Invalid integration platform id: ${JSON.stringify(platform)}`);
|
|
121
|
+
}
|
|
122
|
+
config.platforms[platform] = {};
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (platform && indent > platformIndent) {
|
|
126
|
+
const match = trimmed.match(/^(enabled|skills|hooks):\s*(.*)$/);
|
|
127
|
+
if (!match) {
|
|
128
|
+
throw new Error(`Invalid integration platform setting: ${trimmed}`);
|
|
129
|
+
}
|
|
130
|
+
const key = match[1];
|
|
131
|
+
const current = config.platforms[platform] ?? {};
|
|
132
|
+
current[key] = parseBoolean(match[2] ?? "", `integration.platforms.${platform}.${key}`);
|
|
133
|
+
config.platforms[platform] = current;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return config;
|
|
137
|
+
}
|
|
138
|
+
function renderIntegrationSection(config) {
|
|
139
|
+
const lines = [
|
|
140
|
+
"integration:",
|
|
141
|
+
` enabled: ${config.enabled}`,
|
|
142
|
+
` skills: ${config.skills}`,
|
|
143
|
+
` hooks: ${config.hooks}`,
|
|
144
|
+
];
|
|
145
|
+
const platforms = Object.entries(config.platforms).filter(([, value]) => Object.keys(value).length > 0);
|
|
146
|
+
if (platforms.length > 0) {
|
|
147
|
+
lines.push(" platforms:");
|
|
148
|
+
for (const [id, value] of platforms.sort(([a], [b]) => a.localeCompare(b))) {
|
|
149
|
+
lines.push(` ${id}:`);
|
|
150
|
+
if (value.enabled !== undefined)
|
|
151
|
+
lines.push(` enabled: ${value.enabled}`);
|
|
152
|
+
if (value.skills !== undefined)
|
|
153
|
+
lines.push(` skills: ${value.skills}`);
|
|
154
|
+
if (value.hooks !== undefined)
|
|
155
|
+
lines.push(` hooks: ${value.hooks}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return `${lines.join("\n")}\n`;
|
|
159
|
+
}
|
|
160
|
+
function replaceIntegrationSection(content, config) {
|
|
161
|
+
const lines = content.split(/\r?\n/);
|
|
162
|
+
const start = lines.findIndex((line) => /^\s*integration:\s*(?:#.*)?$/.test(line));
|
|
163
|
+
const rendered = renderIntegrationSection(config).trimEnd().split("\n");
|
|
164
|
+
if (start === -1) {
|
|
165
|
+
return `${content.trimEnd()}\n\n${rendered.join("\n")}\n`;
|
|
166
|
+
}
|
|
167
|
+
const sourceLine = lines[start] ?? "";
|
|
168
|
+
const baseIndent = indentation(sourceLine);
|
|
169
|
+
const prefix = sourceLine.slice(0, baseIndent);
|
|
170
|
+
const indentedRendered = rendered.map((line) => `${prefix}${line}`);
|
|
171
|
+
let end = start + 1;
|
|
172
|
+
while (end < lines.length) {
|
|
173
|
+
const line = lines[end] ?? "";
|
|
174
|
+
if (line.trim().length > 0 && indentation(line) <= baseIndent)
|
|
175
|
+
break;
|
|
176
|
+
end += 1;
|
|
177
|
+
}
|
|
178
|
+
return [...lines.slice(0, start), ...indentedRendered, ...lines.slice(end)]
|
|
179
|
+
.join("\n")
|
|
180
|
+
.replace(/\n*$/, "\n");
|
|
181
|
+
}
|
|
182
|
+
function resolveConfigPath(projectRoot) {
|
|
183
|
+
return integrationPath(projectRoot, ".trellis/config.yaml");
|
|
184
|
+
}
|
|
185
|
+
function readConfig(projectRoot) {
|
|
186
|
+
const configPath = resolveConfigPath(projectRoot);
|
|
187
|
+
if (!fs.existsSync(configPath))
|
|
188
|
+
return defaultConfig();
|
|
189
|
+
return parseIntegrationConfig(fs.readFileSync(configPath, "utf-8"));
|
|
190
|
+
}
|
|
191
|
+
function emptyState() {
|
|
192
|
+
return {
|
|
193
|
+
schemaVersion: 1,
|
|
194
|
+
configuredPlatforms: [],
|
|
195
|
+
parked: [],
|
|
196
|
+
jsonRestores: [],
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
function isObject(value) {
|
|
200
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
201
|
+
}
|
|
202
|
+
function validateState(projectRoot, raw) {
|
|
203
|
+
if (!isObject(raw) || raw.schemaVersion !== 1) {
|
|
204
|
+
throw new Error("Invalid Trellis integration state.");
|
|
205
|
+
}
|
|
206
|
+
if (!Array.isArray(raw.configuredPlatforms) ||
|
|
207
|
+
!raw.configuredPlatforms.every((id) => typeof id === "string") ||
|
|
208
|
+
!Array.isArray(raw.parked) ||
|
|
209
|
+
!Array.isArray(raw.jsonRestores)) {
|
|
210
|
+
throw new Error("Malformed Trellis integration state.");
|
|
211
|
+
}
|
|
212
|
+
if (Object.keys(raw).some((key) => ![
|
|
213
|
+
"schemaVersion",
|
|
214
|
+
"configuredPlatforms",
|
|
215
|
+
"parked",
|
|
216
|
+
"jsonRestores",
|
|
217
|
+
"markdownRestore",
|
|
218
|
+
].includes(key)) ||
|
|
219
|
+
!raw.configuredPlatforms.every((id) => PLATFORM_IDS.includes(String(id)))) {
|
|
220
|
+
throw new Error("Unknown integration recovery state fields or platforms.");
|
|
221
|
+
}
|
|
222
|
+
const validFingerprint = (value) => {
|
|
223
|
+
if (!isObject(value) || typeof value.kind !== "string")
|
|
224
|
+
return false;
|
|
225
|
+
if (value.kind === "absent")
|
|
226
|
+
return false;
|
|
227
|
+
if (value.kind === "file") {
|
|
228
|
+
return (typeof value.sha256 === "string" &&
|
|
229
|
+
/^[a-f0-9]{64}$/.test(value.sha256) &&
|
|
230
|
+
typeof value.size === "number" &&
|
|
231
|
+
Number.isSafeInteger(value.size) &&
|
|
232
|
+
value.size >= 0 &&
|
|
233
|
+
typeof value.mode === "number" &&
|
|
234
|
+
Number.isInteger(value.mode) &&
|
|
235
|
+
value.mode >= 0 &&
|
|
236
|
+
value.mode <= 0o7777 &&
|
|
237
|
+
Object.keys(value).every((key) => ["kind", "sha256", "size", "mode"].includes(key)));
|
|
238
|
+
}
|
|
239
|
+
if (value.kind === "symlink") {
|
|
240
|
+
return (typeof value.target === "string" &&
|
|
241
|
+
typeof value.mode === "number" &&
|
|
242
|
+
Number.isInteger(value.mode) &&
|
|
243
|
+
value.mode >= 0 &&
|
|
244
|
+
value.mode <= 0o7777 &&
|
|
245
|
+
Object.keys(value).every((key) => ["kind", "target", "mode"].includes(key)));
|
|
246
|
+
}
|
|
247
|
+
return false;
|
|
248
|
+
};
|
|
249
|
+
const state = raw;
|
|
250
|
+
const ownedByPlatform = new Map();
|
|
251
|
+
const sharedPaths = new Set();
|
|
252
|
+
if (state.parked.length > 0) {
|
|
253
|
+
for (const id of SUPPORTED_PLATFORMS) {
|
|
254
|
+
const templates = ownedTemplates(projectRoot, id);
|
|
255
|
+
ownedByPlatform.set(id, templates);
|
|
256
|
+
for (const relativePath of templates.keys()) {
|
|
257
|
+
if (/^\.agents\/skills\/[^/]+\/SKILL\.md$/.test(relativePath))
|
|
258
|
+
sharedPaths.add(relativePath);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
const parkedKeys = new Set();
|
|
263
|
+
for (const entry of state.parked) {
|
|
264
|
+
if (!isObject(entry) ||
|
|
265
|
+
typeof entry.path !== "string" ||
|
|
266
|
+
typeof entry.parkedPath !== "string" ||
|
|
267
|
+
typeof entry.platform !== "string" ||
|
|
268
|
+
!["skill", "command", "agent"].includes(String(entry.category)) ||
|
|
269
|
+
!validFingerprint(entry.fingerprint) ||
|
|
270
|
+
(entry.templateHash !== undefined &&
|
|
271
|
+
typeof entry.templateHash !== "string")) {
|
|
272
|
+
throw new Error("Malformed parked integration entry.");
|
|
273
|
+
}
|
|
274
|
+
if (Object.keys(entry).some((key) => ![
|
|
275
|
+
"path",
|
|
276
|
+
"parkedPath",
|
|
277
|
+
"platform",
|
|
278
|
+
"category",
|
|
279
|
+
"fingerprint",
|
|
280
|
+
"templateHash",
|
|
281
|
+
].includes(key))) {
|
|
282
|
+
throw new Error("Unknown parked integration entry fields.");
|
|
283
|
+
}
|
|
284
|
+
validateManagedRelativePath(entry.path);
|
|
285
|
+
validateManagedRelativePath(entry.parkedPath);
|
|
286
|
+
const expectedPath = entry.platform === "shared"
|
|
287
|
+
? `${path.posix.dirname(entry.path)}/.trellis-disabled.md`
|
|
288
|
+
: parkedPathFor(entry.platform, entry.path);
|
|
289
|
+
const owned = entry.platform === "shared"
|
|
290
|
+
? sharedPaths.has(entry.path)
|
|
291
|
+
: ownedByPlatform.get(entry.platform)?.has(entry.path) ===
|
|
292
|
+
true;
|
|
293
|
+
if (!owned ||
|
|
294
|
+
entry.category !== categoryForPath(entry.path) ||
|
|
295
|
+
entry.parkedPath !== expectedPath) {
|
|
296
|
+
throw new Error(`Invalid parked integration ownership: ${entry.path}`);
|
|
297
|
+
}
|
|
298
|
+
const key = `${entry.platform}:${entry.path}`;
|
|
299
|
+
if (parkedKeys.has(key))
|
|
300
|
+
throw new Error(`Duplicate parked integration entry: ${key}`);
|
|
301
|
+
parkedKeys.add(key);
|
|
302
|
+
integrationPath(projectRoot, entry.path, true);
|
|
303
|
+
integrationPath(projectRoot, entry.parkedPath, true);
|
|
304
|
+
}
|
|
305
|
+
const restoreKeys = new Set();
|
|
306
|
+
for (const record of state.jsonRestores) {
|
|
307
|
+
if (!isObject(record) ||
|
|
308
|
+
typeof record.path !== "string" ||
|
|
309
|
+
typeof record.platform !== "string" ||
|
|
310
|
+
!["hooks", "pi-skills", "omp-skills", "claude-skills"].includes(String(record.kind)) ||
|
|
311
|
+
!("data" in record)) {
|
|
312
|
+
throw new Error("Malformed integration restore record.");
|
|
313
|
+
}
|
|
314
|
+
if (Object.keys(record).some((key) => !["path", "platform", "kind", "data"].includes(key))) {
|
|
315
|
+
throw new Error("Unknown integration restore record fields.");
|
|
316
|
+
}
|
|
317
|
+
validateRestoreRecord(projectRoot, record);
|
|
318
|
+
const key = `${record.platform}:${record.kind}`;
|
|
319
|
+
if (restoreKeys.has(key))
|
|
320
|
+
throw new Error(`Duplicate integration restore record: ${key}`);
|
|
321
|
+
restoreKeys.add(key);
|
|
322
|
+
integrationPath(projectRoot, record.path);
|
|
323
|
+
}
|
|
324
|
+
if (state.markdownRestore) {
|
|
325
|
+
if (!isObject(state.markdownRestore) ||
|
|
326
|
+
typeof state.markdownRestore.path !== "string" ||
|
|
327
|
+
typeof state.markdownRestore.block !== "string" ||
|
|
328
|
+
(state.markdownRestore.templateHash !== undefined &&
|
|
329
|
+
typeof state.markdownRestore.templateHash !== "string")) {
|
|
330
|
+
throw new Error("Malformed integration markdown restore record.");
|
|
331
|
+
}
|
|
332
|
+
if (Object.keys(state.markdownRestore).some((key) => !["path", "block", "templateHash"].includes(key))) {
|
|
333
|
+
throw new Error("Unknown integration markdown restore fields.");
|
|
334
|
+
}
|
|
335
|
+
if (state.markdownRestore.path !== "AGENTS.md" ||
|
|
336
|
+
extractManagedBlock(state.markdownRestore.block) !==
|
|
337
|
+
state.markdownRestore.block) {
|
|
338
|
+
throw new Error("Invalid integration markdown ownership.");
|
|
339
|
+
}
|
|
340
|
+
integrationPath(projectRoot, state.markdownRestore.path);
|
|
341
|
+
}
|
|
342
|
+
return state;
|
|
343
|
+
}
|
|
344
|
+
function loadState(projectRoot) {
|
|
345
|
+
const stateFile = integrationPath(projectRoot, STATE_PATH);
|
|
346
|
+
if (!lstatIfPresent(stateFile))
|
|
347
|
+
return emptyState();
|
|
348
|
+
const stat = fs.lstatSync(stateFile);
|
|
349
|
+
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
350
|
+
throw new Error("Trellis integration state must be a regular file.");
|
|
351
|
+
}
|
|
352
|
+
try {
|
|
353
|
+
return validateState(projectRoot, JSON.parse(fs.readFileSync(stateFile, "utf-8")));
|
|
354
|
+
}
|
|
355
|
+
catch (error) {
|
|
356
|
+
if (error instanceof SyntaxError) {
|
|
357
|
+
throw new Error(`Malformed Trellis integration state: ${error.message}`);
|
|
358
|
+
}
|
|
359
|
+
throw error;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
function effective(config, id) {
|
|
363
|
+
const override = config.platforms[id] ?? {};
|
|
364
|
+
const enabled = config.enabled && (override.enabled ?? true);
|
|
365
|
+
return {
|
|
366
|
+
enabled,
|
|
367
|
+
skills: enabled && (override.skills ?? config.skills),
|
|
368
|
+
hooks: enabled && (override.hooks ?? config.hooks),
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
function installedPlatforms(projectRoot, state) {
|
|
372
|
+
const installed = new Set(getConfiguredPlatforms(projectRoot));
|
|
373
|
+
for (const id of state.configuredPlatforms) {
|
|
374
|
+
if (PLATFORM_IDS.includes(id))
|
|
375
|
+
installed.add(id);
|
|
376
|
+
}
|
|
377
|
+
return installed;
|
|
378
|
+
}
|
|
379
|
+
function categoryForPath(relativePath) {
|
|
380
|
+
if (relativePath.includes("/commands/") || relativePath.includes("/prompts/"))
|
|
381
|
+
return "command";
|
|
382
|
+
if (relativePath.includes("/agents/"))
|
|
383
|
+
return "agent";
|
|
384
|
+
if (relativePath.includes("/skills/"))
|
|
385
|
+
return "skill";
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
function shouldParkPath(id, relativePath, config) {
|
|
389
|
+
const category = categoryForPath(relativePath);
|
|
390
|
+
if (!category)
|
|
391
|
+
return null;
|
|
392
|
+
const state = effective(config, id);
|
|
393
|
+
if (!state.enabled && (category === "command" || category === "agent"))
|
|
394
|
+
return category;
|
|
395
|
+
if (!state.skills && category === "skill") {
|
|
396
|
+
if (relativePath.startsWith(".agents/skills/"))
|
|
397
|
+
return null;
|
|
398
|
+
if (id !== "zcode")
|
|
399
|
+
return null;
|
|
400
|
+
return category;
|
|
401
|
+
}
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
function parkedPathFor(id, relativePath) {
|
|
405
|
+
validateManagedRelativePath(relativePath);
|
|
406
|
+
return `${PARKED_ROOT}/${id}/${relativePath}`;
|
|
407
|
+
}
|
|
408
|
+
function cloneState(state) {
|
|
409
|
+
return JSON.parse(JSON.stringify(state));
|
|
410
|
+
}
|
|
411
|
+
function addOperation(plan, operation, action) {
|
|
412
|
+
plan.operations.push(operation);
|
|
413
|
+
const relativePath = operation.kind === "move"
|
|
414
|
+
? `${operation.from} -> ${operation.to}`
|
|
415
|
+
: operation.relativePath;
|
|
416
|
+
plan.changes.push({ path: relativePath, action });
|
|
417
|
+
}
|
|
418
|
+
function plannedWrite(plan, relativePath) {
|
|
419
|
+
return plan.operations.find((operation) => operation.kind === "write" && operation.relativePath === relativePath);
|
|
420
|
+
}
|
|
421
|
+
function planWrite(plan, relativePath, content, action, mode) {
|
|
422
|
+
const pending = plannedWrite(plan, relativePath);
|
|
423
|
+
if (pending) {
|
|
424
|
+
pending.content = content;
|
|
425
|
+
pending.mode = mode ?? pending.mode;
|
|
426
|
+
const change = plan.changes.find((item) => item.path === relativePath && item.action === action);
|
|
427
|
+
if (!change)
|
|
428
|
+
plan.changes.push({ path: relativePath, action });
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
const absPath = integrationPath(plan.projectRoot, relativePath);
|
|
432
|
+
const before = fingerprintPath(absPath);
|
|
433
|
+
if (before.kind !== "file" && before.kind !== "absent")
|
|
434
|
+
throw new Error(`Expected regular integration file: ${relativePath}`);
|
|
435
|
+
mode ??= before.kind === "file" ? before.mode : 0o600;
|
|
436
|
+
if (before.kind === "file" && fs.readFileSync(absPath, "utf-8") === content) {
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
addOperation(plan, { kind: "write", relativePath, before, content, mode }, action);
|
|
440
|
+
}
|
|
441
|
+
function planMove(plan, from, to, action) {
|
|
442
|
+
const fromAbs = integrationPath(plan.projectRoot, from, true);
|
|
443
|
+
const toAbs = integrationPath(plan.projectRoot, to, true);
|
|
444
|
+
const fromBefore = fingerprintPath(fromAbs);
|
|
445
|
+
const toBefore = fingerprintPath(toAbs);
|
|
446
|
+
if (fromBefore.kind === "absent")
|
|
447
|
+
return;
|
|
448
|
+
if (toBefore.kind !== "absent") {
|
|
449
|
+
throw new Error(`Integration target already exists: ${to}`);
|
|
450
|
+
}
|
|
451
|
+
addOperation(plan, { kind: "move", from, to, fromBefore, toBefore }, action);
|
|
452
|
+
}
|
|
453
|
+
function planDelete(plan, relativePath, action) {
|
|
454
|
+
const absPath = integrationPath(plan.projectRoot, relativePath);
|
|
455
|
+
const before = fingerprintPath(absPath);
|
|
456
|
+
if (before.kind === "absent")
|
|
457
|
+
return;
|
|
458
|
+
addOperation(plan, { kind: "delete", relativePath, before }, action);
|
|
459
|
+
}
|
|
460
|
+
function parseJsonFile(plan, relativePath) {
|
|
461
|
+
const pending = plannedWrite(plan, relativePath);
|
|
462
|
+
if (pending) {
|
|
463
|
+
const parsed = JSON.parse(pending.content);
|
|
464
|
+
if (!isObject(parsed))
|
|
465
|
+
throw new Error(`Expected JSON object: ${relativePath}`);
|
|
466
|
+
return parsed;
|
|
467
|
+
}
|
|
468
|
+
const absPath = integrationPath(plan.projectRoot, relativePath);
|
|
469
|
+
const stat = lstatIfPresent(absPath);
|
|
470
|
+
if (!stat)
|
|
471
|
+
return {};
|
|
472
|
+
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
473
|
+
throw new Error(`Expected regular JSON file: ${relativePath}`);
|
|
474
|
+
}
|
|
475
|
+
const parsed = JSON.parse(fs.readFileSync(absPath, "utf-8"));
|
|
476
|
+
if (!isObject(parsed))
|
|
477
|
+
throw new Error(`Expected JSON object: ${relativePath}`);
|
|
478
|
+
return parsed;
|
|
479
|
+
}
|
|
480
|
+
function jsonText(value) {
|
|
481
|
+
return `${JSON.stringify(value, null, 2)}\n`;
|
|
482
|
+
}
|
|
483
|
+
function isTrellisHookHandler(value) {
|
|
484
|
+
if (!isObject(value) || typeof value.command !== "string")
|
|
485
|
+
return false;
|
|
486
|
+
return /(?:\.claude|\.codex|\.zcode)\/hooks\/(?:session-start|inject-workflow-state|inject-subagent-context|inject-shell-session-context)\.py/.test(value.command);
|
|
487
|
+
}
|
|
488
|
+
function removeTrellisHooks(settings) {
|
|
489
|
+
const copy = structuredClone(settings);
|
|
490
|
+
const rootHooks = copy.hooks;
|
|
491
|
+
if (!isObject(rootHooks))
|
|
492
|
+
return copy;
|
|
493
|
+
const hooks = isObject(rootHooks.events) ? rootHooks.events : rootHooks;
|
|
494
|
+
for (const [event, registrations] of Object.entries(hooks)) {
|
|
495
|
+
if (!Array.isArray(registrations))
|
|
496
|
+
continue;
|
|
497
|
+
const kept = registrations.flatMap((registration) => {
|
|
498
|
+
if (!isObject(registration) || !Array.isArray(registration.hooks)) {
|
|
499
|
+
return [registration];
|
|
500
|
+
}
|
|
501
|
+
const handlers = registration.hooks.filter((handler) => !isTrellisHookHandler(handler));
|
|
502
|
+
return handlers.length > 0 ? [{ ...registration, hooks: handlers }] : [];
|
|
503
|
+
});
|
|
504
|
+
if (kept.length === 0)
|
|
505
|
+
Reflect.deleteProperty(hooks, event);
|
|
506
|
+
else
|
|
507
|
+
hooks[event] = kept;
|
|
508
|
+
}
|
|
509
|
+
if (hooks === rootHooks && Object.keys(hooks).length === 0)
|
|
510
|
+
delete copy.hooks;
|
|
511
|
+
return copy;
|
|
512
|
+
}
|
|
513
|
+
function collectTrellisHooks(settings) {
|
|
514
|
+
const result = {};
|
|
515
|
+
const rootHooks = settings.hooks;
|
|
516
|
+
if (!isObject(rootHooks))
|
|
517
|
+
return result;
|
|
518
|
+
const hooks = isObject(rootHooks.events) ? rootHooks.events : rootHooks;
|
|
519
|
+
for (const [event, registrations] of Object.entries(hooks)) {
|
|
520
|
+
if (!Array.isArray(registrations))
|
|
521
|
+
continue;
|
|
522
|
+
const managed = registrations.flatMap((registration) => {
|
|
523
|
+
if (!isObject(registration) || !Array.isArray(registration.hooks))
|
|
524
|
+
return [];
|
|
525
|
+
const handlers = registration.hooks.filter(isTrellisHookHandler);
|
|
526
|
+
return handlers.length > 0 ? [{ ...registration, hooks: handlers }] : [];
|
|
527
|
+
});
|
|
528
|
+
if (managed.length > 0)
|
|
529
|
+
result[event] = managed;
|
|
530
|
+
}
|
|
531
|
+
return result;
|
|
532
|
+
}
|
|
533
|
+
function mergeTrellisHooks(settings, restored, nestedEvents) {
|
|
534
|
+
const existingManaged = collectTrellisHooks(settings);
|
|
535
|
+
if (Object.keys(existingManaged).length > 0) {
|
|
536
|
+
if (JSON.stringify(existingManaged) === JSON.stringify(restored)) {
|
|
537
|
+
return structuredClone(settings);
|
|
538
|
+
}
|
|
539
|
+
throw new Error("Trellis hook registrations changed while disabled; resolve the conflicting handlers before restoring.");
|
|
540
|
+
}
|
|
541
|
+
const copy = structuredClone(settings);
|
|
542
|
+
if (copy.hooks !== undefined && !isObject(copy.hooks)) {
|
|
543
|
+
throw new Error("Cannot restore Trellis hooks over a non-object hooks configuration.");
|
|
544
|
+
}
|
|
545
|
+
const rootHooks = isObject(copy.hooks)
|
|
546
|
+
? copy.hooks
|
|
547
|
+
: {};
|
|
548
|
+
copy.hooks = rootHooks;
|
|
549
|
+
let events = rootHooks;
|
|
550
|
+
if (nestedEvents) {
|
|
551
|
+
if (rootHooks.events !== undefined && !isObject(rootHooks.events)) {
|
|
552
|
+
throw new Error("Cannot restore Trellis hooks over a non-object hooks.events configuration.");
|
|
553
|
+
}
|
|
554
|
+
events = isObject(rootHooks.events) ? rootHooks.events : {};
|
|
555
|
+
rootHooks.events = events;
|
|
556
|
+
}
|
|
557
|
+
const metadataKey = (registration) => JSON.stringify(Object.keys(registration)
|
|
558
|
+
.filter((key) => key !== "hooks")
|
|
559
|
+
.sort()
|
|
560
|
+
.map((key) => [key, registration[key]]));
|
|
561
|
+
for (const [event, registrations] of Object.entries(restored)) {
|
|
562
|
+
const current = events[event];
|
|
563
|
+
if (current !== undefined && !Array.isArray(current)) {
|
|
564
|
+
throw new Error(`Cannot restore Trellis hooks over a non-array ${event} configuration.`);
|
|
565
|
+
}
|
|
566
|
+
const merged = Array.isArray(current) ? current : [];
|
|
567
|
+
for (const registration of registrations) {
|
|
568
|
+
if (!isObject(registration) || !Array.isArray(registration.hooks)) {
|
|
569
|
+
throw new Error(`Invalid Trellis hook recovery registration for ${event}.`);
|
|
570
|
+
}
|
|
571
|
+
const matching = merged.find((entry) => isObject(entry) && metadataKey(entry) === metadataKey(registration));
|
|
572
|
+
if (isObject(matching) && Array.isArray(matching.hooks)) {
|
|
573
|
+
matching.hooks.push(...structuredClone(registration.hooks));
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
merged.push(structuredClone(registration));
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
events[event] = merged;
|
|
580
|
+
}
|
|
581
|
+
return copy;
|
|
582
|
+
}
|
|
583
|
+
function templateJson(id, relativePath) {
|
|
584
|
+
const content = collectPlatformTemplates(id)?.get(relativePath);
|
|
585
|
+
if (!content)
|
|
586
|
+
return null;
|
|
587
|
+
const parsed = JSON.parse(content);
|
|
588
|
+
return isObject(parsed) ? parsed : null;
|
|
589
|
+
}
|
|
590
|
+
function findRestore(state, platform, kind) {
|
|
591
|
+
return state.jsonRestores.find((record) => record.platform === platform && record.kind === kind);
|
|
592
|
+
}
|
|
593
|
+
function putRestore(state, record) {
|
|
594
|
+
const index = state.jsonRestores.findIndex((item) => item.platform === record.platform && item.kind === record.kind);
|
|
595
|
+
if (index === -1)
|
|
596
|
+
state.jsonRestores.push(record);
|
|
597
|
+
else
|
|
598
|
+
state.jsonRestores[index] = record;
|
|
599
|
+
}
|
|
600
|
+
function dropRestore(state, platform, kind) {
|
|
601
|
+
state.jsonRestores = state.jsonRestores.filter((record) => !(record.platform === platform && record.kind === kind));
|
|
602
|
+
}
|
|
603
|
+
function reconcileHooks(plan, id, relativePath) {
|
|
604
|
+
const desired = effective(plan.desired, id).hooks;
|
|
605
|
+
const restore = findRestore(plan.nextState, id, "hooks");
|
|
606
|
+
if (!ownedTemplates(plan.projectRoot, id).has(relativePath) && !restore)
|
|
607
|
+
return;
|
|
608
|
+
const current = parseJsonFile(plan, relativePath);
|
|
609
|
+
if (!desired && restore) {
|
|
610
|
+
const managed = collectTrellisHooks(current);
|
|
611
|
+
if (Object.keys(managed).length > 0) {
|
|
612
|
+
const template = templateJson(id, relativePath);
|
|
613
|
+
const data = isObject(restore.data) ? restore.data : {};
|
|
614
|
+
if (JSON.stringify(managed) !== JSON.stringify(data.hooks) &&
|
|
615
|
+
JSON.stringify(managed) !==
|
|
616
|
+
JSON.stringify(template ? collectTrellisHooks(template) : {})) {
|
|
617
|
+
throw new Error(`Disabled ${id} hook registrations changed; resolve the conflict before applying.`);
|
|
618
|
+
}
|
|
619
|
+
planWrite(plan, relativePath, jsonText(removeTrellisHooks(current)), `disable regenerated ${id} hooks`);
|
|
620
|
+
}
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
if (!desired && !restore) {
|
|
624
|
+
const managed = collectTrellisHooks(current);
|
|
625
|
+
if (Object.keys(managed).length === 0)
|
|
626
|
+
return;
|
|
627
|
+
const template = templateJson(id, relativePath);
|
|
628
|
+
putRestore(plan.nextState, {
|
|
629
|
+
path: relativePath,
|
|
630
|
+
platform: id,
|
|
631
|
+
kind: "hooks",
|
|
632
|
+
data: {
|
|
633
|
+
hooks: managed,
|
|
634
|
+
useTemplate: template !== null &&
|
|
635
|
+
JSON.stringify(managed) ===
|
|
636
|
+
JSON.stringify(collectTrellisHooks(template)),
|
|
637
|
+
},
|
|
638
|
+
});
|
|
639
|
+
planWrite(plan, relativePath, jsonText(removeTrellisHooks(current)), `disable ${id} hooks`);
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
if (desired && restore) {
|
|
643
|
+
const data = isObject(restore.data) ? restore.data : {};
|
|
644
|
+
const storedHooks = isObject(data.hooks) ? data.hooks : {};
|
|
645
|
+
const template = data.useTemplate === true ? templateJson(id, relativePath) : null;
|
|
646
|
+
const hooks = template
|
|
647
|
+
? collectTrellisHooks(template)
|
|
648
|
+
: storedHooks;
|
|
649
|
+
const restored = mergeTrellisHooks(current, hooks, id === "zcode");
|
|
650
|
+
planWrite(plan, relativePath, jsonText(restored), `restore ${id} hooks`);
|
|
651
|
+
dropRestore(plan.nextState, id, "hooks");
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
function piSkillExclusions(projectRoot) {
|
|
655
|
+
const templates = ownedTemplates(projectRoot, "pi");
|
|
656
|
+
const exclusions = new Set();
|
|
657
|
+
for (const relativePath of templates.keys()) {
|
|
658
|
+
const match = relativePath.match(/^\.agents\/skills\/([^/]+)\/SKILL\.md$/);
|
|
659
|
+
if (match?.[1])
|
|
660
|
+
exclusions.add(`-skills/${match[1]}/SKILL.md`);
|
|
661
|
+
}
|
|
662
|
+
return [...exclusions].sort();
|
|
663
|
+
}
|
|
664
|
+
function piHiddenSkillAliases(projectRoot) {
|
|
665
|
+
return sharedSkillEntryPaths(projectRoot).map((relativePath) => `../${path.posix.dirname(relativePath)}/.trellis-disabled.md`);
|
|
666
|
+
}
|
|
667
|
+
function reconcilePiSkills(plan, hiddenSharedSkills) {
|
|
668
|
+
const id = "pi";
|
|
669
|
+
const relativePath = ".pi/settings.json";
|
|
670
|
+
const enabled = effective(plan.desired, id).skills;
|
|
671
|
+
const desiredEntries = enabled
|
|
672
|
+
? hiddenSharedSkills
|
|
673
|
+
? piHiddenSkillAliases(plan.projectRoot)
|
|
674
|
+
: []
|
|
675
|
+
: piSkillExclusions(plan.projectRoot);
|
|
676
|
+
const restore = findRestore(plan.nextState, id, "pi-skills");
|
|
677
|
+
const current = parseJsonFile(plan, relativePath);
|
|
678
|
+
if (current.skills !== undefined &&
|
|
679
|
+
(!Array.isArray(current.skills) ||
|
|
680
|
+
!current.skills.every((value) => typeof value === "string"))) {
|
|
681
|
+
throw new Error("Expected a string array for Pi skills.");
|
|
682
|
+
}
|
|
683
|
+
const skills = Array.isArray(current.skills)
|
|
684
|
+
? current.skills.filter((value) => typeof value === "string")
|
|
685
|
+
: [];
|
|
686
|
+
if (desiredEntries.length > 0) {
|
|
687
|
+
if (restore && !Array.isArray(restore.data)) {
|
|
688
|
+
throw new Error("Malformed Pi skill restore state.");
|
|
689
|
+
}
|
|
690
|
+
const owned = new Set(Array.isArray(restore?.data)
|
|
691
|
+
? restore.data.filter((value) => typeof value === "string")
|
|
692
|
+
: []);
|
|
693
|
+
const next = skills.filter((entry) => !owned.has(entry));
|
|
694
|
+
owned.clear();
|
|
695
|
+
for (const entry of desiredEntries) {
|
|
696
|
+
if (!next.includes(entry)) {
|
|
697
|
+
next.push(entry);
|
|
698
|
+
owned.add(entry);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
if (!restore && owned.size > 0) {
|
|
702
|
+
putRestore(plan.nextState, {
|
|
703
|
+
path: relativePath,
|
|
704
|
+
platform: id,
|
|
705
|
+
kind: "pi-skills",
|
|
706
|
+
data: [...owned],
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
else if (restore) {
|
|
710
|
+
restore.data = [...owned];
|
|
711
|
+
}
|
|
712
|
+
if (JSON.stringify(next) !== JSON.stringify(skills)) {
|
|
713
|
+
current.skills = next;
|
|
714
|
+
planWrite(plan, relativePath, jsonText(current), enabled ? "load hidden Trellis skills in pi" : "disable pi skills");
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
else if (restore) {
|
|
718
|
+
if (!Array.isArray(restore.data)) {
|
|
719
|
+
throw new Error("Malformed Pi skill restore state.");
|
|
720
|
+
}
|
|
721
|
+
const owned = new Set(restore.data.filter((value) => typeof value === "string"));
|
|
722
|
+
if ([...owned].some((entry) => !skills.includes(entry))) {
|
|
723
|
+
throw new Error("Cannot restore Pi skills: managed loading entry changed.");
|
|
724
|
+
}
|
|
725
|
+
const next = skills.filter((value) => !owned.has(value));
|
|
726
|
+
if (next.length === 0)
|
|
727
|
+
delete current.skills;
|
|
728
|
+
else
|
|
729
|
+
current.skills = next;
|
|
730
|
+
planWrite(plan, relativePath, jsonText(current), "restore pi skills");
|
|
731
|
+
dropRestore(plan.nextState, id, "pi-skills");
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
function ompSkillNames(projectRoot) {
|
|
735
|
+
return [...ownedTemplates(projectRoot, "omp").keys()]
|
|
736
|
+
.flatMap((relativePath) => {
|
|
737
|
+
const match = relativePath.match(/^\.omp\/skills\/([^/]+)\/SKILL\.md$/);
|
|
738
|
+
return match?.[1] ? [match[1]] : [];
|
|
739
|
+
})
|
|
740
|
+
.sort();
|
|
741
|
+
}
|
|
742
|
+
function yamlSectionEnd(lines, start) {
|
|
743
|
+
const indent = indentation(lines[start] ?? "");
|
|
744
|
+
let end = start + 1;
|
|
745
|
+
while (end < lines.length) {
|
|
746
|
+
const line = lines[end] ?? "";
|
|
747
|
+
if (line.trim() &&
|
|
748
|
+
!line.trimStart().startsWith("#") &&
|
|
749
|
+
indentation(line) <= indent)
|
|
750
|
+
break;
|
|
751
|
+
end += 1;
|
|
752
|
+
}
|
|
753
|
+
return end;
|
|
754
|
+
}
|
|
755
|
+
function ompSkillSections(lines) {
|
|
756
|
+
const roots = lines.flatMap((line, index) => /^skills\s*:/.test(line) ? [index] : []);
|
|
757
|
+
if (roots.length > 1)
|
|
758
|
+
throw new Error("Duplicate OMP skills mappings.");
|
|
759
|
+
const skills = roots[0] ?? -1;
|
|
760
|
+
if (skills === -1)
|
|
761
|
+
return { skills, ignored: -1 };
|
|
762
|
+
if (!/^skills:\s*(?:#.*)?$/.test(lines[skills] ?? ""))
|
|
763
|
+
throw new Error("Unsupported OMP skills mapping syntax.");
|
|
764
|
+
const end = yamlSectionEnd(lines, skills);
|
|
765
|
+
const children = lines.flatMap((line, index) => index > skills && index < end && /^\s+ignoredSkills\s*:/.test(line)
|
|
766
|
+
? [index]
|
|
767
|
+
: []);
|
|
768
|
+
if (children.length > 1)
|
|
769
|
+
throw new Error("Duplicate OMP ignoredSkills mappings.");
|
|
770
|
+
const ignored = children[0] ?? -1;
|
|
771
|
+
if (ignored !== -1 &&
|
|
772
|
+
!/^ {2}ignoredSkills:\s*(?:#.*)?$/.test(lines[ignored] ?? "")) {
|
|
773
|
+
throw new Error("Unsupported OMP ignoredSkills syntax; use a two-space block list.");
|
|
774
|
+
}
|
|
775
|
+
if (ignored !== -1) {
|
|
776
|
+
for (const line of lines.slice(ignored + 1, yamlSectionEnd(lines, ignored))) {
|
|
777
|
+
if (line.trim() &&
|
|
778
|
+
!line.trimStart().startsWith("#") &&
|
|
779
|
+
!/^ {4}-\s+/.test(line)) {
|
|
780
|
+
throw new Error("Unsupported OMP ignoredSkills list syntax.");
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
return { skills, ignored };
|
|
785
|
+
}
|
|
786
|
+
function reconcileOmpSkills(plan) {
|
|
787
|
+
const id = "omp";
|
|
788
|
+
const relativePath = ".omp/config.yml";
|
|
789
|
+
const desired = effective(plan.desired, id).skills;
|
|
790
|
+
const restore = findRestore(plan.nextState, id, "omp-skills");
|
|
791
|
+
if (desired && !restore)
|
|
792
|
+
return;
|
|
793
|
+
const absPath = integrationPath(plan.projectRoot, relativePath);
|
|
794
|
+
const content = lstatIfPresent(absPath)
|
|
795
|
+
? fs.readFileSync(absPath, "utf-8")
|
|
796
|
+
: "";
|
|
797
|
+
const lines = content.split(/\r?\n/);
|
|
798
|
+
const data = restore
|
|
799
|
+
? restore.data
|
|
800
|
+
: { entries: [], createdSkills: false, createdIgnoredSkills: false };
|
|
801
|
+
let { skills, ignored } = ompSkillSections(lines);
|
|
802
|
+
if (!desired) {
|
|
803
|
+
const names = ompSkillNames(plan.projectRoot);
|
|
804
|
+
if (names.length === 0)
|
|
805
|
+
return;
|
|
806
|
+
if (skills === -1) {
|
|
807
|
+
skills = lines.length;
|
|
808
|
+
lines.push("skills:");
|
|
809
|
+
data.createdSkills = true;
|
|
810
|
+
}
|
|
811
|
+
if (ignored === -1) {
|
|
812
|
+
ignored = skills + 1;
|
|
813
|
+
lines.splice(ignored, 0, " ignoredSkills:");
|
|
814
|
+
data.createdIgnoredSkills = true;
|
|
815
|
+
}
|
|
816
|
+
for (const name of names) {
|
|
817
|
+
const end = yamlSectionEnd(lines, ignored);
|
|
818
|
+
if (!lines
|
|
819
|
+
.slice(ignored + 1, end)
|
|
820
|
+
.some((line) => line.trim() === `- ${name}` ||
|
|
821
|
+
line.trim() === `- "${name}"` ||
|
|
822
|
+
line.trim() === `- '${name}'`)) {
|
|
823
|
+
lines.splice(end, 0, ` - ${name}`);
|
|
824
|
+
if (!data.entries.includes(name))
|
|
825
|
+
data.entries.push(name);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
if (data.entries.length > 0)
|
|
829
|
+
putRestore(plan.nextState, {
|
|
830
|
+
path: relativePath,
|
|
831
|
+
platform: id,
|
|
832
|
+
kind: "omp-skills",
|
|
833
|
+
data,
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
else {
|
|
837
|
+
if (ignored === -1)
|
|
838
|
+
throw new Error("Cannot restore OMP skills: managed ignoredSkills entry changed.");
|
|
839
|
+
for (const name of data.entries) {
|
|
840
|
+
const end = yamlSectionEnd(lines, ignored);
|
|
841
|
+
const index = lines.findIndex((line, index) => index > ignored && index < end && line.trim() === `- ${name}`);
|
|
842
|
+
if (index === -1)
|
|
843
|
+
throw new Error("Cannot restore OMP skills: managed ignoredSkills entry changed.");
|
|
844
|
+
lines.splice(index, 1);
|
|
845
|
+
}
|
|
846
|
+
const hasChildren = (start) => lines
|
|
847
|
+
.slice(start + 1, yamlSectionEnd(lines, start))
|
|
848
|
+
.some((line) => line.trim().length > 0);
|
|
849
|
+
if (data.createdIgnoredSkills && !hasChildren(ignored))
|
|
850
|
+
lines.splice(ignored, 1);
|
|
851
|
+
if (data.createdSkills && !hasChildren(skills))
|
|
852
|
+
lines.splice(skills, 1);
|
|
853
|
+
dropRestore(plan.nextState, id, "omp-skills");
|
|
854
|
+
}
|
|
855
|
+
const next = `${lines.join("\n").replace(/\n*$/, "")}\n`;
|
|
856
|
+
if (next !== content)
|
|
857
|
+
planWrite(plan, relativePath, next, desired ? "restore omp skills" : "disable omp skills");
|
|
858
|
+
}
|
|
859
|
+
function validateRestoreRecord(projectRoot, record) {
|
|
860
|
+
const fail = () => {
|
|
861
|
+
throw new Error(`Invalid integration restore ownership: ${record.platform}/${record.kind}`);
|
|
862
|
+
};
|
|
863
|
+
const data = record.data;
|
|
864
|
+
const stringEntries = (value, allowed) => Array.isArray(value) &&
|
|
865
|
+
value.every((entry) => typeof entry === "string" && allowed.includes(entry)) &&
|
|
866
|
+
new Set(value).size === value.length;
|
|
867
|
+
if (record.kind === "pi-skills") {
|
|
868
|
+
if (record.platform !== "pi" ||
|
|
869
|
+
record.path !== ".pi/settings.json" ||
|
|
870
|
+
!stringEntries(data, [
|
|
871
|
+
...piSkillExclusions(projectRoot),
|
|
872
|
+
...piHiddenSkillAliases(projectRoot),
|
|
873
|
+
]))
|
|
874
|
+
fail();
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
if (record.kind === "omp-skills") {
|
|
878
|
+
if (record.platform !== "omp" ||
|
|
879
|
+
record.path !== ".omp/config.yml" ||
|
|
880
|
+
!isObject(data) ||
|
|
881
|
+
!stringEntries(data.entries, ompSkillNames(projectRoot)) ||
|
|
882
|
+
typeof data.createdSkills !== "boolean" ||
|
|
883
|
+
typeof data.createdIgnoredSkills !== "boolean" ||
|
|
884
|
+
Object.keys(data).some((key) => !["entries", "createdSkills", "createdIgnoredSkills"].includes(key)))
|
|
885
|
+
fail();
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
if (record.kind === "claude-skills") {
|
|
889
|
+
if (record.platform !== "claude-code" ||
|
|
890
|
+
record.path !== ".claude/settings.json" ||
|
|
891
|
+
!isObject(data))
|
|
892
|
+
fail();
|
|
893
|
+
const names = claudeSkillNames(projectRoot);
|
|
894
|
+
if (!isObject(data) ||
|
|
895
|
+
Object.entries(data).some(([name, value]) => !names.includes(name) ||
|
|
896
|
+
(value !== null && !["on", "off", "auto"].includes(String(value)))))
|
|
897
|
+
fail();
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
const hookPaths = {
|
|
901
|
+
"claude-code": ".claude/settings.json",
|
|
902
|
+
codex: ".codex/hooks.json",
|
|
903
|
+
zcode: ".zcode/config.json",
|
|
904
|
+
};
|
|
905
|
+
if (record.path !== hookPaths[record.platform] ||
|
|
906
|
+
!isObject(data) ||
|
|
907
|
+
!isObject(data.hooks) ||
|
|
908
|
+
typeof data.useTemplate !== "boolean" ||
|
|
909
|
+
Object.keys(data).some((key) => !["hooks", "useTemplate"].includes(key)))
|
|
910
|
+
fail();
|
|
911
|
+
if (!isObject(data) || !isObject(data.hooks))
|
|
912
|
+
return;
|
|
913
|
+
const templates = ownedTemplates(projectRoot, record.platform);
|
|
914
|
+
if (!templates.has(record.path))
|
|
915
|
+
fail();
|
|
916
|
+
for (const registrations of Object.values(data.hooks)) {
|
|
917
|
+
if (!Array.isArray(registrations))
|
|
918
|
+
fail();
|
|
919
|
+
for (const registration of registrations) {
|
|
920
|
+
if (!isObject(registration) ||
|
|
921
|
+
!Array.isArray(registration.hooks) ||
|
|
922
|
+
(registration.matcher !== undefined &&
|
|
923
|
+
typeof registration.matcher !== "string") ||
|
|
924
|
+
Object.keys(registration).some((key) => !["matcher", "hooks"].includes(key)))
|
|
925
|
+
fail();
|
|
926
|
+
if (!isObject(registration) || !Array.isArray(registration.hooks))
|
|
927
|
+
continue;
|
|
928
|
+
for (const handler of registration.hooks) {
|
|
929
|
+
if (!isTrellisHookHandler(handler) ||
|
|
930
|
+
!isObject(handler) ||
|
|
931
|
+
typeof handler.command !== "string" ||
|
|
932
|
+
handler.type !== "command" ||
|
|
933
|
+
(handler.timeout !== undefined &&
|
|
934
|
+
typeof handler.timeout !== "number") ||
|
|
935
|
+
(handler.async !== undefined && typeof handler.async !== "boolean") ||
|
|
936
|
+
(handler.once !== undefined && typeof handler.once !== "boolean") ||
|
|
937
|
+
(handler.statusMessage !== undefined &&
|
|
938
|
+
typeof handler.statusMessage !== "string") ||
|
|
939
|
+
Object.keys(handler).some((key) => ![
|
|
940
|
+
"type",
|
|
941
|
+
"command",
|
|
942
|
+
"timeout",
|
|
943
|
+
"async",
|
|
944
|
+
"statusMessage",
|
|
945
|
+
"once",
|
|
946
|
+
].includes(key)))
|
|
947
|
+
fail();
|
|
948
|
+
if (!isObject(handler) || typeof handler.command !== "string")
|
|
949
|
+
continue;
|
|
950
|
+
const match = handler.command.match(/(?:\.claude|\.codex|\.zcode)\/hooks\/[\w-]+\.py/);
|
|
951
|
+
if (!match?.[0] || !templates.has(match[0]))
|
|
952
|
+
fail();
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
function claudeSkillNames(projectRoot) {
|
|
958
|
+
const files = ownedTemplates(projectRoot, "claude-code");
|
|
959
|
+
const names = new Set();
|
|
960
|
+
for (const relativePath of files.keys()) {
|
|
961
|
+
const match = relativePath.match(/^\.claude\/skills\/([^/]+)\/SKILL\.md$/);
|
|
962
|
+
if (match?.[1])
|
|
963
|
+
names.add(match[1]);
|
|
964
|
+
}
|
|
965
|
+
return [...names].sort();
|
|
966
|
+
}
|
|
967
|
+
function reconcileClaudeSkills(plan) {
|
|
968
|
+
const id = "claude-code";
|
|
969
|
+
const relativePath = ".claude/settings.json";
|
|
970
|
+
const desired = effective(plan.desired, id).skills;
|
|
971
|
+
const restore = findRestore(plan.nextState, id, "claude-skills");
|
|
972
|
+
const current = parseJsonFile(plan, relativePath);
|
|
973
|
+
if (current.skillOverrides !== undefined &&
|
|
974
|
+
!isObject(current.skillOverrides)) {
|
|
975
|
+
throw new Error("Expected an object for Claude skillOverrides.");
|
|
976
|
+
}
|
|
977
|
+
const overrides = isObject(current.skillOverrides)
|
|
978
|
+
? current.skillOverrides
|
|
979
|
+
: {};
|
|
980
|
+
if (!desired) {
|
|
981
|
+
const previous = restore && isObject(restore.data) ? restore.data : {};
|
|
982
|
+
for (const name of claudeSkillNames(plan.projectRoot)) {
|
|
983
|
+
if (!Object.hasOwn(previous, name)) {
|
|
984
|
+
if (overrides[name] !== undefined &&
|
|
985
|
+
!["on", "off", "auto"].includes(String(overrides[name]))) {
|
|
986
|
+
throw new Error(`Unsupported Claude skill override: ${name}`);
|
|
987
|
+
}
|
|
988
|
+
previous[name] = Object.hasOwn(overrides, name)
|
|
989
|
+
? overrides[name]
|
|
990
|
+
: null;
|
|
991
|
+
}
|
|
992
|
+
else if (overrides[name] !== undefined && overrides[name] !== "off") {
|
|
993
|
+
throw new Error(`Disabled Claude skill override changed: ${name}`);
|
|
994
|
+
}
|
|
995
|
+
overrides[name] = "off";
|
|
996
|
+
}
|
|
997
|
+
if (Object.keys(previous).length === 0)
|
|
998
|
+
return;
|
|
999
|
+
current.skillOverrides = overrides;
|
|
1000
|
+
putRestore(plan.nextState, {
|
|
1001
|
+
path: relativePath,
|
|
1002
|
+
platform: id,
|
|
1003
|
+
kind: "claude-skills",
|
|
1004
|
+
data: previous,
|
|
1005
|
+
});
|
|
1006
|
+
planWrite(plan, relativePath, jsonText(current), "disable claude-code skills");
|
|
1007
|
+
}
|
|
1008
|
+
else if (desired && restore) {
|
|
1009
|
+
if (!isObject(restore.data)) {
|
|
1010
|
+
throw new Error("Malformed Claude skill restore state.");
|
|
1011
|
+
}
|
|
1012
|
+
for (const [name, previous] of Object.entries(restore.data)) {
|
|
1013
|
+
if (overrides[name] !== "off") {
|
|
1014
|
+
throw new Error(`Cannot restore Claude skill ${name}: managed override changed while disabled.`);
|
|
1015
|
+
}
|
|
1016
|
+
if (previous === null)
|
|
1017
|
+
Reflect.deleteProperty(overrides, name);
|
|
1018
|
+
else
|
|
1019
|
+
overrides[name] = previous;
|
|
1020
|
+
}
|
|
1021
|
+
if (Object.keys(overrides).length === 0)
|
|
1022
|
+
delete current.skillOverrides;
|
|
1023
|
+
else
|
|
1024
|
+
current.skillOverrides = overrides;
|
|
1025
|
+
planWrite(plan, relativePath, jsonText(current), "restore claude-code skills");
|
|
1026
|
+
dropRestore(plan.nextState, id, "claude-skills");
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
function reconcileParkedResources(plan, installed) {
|
|
1030
|
+
const activeKeys = new Set();
|
|
1031
|
+
for (const id of installed) {
|
|
1032
|
+
if (!SUPPORTED_PLATFORMS.has(id))
|
|
1033
|
+
continue;
|
|
1034
|
+
const templates = ownedTemplates(plan.projectRoot, id);
|
|
1035
|
+
for (const [relativePath, templateContent] of templates) {
|
|
1036
|
+
const category = shouldParkPath(id, relativePath, plan.desired);
|
|
1037
|
+
const key = `${id}:${relativePath}`;
|
|
1038
|
+
const existing = plan.nextState.parked.find((entry) => `${entry.platform}:${entry.path}` === key);
|
|
1039
|
+
if (category) {
|
|
1040
|
+
activeKeys.add(key);
|
|
1041
|
+
if (!existing) {
|
|
1042
|
+
const originalAbs = integrationPath(plan.projectRoot, relativePath, true);
|
|
1043
|
+
const original = fingerprintPath(originalAbs);
|
|
1044
|
+
if (original.kind === "absent")
|
|
1045
|
+
continue;
|
|
1046
|
+
const parkedPath = parkedPathFor(id, relativePath);
|
|
1047
|
+
planMove(plan, relativePath, parkedPath, `park ${id} ${category}`);
|
|
1048
|
+
plan.nextState.parked.push({
|
|
1049
|
+
path: relativePath,
|
|
1050
|
+
parkedPath,
|
|
1051
|
+
platform: id,
|
|
1052
|
+
category,
|
|
1053
|
+
fingerprint: original,
|
|
1054
|
+
templateHash: original.kind === "file" &&
|
|
1055
|
+
sha256(fs.readFileSync(originalAbs, "utf-8")) ===
|
|
1056
|
+
sha256(templateContent)
|
|
1057
|
+
? sha256(templateContent)
|
|
1058
|
+
: undefined,
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
else {
|
|
1062
|
+
const originalFingerprint = fingerprintPath(integrationPath(plan.projectRoot, existing.path, true));
|
|
1063
|
+
if (originalFingerprint.kind !== "absent") {
|
|
1064
|
+
const originalAbs = integrationPath(plan.projectRoot, existing.path, true);
|
|
1065
|
+
if (originalFingerprint.kind !== "file" ||
|
|
1066
|
+
fs.readFileSync(originalAbs, "utf-8") !== templateContent) {
|
|
1067
|
+
throw new Error(`Disabled Trellis resource reappeared with unowned content: ${existing.path}`);
|
|
1068
|
+
}
|
|
1069
|
+
planDelete(plan, existing.path, `withdraw regenerated ${id} ${category}`);
|
|
1070
|
+
}
|
|
1071
|
+
const parkedAbs = integrationPath(plan.projectRoot, existing.parkedPath, true);
|
|
1072
|
+
const parkedFingerprint = fingerprintPath(parkedAbs);
|
|
1073
|
+
if (!fingerprintsEqual(parkedFingerprint, existing.fingerprint)) {
|
|
1074
|
+
throw new Error(`Parked Trellis resource changed unexpectedly: ${existing.parkedPath}`);
|
|
1075
|
+
}
|
|
1076
|
+
if (existing.templateHash &&
|
|
1077
|
+
parkedFingerprint.kind === "file" &&
|
|
1078
|
+
existing.templateHash !== sha256(templateContent)) {
|
|
1079
|
+
planWrite(plan, existing.parkedPath, templateContent, `update parked ${id} ${category}`, parkedFingerprint.mode);
|
|
1080
|
+
existing.fingerprint = {
|
|
1081
|
+
kind: "file",
|
|
1082
|
+
sha256: sha256(templateContent),
|
|
1083
|
+
size: Buffer.byteLength(templateContent),
|
|
1084
|
+
mode: parkedFingerprint.mode,
|
|
1085
|
+
};
|
|
1086
|
+
existing.templateHash = sha256(templateContent);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
else if (existing) {
|
|
1091
|
+
const parkedFingerprint = fingerprintPath(integrationPath(plan.projectRoot, existing.parkedPath, true));
|
|
1092
|
+
if (!fingerprintsEqual(parkedFingerprint, existing.fingerprint)) {
|
|
1093
|
+
throw new Error(`Parked Trellis resource changed unexpectedly: ${existing.parkedPath}`);
|
|
1094
|
+
}
|
|
1095
|
+
const originalAbs = integrationPath(plan.projectRoot, existing.path, true);
|
|
1096
|
+
if (fingerprintPath(originalAbs).kind !== "absent") {
|
|
1097
|
+
throw new Error(`Cannot restore Trellis resource over existing path: ${existing.path}`);
|
|
1098
|
+
}
|
|
1099
|
+
planMove(plan, existing.parkedPath, existing.path, `restore ${id} ${existing.category}`);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
plan.nextState.parked = plan.nextState.parked.filter((entry) => {
|
|
1104
|
+
if (entry.platform === "shared")
|
|
1105
|
+
return true;
|
|
1106
|
+
const key = `${entry.platform}:${entry.path}`;
|
|
1107
|
+
return activeKeys.has(key);
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
function sharedSkillEntryPaths(projectRoot) {
|
|
1111
|
+
const paths = new Set();
|
|
1112
|
+
for (const id of SUPPORTED_PLATFORMS) {
|
|
1113
|
+
for (const relativePath of ownedTemplates(projectRoot, id).keys()) {
|
|
1114
|
+
if (/^\.agents\/skills\/[^/]+\/SKILL\.md$/.test(relativePath)) {
|
|
1115
|
+
paths.add(relativePath);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
return [...paths].sort();
|
|
1120
|
+
}
|
|
1121
|
+
function shouldHideSharedSkills(config, installed) {
|
|
1122
|
+
if (installed.has("codex") && effective(config, "codex").skills)
|
|
1123
|
+
return false;
|
|
1124
|
+
return ((installed.has("codex") && !effective(config, "codex").skills) ||
|
|
1125
|
+
(installed.has("pi") && !effective(config, "pi").skills));
|
|
1126
|
+
}
|
|
1127
|
+
function reconcileSharedSkills(plan, installed) {
|
|
1128
|
+
const hidden = shouldHideSharedSkills(plan.desired, installed);
|
|
1129
|
+
if (hidden) {
|
|
1130
|
+
const blockers = ["gemini", "dsh", "kimi"].filter((id) => installed.has(id));
|
|
1131
|
+
if (blockers.length > 0) {
|
|
1132
|
+
throw new Error(`Cannot disable shared Trellis skills while unadapted shared consumers are active: ${blockers.join(", ")}. Remove those project integrations or keep shared Trellis skills enabled.`);
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
const templates = new Map();
|
|
1136
|
+
for (const relativePath of sharedSkillEntryPaths(plan.projectRoot)) {
|
|
1137
|
+
for (const id of SUPPORTED_PLATFORMS) {
|
|
1138
|
+
const content = collectPlatformTemplates(id)?.get(relativePath);
|
|
1139
|
+
if (content !== undefined) {
|
|
1140
|
+
templates.set(relativePath, content);
|
|
1141
|
+
break;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
for (const [relativePath, templateContent] of templates) {
|
|
1146
|
+
const key = `shared:${relativePath}`;
|
|
1147
|
+
const existing = plan.nextState.parked.find((entry) => `${entry.platform}:${entry.path}` === key);
|
|
1148
|
+
const hiddenPath = `${path.posix.dirname(relativePath)}/.trellis-disabled.md`;
|
|
1149
|
+
if (hidden && !existing) {
|
|
1150
|
+
const originalAbs = integrationPath(plan.projectRoot, relativePath, true);
|
|
1151
|
+
const original = fingerprintPath(originalAbs);
|
|
1152
|
+
if (original.kind === "absent") {
|
|
1153
|
+
const hiddenFingerprint = fingerprintPath(integrationPath(plan.projectRoot, hiddenPath, true));
|
|
1154
|
+
if (hiddenFingerprint.kind !== "absent") {
|
|
1155
|
+
throw new Error(`Hidden Trellis skill entry is not owned by recovery state: ${hiddenPath}`);
|
|
1156
|
+
}
|
|
1157
|
+
continue;
|
|
1158
|
+
}
|
|
1159
|
+
planMove(plan, relativePath, hiddenPath, "hide shared Trellis skill entry");
|
|
1160
|
+
plan.nextState.parked.push({
|
|
1161
|
+
path: relativePath,
|
|
1162
|
+
parkedPath: hiddenPath,
|
|
1163
|
+
platform: "shared",
|
|
1164
|
+
category: "skill",
|
|
1165
|
+
fingerprint: original,
|
|
1166
|
+
templateHash: original.kind === "file" &&
|
|
1167
|
+
sha256(fs.readFileSync(originalAbs, "utf-8")) ===
|
|
1168
|
+
sha256(templateContent)
|
|
1169
|
+
? sha256(templateContent)
|
|
1170
|
+
: undefined,
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
else if (existing) {
|
|
1174
|
+
if (existing.parkedPath !== hiddenPath) {
|
|
1175
|
+
throw new Error(`Unexpected shared skill recovery path: ${existing.parkedPath}`);
|
|
1176
|
+
}
|
|
1177
|
+
const parkedFingerprint = fingerprintPath(integrationPath(plan.projectRoot, existing.parkedPath, true));
|
|
1178
|
+
if (!fingerprintsEqual(parkedFingerprint, existing.fingerprint)) {
|
|
1179
|
+
throw new Error(`Hidden Trellis skill entry changed unexpectedly: ${existing.parkedPath}`);
|
|
1180
|
+
}
|
|
1181
|
+
const originalFingerprint = fingerprintPath(integrationPath(plan.projectRoot, relativePath, true));
|
|
1182
|
+
if (hidden && originalFingerprint.kind !== "absent") {
|
|
1183
|
+
const originalAbs = integrationPath(plan.projectRoot, relativePath, true);
|
|
1184
|
+
if (originalFingerprint.kind !== "file" ||
|
|
1185
|
+
fs.readFileSync(originalAbs, "utf-8") !== templateContent) {
|
|
1186
|
+
throw new Error(`Disabled shared Trellis skill reappeared with unowned content: ${relativePath}`);
|
|
1187
|
+
}
|
|
1188
|
+
planDelete(plan, relativePath, "withdraw regenerated shared Trellis skill");
|
|
1189
|
+
}
|
|
1190
|
+
if (hidden &&
|
|
1191
|
+
existing.templateHash &&
|
|
1192
|
+
parkedFingerprint.kind === "file" &&
|
|
1193
|
+
existing.templateHash !== sha256(templateContent)) {
|
|
1194
|
+
planWrite(plan, existing.parkedPath, templateContent, "update hidden shared Trellis skill entry", parkedFingerprint.mode);
|
|
1195
|
+
existing.fingerprint = {
|
|
1196
|
+
kind: "file",
|
|
1197
|
+
sha256: sha256(templateContent),
|
|
1198
|
+
size: Buffer.byteLength(templateContent),
|
|
1199
|
+
mode: parkedFingerprint.mode,
|
|
1200
|
+
};
|
|
1201
|
+
existing.templateHash = sha256(templateContent);
|
|
1202
|
+
}
|
|
1203
|
+
else if (!hidden) {
|
|
1204
|
+
if (originalFingerprint.kind !== "absent") {
|
|
1205
|
+
throw new Error(`Cannot restore shared Trellis skill over existing path: ${relativePath}`);
|
|
1206
|
+
}
|
|
1207
|
+
planMove(plan, existing.parkedPath, relativePath, "restore shared Trellis skill entry");
|
|
1208
|
+
plan.nextState.parked = plan.nextState.parked.filter((entry) => entry !== existing);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
return hidden;
|
|
1213
|
+
}
|
|
1214
|
+
function extractManagedBlock(content) {
|
|
1215
|
+
const start = content.indexOf(TRELLIS_BLOCK_START);
|
|
1216
|
+
const end = content.indexOf(TRELLIS_BLOCK_END, start);
|
|
1217
|
+
if (start === -1 || end === -1)
|
|
1218
|
+
return null;
|
|
1219
|
+
return content.slice(start, end + TRELLIS_BLOCK_END.length);
|
|
1220
|
+
}
|
|
1221
|
+
function removeManagedBlock(content) {
|
|
1222
|
+
const block = extractManagedBlock(content);
|
|
1223
|
+
if (!block)
|
|
1224
|
+
return content;
|
|
1225
|
+
return `${content.replace(block, "").trimEnd()}\n`;
|
|
1226
|
+
}
|
|
1227
|
+
function staticInstructionsNeeded(config, installed) {
|
|
1228
|
+
if (config.enabled && installed.size === 0)
|
|
1229
|
+
return true;
|
|
1230
|
+
for (const id of installed) {
|
|
1231
|
+
if (!SUPPORTED_PLATFORMS.has(id))
|
|
1232
|
+
return true;
|
|
1233
|
+
if (effective(config, id).enabled)
|
|
1234
|
+
return true;
|
|
1235
|
+
}
|
|
1236
|
+
return false;
|
|
1237
|
+
}
|
|
1238
|
+
function reconcileStaticInstructions(plan, installed) {
|
|
1239
|
+
const relativePath = "AGENTS.md";
|
|
1240
|
+
const absPath = integrationPath(plan.projectRoot, relativePath);
|
|
1241
|
+
const stat = lstatIfPresent(absPath);
|
|
1242
|
+
if (!stat || !stat.isFile() || stat.isSymbolicLink())
|
|
1243
|
+
return;
|
|
1244
|
+
const content = fs.readFileSync(absPath, "utf-8");
|
|
1245
|
+
const needed = staticInstructionsNeeded(plan.desired, installed);
|
|
1246
|
+
if (!needed &&
|
|
1247
|
+
!plan.nextState.markdownRestore &&
|
|
1248
|
+
Object.hasOwn(loadHashes(plan.projectRoot), relativePath)) {
|
|
1249
|
+
const block = extractManagedBlock(content);
|
|
1250
|
+
if (!block)
|
|
1251
|
+
return;
|
|
1252
|
+
const templateBlock = extractManagedBlock(agentsMdContent);
|
|
1253
|
+
plan.nextState.markdownRestore = {
|
|
1254
|
+
path: relativePath,
|
|
1255
|
+
block,
|
|
1256
|
+
templateHash: templateBlock && templateBlock === block ? sha256(block) : undefined,
|
|
1257
|
+
};
|
|
1258
|
+
planWrite(plan, relativePath, removeManagedBlock(content), "withdraw Trellis static instructions");
|
|
1259
|
+
}
|
|
1260
|
+
else if (!needed && plan.nextState.markdownRestore) {
|
|
1261
|
+
const block = extractManagedBlock(content);
|
|
1262
|
+
if (block &&
|
|
1263
|
+
block !== plan.nextState.markdownRestore.block &&
|
|
1264
|
+
block !== extractManagedBlock(agentsMdContent)) {
|
|
1265
|
+
throw new Error("Disabled Trellis AGENTS.md block changed; resolve the conflict before applying.");
|
|
1266
|
+
}
|
|
1267
|
+
if (block)
|
|
1268
|
+
planWrite(plan, relativePath, removeManagedBlock(content), "withdraw regenerated Trellis static instructions");
|
|
1269
|
+
}
|
|
1270
|
+
else if (needed && plan.nextState.markdownRestore) {
|
|
1271
|
+
if (extractManagedBlock(content)) {
|
|
1272
|
+
throw new Error("Cannot restore Trellis AGENTS.md block: a managed block already exists.");
|
|
1273
|
+
}
|
|
1274
|
+
const stored = plan.nextState.markdownRestore;
|
|
1275
|
+
const templateBlock = extractManagedBlock(agentsMdContent);
|
|
1276
|
+
const block = stored.templateHash && templateBlock ? templateBlock : stored.block;
|
|
1277
|
+
planWrite(plan, relativePath, `${content.trimEnd()}\n\n${block}\n`, "restore Trellis static instructions");
|
|
1278
|
+
delete plan.nextState.markdownRestore;
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
function buildStatus(projectRoot, config, state) {
|
|
1282
|
+
const installed = installedPlatforms(projectRoot, state);
|
|
1283
|
+
const platforms = [];
|
|
1284
|
+
for (const id of PLATFORM_IDS) {
|
|
1285
|
+
if (!installed.has(id))
|
|
1286
|
+
continue;
|
|
1287
|
+
const supported = SUPPORTED_PLATFORMS.has(id);
|
|
1288
|
+
const value = effective(config, id);
|
|
1289
|
+
const notes = [];
|
|
1290
|
+
if (supported) {
|
|
1291
|
+
notes.push("Only project-managed Trellis resources are controlled; user/global sources are externally managed.");
|
|
1292
|
+
}
|
|
1293
|
+
if (!supported)
|
|
1294
|
+
notes.push("Installed host is not adapted to project integration controls; existing Trellis resources remain active.");
|
|
1295
|
+
if (id === "zcode" && AI_TOOLS.zcode.globalHookPlugin)
|
|
1296
|
+
notes.push("A user-installed global ZCode hook plugin is externally managed.");
|
|
1297
|
+
platforms.push({
|
|
1298
|
+
id,
|
|
1299
|
+
supported,
|
|
1300
|
+
enabled: supported ? value.enabled : true,
|
|
1301
|
+
skills: supported ? value.skills : true,
|
|
1302
|
+
hooks: supported ? value.hooks : true,
|
|
1303
|
+
applied: supported ? true : false,
|
|
1304
|
+
notes,
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1307
|
+
return {
|
|
1308
|
+
projectRoot,
|
|
1309
|
+
config: cloneConfig(config),
|
|
1310
|
+
platforms,
|
|
1311
|
+
notes: [
|
|
1312
|
+
"Changes affect new or reloaded host sessions; already injected context cannot be removed.",
|
|
1313
|
+
],
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
1316
|
+
/** Inspect the current project without changing preferences, managed resources, or recovery state. */
|
|
1317
|
+
export function readIntegrationStatus(cwd) {
|
|
1318
|
+
const projectRoot = fs.realpathSync(cwd);
|
|
1319
|
+
const config = readConfig(projectRoot);
|
|
1320
|
+
const state = loadState(projectRoot);
|
|
1321
|
+
const status = buildStatus(projectRoot, config, state);
|
|
1322
|
+
const preflight = planIntegrationInternal(projectRoot, config, state, false);
|
|
1323
|
+
const applied = preflight.changes.length === 0;
|
|
1324
|
+
for (const row of status.platforms) {
|
|
1325
|
+
if (row.supported)
|
|
1326
|
+
row.applied = applied;
|
|
1327
|
+
}
|
|
1328
|
+
if (!applied) {
|
|
1329
|
+
status.notes.push(`${preflight.changes.length} integration change(s) are pending.`);
|
|
1330
|
+
}
|
|
1331
|
+
return status;
|
|
1332
|
+
}
|
|
1333
|
+
function planIntegrationInternal(projectRoot, desired, state, persistConfig) {
|
|
1334
|
+
const nextState = cloneState(state);
|
|
1335
|
+
const plan = {
|
|
1336
|
+
status: buildStatus(projectRoot, desired, state),
|
|
1337
|
+
changes: [],
|
|
1338
|
+
projectRoot,
|
|
1339
|
+
desired: cloneConfig(desired),
|
|
1340
|
+
operations: [],
|
|
1341
|
+
nextState,
|
|
1342
|
+
stateFingerprint: fingerprintPath(integrationPath(projectRoot, STATE_PATH)),
|
|
1343
|
+
manifestFingerprint: fingerprintPath(integrationPath(projectRoot, MANIFEST_PATH)),
|
|
1344
|
+
configFingerprint: fingerprintPath(resolveConfigPath(projectRoot)),
|
|
1345
|
+
};
|
|
1346
|
+
const installed = installedPlatforms(projectRoot, state);
|
|
1347
|
+
nextState.configuredPlatforms = [...installed].sort();
|
|
1348
|
+
if (persistConfig &&
|
|
1349
|
+
JSON.stringify(readConfig(projectRoot)) !== JSON.stringify(desired)) {
|
|
1350
|
+
const configPath = resolveConfigPath(projectRoot);
|
|
1351
|
+
const content = fs.existsSync(configPath)
|
|
1352
|
+
? fs.readFileSync(configPath, "utf-8")
|
|
1353
|
+
: "";
|
|
1354
|
+
planWrite(plan, ".trellis/config.yaml", replaceIntegrationSection(content, desired), "persist integration preferences");
|
|
1355
|
+
}
|
|
1356
|
+
reconcileParkedResources(plan, installed);
|
|
1357
|
+
const hiddenSharedSkills = reconcileSharedSkills(plan, installed);
|
|
1358
|
+
if (installed.has("pi"))
|
|
1359
|
+
reconcilePiSkills(plan, hiddenSharedSkills);
|
|
1360
|
+
if (installed.has("omp"))
|
|
1361
|
+
reconcileOmpSkills(plan);
|
|
1362
|
+
if (installed.has("codex")) {
|
|
1363
|
+
reconcileHooks(plan, "codex", ".codex/hooks.json");
|
|
1364
|
+
}
|
|
1365
|
+
if (installed.has("claude-code")) {
|
|
1366
|
+
reconcileClaudeSkills(plan);
|
|
1367
|
+
reconcileHooks(plan, "claude-code", ".claude/settings.json");
|
|
1368
|
+
}
|
|
1369
|
+
if (installed.has("zcode"))
|
|
1370
|
+
reconcileHooks(plan, "zcode", ".zcode/config.json");
|
|
1371
|
+
reconcileStaticInstructions(plan, installed);
|
|
1372
|
+
validateState(projectRoot, nextState);
|
|
1373
|
+
const hasRecoveryState = nextState.parked.length > 0 ||
|
|
1374
|
+
nextState.jsonRestores.length > 0 ||
|
|
1375
|
+
nextState.markdownRestore !== undefined;
|
|
1376
|
+
const stateContent = `${JSON.stringify(nextState, null, 2)}\n`;
|
|
1377
|
+
const currentStatePath = integrationPath(projectRoot, STATE_PATH);
|
|
1378
|
+
const currentStateStat = lstatIfPresent(currentStatePath);
|
|
1379
|
+
const currentState = currentStateStat?.isFile()
|
|
1380
|
+
? fs.readFileSync(currentStatePath, "utf-8")
|
|
1381
|
+
: null;
|
|
1382
|
+
if ((hasRecoveryState || currentState !== null) &&
|
|
1383
|
+
currentState !== stateContent) {
|
|
1384
|
+
planWrite(plan, STATE_PATH, stateContent, "update integration recovery state", 0o600);
|
|
1385
|
+
}
|
|
1386
|
+
plan.status = buildStatus(projectRoot, desired, nextState);
|
|
1387
|
+
const applied = plan.changes.length === 0;
|
|
1388
|
+
for (const row of plan.status.platforms) {
|
|
1389
|
+
if (row.supported)
|
|
1390
|
+
row.applied = applied;
|
|
1391
|
+
}
|
|
1392
|
+
return plan;
|
|
1393
|
+
}
|
|
1394
|
+
/** Build a conflict-checked plan; omit desired to reconcile the policy already saved on disk. */
|
|
1395
|
+
export function planIntegration(cwd, desired) {
|
|
1396
|
+
const projectRoot = fs.realpathSync(cwd);
|
|
1397
|
+
const state = loadState(projectRoot);
|
|
1398
|
+
const config = desired ? cloneConfig(desired) : readConfig(projectRoot);
|
|
1399
|
+
return planIntegrationInternal(projectRoot, config, state, desired !== undefined);
|
|
1400
|
+
}
|
|
1401
|
+
function assertOperationFresh(projectRoot, operation) {
|
|
1402
|
+
if (operation.kind === "move") {
|
|
1403
|
+
const from = fingerprintPath(integrationPath(projectRoot, operation.from, true));
|
|
1404
|
+
const to = fingerprintPath(integrationPath(projectRoot, operation.to, true));
|
|
1405
|
+
if (!fingerprintsEqual(from, operation.fromBefore) ||
|
|
1406
|
+
!fingerprintsEqual(to, operation.toBefore)) {
|
|
1407
|
+
throw new Error(`Integration plan is stale for move ${operation.from} -> ${operation.to}.`);
|
|
1408
|
+
}
|
|
1409
|
+
return;
|
|
1410
|
+
}
|
|
1411
|
+
const actual = fingerprintPath(integrationPath(projectRoot, operation.relativePath));
|
|
1412
|
+
if (!fingerprintsEqual(actual, operation.before)) {
|
|
1413
|
+
throw new Error(`Integration plan is stale for ${operation.relativePath}.`);
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
function writeIntegrationFile(absPath, content, mode) {
|
|
1417
|
+
fs.mkdirSync(path.dirname(absPath), { recursive: true });
|
|
1418
|
+
// Reserve the atomic writer's temporary path before it can follow a planted
|
|
1419
|
+
// symlink, and give sensitive mixed settings their final mode before writing.
|
|
1420
|
+
const temporary = path.join(path.dirname(absPath), `.${path.basename(absPath)}.${process.pid}.tmp`);
|
|
1421
|
+
const fd = fs.openSync(temporary, "wx", mode);
|
|
1422
|
+
try {
|
|
1423
|
+
if (process.platform !== "win32")
|
|
1424
|
+
fs.fchmodSync(fd, mode);
|
|
1425
|
+
}
|
|
1426
|
+
catch (error) {
|
|
1427
|
+
fs.unlinkSync(temporary);
|
|
1428
|
+
throw error;
|
|
1429
|
+
}
|
|
1430
|
+
finally {
|
|
1431
|
+
fs.closeSync(fd);
|
|
1432
|
+
}
|
|
1433
|
+
writeFileAtomic(absPath, content);
|
|
1434
|
+
}
|
|
1435
|
+
function applyOperation(projectRoot, operation) {
|
|
1436
|
+
if (operation.kind === "write") {
|
|
1437
|
+
const absPath = integrationPath(projectRoot, operation.relativePath);
|
|
1438
|
+
writeIntegrationFile(absPath, operation.content, operation.mode ?? 0o600);
|
|
1439
|
+
return;
|
|
1440
|
+
}
|
|
1441
|
+
if (operation.kind === "delete") {
|
|
1442
|
+
fs.unlinkSync(integrationPath(projectRoot, operation.relativePath));
|
|
1443
|
+
return;
|
|
1444
|
+
}
|
|
1445
|
+
const from = integrationPath(projectRoot, operation.from, true);
|
|
1446
|
+
const to = integrationPath(projectRoot, operation.to, true);
|
|
1447
|
+
fs.mkdirSync(path.dirname(to), { recursive: true });
|
|
1448
|
+
fs.renameSync(from, to);
|
|
1449
|
+
}
|
|
1450
|
+
function rollbackOperation(projectRoot, operation) {
|
|
1451
|
+
if (operation.kind === "move") {
|
|
1452
|
+
const from = integrationPath(projectRoot, operation.from, true);
|
|
1453
|
+
const to = integrationPath(projectRoot, operation.to, true);
|
|
1454
|
+
if (lstatIfPresent(to) && !lstatIfPresent(from))
|
|
1455
|
+
fs.renameSync(to, from);
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1458
|
+
const absPath = integrationPath(projectRoot, operation.relativePath);
|
|
1459
|
+
if (operation.before.kind === "absent") {
|
|
1460
|
+
if (lstatIfPresent(absPath))
|
|
1461
|
+
fs.unlinkSync(absPath);
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
// Write operations are limited to regular text files. The stale-plan preflight
|
|
1465
|
+
// guarantees the original bytes are still available before mutation.
|
|
1466
|
+
if (operation.before.kind !== "file")
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1469
|
+
function applyIntegrationLocked(plan) {
|
|
1470
|
+
const projectRoot = fs.realpathSync(plan.projectRoot);
|
|
1471
|
+
if (projectRoot !== plan.projectRoot)
|
|
1472
|
+
throw new Error("Integration project root changed since planning.");
|
|
1473
|
+
for (const operation of plan.operations)
|
|
1474
|
+
assertOperationFresh(projectRoot, operation);
|
|
1475
|
+
const currentStateFingerprint = fingerprintPath(integrationPath(projectRoot, STATE_PATH));
|
|
1476
|
+
if (!fingerprintsEqual(currentStateFingerprint, plan.stateFingerprint)) {
|
|
1477
|
+
throw new Error("Integration plan is stale for recovery state.");
|
|
1478
|
+
}
|
|
1479
|
+
if (!fingerprintsEqual(fingerprintPath(integrationPath(projectRoot, MANIFEST_PATH)), plan.manifestFingerprint)) {
|
|
1480
|
+
throw new Error("Integration plan is stale for ownership manifest.");
|
|
1481
|
+
}
|
|
1482
|
+
if (!fingerprintsEqual(fingerprintPath(resolveConfigPath(projectRoot)), plan.configFingerprint)) {
|
|
1483
|
+
throw new Error("Integration plan is stale for project preferences.");
|
|
1484
|
+
}
|
|
1485
|
+
validateState(projectRoot, plan.nextState);
|
|
1486
|
+
const reserved = new Set();
|
|
1487
|
+
for (const operation of plan.operations) {
|
|
1488
|
+
const paths = operation.kind === "move"
|
|
1489
|
+
? [operation.from, operation.to]
|
|
1490
|
+
: [operation.relativePath];
|
|
1491
|
+
for (const relativePath of paths) {
|
|
1492
|
+
if (reserved.has(relativePath))
|
|
1493
|
+
throw new Error(`Overlapping integration operations: ${relativePath}`);
|
|
1494
|
+
reserved.add(relativePath);
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
const backups = new Map();
|
|
1498
|
+
for (const operation of plan.operations) {
|
|
1499
|
+
if (operation.kind === "move" || operation.before.kind !== "file")
|
|
1500
|
+
continue;
|
|
1501
|
+
const absPath = integrationPath(projectRoot, operation.relativePath);
|
|
1502
|
+
backups.set(operation.relativePath, {
|
|
1503
|
+
content: fs.readFileSync(absPath),
|
|
1504
|
+
mode: operation.before.mode,
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
const applied = [];
|
|
1508
|
+
try {
|
|
1509
|
+
for (const operation of plan.operations) {
|
|
1510
|
+
assertOperationFresh(projectRoot, operation);
|
|
1511
|
+
applyOperation(projectRoot, operation);
|
|
1512
|
+
applied.push(operation);
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
catch (error) {
|
|
1516
|
+
for (const operation of [...applied].reverse()) {
|
|
1517
|
+
if (operation.kind !== "move" && operation.before.kind === "file") {
|
|
1518
|
+
const backup = backups.get(operation.relativePath);
|
|
1519
|
+
if (backup) {
|
|
1520
|
+
const absPath = integrationPath(projectRoot, operation.relativePath);
|
|
1521
|
+
writeIntegrationFile(absPath, backup.content, backup.mode);
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
else {
|
|
1525
|
+
rollbackOperation(projectRoot, operation);
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
throw error;
|
|
1529
|
+
}
|
|
1530
|
+
return readIntegrationStatus(projectRoot);
|
|
1531
|
+
}
|
|
1532
|
+
/** Apply a fresh plan under a project reservation, preserving modes and rolling back caught write failures. */
|
|
1533
|
+
export function applyIntegration(plan) {
|
|
1534
|
+
const root = fs.realpathSync(plan.projectRoot);
|
|
1535
|
+
if (root !== plan.projectRoot)
|
|
1536
|
+
throw new Error("Integration project root changed since planning.");
|
|
1537
|
+
if (plan.operations.length === 0)
|
|
1538
|
+
return applyIntegrationLocked(plan);
|
|
1539
|
+
const stateRoot = integrationPath(root, STATE_DIR);
|
|
1540
|
+
const lockFile = integrationPath(root, `${STATE_DIR}/operation.lock`);
|
|
1541
|
+
return withAblationProjectLock({
|
|
1542
|
+
stateRoot,
|
|
1543
|
+
transactionDir: stateRoot,
|
|
1544
|
+
stateFile: integrationPath(root, STATE_PATH),
|
|
1545
|
+
backupDir: integrationPath(root, PARKED_ROOT),
|
|
1546
|
+
lockFile,
|
|
1547
|
+
}, () => applyIntegrationLocked(plan));
|
|
1548
|
+
}
|
|
1549
|
+
//# sourceMappingURL=integration.js.map
|