@pennixrv/trellis 0.6.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +235 -0
- package/README.md +168 -0
- package/bin/trellis.js +3 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +299 -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 +307 -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 +479 -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 +134 -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 +106 -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 +81 -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 +1603 -0
- package/dist/commands/init.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 +423 -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 +2163 -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 +84 -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 +232 -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 +123 -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 +80 -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 +51 -0
- package/dist/configurators/workflow.d.ts.map +1 -0
- package/dist/configurators/workflow.js +210 -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 +9 -0
- package/dist/constants/version.d.ts.map +1 -0
- package/dist/constants/version.js +15 -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 +187 -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.17.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 +57 -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 +67 -0
- package/dist/templates/codex/index.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 +104 -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 +61 -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 +106 -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 +52 -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 +1105 -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 +2027 -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 +140 -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 +64 -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 +158 -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 +121 -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 +1874 -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 +239 -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 +801 -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 +62 -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/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 +172 -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 +994 -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 +265 -0
- package/dist/utils/workflow-resolver.js.map +1 -0
- package/package.json +89 -0
|
@@ -0,0 +1,1105 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
|
|
2
|
+
import { closeSync, existsSync, fstatSync, lstatSync, openSync, readFileSync, readdirSync, realpathSync, statSync, readSync } from "node:fs";
|
|
3
|
+
import { join, dirname, basename, isAbsolute, relative, resolve } from "node:path";
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Project root detection
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
function findProjectRoot(startDir: string): string | null {
|
|
12
|
+
let current = startDir;
|
|
13
|
+
while (true) {
|
|
14
|
+
if (existsSync(join(current, ".trellis"))) return current;
|
|
15
|
+
const parent = dirname(current);
|
|
16
|
+
if (parent === current) break;
|
|
17
|
+
current = parent;
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Session identity helpers (mirrors Python _sanitize_key / _hash_value / _context_key)
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
function sanitizeKey(raw: string): string {
|
|
27
|
+
const safe = raw.trim().replace(/[^A-Za-z0-9._-]+/g, "_").replace(/^[._-]+|[._-]+$/g, "");
|
|
28
|
+
return safe ? safe.slice(0, 160) : "";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function hashValue(raw: string): string {
|
|
32
|
+
return createHash("sha256").update(raw).digest("hex").slice(0, 24);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function buildContextKey(platformName: string, kind: string, value: string): string {
|
|
36
|
+
if (kind === "transcript") {
|
|
37
|
+
return `${platformName}_transcript_${hashValue(value)}`;
|
|
38
|
+
}
|
|
39
|
+
const safeValue = sanitizeKey(value);
|
|
40
|
+
return safeValue ? `${platformName}_${safeValue}` : `${platformName}_${hashValue(value)}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function deriveContextKey(ctx?: { sessionManager?: { getSessionId?: () => string | undefined; getSessionFile?: () => string | undefined } }): string | null {
|
|
44
|
+
const sessionId = ctx?.sessionManager?.getSessionId?.();
|
|
45
|
+
if (sessionId) {
|
|
46
|
+
return buildContextKey("omp", "session", sessionId);
|
|
47
|
+
}
|
|
48
|
+
const sessionFile = ctx?.sessionManager?.getSessionFile?.();
|
|
49
|
+
if (sessionFile) {
|
|
50
|
+
return buildContextKey("omp", "transcript", sessionFile);
|
|
51
|
+
}
|
|
52
|
+
const override = process.env.TRELLIS_CONTEXT_ID?.trim();
|
|
53
|
+
return override ? sanitizeKey(override) || hashValue(override) : null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isInsideRoot(root: string, candidate: string): boolean {
|
|
57
|
+
const rel = relative(root, candidate);
|
|
58
|
+
return rel === "" || (rel !== ".." && !rel.startsWith("../") && !rel.startsWith("..\\") && !isAbsolute(rel));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Trusted context roots (mirrors packages/cli/src/commands/channel/context-trust.ts;
|
|
63
|
+
// standalone copy since templates don't import from the CLI package).
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
|
|
66
|
+
const AUTO_TRUST_ENTRIES = ["tasks", "workspace"];
|
|
67
|
+
|
|
68
|
+
function stripTrustValue(s: string): string {
|
|
69
|
+
return s.trim().replace(/\s*#.*$/, "").trim().replace(/^['"]|['"]$/g, "");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function parseChannelTrustSection(content: string): { trustedDirs: string[]; autoTrustSymlinks?: boolean } {
|
|
73
|
+
const lines = content.split("\n");
|
|
74
|
+
const trustedDirs: string[] = [];
|
|
75
|
+
let autoTrustSymlinks: boolean | undefined;
|
|
76
|
+
let inChannel = false;
|
|
77
|
+
let inList = false;
|
|
78
|
+
|
|
79
|
+
for (const raw of lines) {
|
|
80
|
+
const line = raw.replace(/\r$/, "");
|
|
81
|
+
const trimmed = line.trimEnd();
|
|
82
|
+
if (trimmed.trim().startsWith("#")) continue;
|
|
83
|
+
|
|
84
|
+
if (/^channel:\s*$/.test(trimmed)) {
|
|
85
|
+
inChannel = true;
|
|
86
|
+
inList = false;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (!inChannel) continue;
|
|
90
|
+
|
|
91
|
+
if (trimmed.trim() !== "" && /^\S/.test(line)) {
|
|
92
|
+
inChannel = false;
|
|
93
|
+
inList = false;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (trimmed.trim() === "") continue;
|
|
97
|
+
|
|
98
|
+
if (inList) {
|
|
99
|
+
const item = trimmed.match(/^ {4}-\s*(.+)$/);
|
|
100
|
+
if (item) {
|
|
101
|
+
const val = stripTrustValue(item[1]!);
|
|
102
|
+
if (val) trustedDirs.push(val);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
inList = false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (/^ {2}trusted_context_dirs:\s*$/.test(trimmed)) {
|
|
109
|
+
inList = true;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const boolMatch = trimmed.match(/^ {2}auto_trust_trellis_symlinks:\s*(.+)$/);
|
|
114
|
+
if (boolMatch) {
|
|
115
|
+
const val = stripTrustValue(boolMatch[1]!).toLowerCase();
|
|
116
|
+
if (val === "false") autoTrustSymlinks = false;
|
|
117
|
+
else if (val === "true") autoTrustSymlinks = true;
|
|
118
|
+
else process.stderr.write(`[channel] channel.auto_trust_trellis_symlinks: invalid value '${val}', ignoring\n`);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return { trustedDirs, autoTrustSymlinks };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function resolveTrustedRoots(projectRoot: string): string[] {
|
|
127
|
+
const configPath = join(projectRoot, ".trellis", "config.yaml");
|
|
128
|
+
let config: { trustedDirs: string[]; autoTrustSymlinks?: boolean } = { trustedDirs: [] };
|
|
129
|
+
if (existsSync(configPath)) {
|
|
130
|
+
try {
|
|
131
|
+
config = parseChannelTrustSection(readFileSync(configPath, "utf-8"));
|
|
132
|
+
} catch {
|
|
133
|
+
// ignore
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const roots: string[] = [];
|
|
138
|
+
for (const entry of config.trustedDirs) {
|
|
139
|
+
try {
|
|
140
|
+
roots.push(realpathSync(resolve(projectRoot, entry)));
|
|
141
|
+
} catch {
|
|
142
|
+
// entry not found or invalid — skip
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (config.autoTrustSymlinks !== false) {
|
|
147
|
+
for (const entryName of AUTO_TRUST_ENTRIES) {
|
|
148
|
+
const entryPath = join(projectRoot, ".trellis", entryName);
|
|
149
|
+
try {
|
|
150
|
+
if (lstatSync(entryPath).isSymbolicLink()) {
|
|
151
|
+
roots.push(realpathSync(entryPath));
|
|
152
|
+
}
|
|
153
|
+
} catch {
|
|
154
|
+
// missing / broken symlink — nothing to trust
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return [...new Set(roots)];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function resolveProjectFile(
|
|
163
|
+
projectRoot: string,
|
|
164
|
+
file: string,
|
|
165
|
+
trustedRoots: string[],
|
|
166
|
+
): string | null {
|
|
167
|
+
try {
|
|
168
|
+
const rootReal = realpathSync(projectRoot);
|
|
169
|
+
const targetReal = realpathSync(resolve(projectRoot, file));
|
|
170
|
+
if (isInsideRoot(rootReal, targetReal)) return targetReal;
|
|
171
|
+
if (trustedRoots.some((root) => isInsideRoot(root, targetReal))) return targetReal;
|
|
172
|
+
return null;
|
|
173
|
+
} catch {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function displayProjectPath(projectRoot: string, filePath: string, taskDir?: string): string {
|
|
179
|
+
const direct = relative(projectRoot, filePath).split("\\").join("/");
|
|
180
|
+
if (direct && !direct.startsWith("../") && !isAbsolute(direct)) return direct;
|
|
181
|
+
if (taskDir) {
|
|
182
|
+
const taskRelative = relative(projectRoot, taskDir).split("\\").join("/");
|
|
183
|
+
const taskLabel = taskRelative && !taskRelative.startsWith("../") && !isAbsolute(taskRelative)
|
|
184
|
+
? taskRelative
|
|
185
|
+
: `.trellis/tasks/${basename(taskDir)}`;
|
|
186
|
+
const withinTask = relative(taskDir, filePath).split("\\").join("/");
|
|
187
|
+
if (withinTask && !withinTask.startsWith("../") && !isAbsolute(withinTask)) {
|
|
188
|
+
return `${taskLabel}/${withinTask}`;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
try {
|
|
192
|
+
return relative(realpathSync(projectRoot), realpathSync(filePath)).split("\\").join("/");
|
|
193
|
+
} catch {
|
|
194
|
+
return relative(projectRoot, filePath).split("\\").join("/");
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// ---------------------------------------------------------------------------
|
|
199
|
+
// Active task resolution
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
|
|
202
|
+
function resolveActiveTaskStatus(
|
|
203
|
+
projectRoot: string,
|
|
204
|
+
contextKey: string | null,
|
|
205
|
+
): { status: string; taskDir: string | null; taskTitle: string | null } {
|
|
206
|
+
const sessionsDir = join(projectRoot, ".trellis", ".runtime", "sessions");
|
|
207
|
+
if (!existsSync(sessionsDir)) return { status: "no_task", taskDir: null, taskTitle: null };
|
|
208
|
+
|
|
209
|
+
// --- 通过 context key 解析 session 文件 ---
|
|
210
|
+
let sessionFilePath: string | null = null;
|
|
211
|
+
|
|
212
|
+
if (contextKey) {
|
|
213
|
+
const candidate = join(sessionsDir, `${contextKey}.json`);
|
|
214
|
+
if (existsSync(candidate)) {
|
|
215
|
+
sessionFilePath = candidate;
|
|
216
|
+
} else {
|
|
217
|
+
return { status: "no_task", taskDir: null, taskTitle: null };
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
// No identity: use single-session fallback only when there is exactly one session file.
|
|
221
|
+
let sessionFiles: string[];
|
|
222
|
+
try {
|
|
223
|
+
sessionFiles = readdirSync(sessionsDir).filter((f) => f.endsWith(".json"));
|
|
224
|
+
} catch {
|
|
225
|
+
return { status: "no_task", taskDir: null, taskTitle: null };
|
|
226
|
+
}
|
|
227
|
+
if (sessionFiles.length === 1) {
|
|
228
|
+
sessionFilePath = join(sessionsDir, sessionFiles[0]);
|
|
229
|
+
} else {
|
|
230
|
+
return { status: "no_task", taskDir: null, taskTitle: null };
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// --- 读取 session 数据 ---
|
|
235
|
+
let sessionData: Record<string, unknown>;
|
|
236
|
+
try {
|
|
237
|
+
sessionData = JSON.parse(readFileSync(sessionFilePath, "utf-8"));
|
|
238
|
+
} catch {
|
|
239
|
+
return { status: "no_task", taskDir: null, taskTitle: null };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const currentTask = sessionData.current_task;
|
|
243
|
+
if (typeof currentTask !== "string" || !currentTask)
|
|
244
|
+
return { status: "no_task", taskDir: null, taskTitle: null };
|
|
245
|
+
|
|
246
|
+
// Same jail the jsonl-referenced files already go through below. `task.py`
|
|
247
|
+
// now refuses to store a ref that leaves the project, but a session file
|
|
248
|
+
// written before that fix can still hold one, and `trellis update` does not
|
|
249
|
+
// rewrite session files — so a poisoned pointer outlives the upgrade that
|
|
250
|
+
// closed the writer.
|
|
251
|
+
const taskDir = resolveProjectFile(projectRoot, currentTask, resolveTrustedRoots(projectRoot));
|
|
252
|
+
if (!taskDir) return { status: "no_task", taskDir: null, taskTitle: null };
|
|
253
|
+
const taskJsonPath = join(taskDir, "task.json");
|
|
254
|
+
if (!existsSync(taskJsonPath)) return { status: "no_task", taskDir: null, taskTitle: null };
|
|
255
|
+
|
|
256
|
+
let taskData: Record<string, unknown>;
|
|
257
|
+
try {
|
|
258
|
+
taskData = JSON.parse(readFileSync(taskJsonPath, "utf-8"));
|
|
259
|
+
} catch {
|
|
260
|
+
return { status: "no_task", taskDir: null, taskTitle: null };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
status: typeof taskData.status === "string" ? taskData.status : "planning",
|
|
265
|
+
taskDir,
|
|
266
|
+
taskTitle: typeof taskData.title === "string" ? taskData.title : null,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// ---------------------------------------------------------------------------
|
|
271
|
+
// Session context — spawns get_context.py default mode (same as Claude hook)
|
|
272
|
+
// ---------------------------------------------------------------------------
|
|
273
|
+
|
|
274
|
+
const SESSION_CONTEXT_TIMEOUT_MS = 5000;
|
|
275
|
+
|
|
276
|
+
function buildSessionContext(projectRoot: string, contextKey: string | null): string {
|
|
277
|
+
const script = join(projectRoot, ".trellis", "scripts", "get_context.py");
|
|
278
|
+
if (!existsSync(script)) return "";
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
const result = spawnSync("python3", [script], {
|
|
282
|
+
cwd: projectRoot,
|
|
283
|
+
encoding: "utf-8",
|
|
284
|
+
env: contextKey
|
|
285
|
+
? { ...process.env, TRELLIS_CONTEXT_ID: contextKey }
|
|
286
|
+
: process.env,
|
|
287
|
+
timeout: SESSION_CONTEXT_TIMEOUT_MS,
|
|
288
|
+
windowsHide: true,
|
|
289
|
+
});
|
|
290
|
+
if (result.status !== 0 || !result.stdout?.trim()) {
|
|
291
|
+
return "";
|
|
292
|
+
}
|
|
293
|
+
return `<session-context>\n${result.stdout.trim()}\n</session-context>`;
|
|
294
|
+
} catch {
|
|
295
|
+
return "";
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// ---------------------------------------------------------------------------
|
|
300
|
+
// Task context — prd.md, info.md, and jsonl-referenced spec/research files
|
|
301
|
+
// ---------------------------------------------------------------------------
|
|
302
|
+
|
|
303
|
+
type AgentType = "trellis-implement" | "trellis-check" | "trellis-research" | null;
|
|
304
|
+
|
|
305
|
+
function taskContextJsonlNames(agentType?: AgentType): string[] {
|
|
306
|
+
if (agentType === "trellis-implement") return ["implement.jsonl"];
|
|
307
|
+
if (agentType === "trellis-check") return ["check.jsonl"];
|
|
308
|
+
if (agentType === "trellis-research") return [];
|
|
309
|
+
return ["implement.jsonl", "check.jsonl"];
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function taskContextInputPaths(projectRoot: string, taskDir: string, agentType?: AgentType): string[] {
|
|
313
|
+
const trustedRoots = resolveTrustedRoots(projectRoot);
|
|
314
|
+
const paths = new Set<string>([
|
|
315
|
+
join(projectRoot, ".trellis", "config.yaml"),
|
|
316
|
+
join(taskDir, "prd.md"),
|
|
317
|
+
join(taskDir, "info.md"),
|
|
318
|
+
]);
|
|
319
|
+
for (const jsonlName of taskContextJsonlNames(agentType)) {
|
|
320
|
+
const jsonlPath = join(taskDir, jsonlName);
|
|
321
|
+
paths.add(jsonlPath);
|
|
322
|
+
if (!existsSync(jsonlPath)) continue;
|
|
323
|
+
const displayPath = displayProjectPath(projectRoot, jsonlPath, taskDir);
|
|
324
|
+
const { lines } = readJsonlLines(jsonlPath, displayPath);
|
|
325
|
+
for (const line of lines) {
|
|
326
|
+
try {
|
|
327
|
+
const row = JSON.parse(line.trim()) as Record<string, unknown>;
|
|
328
|
+
const file = typeof row.file === "string" ? row.file.trim() : "";
|
|
329
|
+
const candidatePath = file ? resolve(projectRoot, file) : "";
|
|
330
|
+
if (candidatePath && isInsideRoot(resolve(projectRoot), candidatePath)) paths.add(candidatePath);
|
|
331
|
+
const targetPath = file ? resolveProjectFile(projectRoot, file, trustedRoots) : null;
|
|
332
|
+
if (targetPath) paths.add(targetPath);
|
|
333
|
+
} catch {
|
|
334
|
+
// Seed rows and malformed lines do not contribute referenced files.
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return [...paths];
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function taskContextSignature(projectRoot: string, taskDir: string, agentType?: AgentType): string {
|
|
342
|
+
return taskContextInputPaths(projectRoot, taskDir, agentType).map((filePath) => {
|
|
343
|
+
try {
|
|
344
|
+
const stat = statSync(filePath);
|
|
345
|
+
return `${filePath}:${stat.mtimeMs}:${stat.ctimeMs}:${stat.size}`;
|
|
346
|
+
} catch {
|
|
347
|
+
return `${filePath}:missing`;
|
|
348
|
+
}
|
|
349
|
+
}).join("\n");
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
interface ContextInjectionLimits {
|
|
353
|
+
max_file_bytes: number;
|
|
354
|
+
max_artifact_bytes: number;
|
|
355
|
+
max_total_bytes: number;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const DEFAULT_CONTEXT_INJECTION_LIMITS: ContextInjectionLimits = {
|
|
359
|
+
max_file_bytes: 32768,
|
|
360
|
+
max_artifact_bytes: 65536,
|
|
361
|
+
max_total_bytes: 131072,
|
|
362
|
+
};
|
|
363
|
+
const MAX_JSONL_BYTES = 1024 * 1024;
|
|
364
|
+
|
|
365
|
+
function stripInlineComment(value: string): string {
|
|
366
|
+
let quote: string | null = null;
|
|
367
|
+
for (let i = 0; i < value.length; i++) {
|
|
368
|
+
const char = value[i];
|
|
369
|
+
if (quote) {
|
|
370
|
+
if (char === quote) quote = null;
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
if (char === "'" || char === '"') {
|
|
374
|
+
quote = char;
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
if (char === "#" && (i === 0 || /\s/.test(value[i - 1]!))) {
|
|
378
|
+
return value.slice(0, i);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return value;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function unquoteYaml(value: string): string {
|
|
385
|
+
return value.length >= 2 && value[0] === value[value.length - 1] &&
|
|
386
|
+
(value[0] === "'" || value[0] === '"')
|
|
387
|
+
? value.slice(1, -1)
|
|
388
|
+
: value;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function readContextInjectionLimits(projectRoot: string): ContextInjectionLimits {
|
|
392
|
+
const limits = { ...DEFAULT_CONTEXT_INJECTION_LIMITS };
|
|
393
|
+
let config = "";
|
|
394
|
+
try { config = readFileSync(join(projectRoot, ".trellis", "config.yaml"), "utf-8"); } catch { return limits; }
|
|
395
|
+
|
|
396
|
+
let inSection = false;
|
|
397
|
+
let sectionIndent = -1;
|
|
398
|
+
for (const rawLine of config.split(/\r?\n/)) {
|
|
399
|
+
const trimmed = rawLine.trim();
|
|
400
|
+
if (!inSection) {
|
|
401
|
+
if (/^context_injection\s*:\s*(#.*)?$/.test(trimmed)) {
|
|
402
|
+
inSection = true;
|
|
403
|
+
sectionIndent = rawLine.length - rawLine.trimStart().length;
|
|
404
|
+
}
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
408
|
+
const indent = rawLine.length - rawLine.trimStart().length;
|
|
409
|
+
if (indent <= sectionIndent) break;
|
|
410
|
+
const match = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*:\s*(.*)$/);
|
|
411
|
+
if (!match || !(match[1] in limits)) continue;
|
|
412
|
+
const rawValue = unquoteYaml(stripInlineComment(match[2]!).trim()).trim();
|
|
413
|
+
if (!/^\d+$/.test(rawValue)) continue;
|
|
414
|
+
(limits as unknown as Record<string, number>)[match[1]!] = Number.parseInt(rawValue, 10);
|
|
415
|
+
}
|
|
416
|
+
return limits;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
class ContextBudget {
|
|
420
|
+
private totalBytesUsed: number;
|
|
421
|
+
private terminalSummaryEmitted = false;
|
|
422
|
+
private stopped = false;
|
|
423
|
+
constructor(private readonly maxTotalBytes: number, initialUsed = 0) {
|
|
424
|
+
this.totalBytesUsed = initialUsed;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
get used(): number {
|
|
428
|
+
return this.totalBytesUsed;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
hasRoom(bytes: number): boolean {
|
|
432
|
+
return !this.stopped && (this.maxTotalBytes <= 0 || this.totalBytesUsed + bytes <= this.maxTotalBytes);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
emit(text: string): string | null {
|
|
436
|
+
const bytes = Buffer.byteLength(text, "utf-8");
|
|
437
|
+
if (!this.hasRoom(bytes)) return null;
|
|
438
|
+
this.totalBytesUsed += bytes;
|
|
439
|
+
return text;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
emitCandidate(primary: string | null, fallback: string | null): string | null {
|
|
443
|
+
if (primary !== null) {
|
|
444
|
+
const direct = this.emit(primary);
|
|
445
|
+
if (direct !== null) return direct;
|
|
446
|
+
}
|
|
447
|
+
if (fallback !== null) {
|
|
448
|
+
const notice = this.emit(fallback);
|
|
449
|
+
if (notice !== null) return notice;
|
|
450
|
+
}
|
|
451
|
+
return this.emitTerminalSummary();
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
private emitTerminalSummary(): string | null {
|
|
455
|
+
if (this.terminalSummaryEmitted) return null;
|
|
456
|
+
this.terminalSummaryEmitted = true;
|
|
457
|
+
this.stopped = true;
|
|
458
|
+
const summary = "[Trellis: context limit reached; omitted entries remain authoritative on disk and require_read paths above.]";
|
|
459
|
+
if (this.maxTotalBytes <= 0) {
|
|
460
|
+
this.totalBytesUsed += Buffer.byteLength(summary, "utf-8");
|
|
461
|
+
return summary;
|
|
462
|
+
}
|
|
463
|
+
const remaining = this.maxTotalBytes - this.totalBytesUsed;
|
|
464
|
+
if (remaining <= 0) return null;
|
|
465
|
+
const bounded = truncateUtf8(Buffer.from(summary, "utf-8"), remaining).toString("utf-8");
|
|
466
|
+
this.totalBytesUsed += Buffer.byteLength(bounded, "utf-8");
|
|
467
|
+
return bounded;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
type Utf8Status = "valid" | "incomplete" | "invalid";
|
|
472
|
+
|
|
473
|
+
function utf8Status(data: Buffer): Utf8Status {
|
|
474
|
+
for (let index = 0; index < data.length; index++) {
|
|
475
|
+
const lead = data[index]!;
|
|
476
|
+
if (lead <= 0x7f) continue;
|
|
477
|
+
|
|
478
|
+
let length: number;
|
|
479
|
+
let secondMin = 0x80;
|
|
480
|
+
let secondMax = 0xbf;
|
|
481
|
+
if (lead >= 0xc2 && lead <= 0xdf) length = 2;
|
|
482
|
+
else if (lead >= 0xe0 && lead <= 0xef) {
|
|
483
|
+
length = 3;
|
|
484
|
+
if (lead === 0xe0) secondMin = 0xa0;
|
|
485
|
+
if (lead === 0xed) secondMax = 0x9f;
|
|
486
|
+
} else if (lead >= 0xf0 && lead <= 0xf4) {
|
|
487
|
+
length = 4;
|
|
488
|
+
if (lead === 0xf0) secondMin = 0x90;
|
|
489
|
+
if (lead === 0xf4) secondMax = 0x8f;
|
|
490
|
+
} else return "invalid";
|
|
491
|
+
|
|
492
|
+
if (index + length > data.length) {
|
|
493
|
+
for (let tail = index + 1; tail < data.length; tail++) {
|
|
494
|
+
const min = tail === index + 1 ? secondMin : 0x80;
|
|
495
|
+
const max = tail === index + 1 ? secondMax : 0xbf;
|
|
496
|
+
if (data[tail]! < min || data[tail]! > max) return "invalid";
|
|
497
|
+
}
|
|
498
|
+
return "incomplete";
|
|
499
|
+
}
|
|
500
|
+
const second = data[index + 1]!;
|
|
501
|
+
if (second < secondMin || second > secondMax) return "invalid";
|
|
502
|
+
for (let continuation = index + 2; continuation < index + length; continuation++) {
|
|
503
|
+
const byte = data[continuation]!;
|
|
504
|
+
if (byte < 0x80 || byte > 0xbf) return "invalid";
|
|
505
|
+
}
|
|
506
|
+
index += length - 1;
|
|
507
|
+
}
|
|
508
|
+
return "valid";
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
function truncateUtf8(data: Buffer, cap: number): Buffer {
|
|
512
|
+
if (cap <= 0 || data.length <= cap) return data;
|
|
513
|
+
if (utf8Status(data.subarray(0, cap)) === "valid") return data.subarray(0, cap);
|
|
514
|
+
// A bounded read has already been validated as a complete UTF-8 stream.
|
|
515
|
+
// Only its final, incomplete code point may make the cap prefix invalid.
|
|
516
|
+
for (let end = cap - 1; end >= Math.max(0, cap - 4); end--) {
|
|
517
|
+
const candidate = data.subarray(0, end);
|
|
518
|
+
if (utf8Status(candidate) === "valid") return candidate;
|
|
519
|
+
}
|
|
520
|
+
return Buffer.alloc(0);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function isUtf8(data: Buffer): boolean {
|
|
524
|
+
return utf8Status(data) === "valid";
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function readFilePrefix(filePath: string, maxBytes: number): { data: Buffer; size: number } | null {
|
|
528
|
+
let fd: number | null = null;
|
|
529
|
+
try {
|
|
530
|
+
fd = openSync(filePath, "r");
|
|
531
|
+
if (maxBytes <= 0) {
|
|
532
|
+
const data = readFileSync(fd);
|
|
533
|
+
return { data, size: fstatSync(fd).size };
|
|
534
|
+
}
|
|
535
|
+
// Read a few extra bytes so a UTF-8 sequence crossing the cap can be
|
|
536
|
+
// validated before truncateUtf8 removes its incomplete suffix. The
|
|
537
|
+
// descriptor keeps the read bounded if the path is replaced or grows.
|
|
538
|
+
const data = Buffer.allocUnsafe(maxBytes + 4);
|
|
539
|
+
const bytesRead = readFully(fd, data);
|
|
540
|
+
return { data: data.subarray(0, bytesRead), size: fstatSync(fd).size };
|
|
541
|
+
} catch {
|
|
542
|
+
return null;
|
|
543
|
+
} finally {
|
|
544
|
+
if (fd !== null) closeSync(fd);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function readFully(fd: number, buffer: Buffer): number {
|
|
549
|
+
let total = 0;
|
|
550
|
+
while (total < buffer.length) {
|
|
551
|
+
const bytesRead = readSync(fd, buffer, total, buffer.length - total, total);
|
|
552
|
+
if (bytesRead === 0) break;
|
|
553
|
+
total += bytesRead;
|
|
554
|
+
}
|
|
555
|
+
return total;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function omittedNotice(file: string, size: number | null, reason: string): string {
|
|
559
|
+
const sizeText = size === null ? "unknown bytes" : `${size} bytes`;
|
|
560
|
+
return `### ${file} [omitted]\n\n[Trellis: omitted (${reason}) — ${file} (${sizeText}); required_read: ${file}]`;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
interface MaterializedFile {
|
|
564
|
+
block: string | null;
|
|
565
|
+
notice: string;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function materialize(
|
|
569
|
+
targetPath: string,
|
|
570
|
+
displayPath: string,
|
|
571
|
+
reason: string,
|
|
572
|
+
maxBytes: number,
|
|
573
|
+
kind: "file" | "artifact",
|
|
574
|
+
): MaterializedFile {
|
|
575
|
+
const file = readFilePrefix(targetPath, maxBytes);
|
|
576
|
+
if (!file) {
|
|
577
|
+
return { block: null, notice: omittedNotice(displayPath, null, `${kind} is missing or unreadable`) };
|
|
578
|
+
}
|
|
579
|
+
const { data, size } = file;
|
|
580
|
+
const encodingStatus = utf8Status(data);
|
|
581
|
+
if (data.includes(0) || encodingStatus === "invalid" || (encodingStatus === "incomplete" && size <= data.length)) {
|
|
582
|
+
return { block: null, notice: omittedNotice(displayPath, size, `binary or non-UTF-8 ${kind}: ${reason}`) };
|
|
583
|
+
}
|
|
584
|
+
const truncated = truncateUtf8(data, maxBytes);
|
|
585
|
+
let content = truncated.toString("utf-8");
|
|
586
|
+
let status: "inline" | "truncated" = "inline";
|
|
587
|
+
if (truncated.length < size) {
|
|
588
|
+
status = "truncated";
|
|
589
|
+
content += `\n[Trellis: truncated at ${maxBytes} bytes — read ${displayPath} for the full content]`;
|
|
590
|
+
}
|
|
591
|
+
return {
|
|
592
|
+
block: `### ${displayPath} [${status}]\n\n${content}`,
|
|
593
|
+
notice: omittedNotice(displayPath, size, `total context limit reached: ${reason}`),
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function readJsonlLines(jsonlPath: string, displayPath: string): { lines: string[]; omitted: string | null } {
|
|
598
|
+
let fd: number | null = null;
|
|
599
|
+
try {
|
|
600
|
+
fd = openSync(jsonlPath, "r");
|
|
601
|
+
const data = Buffer.allocUnsafe(MAX_JSONL_BYTES + 1);
|
|
602
|
+
const bytesRead = readFully(fd, data);
|
|
603
|
+
const size = fstatSync(fd).size;
|
|
604
|
+
if (bytesRead > MAX_JSONL_BYTES || size > MAX_JSONL_BYTES) {
|
|
605
|
+
return {
|
|
606
|
+
lines: [],
|
|
607
|
+
omitted: omittedNotice(displayPath, size, `manifest exceeds ${MAX_JSONL_BYTES} byte parse limit`),
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
const content = data.subarray(0, bytesRead);
|
|
611
|
+
if (!isUtf8(content)) {
|
|
612
|
+
return { lines: [], omitted: omittedNotice(displayPath, size, "manifest is not valid UTF-8") };
|
|
613
|
+
}
|
|
614
|
+
return { lines: content.toString("utf-8").split(/\r?\n/), omitted: null };
|
|
615
|
+
} catch {
|
|
616
|
+
return { lines: [], omitted: omittedNotice(displayPath, null, "manifest is missing or unreadable") };
|
|
617
|
+
} finally {
|
|
618
|
+
if (fd !== null) closeSync(fd);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
function buildTaskContext(projectRoot: string, taskDir: string, agentType?: AgentType): string {
|
|
623
|
+
const parts: string[] = [];
|
|
624
|
+
// Resolved once per call (not per referenced file) — avoids re-parsing
|
|
625
|
+
// config.yaml for every jsonl row.
|
|
626
|
+
const trustedRoots = resolveTrustedRoots(projectRoot);
|
|
627
|
+
const limits = readContextInjectionLimits(projectRoot);
|
|
628
|
+
const prefix = "<task-context>\nContext is bounded by .trellis/config.yaml. Files marked [truncated] or [omitted] remain authoritative on disk; use their required_read path before relying on missing detail.\n\n";
|
|
629
|
+
const suffix = "\n</task-context>";
|
|
630
|
+
const wrapperBytes = Buffer.byteLength(prefix + suffix, "utf-8");
|
|
631
|
+
if (limits.max_total_bytes > 0 && limits.max_total_bytes < wrapperBytes) return "";
|
|
632
|
+
const budget = new ContextBudget(limits.max_total_bytes, wrapperBytes);
|
|
633
|
+
|
|
634
|
+
const appendCandidate = (primary: string | null, fallback: string | null = null): void => {
|
|
635
|
+
const separator = parts.length > 0 ? "\n\n" : "";
|
|
636
|
+
const output = budget.emitCandidate(
|
|
637
|
+
primary === null ? null : separator + primary,
|
|
638
|
+
fallback === null ? null : separator + fallback,
|
|
639
|
+
);
|
|
640
|
+
if (output !== null) parts.push(output);
|
|
641
|
+
};
|
|
642
|
+
|
|
643
|
+
// prd.md and info.md — always included
|
|
644
|
+
const prdPath = join(taskDir, "prd.md");
|
|
645
|
+
const relativePrdPath = displayProjectPath(projectRoot, prdPath, taskDir);
|
|
646
|
+
if (existsSync(prdPath)) {
|
|
647
|
+
const artifact = materialize(prdPath, relativePrdPath, "Requirements document", limits.max_artifact_bytes, "artifact");
|
|
648
|
+
appendCandidate(artifact.block, artifact.notice);
|
|
649
|
+
}
|
|
650
|
+
const infoPath = join(taskDir, "info.md");
|
|
651
|
+
const relativeInfoPath = displayProjectPath(projectRoot, infoPath, taskDir);
|
|
652
|
+
if (existsSync(infoPath)) {
|
|
653
|
+
const artifact = materialize(infoPath, relativeInfoPath, "Task information", limits.max_artifact_bytes, "artifact");
|
|
654
|
+
appendCandidate(artifact.block, artifact.notice);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// Determine which jsonl files to read based on agent type
|
|
658
|
+
const jsonlNames = taskContextJsonlNames(agentType);
|
|
659
|
+
|
|
660
|
+
// A file may be referenced by both manifests. Use the resolved real path so
|
|
661
|
+
// relative aliases and symlinked paths cannot consume the context budget twice.
|
|
662
|
+
const includedPaths = new Set<string>();
|
|
663
|
+
|
|
664
|
+
for (const jsonlName of jsonlNames) {
|
|
665
|
+
const jsonlPath = join(taskDir, jsonlName);
|
|
666
|
+
if (!existsSync(jsonlPath)) continue;
|
|
667
|
+
|
|
668
|
+
const relativeJsonlPath = displayProjectPath(projectRoot, jsonlPath, taskDir);
|
|
669
|
+
const manifest = readJsonlLines(jsonlPath, relativeJsonlPath);
|
|
670
|
+
if (manifest.omitted) {
|
|
671
|
+
appendCandidate(`## ${jsonlName}\n\n${manifest.omitted}`);
|
|
672
|
+
continue;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
const fileChunks: string[] = [];
|
|
676
|
+
let sectionHeaderEmitted = false;
|
|
677
|
+
for (const line of manifest.lines) {
|
|
678
|
+
const trimmed = line.trim();
|
|
679
|
+
if (!trimmed) continue;
|
|
680
|
+
try {
|
|
681
|
+
const row = JSON.parse(trimmed) as Record<string, unknown>;
|
|
682
|
+
const file = typeof row.file === "string" ? row.file.trim() : "";
|
|
683
|
+
if (!file) continue;
|
|
684
|
+
const targetPath = resolveProjectFile(projectRoot, file, trustedRoots);
|
|
685
|
+
if (!targetPath) continue;
|
|
686
|
+
if (includedPaths.has(targetPath)) continue;
|
|
687
|
+
includedPaths.add(targetPath);
|
|
688
|
+
const materialized = materialize(targetPath, file, typeof row.reason === "string" ? row.reason : "-", limits.max_file_bytes, "file");
|
|
689
|
+
const sectionPrefix = sectionHeaderEmitted
|
|
690
|
+
? "\n\n---\n\n"
|
|
691
|
+
: `${parts.length > 0 ? "\n\n" : ""}## ${jsonlName}\n\n`;
|
|
692
|
+
const output = budget.emitCandidate(
|
|
693
|
+
materialized.block === null ? null : `${sectionPrefix}${materialized.block}`,
|
|
694
|
+
`${sectionPrefix}${materialized.notice}`,
|
|
695
|
+
);
|
|
696
|
+
if (output !== null) {
|
|
697
|
+
fileChunks.push(output);
|
|
698
|
+
sectionHeaderEmitted = true;
|
|
699
|
+
}
|
|
700
|
+
} catch {
|
|
701
|
+
// seed rows and malformed lines are non-fatal
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (fileChunks.length > 0) parts.push(fileChunks.join(""));
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
if (parts.length === 0) return "";
|
|
709
|
+
const context = `${prefix}${parts.join("")}${suffix}`;
|
|
710
|
+
if (limits.max_total_bytes <= 0 || Buffer.byteLength(context, "utf-8") <= limits.max_total_bytes) return context;
|
|
711
|
+
const suffixBytes = Buffer.byteLength(suffix, "utf-8");
|
|
712
|
+
const bodyLimit = Math.max(0, limits.max_total_bytes - suffixBytes);
|
|
713
|
+
const body = truncateUtf8(Buffer.from(`${prefix}${parts.join("")}`, "utf-8"), bodyLimit).toString("utf-8");
|
|
714
|
+
return `${body}${suffix}`;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// ---------------------------------------------------------------------------
|
|
718
|
+
// Prompt injection config (escape hatch)
|
|
719
|
+
// ---------------------------------------------------------------------------
|
|
720
|
+
|
|
721
|
+
// Mirrors DEFAULT_PROMPT_INJECTION_SKIP_KEYWORD in inject-workflow-state.py:
|
|
722
|
+
// the skip keyword defaults to "no-trellis"; an explicit "" disables the
|
|
723
|
+
// escape hatch entirely.
|
|
724
|
+
const DEFAULT_PROMPT_INJECTION_SKIP_KEYWORD = "no-trellis";
|
|
725
|
+
|
|
726
|
+
// PyYAML-compatible resolution of scalars that parse as non-strings: null
|
|
727
|
+
// (empty, ~, null variants), bool (YAML 1.1 set), and numbers. Quoted scalars
|
|
728
|
+
// never reach this check and stay strings.
|
|
729
|
+
function isYamlNonStringScalar(raw: string): boolean {
|
|
730
|
+
if (raw === "" || raw === "~" || /^(?:null|Null|NULL)$/.test(raw)) return true;
|
|
731
|
+
if (/^(?:true|True|TRUE|false|False|FALSE|yes|Yes|YES|no|No|NO|on|On|ON|off|Off|OFF)$/.test(raw)) return true;
|
|
732
|
+
// PyYAML int resolver: binary/octal/decimal/hex; leading-zero decimals are not ints.
|
|
733
|
+
if (/^[-+]?(?:0[bB][01_]+|0[0-7_]+|0[xX][0-9a-fA-F_]+|[1-9][\d_]*|0)$/.test(raw)) return true;
|
|
734
|
+
// PyYAML float resolver: requires a dot and a signed exponent ("1.5e+3",
|
|
735
|
+
// not "1.5e3" — the latter stays a string in PyYAML).
|
|
736
|
+
return /^[-+]?(?:\d[\d_]*\.[\d_]*|\.[\d_]+)(?:[eE][-+]\d+)?$/.test(raw) ||
|
|
737
|
+
/^[-+]?\.(?:inf|Inf|INF)$/.test(raw) ||
|
|
738
|
+
/^\.(?:nan|NaN|NAN)$/.test(raw);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
function readPromptInjectionSkipKeyword(projectRoot: string): string {
|
|
742
|
+
let config = "";
|
|
743
|
+
try { config = readFileSync(join(projectRoot, ".trellis", "config.yaml"), "utf-8"); } catch { return DEFAULT_PROMPT_INJECTION_SKIP_KEYWORD; }
|
|
744
|
+
|
|
745
|
+
let inSection = false;
|
|
746
|
+
let sectionIndent = -1;
|
|
747
|
+
for (const rawLine of config.split(/\r?\n/)) {
|
|
748
|
+
const trimmed = rawLine.trim();
|
|
749
|
+
if (!inSection) {
|
|
750
|
+
if (/^prompt_injection\s*:\s*(#.*)?$/.test(trimmed)) {
|
|
751
|
+
inSection = true;
|
|
752
|
+
sectionIndent = rawLine.length - rawLine.trimStart().length;
|
|
753
|
+
}
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
757
|
+
const indent = rawLine.length - rawLine.trimStart().length;
|
|
758
|
+
if (indent <= sectionIndent) break;
|
|
759
|
+
const match = trimmed.match(/^skip_keyword\s*:\s*(.*)$/);
|
|
760
|
+
if (!match) continue;
|
|
761
|
+
const rawValue = stripInlineComment(match[1]!).trim();
|
|
762
|
+
const unquoted = unquoteYaml(rawValue);
|
|
763
|
+
// Preserve YAML scalar typing, mirroring _resolve_skip_keyword's
|
|
764
|
+
// isinstance(raw, str) check: a bare non-string scalar (bool/null/
|
|
765
|
+
// number, including an empty value) falls back to the default, while
|
|
766
|
+
// quoted scalars — including an explicit "" — stay strings.
|
|
767
|
+
if (unquoted === rawValue && isYamlNonStringScalar(rawValue)) {
|
|
768
|
+
return DEFAULT_PROMPT_INJECTION_SKIP_KEYWORD;
|
|
769
|
+
}
|
|
770
|
+
return unquoted.trim();
|
|
771
|
+
}
|
|
772
|
+
return DEFAULT_PROMPT_INJECTION_SKIP_KEYWORD;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// Mirrors prompt_has_skip_keyword() in inject-workflow-state.py: hyphen counts
|
|
776
|
+
// as a word char so "no-trellisx" / "xno-trellis" / "foo-no-trellis" don't
|
|
777
|
+
// match, but punctuation/whitespace boundaries do. Empty keyword never matches.
|
|
778
|
+
function shouldSkipWorkflowState(
|
|
779
|
+
userInput: string,
|
|
780
|
+
skipKeyword: string,
|
|
781
|
+
): boolean {
|
|
782
|
+
if (!skipKeyword) return false;
|
|
783
|
+
const escapedKeyword = skipKeyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
784
|
+
const pattern = new RegExp(`(?<![\\w-])${escapedKeyword}(?![\\w-])`, "i");
|
|
785
|
+
return pattern.test(userInput);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// ---------------------------------------------------------------------------
|
|
789
|
+
// Per-turn cache — prevents redundant workflow-state resolution within a
|
|
790
|
+
// single event cascade (input, before_agent_start, and context fire closely)
|
|
791
|
+
// ---------------------------------------------------------------------------
|
|
792
|
+
|
|
793
|
+
const SESSION_OVERVIEW_TEXT =
|
|
794
|
+
"Trellis workflow system active. Use skills and agents as directed by the workflow state.";
|
|
795
|
+
|
|
796
|
+
class TurnContextCache {
|
|
797
|
+
private key: string | null = null;
|
|
798
|
+
private timestamp = 0;
|
|
799
|
+
private workflowMsg = "";
|
|
800
|
+
private skipThisTurn = false;
|
|
801
|
+
private static readonly TTL_MS = 1500;
|
|
802
|
+
|
|
803
|
+
// Called once per user turn (input event) with the skip decision for that
|
|
804
|
+
// turn; invalidates the cache so every reader in the cascade
|
|
805
|
+
// (before_agent_start, context) resolves the same turn state.
|
|
806
|
+
beginTurn(skipThisTurn: boolean): void {
|
|
807
|
+
this.skipThisTurn = skipThisTurn;
|
|
808
|
+
this.key = null;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
get(projectRoot: string, contextKey: string | null): { workflowMsg: string } {
|
|
812
|
+
const now = Date.now();
|
|
813
|
+
const cacheKey = `${projectRoot}:${contextKey ?? ""}`;
|
|
814
|
+
if (
|
|
815
|
+
this.key === cacheKey &&
|
|
816
|
+
now - this.timestamp < TurnContextCache.TTL_MS
|
|
817
|
+
) {
|
|
818
|
+
return { workflowMsg: this.workflowMsg };
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
const { status } = resolveActiveTaskStatus(projectRoot, contextKey);
|
|
822
|
+
|
|
823
|
+
const workflowPath = join(projectRoot, ".trellis", "workflow.md");
|
|
824
|
+
let workflowMd = "";
|
|
825
|
+
try { workflowMd = readFileSync(workflowPath, "utf-8"); } catch { }
|
|
826
|
+
|
|
827
|
+
let workflowBody = "";
|
|
828
|
+
if (workflowMd) {
|
|
829
|
+
const blocks = parseWorkflowStateBlocks(workflowMd);
|
|
830
|
+
const activeBlock = blocks.find((b) => b.status === status);
|
|
831
|
+
if (activeBlock) {
|
|
832
|
+
workflowBody = `[workflow-state:${activeBlock.status}]\n${activeBlock.content}\n[/workflow-state:${activeBlock.status}]`;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
if (!workflowBody) {
|
|
836
|
+
workflowBody = "Refer to workflow.md for current step.";
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// When skip keyword is present, skip workflow state injection this turn
|
|
840
|
+
this.workflowMsg = this.skipThisTurn
|
|
841
|
+
? ""
|
|
842
|
+
: `<workflow-state>\n${workflowBody}\n</workflow-state>\n\n<session-overview>\n${SESSION_OVERVIEW_TEXT}\n</session-overview>`;
|
|
843
|
+
|
|
844
|
+
this.key = cacheKey;
|
|
845
|
+
this.timestamp = now;
|
|
846
|
+
return { workflowMsg: this.workflowMsg };
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// ---------------------------------------------------------------------------
|
|
851
|
+
// Workflow-state tag parsing
|
|
852
|
+
// ---------------------------------------------------------------------------
|
|
853
|
+
|
|
854
|
+
const WORKFLOW_STATE_RE =
|
|
855
|
+
/\[workflow-state:([A-Za-z0-9_-]+)\]\s*\n([\s\S]*?)\n\s*\[\/workflow-state:\1\]/g;
|
|
856
|
+
|
|
857
|
+
interface WorkflowStateBlock {
|
|
858
|
+
status: string;
|
|
859
|
+
content: string;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
function parseWorkflowStateBlocks(markdown: string): WorkflowStateBlock[] {
|
|
863
|
+
const blocks: WorkflowStateBlock[] = [];
|
|
864
|
+
for (const match of markdown.matchAll(WORKFLOW_STATE_RE)) {
|
|
865
|
+
blocks.push({
|
|
866
|
+
status: match[1],
|
|
867
|
+
content: match[2].trim(),
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
return blocks;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
// ---------------------------------------------------------------------------
|
|
874
|
+
// Sub-agent detection
|
|
875
|
+
// ---------------------------------------------------------------------------
|
|
876
|
+
|
|
877
|
+
const TRELLIS_AGENTS = new Set(["trellis-implement", "trellis-check", "trellis-research"]);
|
|
878
|
+
|
|
879
|
+
function detectAgentType(): AgentType {
|
|
880
|
+
const blocked = process.env.PI_BLOCKED_AGENT;
|
|
881
|
+
if (blocked && TRELLIS_AGENTS.has(blocked)) {
|
|
882
|
+
return blocked as AgentType;
|
|
883
|
+
}
|
|
884
|
+
return null;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
interface TaskContextCache {
|
|
888
|
+
projectRoot: string;
|
|
889
|
+
taskDir: string;
|
|
890
|
+
agentType: AgentType;
|
|
891
|
+
signature: string;
|
|
892
|
+
content: string;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// ---------------------------------------------------------------------------
|
|
896
|
+
// Extension entry point
|
|
897
|
+
// ---------------------------------------------------------------------------
|
|
898
|
+
|
|
899
|
+
export default function(pi: ExtensionAPI): void {
|
|
900
|
+
let projectRoot: string | null = null;
|
|
901
|
+
const turnCache = new TurnContextCache();
|
|
902
|
+
const agentType = detectAgentType();
|
|
903
|
+
const isSubAgent = agentType !== null;
|
|
904
|
+
let taskContextCache: TaskContextCache | null = null;
|
|
905
|
+
|
|
906
|
+
// Tracks compaction boundaries — context handler skips scanning when no
|
|
907
|
+
// compaction has occurred since last injection.
|
|
908
|
+
let lastCompactionTs = 0;
|
|
909
|
+
let lastInjectionTs = 0;
|
|
910
|
+
|
|
911
|
+
const rememberContextKey = (ctx?: { sessionManager?: { getSessionId?: () => string | undefined; getSessionFile?: () => string | undefined } }): string | null => {
|
|
912
|
+
const key = deriveContextKey(ctx);
|
|
913
|
+
if (!key) return null;
|
|
914
|
+
return key;
|
|
915
|
+
};
|
|
916
|
+
|
|
917
|
+
const getTaskContext = (taskDir: string, root: string): string => {
|
|
918
|
+
const signature = taskContextSignature(root, taskDir, agentType);
|
|
919
|
+
if (taskContextCache?.projectRoot === root && taskContextCache.taskDir === taskDir && taskContextCache.agentType === agentType && taskContextCache.signature === signature) {
|
|
920
|
+
return taskContextCache.content;
|
|
921
|
+
}
|
|
922
|
+
const content = buildTaskContext(root, taskDir, agentType);
|
|
923
|
+
taskContextCache = { projectRoot: root, taskDir, agentType, signature, content };
|
|
924
|
+
return content;
|
|
925
|
+
};
|
|
926
|
+
|
|
927
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
928
|
+
projectRoot = findProjectRoot(ctx.cwd);
|
|
929
|
+
const contextKey = rememberContextKey(ctx);
|
|
930
|
+
|
|
931
|
+
if (!projectRoot) return;
|
|
932
|
+
|
|
933
|
+
if (isSubAgent) {
|
|
934
|
+
// Sub-agent: inject precise task context once
|
|
935
|
+
const { taskDir } = resolveActiveTaskStatus(projectRoot, contextKey);
|
|
936
|
+
if (taskDir) {
|
|
937
|
+
const taskContext = getTaskContext(taskDir, projectRoot);
|
|
938
|
+
if (taskContext) {
|
|
939
|
+
await pi.sendMessage({
|
|
940
|
+
customType: "trellis-task-context",
|
|
941
|
+
content: taskContext,
|
|
942
|
+
display: false,
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
} else {
|
|
947
|
+
// Main session: inject session context (global map) + task context
|
|
948
|
+
const sessionContext = buildSessionContext(projectRoot, contextKey);
|
|
949
|
+
if (sessionContext) {
|
|
950
|
+
await pi.sendMessage({
|
|
951
|
+
customType: "trellis-session-context",
|
|
952
|
+
content: sessionContext,
|
|
953
|
+
display: false,
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
const { taskDir } = resolveActiveTaskStatus(projectRoot, contextKey);
|
|
958
|
+
if (taskDir) {
|
|
959
|
+
const taskContext = getTaskContext(taskDir, projectRoot);
|
|
960
|
+
if (taskContext) {
|
|
961
|
+
await pi.sendMessage({
|
|
962
|
+
customType: "trellis-task-context",
|
|
963
|
+
content: taskContext,
|
|
964
|
+
display: false,
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
ctx.ui.notify("Trellis workflow system available", "info");
|
|
970
|
+
}
|
|
971
|
+
});
|
|
972
|
+
|
|
973
|
+
pi.on("session_before_compact", async () => {
|
|
974
|
+
lastCompactionTs = Date.now();
|
|
975
|
+
});
|
|
976
|
+
|
|
977
|
+
pi.on("before_agent_start", async (_event, ctx) => {
|
|
978
|
+
if (!projectRoot) {
|
|
979
|
+
projectRoot = findProjectRoot(ctx.cwd);
|
|
980
|
+
}
|
|
981
|
+
if (!projectRoot) return;
|
|
982
|
+
const contextKey = rememberContextKey(ctx);
|
|
983
|
+
|
|
984
|
+
// Persistent injection: workflow state for this turn
|
|
985
|
+
const cached = turnCache.get(projectRoot, contextKey);
|
|
986
|
+
lastInjectionTs = Date.now();
|
|
987
|
+
|
|
988
|
+
// Skip turn: inject nothing (escape hatch)
|
|
989
|
+
if (!cached.workflowMsg) return;
|
|
990
|
+
|
|
991
|
+
return {
|
|
992
|
+
message: {
|
|
993
|
+
customType: "trellis-workflow-state",
|
|
994
|
+
content: cached.workflowMsg,
|
|
995
|
+
display: false,
|
|
996
|
+
},
|
|
997
|
+
};
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
// context fires before EVERY LLM API call (including tool-use continuations
|
|
1001
|
+
// and post-compaction agent.continue() paths). Acts as a safety net when
|
|
1002
|
+
// before_agent_start's persisted message was removed by compaction.
|
|
1003
|
+
pi.on("context", async (event, ctx) => {
|
|
1004
|
+
if (!projectRoot) return;
|
|
1005
|
+
const contextKey = rememberContextKey(ctx);
|
|
1006
|
+
|
|
1007
|
+
const messages = event.messages as { role?: string; customType?: string; content?: string }[];
|
|
1008
|
+
const { taskDir } = resolveActiveTaskStatus(projectRoot, contextKey);
|
|
1009
|
+
const currentTaskContext = taskDir ? getTaskContext(taskDir, projectRoot) : "";
|
|
1010
|
+
const taskContextIndexes = messages
|
|
1011
|
+
.map((message, index) => message.customType === "trellis-task-context" ? index : -1)
|
|
1012
|
+
.filter((index) => index >= 0);
|
|
1013
|
+
const existingTaskContext = taskContextIndexes.length > 0
|
|
1014
|
+
? messages[taskContextIndexes[0]]?.content ?? ""
|
|
1015
|
+
: "";
|
|
1016
|
+
const taskContextChanged = existingTaskContext !== currentTaskContext || taskContextIndexes.length > 1;
|
|
1017
|
+
let projectedMessages = messages;
|
|
1018
|
+
if (taskContextChanged) {
|
|
1019
|
+
const replacement = currentTaskContext
|
|
1020
|
+
? { role: "custom" as const, customType: "trellis-task-context", content: currentTaskContext, display: false, timestamp: Date.now() }
|
|
1021
|
+
: null;
|
|
1022
|
+
let replaced = false;
|
|
1023
|
+
projectedMessages = messages.flatMap((message) => {
|
|
1024
|
+
if (message.customType !== "trellis-task-context") return [message];
|
|
1025
|
+
if (replaced || !replacement) return [];
|
|
1026
|
+
replaced = true;
|
|
1027
|
+
return [replacement];
|
|
1028
|
+
});
|
|
1029
|
+
if (replacement && !replaced) projectedMessages.push(replacement);
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
// Resolve the turn state before the fast path: a skip turn must still
|
|
1033
|
+
// run breadcrumb cleanup even when nothing else changed.
|
|
1034
|
+
const cached = turnCache.get(projectRoot, contextKey);
|
|
1035
|
+
const skipping = !cached.workflowMsg;
|
|
1036
|
+
|
|
1037
|
+
// Fast path: no task change, no compaction, not skipping — all persisted context is current.
|
|
1038
|
+
if (!taskContextChanged && !skipping && lastInjectionTs > lastCompactionTs) return;
|
|
1039
|
+
|
|
1040
|
+
if (skipping) {
|
|
1041
|
+
// Skip turn (escape hatch): drop any persisted breadcrumb from an
|
|
1042
|
+
// earlier turn so the skip actually takes effect.
|
|
1043
|
+
const withoutBreadcrumb = projectedMessages.filter(
|
|
1044
|
+
(message) => !(message.role === "custom" && message.customType === "trellis-workflow-state"),
|
|
1045
|
+
);
|
|
1046
|
+
if (withoutBreadcrumb.length === projectedMessages.length && !taskContextChanged) return;
|
|
1047
|
+
lastInjectionTs = Date.now();
|
|
1048
|
+
return { messages: withoutBreadcrumb };
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
// Post-compaction: reverse-scan to confirm absence before injecting
|
|
1052
|
+
for (let i = projectedMessages.length - 1; i >= 0; i--) {
|
|
1053
|
+
if (projectedMessages[i].role === "custom" && projectedMessages[i].customType === "trellis-workflow-state") {
|
|
1054
|
+
lastInjectionTs = Date.now();
|
|
1055
|
+
return taskContextChanged ? { messages: projectedMessages } : undefined;
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
lastInjectionTs = Date.now();
|
|
1060
|
+
return {
|
|
1061
|
+
messages: [
|
|
1062
|
+
...projectedMessages,
|
|
1063
|
+
{
|
|
1064
|
+
role: "custom" as const,
|
|
1065
|
+
customType: "trellis-workflow-state",
|
|
1066
|
+
content: cached.workflowMsg,
|
|
1067
|
+
display: false,
|
|
1068
|
+
timestamp: Date.now(),
|
|
1069
|
+
},
|
|
1070
|
+
],
|
|
1071
|
+
};
|
|
1072
|
+
});
|
|
1073
|
+
|
|
1074
|
+
// OMP passes Bash event.input through to the tool execution parameters, so
|
|
1075
|
+
// inject the session key through the shell-agnostic env field. An explicit
|
|
1076
|
+
// per-call value wins over the derived key.
|
|
1077
|
+
pi.on("tool_call", (event, ctx) => {
|
|
1078
|
+
if (event.toolName !== "bash") return;
|
|
1079
|
+
const contextKey = rememberContextKey(ctx);
|
|
1080
|
+
if (!contextKey) return;
|
|
1081
|
+
const input = event.input as { env?: Record<string, string> };
|
|
1082
|
+
input.env = {
|
|
1083
|
+
TRELLIS_CONTEXT_ID: contextKey,
|
|
1084
|
+
...input.env,
|
|
1085
|
+
};
|
|
1086
|
+
});
|
|
1087
|
+
|
|
1088
|
+
pi.on("input", async (event, ctx) => {
|
|
1089
|
+
if (!projectRoot) {
|
|
1090
|
+
projectRoot = findProjectRoot(ctx.cwd);
|
|
1091
|
+
}
|
|
1092
|
+
// Resolve projectRoot on first input if session_start missed it
|
|
1093
|
+
if (!projectRoot) return;
|
|
1094
|
+
const contextKey = rememberContextKey(ctx);
|
|
1095
|
+
|
|
1096
|
+
// Check if this turn should skip workflow state injection
|
|
1097
|
+
const skipKeyword = readPromptInjectionSkipKeyword(projectRoot);
|
|
1098
|
+
const skipThisTurn = shouldSkipWorkflowState(event.text ?? "", skipKeyword);
|
|
1099
|
+
|
|
1100
|
+
// Record the turn's skip decision and pre-warm the cache so
|
|
1101
|
+
// before_agent_start and context can use it
|
|
1102
|
+
turnCache.beginTurn(skipThisTurn);
|
|
1103
|
+
turnCache.get(projectRoot, contextKey);
|
|
1104
|
+
});
|
|
1105
|
+
}
|