@blulotus/trellis 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +235 -0
- package/README.md +149 -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 +276 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/commands/channel/adapters/claude.d.ts +29 -0
- package/dist/commands/channel/adapters/claude.d.ts.map +1 -0
- package/dist/commands/channel/adapters/claude.js +203 -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 +86 -0
- package/dist/commands/channel/adapters/index.d.ts.map +1 -0
- package/dist/commands/channel/adapters/index.js +115 -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 +121 -0
- package/dist/commands/channel/kill.js.map +1 -0
- package/dist/commands/channel/list.d.ts +17 -0
- package/dist/commands/channel/list.d.ts.map +1 -0
- package/dist/commands/channel/list.js +233 -0
- package/dist/commands/channel/list.js.map +1 -0
- package/dist/commands/channel/messages.d.ts +15 -0
- package/dist/commands/channel/messages.d.ts.map +1 -0
- package/dist/commands/channel/messages.js +245 -0
- package/dist/commands/channel/messages.js.map +1 -0
- package/dist/commands/channel/rm.d.ts +27 -0
- package/dist/commands/channel/rm.d.ts.map +1 -0
- package/dist/commands/channel/rm.js +216 -0
- package/dist/commands/channel/rm.js.map +1 -0
- package/dist/commands/channel/run.d.ts +30 -0
- package/dist/commands/channel/run.d.ts.map +1 -0
- package/dist/commands/channel/run.js +130 -0
- package/dist/commands/channel/run.js.map +1 -0
- package/dist/commands/channel/send.d.ts +11 -0
- package/dist/commands/channel/send.d.ts.map +1 -0
- package/dist/commands/channel/send.js +24 -0
- package/dist/commands/channel/send.js.map +1 -0
- package/dist/commands/channel/spawn.d.ts +43 -0
- package/dist/commands/channel/spawn.d.ts.map +1 -0
- package/dist/commands/channel/spawn.js +247 -0
- package/dist/commands/channel/spawn.js.map +1 -0
- package/dist/commands/channel/store/events.d.ts +39 -0
- package/dist/commands/channel/store/events.d.ts.map +1 -0
- package/dist/commands/channel/store/events.js +87 -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 +146 -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 +53 -0
- package/dist/commands/channel/supervisor/stdout.d.ts.map +1 -0
- package/dist/commands/channel/supervisor/stdout.js +168 -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 +75 -0
- package/dist/commands/channel/supervisor.d.ts.map +1 -0
- package/dist/commands/channel/supervisor.js +397 -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 +75 -0
- package/dist/commands/channel/wait.js.map +1 -0
- package/dist/commands/init.d.ts +71 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +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 +447 -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 +410 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/update.d.ts +111 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +2152 -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 +52 -0
- package/dist/commands/workflow.d.ts.map +1 -0
- package/dist/commands/workflow.js +454 -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 +26 -0
- package/dist/configurators/dsh.d.ts.map +1 -0
- package/dist/configurators/dsh.js +76 -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 +84 -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 +590 -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 +23 -0
- package/dist/configurators/zcode.d.ts.map +1 -0
- package/dist/configurators/zcode.js +63 -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.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 +115 -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 +95 -0
- package/dist/templates/codebuddy/agents/trellis-check.md +115 -0
- package/dist/templates/codebuddy/agents/trellis-implement.md +115 -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 +568 -0
- package/dist/templates/codex/hooks.json +52 -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 +84 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/hooks-and-settings.md +73 -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 +113 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/skills-and-commands.md +87 -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 +212 -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/tdd.md +142 -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 +564 -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 +173 -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 +114 -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 +107 -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 +80 -0
- package/dist/templates/dsh/agents/trellis-check.md +99 -0
- package/dist/templates/dsh/agents/trellis-implement.md +106 -0
- package/dist/templates/dsh/agents/trellis-research.md +134 -0
- package/dist/templates/dsh/index.d.ts +22 -0
- package/dist/templates/dsh/index.d.ts.map +1 -0
- package/dist/templates/dsh/index.js +27 -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 +107 -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 +106 -0
- package/dist/templates/grok/agents/trellis-implement.md +121 -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 +104 -0
- package/dist/templates/kimi/agents/trellis-implement.md +120 -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 +55 -0
- package/dist/templates/omp/agents/trellis-research.md +30 -0
- package/dist/templates/omp/extensions/trellis/index.ts.txt +592 -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 +123 -0
- package/dist/templates/opencode/agents/trellis-research.md +145 -0
- package/dist/templates/opencode/lib/context-visibility.js +86 -0
- package/dist/templates/opencode/lib/session-utils.js +526 -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-spec-context.js +121 -0
- package/dist/templates/opencode/plugins/inject-subagent-context.js +569 -0
- package/dist/templates/opencode/plugins/inject-workflow-state.js +340 -0
- package/dist/templates/opencode/plugins/session-start.js +74 -0
- package/dist/templates/pi/agents/trellis-check.md +37 -0
- package/dist/templates/pi/agents/trellis-implement.md +52 -0
- package/dist/templates/pi/agents/trellis-research.md +25 -0
- package/dist/templates/pi/extensions/trellis/index.ts.txt +2033 -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 +108 -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 +51 -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 +83 -0
- package/dist/templates/shared-hooks/index.d.ts.map +1 -0
- package/dist/templates/shared-hooks/index.js +152 -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-spec-context.py +844 -0
- package/dist/templates/shared-hooks/inject-subagent-context.py +1175 -0
- package/dist/templates/shared-hooks/inject-workflow-state.py +501 -0
- package/dist/templates/shared-hooks/session-start.py +969 -0
- package/dist/templates/snow/SNOW.md +86 -0
- package/dist/templates/snow/agents/trellis-check.md +136 -0
- package/dist/templates/snow/agents/trellis-implement.md +158 -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 +108 -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 +81 -0
- package/dist/templates/trellis/config.yaml +205 -0
- package/dist/templates/trellis/gitattributes.txt +9 -0
- package/dist/templates/trellis/gitignore.txt +32 -0
- package/dist/templates/trellis/index.d.ts +70 -0
- package/dist/templates/trellis/index.d.ts.map +1 -0
- package/dist/templates/trellis/index.js +127 -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 +681 -0
- package/dist/templates/trellis/scripts/common/__init__.py +92 -0
- package/dist/templates/trellis/scripts/common/active_task.py +787 -0
- package/dist/templates/trellis/scripts/common/cli_adapter.py +982 -0
- package/dist/templates/trellis/scripts/common/config.py +587 -0
- package/dist/templates/trellis/scripts/common/developer.py +190 -0
- package/dist/templates/trellis/scripts/common/git.py +74 -0
- package/dist/templates/trellis/scripts/common/git_context.py +138 -0
- package/dist/templates/trellis/scripts/common/io.py +61 -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 +508 -0
- package/dist/templates/trellis/scripts/common/safe_commit.py +315 -0
- package/dist/templates/trellis/scripts/common/session_context.py +892 -0
- package/dist/templates/trellis/scripts/common/spec_inject.py +439 -0
- package/dist/templates/trellis/scripts/common/spec_match.py +395 -0
- package/dist/templates/trellis/scripts/common/task_context.py +372 -0
- package/dist/templates/trellis/scripts/common/task_queue.py +188 -0
- package/dist/templates/trellis/scripts/common/task_store.py +1016 -0
- package/dist/templates/trellis/scripts/common/task_utils.py +309 -0
- package/dist/templates/trellis/scripts/common/tasks.py +112 -0
- package/dist/templates/trellis/scripts/common/trellis_config.py +132 -0
- package/dist/templates/trellis/scripts/common/types.py +110 -0
- package/dist/templates/trellis/scripts/common/workflow_phase.py +243 -0
- package/dist/templates/trellis/scripts/common/workflow_selection.py +177 -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 +694 -0
- package/dist/templates/trellis/tasks/.gitkeep +0 -0
- package/dist/templates/trellis/workflow.md +777 -0
- package/dist/templates/zcode/agents/trellis-check.md +110 -0
- package/dist/templates/zcode/agents/trellis-implement.md +116 -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 +95 -0
- package/dist/types/ai-tools.d.ts.map +1 -0
- package/dist/types/ai-tools.js +509 -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/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/manifest-prune.d.ts +61 -0
- package/dist/utils/manifest-prune.d.ts.map +1 -0
- package/dist/utils/manifest-prune.js +136 -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 +123 -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 +67 -0
- package/dist/utils/uninstall-scrubbers.d.ts.map +1 -0
- package/dist/utils/uninstall-scrubbers.js +361 -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,2033 @@
|
|
|
1
|
+
import { existsSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
2
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
3
|
+
import {
|
|
4
|
+
delimiter,
|
|
5
|
+
dirname,
|
|
6
|
+
isAbsolute,
|
|
7
|
+
join,
|
|
8
|
+
relative,
|
|
9
|
+
resolve,
|
|
10
|
+
} from "node:path";
|
|
11
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
12
|
+
import { isUtf8 } from "node:buffer";
|
|
13
|
+
|
|
14
|
+
// ── Types ──────────────────────────────────────────────────────────────
|
|
15
|
+
type JsonObject = Record<string, unknown>;
|
|
16
|
+
type TextContent = { type: "text"; text: string };
|
|
17
|
+
interface PiToolResult {
|
|
18
|
+
content: TextContent[];
|
|
19
|
+
details?: unknown;
|
|
20
|
+
}
|
|
21
|
+
interface PiExtensionContext {
|
|
22
|
+
hasUI?: boolean;
|
|
23
|
+
model?: {
|
|
24
|
+
provider?: string;
|
|
25
|
+
id?: string;
|
|
26
|
+
};
|
|
27
|
+
sessionManager?: {
|
|
28
|
+
getSessionId?: () => string;
|
|
29
|
+
getSessionFile?: () => string | undefined;
|
|
30
|
+
};
|
|
31
|
+
ui?: {
|
|
32
|
+
notify?: (msg: string, type?: "info" | "warning" | "error") => void;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
interface SubagentInput {
|
|
36
|
+
agent?: string;
|
|
37
|
+
prompt?: string;
|
|
38
|
+
mode?: "single" | "parallel" | "chain";
|
|
39
|
+
prompts?: string[];
|
|
40
|
+
model?: string;
|
|
41
|
+
thinking?: string;
|
|
42
|
+
}
|
|
43
|
+
interface AgentConfig {
|
|
44
|
+
model?: string;
|
|
45
|
+
thinking?: string;
|
|
46
|
+
tools?: string[];
|
|
47
|
+
fallbackModels: string[];
|
|
48
|
+
}
|
|
49
|
+
interface PiRunConfig {
|
|
50
|
+
model?: string;
|
|
51
|
+
thinking?: string;
|
|
52
|
+
tools?: string[];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ── Lazy-load pi-tui (avoid failing top-level imports) ─────────────────
|
|
56
|
+
let _piTui: {
|
|
57
|
+
visibleWidth?: (s: string) => number;
|
|
58
|
+
truncateToWidth?: (s: string, w: number, ellipsis?: string) => string;
|
|
59
|
+
} | null = null;
|
|
60
|
+
function piTui() {
|
|
61
|
+
if (!_piTui) {
|
|
62
|
+
try {
|
|
63
|
+
_piTui = require("@earendil-works/pi-tui");
|
|
64
|
+
} catch {
|
|
65
|
+
_piTui = {};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return _piTui;
|
|
69
|
+
}
|
|
70
|
+
function trunc(s: string, w: number) {
|
|
71
|
+
const t = piTui();
|
|
72
|
+
return t.truncateToWidth
|
|
73
|
+
? t.truncateToWidth(s, w, "…")
|
|
74
|
+
: s.length <= w
|
|
75
|
+
? s
|
|
76
|
+
: w > 1
|
|
77
|
+
? s.slice(0, w - 1) + "…"
|
|
78
|
+
: s.slice(0, w);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ── Constants ─────────────────────────────────────────────────────────
|
|
82
|
+
const TRELLIS_AGENT_JSONL: Record<string, string> = {
|
|
83
|
+
"trellis-implement": "implement.jsonl",
|
|
84
|
+
implement: "implement.jsonl",
|
|
85
|
+
"trellis-check": "check.jsonl",
|
|
86
|
+
check: "check.jsonl",
|
|
87
|
+
};
|
|
88
|
+
const MAX_STDOUT = 8 * 1024 * 1024;
|
|
89
|
+
const MAX_STDERR = 1024 * 1024;
|
|
90
|
+
const MAX_TAIL = 256 * 1024;
|
|
91
|
+
const MAX_LINE_BUFFER = 1024 * 1024;
|
|
92
|
+
const MAX_TOOL_ARG_CHARS = 2048;
|
|
93
|
+
const MAX_TOOLS = 256;
|
|
94
|
+
const MAX_PARALLEL_PROMPTS = 6;
|
|
95
|
+
const ABORT_KILL_GRACE_MS = 1500;
|
|
96
|
+
const SESSION_OVERVIEW_TIMEOUT_MS = 1500;
|
|
97
|
+
const THROTTLE_MS = 500;
|
|
98
|
+
const FIRST_REPLY_NOTICE = `<first-reply-notice>
|
|
99
|
+
On the first visible assistant reply in this session, briefly acknowledge that Trellis SessionStart context loaded.
|
|
100
|
+
Choose the acknowledgment language in this order:
|
|
101
|
+
1. Use the language of the user's current request (the user message that triggered this reply).
|
|
102
|
+
2. If that request has no clear natural language, use an explicitly established project communication language.
|
|
103
|
+
3. If neither provides a language, output the language-neutral fallback exactly: \`Trellis SessionStart ✓\`.
|
|
104
|
+
Continue directly with the user's request after the acknowledgment.
|
|
105
|
+
The acknowledgment must not alter the language used for the remainder of the response.
|
|
106
|
+
This notice is one-shot: do not repeat it after the first visible assistant reply in this session.
|
|
107
|
+
</first-reply-notice>`;
|
|
108
|
+
|
|
109
|
+
// ── State types ───────────────────────────────────────────────────────
|
|
110
|
+
type RunStatus = "pending" | "running" | "succeeded" | "failed" | "cancelled";
|
|
111
|
+
type ToolStatus = "running" | "succeeded" | "failed";
|
|
112
|
+
|
|
113
|
+
interface Usage {
|
|
114
|
+
input: number;
|
|
115
|
+
output: number;
|
|
116
|
+
cacheRead: number;
|
|
117
|
+
cacheWrite: number;
|
|
118
|
+
cost: number;
|
|
119
|
+
ctxTokens: number;
|
|
120
|
+
turns: number;
|
|
121
|
+
}
|
|
122
|
+
interface ToolTrace {
|
|
123
|
+
id: string;
|
|
124
|
+
name: string;
|
|
125
|
+
args: string;
|
|
126
|
+
status: ToolStatus;
|
|
127
|
+
startedAt: number;
|
|
128
|
+
finishedAt?: number;
|
|
129
|
+
}
|
|
130
|
+
interface RunState {
|
|
131
|
+
id: string;
|
|
132
|
+
agent: string;
|
|
133
|
+
prompt: string;
|
|
134
|
+
step?: number;
|
|
135
|
+
status: RunStatus;
|
|
136
|
+
startedAt?: number;
|
|
137
|
+
finishedAt?: number;
|
|
138
|
+
finalText: string;
|
|
139
|
+
textTail: string;
|
|
140
|
+
thinkingTail: string;
|
|
141
|
+
stderrTail: string;
|
|
142
|
+
tools: ToolTrace[];
|
|
143
|
+
usage: Usage;
|
|
144
|
+
model?: string;
|
|
145
|
+
thinking?: string;
|
|
146
|
+
errorMessage?: string;
|
|
147
|
+
}
|
|
148
|
+
interface ProgressDetails {
|
|
149
|
+
kind: "trellis-subagent-progress";
|
|
150
|
+
agent: string;
|
|
151
|
+
mode: "single" | "parallel" | "chain";
|
|
152
|
+
startedAt: number;
|
|
153
|
+
updatedAt: number;
|
|
154
|
+
final: boolean;
|
|
155
|
+
runs: RunState[];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ── Native partial-update card state ──────────────────────────────────
|
|
159
|
+
interface NativeCardHandle {
|
|
160
|
+
state: JsonObject;
|
|
161
|
+
invalidate: () => void;
|
|
162
|
+
updatedAt: number;
|
|
163
|
+
}
|
|
164
|
+
const MAX_NATIVE_CARDS = 20;
|
|
165
|
+
const nativeCards = new Map<string, NativeCardHandle>();
|
|
166
|
+
let activeSubagentToolCallId: string | null = null;
|
|
167
|
+
function rememberNativeCard(id: string, card: NativeCardHandle) {
|
|
168
|
+
nativeCards.set(id, card);
|
|
169
|
+
const active = activeSubagentToolCallId
|
|
170
|
+
? nativeCards.get(activeSubagentToolCallId)
|
|
171
|
+
: undefined;
|
|
172
|
+
if (!active || card.updatedAt >= active.updatedAt)
|
|
173
|
+
activeSubagentToolCallId = id;
|
|
174
|
+
for (const key of nativeCards.keys()) {
|
|
175
|
+
if (nativeCards.size <= MAX_NATIVE_CARDS) break;
|
|
176
|
+
if (key !== activeSubagentToolCallId) nativeCards.delete(key);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function totalUsage(d: ProgressDetails): Usage {
|
|
180
|
+
const u: Usage = {
|
|
181
|
+
input: 0,
|
|
182
|
+
output: 0,
|
|
183
|
+
cacheRead: 0,
|
|
184
|
+
cacheWrite: 0,
|
|
185
|
+
cost: 0,
|
|
186
|
+
ctxTokens: 0,
|
|
187
|
+
turns: 0,
|
|
188
|
+
};
|
|
189
|
+
for (const r of d.runs) {
|
|
190
|
+
u.input += r.usage.input;
|
|
191
|
+
u.output += r.usage.output;
|
|
192
|
+
u.cacheRead += r.usage.cacheRead;
|
|
193
|
+
u.cacheWrite += r.usage.cacheWrite;
|
|
194
|
+
u.cost += r.usage.cost;
|
|
195
|
+
u.ctxTokens = Math.max(u.ctxTokens, r.usage.ctxTokens);
|
|
196
|
+
u.turns += r.usage.turns;
|
|
197
|
+
}
|
|
198
|
+
return u;
|
|
199
|
+
}
|
|
200
|
+
function activeRun(d: ProgressDetails) {
|
|
201
|
+
return d.runs.find((r) => r.status === "running") ?? d.runs.at(-1);
|
|
202
|
+
}
|
|
203
|
+
function toolArgs(t: ToolTrace) {
|
|
204
|
+
try {
|
|
205
|
+
return JSON.parse(t.args) as Record<string, unknown>;
|
|
206
|
+
} catch {
|
|
207
|
+
return {};
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function bashCommand(t: ToolTrace) {
|
|
211
|
+
const a = toolArgs(t);
|
|
212
|
+
return String(a.command || "").toLowerCase();
|
|
213
|
+
}
|
|
214
|
+
function isSearchTool(t: ToolTrace) {
|
|
215
|
+
return t.name === "read" || t.name === "grep" || t.name === "find";
|
|
216
|
+
}
|
|
217
|
+
function isMutationTool(t: ToolTrace) {
|
|
218
|
+
return t.name === "edit" || t.name === "write";
|
|
219
|
+
}
|
|
220
|
+
function isValidationCommand(t: ToolTrace) {
|
|
221
|
+
const c = bashCommand(t);
|
|
222
|
+
return /\b(test|typecheck|lint|build|gofmt|go test|npm run|pnpm|vitest|tsc)\b/.test(
|
|
223
|
+
c,
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
function isInspectionCommand(t: ToolTrace) {
|
|
227
|
+
const c = bashCommand(t);
|
|
228
|
+
return /\b(rg|grep|find|git diff|git status|ls|tree)\b/.test(c);
|
|
229
|
+
}
|
|
230
|
+
function thinkingIntent(text: string) {
|
|
231
|
+
const s = text.toLowerCase();
|
|
232
|
+
if (/error|failed|failure|panic|exception|报错|失败|错误|异常/.test(s))
|
|
233
|
+
return "Analyzing failure cause";
|
|
234
|
+
if (/test|verify|check|typecheck|lint|验证|测试|检查/.test(s))
|
|
235
|
+
return "Planning verification steps";
|
|
236
|
+
if (/plan|approach|design|strategy|方案|计划|思路|设计/.test(s))
|
|
237
|
+
return "Structuring the implementation approach";
|
|
238
|
+
if (/implement|change|edit|modify|refactor|实现|修改|重构/.test(s))
|
|
239
|
+
return "Reasoning through code changes";
|
|
240
|
+
if (/inspect|search|locate|read|context|定位|搜索|阅读|上下文/.test(s))
|
|
241
|
+
return "Locating relevant context";
|
|
242
|
+
return "";
|
|
243
|
+
}
|
|
244
|
+
function behaviorSummary(r: RunState) {
|
|
245
|
+
if (r.status === "succeeded") return "Task completed and result returned";
|
|
246
|
+
if (r.status === "failed")
|
|
247
|
+
return "Task failed and error details were retained";
|
|
248
|
+
|
|
249
|
+
const runningTool = r.tools.findLast((t) => t.status === "running");
|
|
250
|
+
if (runningTool) {
|
|
251
|
+
if (isMutationTool(runningTool)) return "Applying the plan to code";
|
|
252
|
+
if (runningTool.name === "bash" && isValidationCommand(runningTool))
|
|
253
|
+
return "Verifying whether the implementation passes";
|
|
254
|
+
if (runningTool.name === "bash" && isInspectionCommand(runningTool))
|
|
255
|
+
return "Inspecting current code state";
|
|
256
|
+
if (isSearchTool(runningTool)) return "Locating relevant code and context";
|
|
257
|
+
if (runningTool.name === "bash")
|
|
258
|
+
return "Validating assumptions with commands";
|
|
259
|
+
return "Using tools to advance the task";
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const recent = r.tools.slice(-5);
|
|
263
|
+
if (recent.some((t) => t.status === "failed"))
|
|
264
|
+
return "Investigating tool or command failure";
|
|
265
|
+
if (recent.some(isMutationTool)) return "Reviewing recent changes";
|
|
266
|
+
if (recent.some((t) => t.name === "bash" && isValidationCommand(t)))
|
|
267
|
+
return "Analyzing verification results";
|
|
268
|
+
if (
|
|
269
|
+
recent.length >= 2 &&
|
|
270
|
+
recent.every(
|
|
271
|
+
(t) => isSearchTool(t) || (t.name === "bash" && isInspectionCommand(t)),
|
|
272
|
+
)
|
|
273
|
+
)
|
|
274
|
+
return "Mapping code structure and impact";
|
|
275
|
+
|
|
276
|
+
const intent = thinkingIntent(`${r.thinkingTail}\n${r.textTail}`);
|
|
277
|
+
if (intent) return intent;
|
|
278
|
+
if (!r.tools.length) return "Understanding the task and planning execution";
|
|
279
|
+
return "Advancing the task and preparing next steps";
|
|
280
|
+
}
|
|
281
|
+
function progressState(d: ProgressDetails) {
|
|
282
|
+
const running = d.runs.filter((r) => r.status === "running").length;
|
|
283
|
+
const failed = d.runs.some((r) => r.status === "failed");
|
|
284
|
+
return failed
|
|
285
|
+
? "failed"
|
|
286
|
+
: d.final
|
|
287
|
+
? "completed"
|
|
288
|
+
: running
|
|
289
|
+
? `${running} running`
|
|
290
|
+
: "pending";
|
|
291
|
+
}
|
|
292
|
+
function progressDone(d: ProgressDetails) {
|
|
293
|
+
return d.runs.filter((r) => r.status !== "pending" && r.status !== "running")
|
|
294
|
+
.length;
|
|
295
|
+
}
|
|
296
|
+
function summaryText(text: string) {
|
|
297
|
+
return `${text.trim().replace(/[。.!?…]+$/u, "")}...`;
|
|
298
|
+
}
|
|
299
|
+
function splitModelThinking(model?: string, fallbackThinking?: string) {
|
|
300
|
+
const m = model?.match(/^(.*):(off|minimal|low|medium|high|xhigh|max)$/i);
|
|
301
|
+
return {
|
|
302
|
+
model: m ? m[1] : model,
|
|
303
|
+
thinking: (m?.[2] ?? fallbackThinking)?.toLowerCase(),
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
function modelLabel(r: RunState) {
|
|
307
|
+
const { model, thinking } = splitModelThinking(r.model, r.thinking);
|
|
308
|
+
if (!model) return undefined;
|
|
309
|
+
return thinking && thinking !== "off" ? `${model}(${thinking})` : model;
|
|
310
|
+
}
|
|
311
|
+
function applyRunConfig(r: RunState, cfg: PiRunConfig) {
|
|
312
|
+
const parsed = splitModelThinking(cfg.model, cfg.thinking);
|
|
313
|
+
r.model = parsed.model;
|
|
314
|
+
r.thinking = parsed.thinking;
|
|
315
|
+
}
|
|
316
|
+
function runElapsed(d: ProgressDetails, r: RunState) {
|
|
317
|
+
const start = r.startedAt ?? d.startedAt;
|
|
318
|
+
const end =
|
|
319
|
+
r.finishedAt ?? (r.status === "running" ? Date.now() : d.updatedAt);
|
|
320
|
+
return fmtDur(Math.max(0, end - start));
|
|
321
|
+
}
|
|
322
|
+
function runHeader(d: ProgressDetails, r: RunState) {
|
|
323
|
+
const usage = fmtUsage(r.usage, modelLabel(r)) || fmtUsage(totalUsage(d));
|
|
324
|
+
return `${r.agent} · ${progressDone(d)}/${d.runs.length} done · ${progressState(d)} · ${runElapsed(d, r)}${usage ? ` · ${usage}` : ""}`;
|
|
325
|
+
}
|
|
326
|
+
function renderRunBlock(
|
|
327
|
+
lines: string[],
|
|
328
|
+
d: ProgressDetails,
|
|
329
|
+
run: RunState,
|
|
330
|
+
expanded: boolean,
|
|
331
|
+
) {
|
|
332
|
+
const step = run.step ? `step ${run.step} · ` : "";
|
|
333
|
+
lines.push(` - ${step}${runHeader(d, run)}`);
|
|
334
|
+
const summary = behaviorSummary(run);
|
|
335
|
+
if (summary) lines.push(` › ${summaryText(summary)}`);
|
|
336
|
+
const visibleTools = expanded ? run.tools.slice(-8) : run.tools.slice(-1);
|
|
337
|
+
for (const t of visibleTools)
|
|
338
|
+
lines.push(` ${toolIcon(t.status)} ${toolBrief(t)}`);
|
|
339
|
+
if (expanded && run.errorMessage) {
|
|
340
|
+
lines.push(` ✗ ${oneLine(run.errorMessage, 120)}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
function renderProgressCard(
|
|
344
|
+
d: ProgressDetails,
|
|
345
|
+
expanded: boolean,
|
|
346
|
+
w: number,
|
|
347
|
+
): string[] {
|
|
348
|
+
const r = activeRun(d);
|
|
349
|
+
if (!r) return [];
|
|
350
|
+
const spinner = ["◐", "◓", "◑", "◒"][Math.floor(Date.now() / 250) % 4]!;
|
|
351
|
+
const icon = d.final
|
|
352
|
+
? d.runs.some((x) => x.status === "failed")
|
|
353
|
+
? "✗"
|
|
354
|
+
: "✓"
|
|
355
|
+
: spinner;
|
|
356
|
+
const totalElapsed = fmtDur(
|
|
357
|
+
(d.final ? d.updatedAt : Date.now()) - d.startedAt,
|
|
358
|
+
);
|
|
359
|
+
const lines: string[] = [
|
|
360
|
+
`${icon} subagent ${d.mode} · total ${totalElapsed}`,
|
|
361
|
+
];
|
|
362
|
+
|
|
363
|
+
if (!expanded) {
|
|
364
|
+
renderRunBlock(lines, d, r, false);
|
|
365
|
+
lines.push(" Alt+O expand latest subagent card");
|
|
366
|
+
return lines.map((l) => trunc(l, w));
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
for (const run of d.runs) renderRunBlock(lines, d, run, true);
|
|
370
|
+
lines.push(" Alt+O collapse latest subagent card");
|
|
371
|
+
const max = 48;
|
|
372
|
+
const shown =
|
|
373
|
+
lines.length > max
|
|
374
|
+
? [
|
|
375
|
+
...lines.slice(0, max - 1),
|
|
376
|
+
` … ${lines.length - max + 1} lines hidden`,
|
|
377
|
+
]
|
|
378
|
+
: lines;
|
|
379
|
+
return shown.map((l) => trunc(l, w));
|
|
380
|
+
}
|
|
381
|
+
function progressKey(d: ProgressDetails) {
|
|
382
|
+
return d.runs
|
|
383
|
+
.map((r) => {
|
|
384
|
+
const t = r.tools.at(-1);
|
|
385
|
+
return [
|
|
386
|
+
r.id,
|
|
387
|
+
r.status,
|
|
388
|
+
r.tools.length,
|
|
389
|
+
t?.id ?? "",
|
|
390
|
+
t?.status ?? "",
|
|
391
|
+
r.usage.turns,
|
|
392
|
+
r.usage.input,
|
|
393
|
+
r.usage.output,
|
|
394
|
+
r.usage.cacheRead,
|
|
395
|
+
r.usage.cacheWrite,
|
|
396
|
+
r.usage.ctxTokens,
|
|
397
|
+
r.model ?? "",
|
|
398
|
+
r.thinking ?? "",
|
|
399
|
+
r.errorMessage ?? "",
|
|
400
|
+
].join("~");
|
|
401
|
+
})
|
|
402
|
+
.join("|");
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// ── Utilities ─────────────────────────────────────────────────────────
|
|
406
|
+
function isObj(v: unknown): v is JsonObject {
|
|
407
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
408
|
+
}
|
|
409
|
+
function str(v: unknown): string | null {
|
|
410
|
+
return typeof v === "string" && v.trim() ? v.trim() : null;
|
|
411
|
+
}
|
|
412
|
+
function num(v: unknown): number {
|
|
413
|
+
return typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
414
|
+
}
|
|
415
|
+
function hash(s: string) {
|
|
416
|
+
return createHash("sha256").update(s).digest("hex").slice(0, 24);
|
|
417
|
+
}
|
|
418
|
+
function readText(p: string) {
|
|
419
|
+
try {
|
|
420
|
+
return readFileSync(p, "utf-8");
|
|
421
|
+
} catch {
|
|
422
|
+
return "";
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
function exists(p: string) {
|
|
426
|
+
try {
|
|
427
|
+
return statSync(p).isFile();
|
|
428
|
+
} catch {
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
function shellQuote(v: string) {
|
|
433
|
+
return `'${v.replace(/'/g, `'\\''`)}'`;
|
|
434
|
+
}
|
|
435
|
+
function callStr(
|
|
436
|
+
cb: (() => string | undefined) | undefined,
|
|
437
|
+
receiver?: unknown,
|
|
438
|
+
): string | null {
|
|
439
|
+
if (!cb) return null;
|
|
440
|
+
try {
|
|
441
|
+
return str(cb.call(receiver));
|
|
442
|
+
} catch {
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
function lookupStr(data: unknown, keys: string[]): string | null {
|
|
447
|
+
if (!isObj(data)) return null;
|
|
448
|
+
for (const k of keys) {
|
|
449
|
+
const v = str(data[k]);
|
|
450
|
+
if (v) return v;
|
|
451
|
+
}
|
|
452
|
+
for (const nk of [
|
|
453
|
+
"input",
|
|
454
|
+
"properties",
|
|
455
|
+
"event",
|
|
456
|
+
"hook_input",
|
|
457
|
+
"hookInput",
|
|
458
|
+
]) {
|
|
459
|
+
const nested = data[nk];
|
|
460
|
+
const v = lookupStr(nested, keys);
|
|
461
|
+
if (v) return v;
|
|
462
|
+
}
|
|
463
|
+
return null;
|
|
464
|
+
}
|
|
465
|
+
function cmdHasTrellisCtx(cmd: string) {
|
|
466
|
+
const t = cmd.trimStart();
|
|
467
|
+
return (
|
|
468
|
+
/^export\s+TRELLIS_CONTEXT_ID=/.test(t) ||
|
|
469
|
+
/^TRELLIS_CONTEXT_ID=/.test(t) ||
|
|
470
|
+
/^env\s+.*TRELLIS_CONTEXT_ID=/.test(t)
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
function fmtDur(ms: number) {
|
|
474
|
+
if (ms < 1000) return `${ms}ms`;
|
|
475
|
+
const s = Math.floor(ms / 1000);
|
|
476
|
+
if (s < 60) return `${s}s`;
|
|
477
|
+
return `${Math.floor(s / 60)}m${s % 60}s`;
|
|
478
|
+
}
|
|
479
|
+
function fmtNum(n: number) {
|
|
480
|
+
if (!n) return "0";
|
|
481
|
+
if (Math.abs(n) < 1000) return `${n}`;
|
|
482
|
+
if (Math.abs(n) < 1000000) return `${(n / 1000).toFixed(1)}k`;
|
|
483
|
+
return `${(n / 1000000).toFixed(1)}m`;
|
|
484
|
+
}
|
|
485
|
+
function fmtUsage(u: Usage, m?: string) {
|
|
486
|
+
const p: string[] = [];
|
|
487
|
+
if (u.turns) p.push(`${u.turns}t`);
|
|
488
|
+
if (u.input) p.push(`↑${fmtNum(u.input)}`);
|
|
489
|
+
if (u.output) p.push(`↓${fmtNum(u.output)}`);
|
|
490
|
+
if (u.cost) p.push(`$${u.cost.toFixed(3)}`);
|
|
491
|
+
if (u.ctxTokens) p.push(`ctx:${fmtNum(u.ctxTokens)}`);
|
|
492
|
+
if (m) p.push(m);
|
|
493
|
+
return p.join(" ");
|
|
494
|
+
}
|
|
495
|
+
function statusIcon(s: RunStatus) {
|
|
496
|
+
return s === "pending"
|
|
497
|
+
? "○"
|
|
498
|
+
: s === "running"
|
|
499
|
+
? "●"
|
|
500
|
+
: s === "succeeded"
|
|
501
|
+
? "✓"
|
|
502
|
+
: s === "failed"
|
|
503
|
+
? "✗"
|
|
504
|
+
: "⊘";
|
|
505
|
+
}
|
|
506
|
+
function toolIcon(s: ToolStatus) {
|
|
507
|
+
return s === "running" ? "•" : s === "succeeded" ? "✓" : "✗";
|
|
508
|
+
}
|
|
509
|
+
function latest(text: string, n: number) {
|
|
510
|
+
return text
|
|
511
|
+
.split(/\r?\n/)
|
|
512
|
+
.map((l) => l.trimEnd())
|
|
513
|
+
.filter((l) => l.trim())
|
|
514
|
+
.slice(-n);
|
|
515
|
+
}
|
|
516
|
+
function appendTail(cur: string, next: string, max: number) {
|
|
517
|
+
if (!next) return cur;
|
|
518
|
+
const c = cur + next;
|
|
519
|
+
return c.length <= max ? c : c.slice(-max);
|
|
520
|
+
}
|
|
521
|
+
function extractText(content: unknown): string {
|
|
522
|
+
if (typeof content === "string") return content;
|
|
523
|
+
if (!Array.isArray(content)) return "";
|
|
524
|
+
return content
|
|
525
|
+
.map((b) =>
|
|
526
|
+
isObj(b) && b.type === "text" && typeof b.text === "string" ? b.text : "",
|
|
527
|
+
)
|
|
528
|
+
.join("");
|
|
529
|
+
}
|
|
530
|
+
function extractThinking(content: unknown): string {
|
|
531
|
+
if (!Array.isArray(content)) return "";
|
|
532
|
+
return content
|
|
533
|
+
.map((b) =>
|
|
534
|
+
isObj(b) && b.type === "thinking" && typeof b.thinking === "string"
|
|
535
|
+
? b.thinking
|
|
536
|
+
: "",
|
|
537
|
+
)
|
|
538
|
+
.join("\n");
|
|
539
|
+
}
|
|
540
|
+
function newUsage(): Usage {
|
|
541
|
+
return {
|
|
542
|
+
input: 0,
|
|
543
|
+
output: 0,
|
|
544
|
+
cacheRead: 0,
|
|
545
|
+
cacheWrite: 0,
|
|
546
|
+
cost: 0,
|
|
547
|
+
ctxTokens: 0,
|
|
548
|
+
turns: 0,
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
function newRun(
|
|
552
|
+
id: string,
|
|
553
|
+
agent: string,
|
|
554
|
+
prompt: string,
|
|
555
|
+
step?: number,
|
|
556
|
+
): RunState {
|
|
557
|
+
return {
|
|
558
|
+
id,
|
|
559
|
+
agent,
|
|
560
|
+
prompt: trunc(prompt.replace(/\s+/g, " ").trim(), 120) || "(empty)",
|
|
561
|
+
step,
|
|
562
|
+
status: "pending",
|
|
563
|
+
finalText: "",
|
|
564
|
+
textTail: "",
|
|
565
|
+
thinkingTail: "",
|
|
566
|
+
stderrTail: "",
|
|
567
|
+
tools: [],
|
|
568
|
+
usage: newUsage(),
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
function cloneProgress(d: ProgressDetails): ProgressDetails {
|
|
572
|
+
return {
|
|
573
|
+
...d,
|
|
574
|
+
runs: d.runs.map((r) => ({
|
|
575
|
+
...r,
|
|
576
|
+
tools: r.tools.map((t) => ({ ...t })),
|
|
577
|
+
usage: { ...r.usage },
|
|
578
|
+
})),
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function oneLine(v: unknown, max = 80) {
|
|
583
|
+
return String(v || "...")
|
|
584
|
+
.replace(/\s+/g, " ")
|
|
585
|
+
.trim()
|
|
586
|
+
.slice(0, max);
|
|
587
|
+
}
|
|
588
|
+
function summarizeToolArgs(name: string, args: unknown): string {
|
|
589
|
+
const a = isObj(args) ? args : {};
|
|
590
|
+
const summary: JsonObject = {};
|
|
591
|
+
if ("path" in a) summary.path = oneLine(a.path, 240);
|
|
592
|
+
if ("file_path" in a) summary.file_path = oneLine(a.file_path, 240);
|
|
593
|
+
if ("command" in a) summary.command = oneLine(a.command, 240);
|
|
594
|
+
if ("pattern" in a) summary.pattern = oneLine(a.pattern, 120);
|
|
595
|
+
if ("limit" in a) summary.limit = a.limit;
|
|
596
|
+
if ("offset" in a) summary.offset = a.offset;
|
|
597
|
+
if (name === "edit" && Array.isArray(a.edits))
|
|
598
|
+
summary.edits = `${a.edits.length} edit(s)`;
|
|
599
|
+
if (name === "write" && "content" in a)
|
|
600
|
+
summary.content = `<${String(a.content ?? "").length} chars>`;
|
|
601
|
+
const json = JSON.stringify(
|
|
602
|
+
Object.keys(summary).length ? summary : { tool: name },
|
|
603
|
+
);
|
|
604
|
+
return json.length <= MAX_TOOL_ARG_CHARS
|
|
605
|
+
? json
|
|
606
|
+
: json.slice(0, MAX_TOOL_ARG_CHARS);
|
|
607
|
+
}
|
|
608
|
+
function toolBrief(t: ToolTrace): string {
|
|
609
|
+
const a = toolArgs(t);
|
|
610
|
+
if (t.name === "read") return `read: ${oneLine(a.path || a.file_path, 80)}`;
|
|
611
|
+
if (t.name === "bash") return `bash: ${oneLine(a.command, 60)}`;
|
|
612
|
+
if (t.name === "write") return `write: ${oneLine(a.path || a.file_path, 80)}`;
|
|
613
|
+
if (t.name === "edit") return `edit: ${oneLine(a.path || a.file_path, 80)}`;
|
|
614
|
+
if (t.name === "grep") return `grep: ${oneLine(a.pattern, 50)}`;
|
|
615
|
+
if (t.name === "find") return `find: ${oneLine(a.pattern || "*", 50)}`;
|
|
616
|
+
return oneLine(t.name, 50);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// ── Pi CLI path resolution ────────────────────────────────────────────
|
|
620
|
+
const PI_CLI_SEGMENTS = [
|
|
621
|
+
["node_modules", "@earendil-works", "pi-coding-agent", "dist", "cli.js"],
|
|
622
|
+
["node_modules", "@mariozechner", "pi-coding-agent", "dist", "cli.js"],
|
|
623
|
+
];
|
|
624
|
+
|
|
625
|
+
function resolvePiCli(): { command: string; args: string[] } {
|
|
626
|
+
const envCli = str(process.env.TRELLIS_PI_CLI_JS);
|
|
627
|
+
if (envCli) {
|
|
628
|
+
const p = resolve(envCli);
|
|
629
|
+
if (!exists(p)) throw new Error(`TRELLIS_PI_CLI_JS missing: ${p}`);
|
|
630
|
+
return { command: process.execPath, args: [p] };
|
|
631
|
+
}
|
|
632
|
+
const candidates: string[] = [];
|
|
633
|
+
for (const arg of process.argv)
|
|
634
|
+
if (/pi-coding-agent[\\/]dist[\\/]cli\.js$/i.test(arg))
|
|
635
|
+
candidates.push(resolve(arg));
|
|
636
|
+
const prefix =
|
|
637
|
+
str(process.env.npm_config_prefix) ?? str(process.env.NPM_CONFIG_PREFIX);
|
|
638
|
+
const appData = str(process.env.APPDATA);
|
|
639
|
+
const pathVal = process.env.PATH ?? process.env.Path ?? "";
|
|
640
|
+
const addBase = (base: string) => {
|
|
641
|
+
for (const seg of PI_CLI_SEGMENTS) candidates.push(join(base, ...seg));
|
|
642
|
+
};
|
|
643
|
+
if (prefix) {
|
|
644
|
+
addBase(prefix);
|
|
645
|
+
addBase(join(prefix, "lib"));
|
|
646
|
+
}
|
|
647
|
+
if (appData) addBase(join(appData, "npm"));
|
|
648
|
+
for (const entry of pathVal.split(delimiter)) {
|
|
649
|
+
const e = entry.trim();
|
|
650
|
+
if (!e) continue;
|
|
651
|
+
addBase(e);
|
|
652
|
+
addBase(dirname(e));
|
|
653
|
+
addBase(join(dirname(e), "lib"));
|
|
654
|
+
}
|
|
655
|
+
for (const c of [...new Set(candidates)])
|
|
656
|
+
if (exists(c)) return { command: process.execPath, args: [c] };
|
|
657
|
+
return { command: "pi", args: [] };
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function resolveRunCfg(
|
|
661
|
+
input: SubagentInput,
|
|
662
|
+
agentCfg: AgentConfig,
|
|
663
|
+
inheritedThinking?: string,
|
|
664
|
+
inheritedModel?: string,
|
|
665
|
+
): PiRunConfig {
|
|
666
|
+
const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
667
|
+
const normalize = (v: unknown): string | undefined => {
|
|
668
|
+
const s = typeof v === "string" && v.trim() ? v.trim().toLowerCase() : "";
|
|
669
|
+
return THINKING_LEVELS.includes(s) ? s : undefined;
|
|
670
|
+
};
|
|
671
|
+
const suffixRe = /:(off|minimal|low|medium|high|xhigh|max)$/i;
|
|
672
|
+
const inputModel = str(input.model);
|
|
673
|
+
const agentModel = agentCfg.model;
|
|
674
|
+
const rawModel = inputModel ?? agentModel ?? str(inheritedModel);
|
|
675
|
+
const inputSuffixThinking = normalize(inputModel?.match(suffixRe)?.[1]);
|
|
676
|
+
const agentSuffixThinking = normalize(agentModel?.match(suffixRe)?.[1]);
|
|
677
|
+
const baseModel = rawModel?.replace(suffixRe, "");
|
|
678
|
+
const thinking =
|
|
679
|
+
normalize(input.thinking) ??
|
|
680
|
+
inputSuffixThinking ??
|
|
681
|
+
normalize(agentCfg.thinking) ??
|
|
682
|
+
agentSuffixThinking ??
|
|
683
|
+
normalize(inheritedThinking);
|
|
684
|
+
if (baseModel && thinking && thinking !== "off")
|
|
685
|
+
return { model: `${baseModel}:${thinking}`, thinking, tools: agentCfg.tools };
|
|
686
|
+
return { model: baseModel || rawModel, thinking, tools: agentCfg.tools };
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
function contextModelRef(ctx?: PiExtensionContext): string | undefined {
|
|
690
|
+
const provider = str(ctx?.model?.provider);
|
|
691
|
+
const modelId = str(ctx?.model?.id);
|
|
692
|
+
return provider && modelId ? `${provider}/${modelId}` : undefined;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function buildPiArgs(cfg: PiRunConfig): string[] {
|
|
696
|
+
const args = ["--mode", "json", "-p", "--no-session"];
|
|
697
|
+
if (cfg.model)
|
|
698
|
+
args.push(
|
|
699
|
+
"--model",
|
|
700
|
+
cfg.thinking && cfg.thinking !== "off" && !cfg.model.includes(":")
|
|
701
|
+
? `${cfg.model}:${cfg.thinking}`
|
|
702
|
+
: cfg.model,
|
|
703
|
+
);
|
|
704
|
+
else if (cfg.thinking && cfg.thinking !== "off")
|
|
705
|
+
args.push("--thinking", cfg.thinking);
|
|
706
|
+
if (cfg.tools && cfg.tools.length > 0)
|
|
707
|
+
args.push("--tools", cfg.tools.join(","));
|
|
708
|
+
return args;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// ── BoundedBufferCollector ─────────────────────────────────────────────
|
|
712
|
+
class BBC {
|
|
713
|
+
private c: Buffer[] = [];
|
|
714
|
+
private len = 0;
|
|
715
|
+
private trunc = 0;
|
|
716
|
+
constructor(private max: number) {}
|
|
717
|
+
append(b: Buffer) {
|
|
718
|
+
if (b.length >= this.max) {
|
|
719
|
+
this.trunc += this.len + b.length - this.max;
|
|
720
|
+
this.c = [b.subarray(b.length - this.max)];
|
|
721
|
+
this.len = this.max;
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
this.c.push(b);
|
|
725
|
+
this.len += b.length;
|
|
726
|
+
while (this.len > this.max) {
|
|
727
|
+
const f = this.c[0]!;
|
|
728
|
+
if (f.length <= this.len - this.max) {
|
|
729
|
+
this.c.shift();
|
|
730
|
+
this.len -= f.length;
|
|
731
|
+
this.trunc += f.length;
|
|
732
|
+
} else {
|
|
733
|
+
const ov = this.len - this.max;
|
|
734
|
+
this.c[0] = f.subarray(ov);
|
|
735
|
+
this.len -= ov;
|
|
736
|
+
this.trunc += ov;
|
|
737
|
+
break;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
toString() {
|
|
742
|
+
const body = Buffer.concat(this.c, this.len).toString("utf-8");
|
|
743
|
+
return this.trunc ? `[${this.trunc} bytes truncated]\n${body}` : body;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// ── Context Injection Limits (issue #441) ───────────────────────────────
|
|
748
|
+
//
|
|
749
|
+
// Notice text and behavior mirrored byte-for-byte from the shared-hooks
|
|
750
|
+
// Python sub-agent context injection hook. Changing wording there requires
|
|
751
|
+
// changing it here too.
|
|
752
|
+
interface ContextInjectionLimits {
|
|
753
|
+
max_file_bytes: number;
|
|
754
|
+
max_artifact_bytes: number;
|
|
755
|
+
max_total_bytes: number;
|
|
756
|
+
}
|
|
757
|
+
const DEFAULT_CONTEXT_INJECTION_LIMITS: ContextInjectionLimits = {
|
|
758
|
+
max_file_bytes: 32768,
|
|
759
|
+
max_artifact_bytes: 65536,
|
|
760
|
+
max_total_bytes: 131072,
|
|
761
|
+
};
|
|
762
|
+
|
|
763
|
+
function truncateUtf8(buf: Buffer, cap: number): Buffer {
|
|
764
|
+
if (cap <= 0 || buf.length <= cap) return buf;
|
|
765
|
+
let i = cap;
|
|
766
|
+
// Back off over continuation bytes (10xxxxxx) to find the lead byte.
|
|
767
|
+
while (i > 0 && (buf[i - 1]! & 0xc0) === 0x80) i--;
|
|
768
|
+
if (i === 0) return Buffer.alloc(0);
|
|
769
|
+
const lead = buf[i - 1]!;
|
|
770
|
+
if (lead & 0x80) {
|
|
771
|
+
let seqLen = 1;
|
|
772
|
+
if ((lead & 0xe0) === 0xc0) seqLen = 2;
|
|
773
|
+
else if ((lead & 0xf0) === 0xe0) seqLen = 3;
|
|
774
|
+
else if ((lead & 0xf8) === 0xf0) seqLen = 4;
|
|
775
|
+
// Cut before the lead byte when its full sequence didn't fit;
|
|
776
|
+
// otherwise the trailing sequence is complete — keep it whole.
|
|
777
|
+
if (i - 1 + seqLen > cap) return buf.subarray(0, i - 1);
|
|
778
|
+
}
|
|
779
|
+
return buf.subarray(0, cap);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
function stripInlineComment(value: string): string {
|
|
783
|
+
let inQuote: string | null = null;
|
|
784
|
+
for (let idx = 0; idx < value.length; idx++) {
|
|
785
|
+
const ch = value[idx]!;
|
|
786
|
+
if (inQuote) {
|
|
787
|
+
if (ch === inQuote) inQuote = null;
|
|
788
|
+
continue;
|
|
789
|
+
}
|
|
790
|
+
if (ch === '"' || ch === "'") {
|
|
791
|
+
inQuote = ch;
|
|
792
|
+
continue;
|
|
793
|
+
}
|
|
794
|
+
if (ch === "#" && (idx === 0 || /\s/.test(value[idx - 1]!)))
|
|
795
|
+
return value.slice(0, idx);
|
|
796
|
+
}
|
|
797
|
+
return value;
|
|
798
|
+
}
|
|
799
|
+
function unquoteYaml(s: string): string {
|
|
800
|
+
if (s.length >= 2 && s[0] === s[s.length - 1] && (s[0] === '"' || s[0] === "'"))
|
|
801
|
+
return s.slice(1, -1);
|
|
802
|
+
return s;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/** Line-based parser for ONLY the `context_injection:` block of
|
|
806
|
+
* `.trellis/config.yaml`. Not a general YAML parser — mirrors
|
|
807
|
+
* `common.config.get_context_injection_limits()` semantics for this
|
|
808
|
+
* section only (missing keys keep the default; invalid/negative values
|
|
809
|
+
* fall back to the default for that key). */
|
|
810
|
+
function readContextInjectionLimits(repoRoot: string): ContextInjectionLimits {
|
|
811
|
+
const limits: ContextInjectionLimits = { ...DEFAULT_CONTEXT_INJECTION_LIMITS };
|
|
812
|
+
const text = readText(join(repoRoot, ".trellis", "config.yaml"));
|
|
813
|
+
if (!text) return limits;
|
|
814
|
+
|
|
815
|
+
let inSection = false;
|
|
816
|
+
let sectionIndent = -1;
|
|
817
|
+
for (const rawLine of text.split(/\r?\n/)) {
|
|
818
|
+
const trimmed = rawLine.trim();
|
|
819
|
+
if (!inSection) {
|
|
820
|
+
if (/^context_injection\s*:\s*(#.*)?$/.test(trimmed)) {
|
|
821
|
+
inSection = true;
|
|
822
|
+
sectionIndent = rawLine.length - rawLine.trimStart().length;
|
|
823
|
+
}
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
826
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
827
|
+
const indent = rawLine.length - rawLine.trimStart().length;
|
|
828
|
+
if (indent <= sectionIndent) break;
|
|
829
|
+
const m = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*:\s*(.*)$/);
|
|
830
|
+
if (!m) continue;
|
|
831
|
+
const key = m[1]!;
|
|
832
|
+
if (!(key in limits)) continue;
|
|
833
|
+
const raw = unquoteYaml(stripInlineComment(m[2]!).trim()).trim();
|
|
834
|
+
if (!/^-?\d+$/.test(raw)) continue; // invalid -> keep default
|
|
835
|
+
const value = parseInt(raw, 10);
|
|
836
|
+
if (value < 0) continue; // negative -> keep default
|
|
837
|
+
(limits as unknown as Record<string, number>)[key] = value;
|
|
838
|
+
}
|
|
839
|
+
return limits;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
class ContextBudget {
|
|
843
|
+
used = 0;
|
|
844
|
+
constructor(private maxTotalBytes: number) {}
|
|
845
|
+
hasRoom(size: number): boolean {
|
|
846
|
+
if (this.maxTotalBytes <= 0) return true;
|
|
847
|
+
return this.used + size <= this.maxTotalBytes;
|
|
848
|
+
}
|
|
849
|
+
add(size: number): void {
|
|
850
|
+
this.used += size;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
function truncateNotice(path: string, cap: number): string {
|
|
855
|
+
return `\n[Trellis: truncated at ${cap} bytes — read ${path} for the full content]`;
|
|
856
|
+
}
|
|
857
|
+
function isBinaryContent(data: Buffer): boolean {
|
|
858
|
+
return data.includes(0) || !isUtf8(data);
|
|
859
|
+
}
|
|
860
|
+
function binaryNotice(path: string, size: number, reason: string): string {
|
|
861
|
+
return `[Trellis: not inlined (binary file) — ${path} (${size} bytes): ${reason}]`;
|
|
862
|
+
}
|
|
863
|
+
function indexNotice(path: string, size: number, reason: string): string {
|
|
864
|
+
return `[Trellis: not inlined (total context limit reached) — ${path} (${size} bytes): ${reason}]`;
|
|
865
|
+
}
|
|
866
|
+
function budgetedBlock(
|
|
867
|
+
budget: ContextBudget,
|
|
868
|
+
header: string,
|
|
869
|
+
plainPath: string,
|
|
870
|
+
content: string,
|
|
871
|
+
reason: string,
|
|
872
|
+
sizeForIndex: number,
|
|
873
|
+
): string {
|
|
874
|
+
const block = `=== ${header} ===\n${content}`;
|
|
875
|
+
const blockBytes = Buffer.byteLength(block, "utf-8");
|
|
876
|
+
if (!budget.hasRoom(blockBytes)) {
|
|
877
|
+
const notice = indexNotice(plainPath, sizeForIndex, reason);
|
|
878
|
+
budget.add(Buffer.byteLength(notice, "utf-8"));
|
|
879
|
+
return notice;
|
|
880
|
+
}
|
|
881
|
+
budget.add(blockBytes);
|
|
882
|
+
return block;
|
|
883
|
+
}
|
|
884
|
+
function readFileBytes(basePath: string, filePath: string): Buffer | null {
|
|
885
|
+
const full = join(basePath, filePath);
|
|
886
|
+
try {
|
|
887
|
+
if (!statSync(full).isFile()) return null;
|
|
888
|
+
} catch {
|
|
889
|
+
return null;
|
|
890
|
+
}
|
|
891
|
+
try {
|
|
892
|
+
return readFileSync(full);
|
|
893
|
+
} catch {
|
|
894
|
+
return null;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
function materializeFile(
|
|
898
|
+
basePath: string,
|
|
899
|
+
filePath: string,
|
|
900
|
+
reason: string,
|
|
901
|
+
limits: ContextInjectionLimits,
|
|
902
|
+
budget: ContextBudget,
|
|
903
|
+
): string | null {
|
|
904
|
+
const data = readFileBytes(basePath, filePath);
|
|
905
|
+
if (data === null) return null;
|
|
906
|
+
const size = data.length;
|
|
907
|
+
if (isBinaryContent(data)) {
|
|
908
|
+
const notice = binaryNotice(filePath, size, reason);
|
|
909
|
+
budget.add(Buffer.byteLength(notice, "utf-8"));
|
|
910
|
+
return notice;
|
|
911
|
+
}
|
|
912
|
+
const cap = limits.max_file_bytes;
|
|
913
|
+
const truncated = truncateUtf8(data, cap);
|
|
914
|
+
let content = truncated.toString("utf-8");
|
|
915
|
+
if (truncated.length < size) content += truncateNotice(filePath, cap);
|
|
916
|
+
return budgetedBlock(budget, filePath, filePath, content, reason, size);
|
|
917
|
+
}
|
|
918
|
+
function materializeArtifact(
|
|
919
|
+
basePath: string,
|
|
920
|
+
filePath: string,
|
|
921
|
+
headerLabel: string,
|
|
922
|
+
reason: string,
|
|
923
|
+
limits: ContextInjectionLimits,
|
|
924
|
+
budget: ContextBudget,
|
|
925
|
+
): string | null {
|
|
926
|
+
const data = readFileBytes(basePath, filePath);
|
|
927
|
+
if (data === null) return null;
|
|
928
|
+
const size = data.length;
|
|
929
|
+
const cap = limits.max_artifact_bytes;
|
|
930
|
+
const truncated = truncateUtf8(data, cap);
|
|
931
|
+
let content = truncated.toString("utf-8");
|
|
932
|
+
if (truncated.length < size) content += truncateNotice(filePath, cap);
|
|
933
|
+
return budgetedBlock(budget, headerLabel, filePath, content, reason, size);
|
|
934
|
+
}
|
|
935
|
+
interface JsonlEntry {
|
|
936
|
+
file: string;
|
|
937
|
+
type: string;
|
|
938
|
+
reason: string;
|
|
939
|
+
}
|
|
940
|
+
function readJsonlEntries(basePath: string, jsonlPath: string): JsonlEntry[] {
|
|
941
|
+
const text = readText(join(basePath, jsonlPath));
|
|
942
|
+
if (!text) return [];
|
|
943
|
+
const entries: JsonlEntry[] = [];
|
|
944
|
+
for (const line of text.split(/\r?\n/)) {
|
|
945
|
+
const t = line.trim();
|
|
946
|
+
if (!t) continue;
|
|
947
|
+
try {
|
|
948
|
+
const item = JSON.parse(t) as JsonObject;
|
|
949
|
+
const filePath =
|
|
950
|
+
(typeof item.file === "string" && item.file) ||
|
|
951
|
+
(typeof item.path === "string" && item.path) ||
|
|
952
|
+
"";
|
|
953
|
+
if (!filePath) continue;
|
|
954
|
+
entries.push({
|
|
955
|
+
file: filePath,
|
|
956
|
+
type: typeof item.type === "string" ? item.type : "file",
|
|
957
|
+
reason: (typeof item.reason === "string" && item.reason) || "-",
|
|
958
|
+
});
|
|
959
|
+
} catch {}
|
|
960
|
+
}
|
|
961
|
+
return entries;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// ── Trellis Context ────────────────────────────────────────────────────
|
|
965
|
+
function findRoot(start: string): string {
|
|
966
|
+
let c = resolve(start);
|
|
967
|
+
while (true) {
|
|
968
|
+
if (existsSync(join(c, ".trellis")) || existsSync(join(c, ".pi"))) return c;
|
|
969
|
+
const p = dirname(c);
|
|
970
|
+
if (p === c) return resolve(start);
|
|
971
|
+
c = p;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
function splitFM(c: string) {
|
|
975
|
+
const m = c.replace(/^\uFEFF/, "").match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
|
|
976
|
+
return m
|
|
977
|
+
? { fm: m[1] ?? "", body: c.slice(m[0].length) }
|
|
978
|
+
: { fm: "", body: c };
|
|
979
|
+
}
|
|
980
|
+
function stripFM(c: string) {
|
|
981
|
+
return splitFM(c).body.trimStart();
|
|
982
|
+
}
|
|
983
|
+
function parseAgentFM(c: string): AgentConfig {
|
|
984
|
+
const cfg: AgentConfig = { fallbackModels: [] };
|
|
985
|
+
const { fm } = splitFM(c);
|
|
986
|
+
const lines = fm.split(/\r?\n/);
|
|
987
|
+
for (let i = 0; i < lines.length; i++) {
|
|
988
|
+
const m = (lines[i] ?? "").match(/^([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.*)$/);
|
|
989
|
+
if (!m) continue;
|
|
990
|
+
const k = m[1] ?? "",
|
|
991
|
+
v = m[2] ?? "";
|
|
992
|
+
if (k === "model")
|
|
993
|
+
cfg.model = v.trim().replace(/^["']|["']$/g, "") || undefined;
|
|
994
|
+
else if (k === "thinking")
|
|
995
|
+
cfg.thinking = (v.trim().replace(/^["']|["']$/g, "") || undefined) as
|
|
996
|
+
| string
|
|
997
|
+
| undefined;
|
|
998
|
+
else if (k === "fallbackModels" || k === "fallback_models") {
|
|
999
|
+
if (v.trim()) {
|
|
1000
|
+
cfg.fallbackModels = v
|
|
1001
|
+
.trim()
|
|
1002
|
+
.replace(/^\[|\]$/g, "")
|
|
1003
|
+
.split(",")
|
|
1004
|
+
.map((s) => s.trim().replace(/^["']|["']$/g, ""))
|
|
1005
|
+
.filter(Boolean);
|
|
1006
|
+
} else {
|
|
1007
|
+
i++;
|
|
1008
|
+
while (i < lines.length && /^\s+-\s/.test(lines[i] ?? "")) {
|
|
1009
|
+
const item = (lines[i] ?? "")
|
|
1010
|
+
.trim()
|
|
1011
|
+
.replace(/^-\s+/, "")
|
|
1012
|
+
.replace(/^["']|["']$/g, "");
|
|
1013
|
+
if (item) cfg.fallbackModels.push(item);
|
|
1014
|
+
i++;
|
|
1015
|
+
}
|
|
1016
|
+
i--;
|
|
1017
|
+
}
|
|
1018
|
+
} else if (k === "tools") {
|
|
1019
|
+
// Pi tool names are lowercase (read, bash, edit, write, grep, find, ls).
|
|
1020
|
+
// Normalize to lowercase so mixed-case frontmatter still matches.
|
|
1021
|
+
if (v.trim()) {
|
|
1022
|
+
cfg.tools = v
|
|
1023
|
+
.trim()
|
|
1024
|
+
.split(",")
|
|
1025
|
+
.map((s) => s.trim().replace(/^["']|["']$/g, "").toLowerCase())
|
|
1026
|
+
.filter(Boolean);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
return cfg;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
function contextKey(input?: unknown, ctx?: PiExtensionContext): string | null {
|
|
1034
|
+
const sessionId =
|
|
1035
|
+
callStr(ctx?.sessionManager?.getSessionId, ctx?.sessionManager) ??
|
|
1036
|
+
str(process.env.PI_SESSION_ID) ??
|
|
1037
|
+
str(process.env.PI_SESSIONID) ??
|
|
1038
|
+
lookupStr(input, ["session_id", "sessionId", "sessionID"]);
|
|
1039
|
+
if (sessionId) {
|
|
1040
|
+
const normalized = sessionId.replace(/[^A-Za-z0-9._-]+/g, "_");
|
|
1041
|
+
if (!normalized) return `pi_${hash(sessionId)}`;
|
|
1042
|
+
return `pi_${normalized}${normalized === sessionId ? "" : `_${hash(sessionId)}`}`;
|
|
1043
|
+
}
|
|
1044
|
+
const transcriptPath =
|
|
1045
|
+
callStr(ctx?.sessionManager?.getSessionFile, ctx?.sessionManager) ??
|
|
1046
|
+
lookupStr(input, ["transcript_path", "transcriptPath", "transcript"]);
|
|
1047
|
+
if (transcriptPath) return `pi_transcript_${hash(transcriptPath)}`;
|
|
1048
|
+
return null;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
/**
|
|
1052
|
+
* Return `candidate` when it lands inside `root`, else null.
|
|
1053
|
+
*
|
|
1054
|
+
* A session pointer is not always something the user typed. `task.py` now
|
|
1055
|
+
* refuses to store a ref that leaves the project, but a session file written
|
|
1056
|
+
* before that fix can still hold one, and `trellis update` does not rewrite
|
|
1057
|
+
* session files — so a poisoned pointer outlives the upgrade that closed the
|
|
1058
|
+
* writer. Both sides are resolved so a task directory symlinked outside is
|
|
1059
|
+
* refused too, but the original `candidate` is returned on success: callers do
|
|
1060
|
+
* `relative(root, dir)`, and handing them a realpath would break that whenever
|
|
1061
|
+
* `root` itself sits behind a symlink (`/tmp` does on macOS).
|
|
1062
|
+
*/
|
|
1063
|
+
function containInRoot(root: string, candidate: string): string | null {
|
|
1064
|
+
try {
|
|
1065
|
+
const rel = relative(realpathSync(root), realpathSync(candidate));
|
|
1066
|
+
if (rel !== "" && (rel.startsWith("..") || isAbsolute(rel))) return null;
|
|
1067
|
+
return candidate;
|
|
1068
|
+
} catch {
|
|
1069
|
+
return null;
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
function readTaskDir(root: string, key: string | null): string | null {
|
|
1074
|
+
if (!key) return null;
|
|
1075
|
+
try {
|
|
1076
|
+
const ctx = JSON.parse(
|
|
1077
|
+
readText(join(root, ".trellis", ".runtime", "sessions", `${key}.json`)),
|
|
1078
|
+
) as JsonObject;
|
|
1079
|
+
let ref = str(ctx.current_task);
|
|
1080
|
+
if (!ref) return null;
|
|
1081
|
+
ref = ref;
|
|
1082
|
+
ref = ref.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
1083
|
+
if (ref.startsWith("tasks/")) ref = `.trellis/${ref}`;
|
|
1084
|
+
const candidate = ref.startsWith(".trellis/")
|
|
1085
|
+
? join(root, ref)
|
|
1086
|
+
: isAbsolute(ref)
|
|
1087
|
+
? ref
|
|
1088
|
+
: join(root, ".trellis", "tasks", ref);
|
|
1089
|
+
return containInRoot(root, candidate);
|
|
1090
|
+
} catch {
|
|
1091
|
+
return null;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
// ── Workflow State Breadcrumb ─────────────────────────────────────────
|
|
1096
|
+
const WORKFLOW_ID_RE = /^[A-Za-z0-9_-]+$/;
|
|
1097
|
+
const DEFAULT_WORKFLOW_RE =
|
|
1098
|
+
/^default_workflow:\s*(['"]?)([A-Za-z0-9_-]+)\1\s*(?:#.*)?$/m;
|
|
1099
|
+
|
|
1100
|
+
function workflowVariant(root: string, workflowId: string): string {
|
|
1101
|
+
if (!WORKFLOW_ID_RE.test(workflowId)) return "";
|
|
1102
|
+
const path = join(root, ".trellis", "workflows", `${workflowId}.md`);
|
|
1103
|
+
return exists(path) ? path : "";
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
function developerWorkflowId(root: string): string {
|
|
1107
|
+
for (const line of readText(join(root, ".trellis", ".developer")).split(
|
|
1108
|
+
/\r?\n/,
|
|
1109
|
+
)) {
|
|
1110
|
+
if (line.startsWith("workflow="))
|
|
1111
|
+
return line.slice("workflow=".length).trim();
|
|
1112
|
+
}
|
|
1113
|
+
return "";
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
function configDefaultWorkflowId(root: string): string {
|
|
1117
|
+
return (
|
|
1118
|
+
readText(join(root, ".trellis", "config.yaml")).match(
|
|
1119
|
+
DEFAULT_WORKFLOW_RE,
|
|
1120
|
+
)?.[2] ?? ""
|
|
1121
|
+
);
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
/** Mirrors common/workflow_selection.py without spawning another Python
|
|
1125
|
+
* process on every Pi turn. Invalid or missing selections fall through. */
|
|
1126
|
+
function resolveWorkflowMd(root: string, key: string | null): string {
|
|
1127
|
+
const taskDir = readTaskDir(root, key);
|
|
1128
|
+
let workflowId = "";
|
|
1129
|
+
if (taskDir) {
|
|
1130
|
+
try {
|
|
1131
|
+
const task = JSON.parse(
|
|
1132
|
+
readText(join(taskDir, "task.json")),
|
|
1133
|
+
) as JsonObject;
|
|
1134
|
+
workflowId = typeof task.workflow === "string" ? task.workflow : "";
|
|
1135
|
+
} catch {}
|
|
1136
|
+
}
|
|
1137
|
+
if (workflowId) {
|
|
1138
|
+
const pinned = workflowVariant(root, workflowId);
|
|
1139
|
+
if (pinned) return pinned;
|
|
1140
|
+
console.error(
|
|
1141
|
+
`Warning: active task selects workflow ${JSON.stringify(workflowId)} but .trellis/workflows/ has no matching file; using default workflow resolution`,
|
|
1142
|
+
);
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
const personal = workflowVariant(root, developerWorkflowId(root));
|
|
1146
|
+
if (personal) return personal;
|
|
1147
|
+
const team = workflowVariant(root, configDefaultWorkflowId(root));
|
|
1148
|
+
if (team) return team;
|
|
1149
|
+
return join(root, ".trellis", "workflow.md");
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
const WF_RE =
|
|
1153
|
+
/\[workflow-state:([A-Za-z0-9_-]+)\]\s*\n([\s\S]*?)\n\s*\[\/workflow-state:\1\]/g;
|
|
1154
|
+
function workflowBreadcrumb(root: string, key: string | null): string {
|
|
1155
|
+
const wf = readText(resolveWorkflowMd(root, key));
|
|
1156
|
+
if (!wf) return "";
|
|
1157
|
+
const templates: Record<string, string> = {};
|
|
1158
|
+
for (const m of wf.matchAll(WF_RE)) {
|
|
1159
|
+
const s = m[1] ?? "",
|
|
1160
|
+
b = (m[2] ?? "").trim();
|
|
1161
|
+
if (s && b) templates[s] = b;
|
|
1162
|
+
}
|
|
1163
|
+
const dir = readTaskDir(root, key);
|
|
1164
|
+
let header = "Status: no_task",
|
|
1165
|
+
lookup = "no_task";
|
|
1166
|
+
if (dir) {
|
|
1167
|
+
try {
|
|
1168
|
+
const d = JSON.parse(readText(join(dir, "task.json"))) as JsonObject;
|
|
1169
|
+
const status = str(d.status) ?? "";
|
|
1170
|
+
const id = str(d.id) ?? dir.split(/[\\/]/).pop() ?? "";
|
|
1171
|
+
if (status) {
|
|
1172
|
+
header = `Task: ${id} (${status})`;
|
|
1173
|
+
lookup = status;
|
|
1174
|
+
}
|
|
1175
|
+
} catch {}
|
|
1176
|
+
}
|
|
1177
|
+
const body = templates[lookup] ?? "Refer to workflow.md for current step.";
|
|
1178
|
+
return `<workflow-state>\n${header}\n${body}\n</workflow-state>`;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// ── Session Overview ───────────────────────────────────────────────────
|
|
1182
|
+
function runContextScript(root: string, key: string | null, args: string[]): string {
|
|
1183
|
+
const script = join(root, ".trellis", "scripts", "get_context.py");
|
|
1184
|
+
if (!exists(script)) return "";
|
|
1185
|
+
try {
|
|
1186
|
+
const py = process.platform === "win32" ? "python" : "python3";
|
|
1187
|
+
const result = spawnSync(py, [script, ...args], {
|
|
1188
|
+
cwd: root,
|
|
1189
|
+
env: key ? { ...process.env, TRELLIS_CONTEXT_ID: key } : process.env,
|
|
1190
|
+
encoding: "utf-8",
|
|
1191
|
+
timeout: SESSION_OVERVIEW_TIMEOUT_MS,
|
|
1192
|
+
windowsHide: true,
|
|
1193
|
+
});
|
|
1194
|
+
if (result.status !== 0) return "";
|
|
1195
|
+
const stdout = (result.stdout ?? "").trim();
|
|
1196
|
+
return stdout;
|
|
1197
|
+
} catch {
|
|
1198
|
+
return "";
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
function sessionOverview(root: string, key: string | null): string {
|
|
1203
|
+
const stdout = runContextScript(root, key, []);
|
|
1204
|
+
return stdout ? `<session-overview>\n${stdout}\n</session-overview>` : "";
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
function workflowOverview(root: string, key: string | null): string {
|
|
1208
|
+
const stdout = runContextScript(root, key, [
|
|
1209
|
+
"--mode",
|
|
1210
|
+
"phase",
|
|
1211
|
+
"--platform",
|
|
1212
|
+
"pi",
|
|
1213
|
+
]);
|
|
1214
|
+
return stdout ? `<trellis-workflow>\n${stdout}\n</trellis-workflow>` : "";
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
function buildStartupContext(
|
|
1218
|
+
root: string,
|
|
1219
|
+
key: string | null,
|
|
1220
|
+
overview: string,
|
|
1221
|
+
): string {
|
|
1222
|
+
const workflow = workflowOverview(root, key);
|
|
1223
|
+
return [
|
|
1224
|
+
"<session-context>\nTrellis compact SessionStart context. Use it to orient the session; load details on demand.\n</session-context>",
|
|
1225
|
+
FIRST_REPLY_NOTICE,
|
|
1226
|
+
overview,
|
|
1227
|
+
workflow,
|
|
1228
|
+
"<ready>\nUse the current workflow state to decide whether to create, continue, or skip a Trellis task.\n</ready>",
|
|
1229
|
+
]
|
|
1230
|
+
.filter(Boolean)
|
|
1231
|
+
.join("\n\n");
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
function buildContext(root: string, agent: string, key: string | null): string {
|
|
1235
|
+
const dir = readTaskDir(root, key);
|
|
1236
|
+
if (!dir)
|
|
1237
|
+
return "No active Trellis task found. Read .trellis/ before proceeding.";
|
|
1238
|
+
const relTaskDir = relative(root, dir).replace(/\\/g, "/");
|
|
1239
|
+
const limits = readContextInjectionLimits(root);
|
|
1240
|
+
const budget = new ContextBudget(limits.max_total_bytes);
|
|
1241
|
+
|
|
1242
|
+
// 1. Curated spec/research files from {agent}.jsonl (same order, budget
|
|
1243
|
+
// processed first, matching Python's get_agent_context()).
|
|
1244
|
+
const jsonlName = TRELLIS_AGENT_JSONL[agent] ?? "";
|
|
1245
|
+
const specBlocks: string[] = [];
|
|
1246
|
+
if (jsonlName) {
|
|
1247
|
+
for (const entry of readJsonlEntries(dir, jsonlName)) {
|
|
1248
|
+
if (entry.type === "directory") continue;
|
|
1249
|
+
const block = materializeFile(root, entry.file, entry.reason, limits, budget);
|
|
1250
|
+
if (block) specBlocks.push(block);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
const spec = specBlocks.join("\n\n");
|
|
1254
|
+
|
|
1255
|
+
// 2-4. Task artifacts, in order: prd.md -> design.md -> implement.md.
|
|
1256
|
+
const prd = materializeArtifact(
|
|
1257
|
+
root,
|
|
1258
|
+
`${relTaskDir}/prd.md`,
|
|
1259
|
+
`${relTaskDir}/prd.md (Requirements)`,
|
|
1260
|
+
"Requirements document",
|
|
1261
|
+
limits,
|
|
1262
|
+
budget,
|
|
1263
|
+
);
|
|
1264
|
+
const design = materializeArtifact(
|
|
1265
|
+
root,
|
|
1266
|
+
`${relTaskDir}/design.md`,
|
|
1267
|
+
`${relTaskDir}/design.md (Technical Design)`,
|
|
1268
|
+
"Technical design document",
|
|
1269
|
+
limits,
|
|
1270
|
+
budget,
|
|
1271
|
+
);
|
|
1272
|
+
const impl = materializeArtifact(
|
|
1273
|
+
root,
|
|
1274
|
+
`${relTaskDir}/implement.md`,
|
|
1275
|
+
`${relTaskDir}/implement.md (Execution Plan)`,
|
|
1276
|
+
"Execution plan document",
|
|
1277
|
+
limits,
|
|
1278
|
+
budget,
|
|
1279
|
+
);
|
|
1280
|
+
|
|
1281
|
+
// prd/design/impl already carry their own "=== path (label) ===" header
|
|
1282
|
+
// (from materializeArtifact) — no extra "### x.md" wrapper needed, that
|
|
1283
|
+
// would just double the header.
|
|
1284
|
+
return [
|
|
1285
|
+
`## Trellis Task Context`,
|
|
1286
|
+
`Task directory: ${dir}`,
|
|
1287
|
+
"",
|
|
1288
|
+
prd ?? `(missing) ${relTaskDir}/prd.md`,
|
|
1289
|
+
design ? "\n" + design : "",
|
|
1290
|
+
impl ? "\n" + impl : "",
|
|
1291
|
+
spec ? "\n### Curated Spec / Research Context\n" + spec : "",
|
|
1292
|
+
].join("\n");
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
function normalizeAgent(agent: string | undefined): string {
|
|
1296
|
+
const name = agent ?? "trellis-implement";
|
|
1297
|
+
return name.startsWith("trellis-") ? name : `trellis-${name}`;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
function isTrellisAgent(root: string, agent: string): boolean {
|
|
1301
|
+
return existsSync(join(root, ".pi", "agents", `${agent}.md`));
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
function buildPrompt(
|
|
1305
|
+
root: string,
|
|
1306
|
+
input: SubagentInput,
|
|
1307
|
+
key: string | null,
|
|
1308
|
+
): string {
|
|
1309
|
+
const agent = normalizeAgent(input.agent);
|
|
1310
|
+
const raw = readText(join(root, ".pi", "agents", `${agent}.md`));
|
|
1311
|
+
const def = stripFM(raw);
|
|
1312
|
+
const ctx = buildContext(root, agent, key);
|
|
1313
|
+
return [
|
|
1314
|
+
"## Trellis Agent Definition",
|
|
1315
|
+
def || "(missing)",
|
|
1316
|
+
"",
|
|
1317
|
+
ctx,
|
|
1318
|
+
"",
|
|
1319
|
+
"## Delegated Task",
|
|
1320
|
+
input.prompt ?? "",
|
|
1321
|
+
].join("\n");
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
// ── Event parsing ─────────────────────────────────────────────────────
|
|
1325
|
+
function parseJsonEvent(line: string): JsonObject | null {
|
|
1326
|
+
const t = line.trim();
|
|
1327
|
+
if (!t) return null;
|
|
1328
|
+
const i = t.indexOf("{");
|
|
1329
|
+
if (i < 0) return null;
|
|
1330
|
+
try {
|
|
1331
|
+
const p = JSON.parse(t.slice(i));
|
|
1332
|
+
return isObj(p) ? p : null;
|
|
1333
|
+
} catch {
|
|
1334
|
+
return null;
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
function applyEvent(r: RunState, evt: JsonObject): boolean {
|
|
1339
|
+
const type = typeof evt.type === "string" ? evt.type : "";
|
|
1340
|
+
if (!type) return false;
|
|
1341
|
+
if (type === "agent_start" || type === "turn_start") {
|
|
1342
|
+
r.status = "running";
|
|
1343
|
+
r.startedAt ??= Date.now();
|
|
1344
|
+
return true;
|
|
1345
|
+
}
|
|
1346
|
+
if (type === "message_update") {
|
|
1347
|
+
const ae = isObj(evt.assistantMessageEvent)
|
|
1348
|
+
? evt.assistantMessageEvent
|
|
1349
|
+
: null;
|
|
1350
|
+
if (!ae || typeof ae.delta !== "string") return false;
|
|
1351
|
+
if (ae.type === "thinking_delta") {
|
|
1352
|
+
r.thinkingTail = appendTail(r.thinkingTail, ae.delta, MAX_TAIL);
|
|
1353
|
+
return true;
|
|
1354
|
+
}
|
|
1355
|
+
if (ae.type === "text_delta") {
|
|
1356
|
+
r.textTail = appendTail(r.textTail, ae.delta, MAX_TAIL);
|
|
1357
|
+
return true;
|
|
1358
|
+
}
|
|
1359
|
+
return false;
|
|
1360
|
+
}
|
|
1361
|
+
if (type === "message_end" && isObj(evt.message)) {
|
|
1362
|
+
const msg = evt.message;
|
|
1363
|
+
if (msg.role !== "assistant") return false;
|
|
1364
|
+
r.usage.turns += 1;
|
|
1365
|
+
const u = isObj(msg.usage) ? msg.usage : null;
|
|
1366
|
+
const cost = isObj(u?.cost) ? u.cost : null;
|
|
1367
|
+
r.usage.input += num(u?.input);
|
|
1368
|
+
r.usage.output += num(u?.output);
|
|
1369
|
+
r.usage.cacheRead += num(u?.cacheRead);
|
|
1370
|
+
r.usage.cacheWrite += num(u?.cacheWrite);
|
|
1371
|
+
r.usage.cost += num(cost?.total);
|
|
1372
|
+
r.usage.ctxTokens = num(u?.totalTokens);
|
|
1373
|
+
const thinking = extractThinking(msg.content);
|
|
1374
|
+
if (thinking) r.thinkingTail = appendTail("", thinking, MAX_TAIL);
|
|
1375
|
+
const text = extractText(msg.content);
|
|
1376
|
+
if (text) {
|
|
1377
|
+
r.finalText = text;
|
|
1378
|
+
r.textTail = appendTail("", text, MAX_TAIL);
|
|
1379
|
+
}
|
|
1380
|
+
if (typeof msg.model === "string") {
|
|
1381
|
+
const parsed = splitModelThinking(msg.model, r.thinking);
|
|
1382
|
+
r.model = parsed.model;
|
|
1383
|
+
r.thinking = parsed.thinking;
|
|
1384
|
+
}
|
|
1385
|
+
if (typeof msg.errorMessage === "string") r.errorMessage = msg.errorMessage;
|
|
1386
|
+
return true;
|
|
1387
|
+
}
|
|
1388
|
+
if (type === "tool_execution_start") {
|
|
1389
|
+
const id =
|
|
1390
|
+
typeof evt.toolCallId === "string"
|
|
1391
|
+
? evt.toolCallId
|
|
1392
|
+
: hash(`${Date.now()}`);
|
|
1393
|
+
const name = typeof evt.toolName === "string" ? evt.toolName : "tool";
|
|
1394
|
+
const args = summarizeToolArgs(name, evt.args);
|
|
1395
|
+
const existing = r.tools.findIndex((t) => t.id === id);
|
|
1396
|
+
if (existing >= 0)
|
|
1397
|
+
r.tools[existing] = { ...r.tools[existing]!, args, status: "running" };
|
|
1398
|
+
else
|
|
1399
|
+
r.tools.push({
|
|
1400
|
+
id,
|
|
1401
|
+
name,
|
|
1402
|
+
args,
|
|
1403
|
+
status: "running",
|
|
1404
|
+
startedAt: Date.now(),
|
|
1405
|
+
});
|
|
1406
|
+
if (r.tools.length > MAX_TOOLS)
|
|
1407
|
+
r.tools.splice(0, r.tools.length - MAX_TOOLS);
|
|
1408
|
+
return true;
|
|
1409
|
+
}
|
|
1410
|
+
if (type === "tool_execution_end") {
|
|
1411
|
+
const id = typeof evt.toolCallId === "string" ? evt.toolCallId : "";
|
|
1412
|
+
const idx = r.tools.findIndex((t) => t.id === id);
|
|
1413
|
+
if (idx >= 0)
|
|
1414
|
+
r.tools[idx] = {
|
|
1415
|
+
...r.tools[idx]!,
|
|
1416
|
+
status: evt.isError ? "failed" : "succeeded",
|
|
1417
|
+
finishedAt: Date.now(),
|
|
1418
|
+
};
|
|
1419
|
+
return true;
|
|
1420
|
+
}
|
|
1421
|
+
if (type === "agent_end") {
|
|
1422
|
+
r.finishedAt = Date.now();
|
|
1423
|
+
if (r.status === "running" || r.status === "pending")
|
|
1424
|
+
r.status = "succeeded";
|
|
1425
|
+
return true;
|
|
1426
|
+
}
|
|
1427
|
+
return false;
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
function finalize(r: RunState, fallback: string): string {
|
|
1431
|
+
return r.finalText || fallback.trim() || r.stderrTail.trim();
|
|
1432
|
+
}
|
|
1433
|
+
function formatPiOutput(stdout: string, stderr: string): string {
|
|
1434
|
+
let ft = "";
|
|
1435
|
+
for (const line of stdout.split(/\r?\n/)) {
|
|
1436
|
+
const t = line.trim();
|
|
1437
|
+
if (!t) continue;
|
|
1438
|
+
try {
|
|
1439
|
+
const evt = JSON.parse(t) as JsonObject;
|
|
1440
|
+
const msg = isObj(evt.message) ? evt.message : null;
|
|
1441
|
+
if (msg?.role === "assistant") {
|
|
1442
|
+
const txt = extractText(msg.content);
|
|
1443
|
+
if (txt) ft = txt;
|
|
1444
|
+
}
|
|
1445
|
+
} catch {}
|
|
1446
|
+
}
|
|
1447
|
+
return ft || stdout || stderr;
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
// ── runPi: subprocess execution + event processing ───────────────────
|
|
1451
|
+
function runPi(
|
|
1452
|
+
root: string,
|
|
1453
|
+
prompt: string,
|
|
1454
|
+
cfg: PiRunConfig,
|
|
1455
|
+
state: RunState,
|
|
1456
|
+
emit: () => void,
|
|
1457
|
+
key?: string | null,
|
|
1458
|
+
signal?: AbortSignal,
|
|
1459
|
+
): Promise<{ output: string; failed: boolean }> {
|
|
1460
|
+
return new Promise((resolve) => {
|
|
1461
|
+
if (signal?.aborted) {
|
|
1462
|
+
state.status = "cancelled";
|
|
1463
|
+
state.errorMessage = "cancelled";
|
|
1464
|
+
state.finishedAt = Date.now();
|
|
1465
|
+
emit();
|
|
1466
|
+
resolve({ output: "cancelled", failed: true });
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1469
|
+
const inv = resolvePiCli();
|
|
1470
|
+
const childEnv = {
|
|
1471
|
+
...process.env,
|
|
1472
|
+
TRELLIS_SUBAGENT_CHILD: "1",
|
|
1473
|
+
...(key ? { TRELLIS_CONTEXT_ID: key } : {}),
|
|
1474
|
+
};
|
|
1475
|
+
const cli = spawn(inv.command, [...inv.args, ...buildPiArgs(cfg)], {
|
|
1476
|
+
cwd: root,
|
|
1477
|
+
env: childEnv,
|
|
1478
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1479
|
+
windowsHide: true,
|
|
1480
|
+
});
|
|
1481
|
+
const stdout = new BBC(MAX_STDOUT);
|
|
1482
|
+
const stderr = new BBC(MAX_STDERR);
|
|
1483
|
+
let buf = "";
|
|
1484
|
+
let settled = false;
|
|
1485
|
+
let aborted = false;
|
|
1486
|
+
let killTimer: ReturnType<typeof setTimeout> | null = null;
|
|
1487
|
+
const abort = () => {
|
|
1488
|
+
aborted = true;
|
|
1489
|
+
cli.kill();
|
|
1490
|
+
killTimer = setTimeout(() => {
|
|
1491
|
+
if (!settled && cli.exitCode === null) cli.kill("SIGKILL");
|
|
1492
|
+
}, ABORT_KILL_GRACE_MS);
|
|
1493
|
+
killTimer?.unref?.();
|
|
1494
|
+
};
|
|
1495
|
+
const done = (v: { output: string; failed: boolean }) => {
|
|
1496
|
+
if (settled) return;
|
|
1497
|
+
settled = true;
|
|
1498
|
+
if (killTimer) clearTimeout(killTimer);
|
|
1499
|
+
signal?.removeEventListener("abort", abort);
|
|
1500
|
+
emit();
|
|
1501
|
+
resolve(v);
|
|
1502
|
+
};
|
|
1503
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
1504
|
+
state.status = "running";
|
|
1505
|
+
state.startedAt = Date.now();
|
|
1506
|
+
emit();
|
|
1507
|
+
const processLine = (line: string) => {
|
|
1508
|
+
const evt = parseJsonEvent(line);
|
|
1509
|
+
if (evt && applyEvent(state, evt)) emit();
|
|
1510
|
+
};
|
|
1511
|
+
cli.stdout?.on("data", (d: Buffer) => {
|
|
1512
|
+
stdout.append(d);
|
|
1513
|
+
buf += d.toString("utf-8");
|
|
1514
|
+
if (buf.length > MAX_LINE_BUFFER) buf = buf.slice(-MAX_LINE_BUFFER);
|
|
1515
|
+
const lines = buf.split(/\r?\n/);
|
|
1516
|
+
buf = lines.pop() ?? "";
|
|
1517
|
+
for (const l of lines) processLine(l);
|
|
1518
|
+
});
|
|
1519
|
+
cli.stderr?.on("data", (d: Buffer) => {
|
|
1520
|
+
stderr.append(d);
|
|
1521
|
+
state.stderrTail = appendTail(
|
|
1522
|
+
state.stderrTail,
|
|
1523
|
+
d.toString("utf-8"),
|
|
1524
|
+
MAX_TAIL,
|
|
1525
|
+
);
|
|
1526
|
+
});
|
|
1527
|
+
cli.stdin?.on("error", (e: Error & { code?: string }) => {
|
|
1528
|
+
if (!aborted && e.code !== "EPIPE")
|
|
1529
|
+
done({ output: e.message, failed: true });
|
|
1530
|
+
});
|
|
1531
|
+
cli.on("error", (e) => {
|
|
1532
|
+
state.status = aborted ? "cancelled" : "failed";
|
|
1533
|
+
state.errorMessage = e instanceof Error ? e.message : String(e);
|
|
1534
|
+
state.finishedAt = Date.now();
|
|
1535
|
+
done({ output: finalize(state, state.errorMessage), failed: true });
|
|
1536
|
+
});
|
|
1537
|
+
cli.on("close", (code) => {
|
|
1538
|
+
if (buf.trim()) processLine(buf);
|
|
1539
|
+
const out = stdout.toString();
|
|
1540
|
+
const err = stderr.toString();
|
|
1541
|
+
state.stderrTail = appendTail("", err, MAX_TAIL);
|
|
1542
|
+
state.finishedAt = Date.now();
|
|
1543
|
+
if (aborted) {
|
|
1544
|
+
state.status = "cancelled";
|
|
1545
|
+
state.errorMessage = "cancelled";
|
|
1546
|
+
done({ output: finalize(state, "cancelled"), failed: true });
|
|
1547
|
+
return;
|
|
1548
|
+
}
|
|
1549
|
+
if (code === 0) {
|
|
1550
|
+
if (state.status === "pending" || state.status === "running")
|
|
1551
|
+
state.status = "succeeded";
|
|
1552
|
+
done({
|
|
1553
|
+
output: finalize(state, formatPiOutput(out, err)),
|
|
1554
|
+
failed: false,
|
|
1555
|
+
});
|
|
1556
|
+
return;
|
|
1557
|
+
}
|
|
1558
|
+
state.status = "failed";
|
|
1559
|
+
state.errorMessage = err || out || `exit ${code ?? "?"}`;
|
|
1560
|
+
done({ output: finalize(state, state.errorMessage), failed: true });
|
|
1561
|
+
});
|
|
1562
|
+
cli.stdin?.end(prompt);
|
|
1563
|
+
});
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
// ── runSubagent: orchestrate single/parallel/chain via native partial updates ──
|
|
1567
|
+
async function runSubagent(
|
|
1568
|
+
root: string,
|
|
1569
|
+
input: SubagentInput,
|
|
1570
|
+
key: string | null,
|
|
1571
|
+
signal?: AbortSignal,
|
|
1572
|
+
onUpdate?: (r: PiToolResult) => void,
|
|
1573
|
+
inheritedThinking?: string,
|
|
1574
|
+
inheritedModel?: string,
|
|
1575
|
+
): Promise<{ output: string; details: ProgressDetails; failed: boolean }> {
|
|
1576
|
+
const agentName = normalizeAgent(input.agent);
|
|
1577
|
+
const agentRaw = readText(join(root, ".pi", "agents", `${agentName}.md`));
|
|
1578
|
+
const agentCfg = parseAgentFM(agentRaw);
|
|
1579
|
+
const runCfg = resolveRunCfg(
|
|
1580
|
+
input,
|
|
1581
|
+
agentCfg,
|
|
1582
|
+
inheritedThinking,
|
|
1583
|
+
inheritedModel,
|
|
1584
|
+
);
|
|
1585
|
+
const mode = input.mode ?? "single";
|
|
1586
|
+
const startedAt = Date.now();
|
|
1587
|
+
const details: ProgressDetails = {
|
|
1588
|
+
kind: "trellis-subagent-progress",
|
|
1589
|
+
agent: agentName,
|
|
1590
|
+
mode,
|
|
1591
|
+
startedAt,
|
|
1592
|
+
updatedAt: startedAt,
|
|
1593
|
+
final: false,
|
|
1594
|
+
runs: [],
|
|
1595
|
+
};
|
|
1596
|
+
let lastEmit = 0;
|
|
1597
|
+
let lastPartialKey = "";
|
|
1598
|
+
let closed = false;
|
|
1599
|
+
const pushPartial = (force = false) => {
|
|
1600
|
+
if (closed || !onUpdate) return;
|
|
1601
|
+
const key = progressKey(details);
|
|
1602
|
+
if (!force && key === lastPartialKey) return;
|
|
1603
|
+
lastPartialKey = key;
|
|
1604
|
+
onUpdate({
|
|
1605
|
+
// Keep native partial content stable; renderResult owns the visible progress UI.
|
|
1606
|
+
content: [{ type: "text", text: "subagent running" }],
|
|
1607
|
+
details: cloneProgress(details),
|
|
1608
|
+
});
|
|
1609
|
+
};
|
|
1610
|
+
const emit = (force = false) => {
|
|
1611
|
+
const now = Date.now();
|
|
1612
|
+
if (!force && now - lastEmit < THROTTLE_MS) return;
|
|
1613
|
+
lastEmit = now;
|
|
1614
|
+
details.updatedAt = now;
|
|
1615
|
+
pushPartial(force);
|
|
1616
|
+
};
|
|
1617
|
+
const finish = (output: string, failed: boolean) => {
|
|
1618
|
+
closed = true;
|
|
1619
|
+
details.final = true;
|
|
1620
|
+
details.updatedAt = Date.now();
|
|
1621
|
+
return { output, details: cloneProgress(details), failed };
|
|
1622
|
+
};
|
|
1623
|
+
|
|
1624
|
+
try {
|
|
1625
|
+
if (mode === "parallel") {
|
|
1626
|
+
const prompts = input.prompts ?? (input.prompt ? [input.prompt] : []);
|
|
1627
|
+
details.runs = prompts.map((p, i) => {
|
|
1628
|
+
const r = newRun(`${agentName}-${i + 1}`, agentName, p);
|
|
1629
|
+
applyRunConfig(r, runCfg);
|
|
1630
|
+
return r;
|
|
1631
|
+
});
|
|
1632
|
+
emit(true);
|
|
1633
|
+
const results = await Promise.all(
|
|
1634
|
+
prompts.map((p, i) =>
|
|
1635
|
+
runPi(
|
|
1636
|
+
root,
|
|
1637
|
+
buildPrompt(root, { ...input, prompt: p }, key),
|
|
1638
|
+
runCfg,
|
|
1639
|
+
details.runs[i]!,
|
|
1640
|
+
emit,
|
|
1641
|
+
key,
|
|
1642
|
+
signal,
|
|
1643
|
+
),
|
|
1644
|
+
),
|
|
1645
|
+
);
|
|
1646
|
+
return finish(
|
|
1647
|
+
results.map((r) => r.output).join("\n\n---\n\n"),
|
|
1648
|
+
results.some((r) => r.failed),
|
|
1649
|
+
);
|
|
1650
|
+
}
|
|
1651
|
+
if (mode === "chain") {
|
|
1652
|
+
let prev = "";
|
|
1653
|
+
let failed = false;
|
|
1654
|
+
for (let i = 0; i < (input.prompts?.length ?? 1); i++) {
|
|
1655
|
+
const p = input.prompts?.[i] ?? input.prompt ?? "";
|
|
1656
|
+
const rs = newRun(`${agentName}-${i + 1}`, agentName, p, i + 1);
|
|
1657
|
+
applyRunConfig(rs, runCfg);
|
|
1658
|
+
details.runs.push(rs);
|
|
1659
|
+
emit(true);
|
|
1660
|
+
const result = await runPi(
|
|
1661
|
+
root,
|
|
1662
|
+
buildPrompt(
|
|
1663
|
+
root,
|
|
1664
|
+
{
|
|
1665
|
+
...input,
|
|
1666
|
+
prompt: prev ? `${p}\n\nPrevious output:\n${prev}` : p,
|
|
1667
|
+
},
|
|
1668
|
+
key,
|
|
1669
|
+
),
|
|
1670
|
+
runCfg,
|
|
1671
|
+
rs,
|
|
1672
|
+
emit,
|
|
1673
|
+
key,
|
|
1674
|
+
signal,
|
|
1675
|
+
);
|
|
1676
|
+
prev = result.output;
|
|
1677
|
+
failed = failed || result.failed;
|
|
1678
|
+
if (result.failed) break;
|
|
1679
|
+
}
|
|
1680
|
+
return finish(prev, failed);
|
|
1681
|
+
}
|
|
1682
|
+
const rs = newRun(`${agentName}-1`, agentName, input.prompt ?? "");
|
|
1683
|
+
applyRunConfig(rs, runCfg);
|
|
1684
|
+
details.runs = [rs];
|
|
1685
|
+
emit(true);
|
|
1686
|
+
const result = await runPi(
|
|
1687
|
+
root,
|
|
1688
|
+
buildPrompt(root, input, key),
|
|
1689
|
+
runCfg,
|
|
1690
|
+
rs,
|
|
1691
|
+
emit,
|
|
1692
|
+
key,
|
|
1693
|
+
signal,
|
|
1694
|
+
);
|
|
1695
|
+
return finish(result.output, result.failed);
|
|
1696
|
+
} catch (e) {
|
|
1697
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
1698
|
+
const r = activeRun(details);
|
|
1699
|
+
if (r) {
|
|
1700
|
+
r.status = "failed";
|
|
1701
|
+
r.errorMessage = message;
|
|
1702
|
+
r.finishedAt = Date.now();
|
|
1703
|
+
}
|
|
1704
|
+
return finish(message, true);
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
// ── Extension ──────────────────────────────────────────────────────────
|
|
1709
|
+
export default function trellisExtension(pi: {
|
|
1710
|
+
registerTool?: (tool: JsonObject) => void;
|
|
1711
|
+
registerShortcut?: (
|
|
1712
|
+
key: string,
|
|
1713
|
+
opts: {
|
|
1714
|
+
description?: string;
|
|
1715
|
+
handler: (ctx: PiExtensionContext) => unknown;
|
|
1716
|
+
},
|
|
1717
|
+
) => void;
|
|
1718
|
+
on?: (
|
|
1719
|
+
event: string,
|
|
1720
|
+
handler: (event: unknown, ctx?: PiExtensionContext) => unknown,
|
|
1721
|
+
) => void;
|
|
1722
|
+
getThinkingLevel?: () => string;
|
|
1723
|
+
}): void {
|
|
1724
|
+
if (process.env.TRELLIS_SUBAGENT_CHILD === "1") return;
|
|
1725
|
+
const root = findRoot(process.cwd());
|
|
1726
|
+
const procKey = `pi_process_${hash([root, process.pid, Date.now(), randomBytes(8).toString("hex")].join(":"))}`;
|
|
1727
|
+
let curKey: string | null = null;
|
|
1728
|
+
|
|
1729
|
+
const getKey = (input?: unknown, ctx?: PiExtensionContext) => {
|
|
1730
|
+
const k = contextKey(input, ctx) ?? curKey ?? procKey;
|
|
1731
|
+
curKey = k;
|
|
1732
|
+
return k;
|
|
1733
|
+
};
|
|
1734
|
+
|
|
1735
|
+
// Per-turn cache to avoid double-spawning python
|
|
1736
|
+
let turnCache: {
|
|
1737
|
+
key: string | null;
|
|
1738
|
+
ts: number;
|
|
1739
|
+
wf: string;
|
|
1740
|
+
ov: string;
|
|
1741
|
+
} | null = null;
|
|
1742
|
+
const getTurnCtx = (k: string | null) => {
|
|
1743
|
+
const now = Date.now();
|
|
1744
|
+
if (turnCache && turnCache.key === k && now - turnCache.ts < 1500)
|
|
1745
|
+
return turnCache;
|
|
1746
|
+
turnCache = {
|
|
1747
|
+
key: k,
|
|
1748
|
+
ts: now,
|
|
1749
|
+
wf: workflowBreadcrumb(root, k),
|
|
1750
|
+
ov: sessionOverview(root, k),
|
|
1751
|
+
};
|
|
1752
|
+
return turnCache;
|
|
1753
|
+
};
|
|
1754
|
+
// Provider prefix caches invalidate from byte 0 whenever the system prompt
|
|
1755
|
+
// changes, so everything injected into systemPrompt is memoized per context
|
|
1756
|
+
// key and stays byte-identical for the life of the process. Volatile state
|
|
1757
|
+
// travels through persisted custom messages instead (append-only history).
|
|
1758
|
+
const startupCtxCache = new Map<string, string>();
|
|
1759
|
+
const getStartupCtx = (
|
|
1760
|
+
k: string | null,
|
|
1761
|
+
turn: { ov: string },
|
|
1762
|
+
): string => {
|
|
1763
|
+
const key = k ?? "default";
|
|
1764
|
+
let startup = startupCtxCache.get(key);
|
|
1765
|
+
if (startup === undefined) {
|
|
1766
|
+
startup = buildStartupContext(root, k, turn.ov);
|
|
1767
|
+
startupCtxCache.set(key, startup);
|
|
1768
|
+
}
|
|
1769
|
+
return startup;
|
|
1770
|
+
};
|
|
1771
|
+
const taskCtxSnapshot = new Map<string, string>();
|
|
1772
|
+
const lastSentTaskCtx = new Map<string, string>();
|
|
1773
|
+
const lastSentRuntimeCtx = new Map<string, string>();
|
|
1774
|
+
|
|
1775
|
+
// Toggle only the latest subagent native card; do not use Pi global tool expansion.
|
|
1776
|
+
const toggleDetail = (ctx: PiExtensionContext) => {
|
|
1777
|
+
const id = activeSubagentToolCallId;
|
|
1778
|
+
const card = id ? nativeCards.get(id) : undefined;
|
|
1779
|
+
if (!card) {
|
|
1780
|
+
ctx.ui?.notify?.("No subagent card to toggle yet.", "warning");
|
|
1781
|
+
return;
|
|
1782
|
+
}
|
|
1783
|
+
card.state.localExpanded = card.state.localExpanded !== true;
|
|
1784
|
+
card.invalidate();
|
|
1785
|
+
};
|
|
1786
|
+
|
|
1787
|
+
pi.registerShortcut?.("alt+o", {
|
|
1788
|
+
description: "Toggle latest subagent card details",
|
|
1789
|
+
handler: async (ctx: PiExtensionContext) => toggleDetail(ctx),
|
|
1790
|
+
});
|
|
1791
|
+
|
|
1792
|
+
// Tool registration
|
|
1793
|
+
pi.registerTool?.({
|
|
1794
|
+
name: "trellis_subagent",
|
|
1795
|
+
label: "Trellis Subagent",
|
|
1796
|
+
description: "Run a Trellis project sub-agent with active task context.",
|
|
1797
|
+
promptSnippet:
|
|
1798
|
+
'Sub-agent dispatch protocol (Trellis): your dispatch prompt MUST start with one line "Active task: <task path from `task.py current`>" before any other instructions.',
|
|
1799
|
+
promptGuidelines: [
|
|
1800
|
+
'Use subagent for task delegation. Your dispatch prompt MUST start with "Active task: <task path from `task.py current`>".',
|
|
1801
|
+
],
|
|
1802
|
+
parameters: {
|
|
1803
|
+
type: "object",
|
|
1804
|
+
properties: {
|
|
1805
|
+
agent: {
|
|
1806
|
+
type: "string",
|
|
1807
|
+
description:
|
|
1808
|
+
"Agent name, such as trellis-implement or trellis-check.",
|
|
1809
|
+
},
|
|
1810
|
+
prompt: {
|
|
1811
|
+
type: "string",
|
|
1812
|
+
description: "Task prompt for the sub-agent.",
|
|
1813
|
+
},
|
|
1814
|
+
mode: { type: "string", enum: ["single", "parallel", "chain"] },
|
|
1815
|
+
prompts: {
|
|
1816
|
+
type: "array",
|
|
1817
|
+
items: { type: "string" },
|
|
1818
|
+
maxItems: MAX_PARALLEL_PROMPTS,
|
|
1819
|
+
},
|
|
1820
|
+
model: {
|
|
1821
|
+
type: "string",
|
|
1822
|
+
description:
|
|
1823
|
+
"Optional Pi model override for the child sub-agent process.",
|
|
1824
|
+
},
|
|
1825
|
+
thinking: {
|
|
1826
|
+
type: "string",
|
|
1827
|
+
description:
|
|
1828
|
+
"Optional Pi thinking level override for the child sub-agent process.",
|
|
1829
|
+
enum: ["off", "minimal", "low", "medium", "high", "xhigh", "max"],
|
|
1830
|
+
},
|
|
1831
|
+
},
|
|
1832
|
+
},
|
|
1833
|
+
execute: async (
|
|
1834
|
+
id: string,
|
|
1835
|
+
input: SubagentInput,
|
|
1836
|
+
signal?: AbortSignal,
|
|
1837
|
+
onUpdate?: (r: PiToolResult) => void,
|
|
1838
|
+
ctx?: PiExtensionContext,
|
|
1839
|
+
) => {
|
|
1840
|
+
activeSubagentToolCallId = id;
|
|
1841
|
+
const agentName = normalizeAgent(input.agent);
|
|
1842
|
+
if (!isTrellisAgent(root, agentName)) {
|
|
1843
|
+
return {
|
|
1844
|
+
content: [
|
|
1845
|
+
{
|
|
1846
|
+
type: "text",
|
|
1847
|
+
text:
|
|
1848
|
+
"`trellis_subagent` is only for Trellis workflow agents with a definition file in .pi/agents/.\n\n" +
|
|
1849
|
+
`No definition found for: ${agentName}\n\n` +
|
|
1850
|
+
"For general-purpose sub-agents, use one of these community tools:\n" +
|
|
1851
|
+
"- `subagent` tool from npm:pi-subagents (nicobailon/pi-subagents)\n" +
|
|
1852
|
+
"- `Agent` tool from npm:@tintinweb/pi-subagents\n\n" +
|
|
1853
|
+
"If neither is installed, ask the user to either:\n" +
|
|
1854
|
+
`- Create .pi/agents/${agentName}.md for your custom Trellis agent\n` +
|
|
1855
|
+
"- Install a community subagent package: pi install -l npm:@tintinweb/pi-subagents",
|
|
1856
|
+
},
|
|
1857
|
+
],
|
|
1858
|
+
details: { agent: agentName, error: "not a trellis workflow agent" },
|
|
1859
|
+
};
|
|
1860
|
+
}
|
|
1861
|
+
const mode = input.mode ?? "single";
|
|
1862
|
+
const prompt = input.prompt?.trim();
|
|
1863
|
+
const prompts = input.prompts?.map((p) => p.trim()).filter(Boolean);
|
|
1864
|
+
if (mode === "single" && !prompt)
|
|
1865
|
+
throw new Error("subagent prompt is required for single mode");
|
|
1866
|
+
if (
|
|
1867
|
+
(mode === "parallel" || mode === "chain") &&
|
|
1868
|
+
!prompt &&
|
|
1869
|
+
!prompts?.length
|
|
1870
|
+
)
|
|
1871
|
+
throw new Error(
|
|
1872
|
+
"subagent prompt or prompts are required for parallel/chain mode",
|
|
1873
|
+
);
|
|
1874
|
+
if (
|
|
1875
|
+
mode === "parallel" &&
|
|
1876
|
+
prompts &&
|
|
1877
|
+
prompts.length > MAX_PARALLEL_PROMPTS
|
|
1878
|
+
)
|
|
1879
|
+
throw new Error(
|
|
1880
|
+
`subagent parallel mode supports at most ${MAX_PARALLEL_PROMPTS} prompts`,
|
|
1881
|
+
);
|
|
1882
|
+
const cleanInput: SubagentInput = {
|
|
1883
|
+
...input,
|
|
1884
|
+
prompt,
|
|
1885
|
+
prompts: prompts?.length ? prompts : undefined,
|
|
1886
|
+
};
|
|
1887
|
+
const key = getKey(cleanInput, ctx);
|
|
1888
|
+
const inheritedThinking = pi.getThinkingLevel?.();
|
|
1889
|
+
const inheritedModel = contextModelRef(ctx);
|
|
1890
|
+
const result = await runSubagent(
|
|
1891
|
+
root,
|
|
1892
|
+
cleanInput,
|
|
1893
|
+
key,
|
|
1894
|
+
signal,
|
|
1895
|
+
onUpdate,
|
|
1896
|
+
inheritedThinking,
|
|
1897
|
+
inheritedModel,
|
|
1898
|
+
);
|
|
1899
|
+
return {
|
|
1900
|
+
content: [{ type: "text", text: result.output }],
|
|
1901
|
+
details: result.details,
|
|
1902
|
+
};
|
|
1903
|
+
},
|
|
1904
|
+
// Hide the call renderer so the native card only shows result/progress once.
|
|
1905
|
+
renderCall: () => ({
|
|
1906
|
+
render() {
|
|
1907
|
+
return [];
|
|
1908
|
+
},
|
|
1909
|
+
invalidate() {},
|
|
1910
|
+
}),
|
|
1911
|
+
renderResult: (
|
|
1912
|
+
result: PiToolResult,
|
|
1913
|
+
_opts?: { expanded?: boolean; isPartial?: boolean },
|
|
1914
|
+
_theme?: unknown,
|
|
1915
|
+
context?: unknown,
|
|
1916
|
+
) => {
|
|
1917
|
+
const ctxObj = isObj(context) ? context : null;
|
|
1918
|
+
const toolCallId = str(ctxObj?.toolCallId);
|
|
1919
|
+
const state = isObj(ctxObj?.state) ? (ctxObj.state as JsonObject) : null;
|
|
1920
|
+
const invalidate =
|
|
1921
|
+
typeof ctxObj?.invalidate === "function"
|
|
1922
|
+
? (ctxObj.invalidate as () => void)
|
|
1923
|
+
: null;
|
|
1924
|
+
const isProgress =
|
|
1925
|
+
isObj(result.details) &&
|
|
1926
|
+
result.details.kind === "trellis-subagent-progress";
|
|
1927
|
+
if (toolCallId && state && invalidate) {
|
|
1928
|
+
const updatedAt = isProgress
|
|
1929
|
+
? (result.details as ProgressDetails).updatedAt
|
|
1930
|
+
: Date.now();
|
|
1931
|
+
rememberNativeCard(toolCallId, { state, invalidate, updatedAt });
|
|
1932
|
+
}
|
|
1933
|
+
return {
|
|
1934
|
+
render(w: number) {
|
|
1935
|
+
if (isProgress) {
|
|
1936
|
+
const expanded = state?.localExpanded === true;
|
|
1937
|
+
return renderProgressCard(
|
|
1938
|
+
result.details as ProgressDetails,
|
|
1939
|
+
expanded,
|
|
1940
|
+
w,
|
|
1941
|
+
);
|
|
1942
|
+
}
|
|
1943
|
+
return [trunc(result.content?.[0]?.text ?? "(no output)", w)];
|
|
1944
|
+
},
|
|
1945
|
+
invalidate() {},
|
|
1946
|
+
};
|
|
1947
|
+
},
|
|
1948
|
+
});
|
|
1949
|
+
|
|
1950
|
+
// Events
|
|
1951
|
+
pi.on?.("session_start", (event, ctx) => {
|
|
1952
|
+
getKey(event, ctx);
|
|
1953
|
+
ctx?.ui?.notify?.(
|
|
1954
|
+
"Trellis project context is available. Use /trellis-start to bootstrap or /trellis-continue to resume.",
|
|
1955
|
+
"info",
|
|
1956
|
+
);
|
|
1957
|
+
});
|
|
1958
|
+
pi.on?.("session_shutdown", () => {
|
|
1959
|
+
nativeCards.clear();
|
|
1960
|
+
activeSubagentToolCallId = null;
|
|
1961
|
+
});
|
|
1962
|
+
pi.on?.("tool_call", (event, ctx) => {
|
|
1963
|
+
const k = getKey(event, ctx);
|
|
1964
|
+
const ev = event as { toolName?: string; input?: JsonObject };
|
|
1965
|
+
if (
|
|
1966
|
+
ev.toolName === "bash" &&
|
|
1967
|
+
isObj(ev.input) &&
|
|
1968
|
+
typeof ev.input.command === "string" &&
|
|
1969
|
+
!cmdHasTrellisCtx(ev.input.command)
|
|
1970
|
+
)
|
|
1971
|
+
ev.input.command = `export TRELLIS_CONTEXT_ID=${shellQuote(k)}; ${ev.input.command}`;
|
|
1972
|
+
});
|
|
1973
|
+
// Preserve progress details from execute(); mark failed subagent results through
|
|
1974
|
+
// the official tool_result patch hook instead of throwing away renderer details.
|
|
1975
|
+
pi.on?.("tool_result", (event) => {
|
|
1976
|
+
const ev = event as { toolName?: string; details?: unknown };
|
|
1977
|
+
if (
|
|
1978
|
+
ev.toolName === "trellis_subagent" &&
|
|
1979
|
+
isObj(ev.details) &&
|
|
1980
|
+
ev.details.kind === "trellis-subagent-progress" &&
|
|
1981
|
+
Array.isArray(ev.details.runs) &&
|
|
1982
|
+
ev.details.runs.some(
|
|
1983
|
+
(r) => isObj(r) && (r.status === "failed" || r.status === "cancelled"),
|
|
1984
|
+
)
|
|
1985
|
+
)
|
|
1986
|
+
return { isError: true };
|
|
1987
|
+
return undefined;
|
|
1988
|
+
});
|
|
1989
|
+
pi.on?.("before_agent_start", (event, ctx) => {
|
|
1990
|
+
const k = getKey(event, ctx);
|
|
1991
|
+
const key = k ?? "default";
|
|
1992
|
+
const cur = (event as { systemPrompt?: string }).systemPrompt ?? "";
|
|
1993
|
+
const turn = getTurnCtx(k);
|
|
1994
|
+
const startup = getStartupCtx(k, turn);
|
|
1995
|
+
// Task context is snapshotted into systemPrompt once; later on-disk
|
|
1996
|
+
// changes are delivered as persisted messages so the prefix stays stable.
|
|
1997
|
+
const freshTaskCtx = buildContext(root, "trellis-implement", k);
|
|
1998
|
+
let taskCtx = taskCtxSnapshot.get(key);
|
|
1999
|
+
if (taskCtx === undefined) {
|
|
2000
|
+
taskCtx = freshTaskCtx;
|
|
2001
|
+
taskCtxSnapshot.set(key, taskCtx);
|
|
2002
|
+
lastSentTaskCtx.set(key, freshTaskCtx);
|
|
2003
|
+
}
|
|
2004
|
+
const updates: string[] = [];
|
|
2005
|
+
const runtimeContext = [turn.wf, turn.ov].filter(Boolean).join("\n\n");
|
|
2006
|
+
if (runtimeContext && runtimeContext !== lastSentRuntimeCtx.get(key)) {
|
|
2007
|
+
lastSentRuntimeCtx.set(key, runtimeContext);
|
|
2008
|
+
updates.push(runtimeContext);
|
|
2009
|
+
}
|
|
2010
|
+
if (freshTaskCtx !== lastSentTaskCtx.get(key)) {
|
|
2011
|
+
lastSentTaskCtx.set(key, freshTaskCtx);
|
|
2012
|
+
updates.push(
|
|
2013
|
+
"<trellis-task-context-update>\nTask context changed on disk. This supersedes the Trellis Task Context in the system prompt.\n\n" +
|
|
2014
|
+
freshTaskCtx +
|
|
2015
|
+
"\n</trellis-task-context-update>",
|
|
2016
|
+
);
|
|
2017
|
+
}
|
|
2018
|
+
const content = updates.join("\n\n");
|
|
2019
|
+
return {
|
|
2020
|
+
message: content
|
|
2021
|
+
? {
|
|
2022
|
+
customType: "trellis-runtime-context",
|
|
2023
|
+
content,
|
|
2024
|
+
display: false,
|
|
2025
|
+
}
|
|
2026
|
+
: undefined,
|
|
2027
|
+
systemPrompt: [cur, startup, taskCtx].filter(Boolean).join("\n\n"),
|
|
2028
|
+
};
|
|
2029
|
+
});
|
|
2030
|
+
pi.on?.("context", (event, ctx) => {
|
|
2031
|
+
getKey(event, ctx);
|
|
2032
|
+
});
|
|
2033
|
+
}
|