trellis-herbivore 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +174 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/commands/channel/adapters/claude.d.ts +38 -0
- package/dist/commands/channel/adapters/claude.d.ts.map +1 -0
- package/dist/commands/channel/adapters/claude.js +209 -0
- package/dist/commands/channel/adapters/claude.js.map +1 -0
- package/dist/commands/channel/adapters/codex.d.ts +77 -0
- package/dist/commands/channel/adapters/codex.d.ts.map +1 -0
- package/dist/commands/channel/adapters/codex.js +495 -0
- package/dist/commands/channel/adapters/codex.js.map +1 -0
- package/dist/commands/channel/adapters/index.d.ts +79 -0
- package/dist/commands/channel/adapters/index.d.ts.map +1 -0
- package/dist/commands/channel/adapters/index.js +109 -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 +154 -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 +290 -0
- package/dist/commands/channel/context-loader.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/index.d.ts +3 -0
- package/dist/commands/channel/index.d.ts.map +1 -0
- package/dist/commands/channel/index.js +496 -0
- package/dist/commands/channel/index.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 +16 -0
- package/dist/commands/channel/messages.d.ts.map +1 -0
- package/dist/commands/channel/messages.js +237 -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 +31 -0
- package/dist/commands/channel/run.d.ts.map +1 -0
- package/dist/commands/channel/run.js +137 -0
- package/dist/commands/channel/run.js.map +1 -0
- package/dist/commands/channel/send.d.ts +12 -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 +25 -0
- package/dist/commands/channel/spawn.d.ts.map +1 -0
- package/dist/commands/channel/spawn.js +192 -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 +63 -0
- package/dist/commands/channel/store/paths.d.ts.map +1 -0
- package/dist/commands/channel/store/paths.js +246 -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 +130 -0
- package/dist/commands/channel/store/watch.js.map +1 -0
- package/dist/commands/channel/supervisor/inbox.d.ts +25 -0
- package/dist/commands/channel/supervisor/inbox.d.ts.map +1 -0
- package/dist/commands/channel/supervisor/inbox.js +99 -0
- package/dist/commands/channel/supervisor/inbox.js.map +1 -0
- package/dist/commands/channel/supervisor/shutdown.d.ts +66 -0
- package/dist/commands/channel/supervisor/shutdown.d.ts.map +1 -0
- package/dist/commands/channel/supervisor/shutdown.js +143 -0
- package/dist/commands/channel/supervisor/shutdown.js.map +1 -0
- package/dist/commands/channel/supervisor/stdout.d.ts +49 -0
- package/dist/commands/channel/supervisor/stdout.d.ts.map +1 -0
- package/dist/commands/channel/supervisor/stdout.js +107 -0
- package/dist/commands/channel/supervisor/stdout.js.map +1 -0
- package/dist/commands/channel/supervisor.d.ts +47 -0
- package/dist/commands/channel/supervisor.d.ts.map +1 -0
- package/dist/commands/channel/supervisor.js +283 -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 +18 -0
- package/dist/commands/channel/wait.d.ts.map +1 -0
- package/dist/commands/channel/wait.js +76 -0
- package/dist/commands/channel/wait.js.map +1 -0
- package/dist/commands/init.d.ts +57 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +1466 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/mem.d.ts +234 -0
- package/dist/commands/mem.d.ts.map +1 -0
- package/dist/commands/mem.js +1869 -0
- package/dist/commands/mem.js.map +1 -0
- package/dist/commands/uninstall.d.ts +27 -0
- package/dist/commands/uninstall.d.ts.map +1 -0
- package/dist/commands/uninstall.js +339 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/update.d.ts +72 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +1926 -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/configurators/antigravity.d.ts +7 -0
- package/dist/configurators/antigravity.d.ts.map +1 -0
- package/dist/configurators/antigravity.js +19 -0
- package/dist/configurators/antigravity.js.map +1 -0
- package/dist/configurators/claude.d.ts +9 -0
- package/dist/configurators/claude.d.ts.map +1 -0
- package/dist/configurators/claude.js +72 -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 +30 -0
- package/dist/configurators/codebuddy.js.map +1 -0
- package/dist/configurators/codex.d.ts +8 -0
- package/dist/configurators/codex.d.ts.map +1 -0
- package/dist/configurators/codex.js +87 -0
- package/dist/configurators/codex.js.map +1 -0
- package/dist/configurators/copilot.d.ts +10 -0
- package/dist/configurators/copilot.d.ts.map +1 -0
- package/dist/configurators/copilot.js +51 -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 +29 -0
- package/dist/configurators/cursor.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 +30 -0
- package/dist/configurators/droid.js.map +1 -0
- package/dist/configurators/gemini.d.ts +16 -0
- package/dist/configurators/gemini.d.ts.map +1 -0
- package/dist/configurators/gemini.js +38 -0
- package/dist/configurators/gemini.js.map +1 -0
- package/dist/configurators/index.d.ts +65 -0
- package/dist/configurators/index.d.ts.map +1 -0
- package/dist/configurators/index.js +367 -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 +19 -0
- package/dist/configurators/kilo.js.map +1 -0
- package/dist/configurators/kiro.d.ts +8 -0
- package/dist/configurators/kiro.d.ts.map +1 -0
- package/dist/configurators/kiro.js +24 -0
- package/dist/configurators/kiro.js.map +1 -0
- package/dist/configurators/opencode.d.ts +14 -0
- package/dist/configurators/opencode.d.ts.map +1 -0
- package/dist/configurators/opencode.js +96 -0
- package/dist/configurators/opencode.js.map +1 -0
- package/dist/configurators/pi.d.ts +3 -0
- package/dist/configurators/pi.d.ts.map +1 -0
- package/dist/configurators/pi.js +45 -0
- package/dist/configurators/pi.js.map +1 -0
- package/dist/configurators/qoder.d.ts +11 -0
- package/dist/configurators/qoder.d.ts.map +1 -0
- package/dist/configurators/qoder.js +31 -0
- package/dist/configurators/qoder.js.map +1 -0
- package/dist/configurators/shared.d.ts +178 -0
- package/dist/configurators/shared.d.ts.map +1 -0
- package/dist/configurators/shared.js +538 -0
- package/dist/configurators/shared.js.map +1 -0
- package/dist/configurators/windsurf.d.ts +7 -0
- package/dist/configurators/windsurf.d.ts.map +1 -0
- package/dist/configurators/windsurf.js +19 -0
- package/dist/configurators/windsurf.js.map +1 -0
- package/dist/configurators/workflow.d.ts +29 -0
- package/dist/configurators/workflow.d.ts.map +1 -0
- package/dist/configurators/workflow.js +163 -0
- package/dist/configurators/workflow.js.map +1 -0
- package/dist/constants/paths.d.ts +70 -0
- package/dist/constants/paths.d.ts.map +1 -0
- package/dist/constants/paths.js +79 -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.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.2.json +9 -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/templates/claude/agents/trellis-check.md +114 -0
- package/dist/templates/claude/agents/trellis-implement.md +113 -0
- package/dist/templates/claude/agents/trellis-research.md +137 -0
- package/dist/templates/claude/index.d.ts +22 -0
- package/dist/templates/claude/index.d.ts.map +1 -0
- package/dist/templates/claude/index.js +46 -0
- package/dist/templates/claude/index.js.map +1 -0
- package/dist/templates/claude/settings.json +73 -0
- package/dist/templates/codebuddy/agents/trellis-check.md +109 -0
- package/dist/templates/codebuddy/agents/trellis-implement.md +110 -0
- package/dist/templates/codebuddy/agents/trellis-research.md +137 -0
- package/dist/templates/codebuddy/index.d.ts +15 -0
- package/dist/templates/codebuddy/index.d.ts.map +1 -0
- package/dist/templates/codebuddy/index.js +15 -0
- package/dist/templates/codebuddy/index.js.map +1 -0
- package/dist/templates/codebuddy/settings.json +59 -0
- package/dist/templates/codex/agents/trellis-check.toml +84 -0
- package/dist/templates/codex/agents/trellis-implement.toml +65 -0
- package/dist/templates/codex/agents/trellis-research.toml +73 -0
- package/dist/templates/codex/config.toml +35 -0
- package/dist/templates/codex/hooks/session-start.py +545 -0
- package/dist/templates/codex/hooks.json +15 -0
- package/dist/templates/codex/index.d.ts +39 -0
- package/dist/templates/codex/index.d.ts.map +1 -0
- package/dist/templates/codex/index.js +85 -0
- package/dist/templates/codex/index.js.map +1 -0
- package/dist/templates/codex/skills/before-dev/SKILL.md +40 -0
- package/dist/templates/codex/skills/brainstorm/SKILL.md +112 -0
- package/dist/templates/codex/skills/break-loop/SKILL.md +130 -0
- package/dist/templates/codex/skills/check/SKILL.md +98 -0
- package/dist/templates/codex/skills/check-cross-layer/SKILL.md +158 -0
- package/dist/templates/codex/skills/create-command/SKILL.md +101 -0
- package/dist/templates/codex/skills/finish-work/SKILL.md +90 -0
- package/dist/templates/codex/skills/improve-ut/SKILL.md +69 -0
- package/dist/templates/codex/skills/integrate-skill/SKILL.md +221 -0
- package/dist/templates/codex/skills/onboard/SKILL.md +363 -0
- package/dist/templates/codex/skills/record-session/SKILL.md +67 -0
- package/dist/templates/codex/skills/start/SKILL.md +64 -0
- package/dist/templates/codex/skills/update-spec/SKILL.md +335 -0
- package/dist/templates/common/bundled-skills/trellis-meta/SKILL.md +73 -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 +54 -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 +78 -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/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/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 +103 -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 +80 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/hooks-and-settings.md +69 -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 +74 -0
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/skills-and-commands.md +83 -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 +35 -0
- package/dist/templates/common/skills/brainstorm.md +112 -0
- package/dist/templates/common/skills/break-loop.md +125 -0
- package/dist/templates/common/skills/check.md +93 -0
- package/dist/templates/common/skills/update-spec.md +351 -0
- package/dist/templates/copilot/hooks/session-start.py +547 -0
- package/dist/templates/copilot/hooks.json +19 -0
- package/dist/templates/copilot/index.d.ts +23 -0
- package/dist/templates/copilot/index.d.ts.map +1 -0
- package/dist/templates/copilot/index.js +54 -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 +111 -0
- package/dist/templates/copilot/prompts/break-loop.prompt.md +129 -0
- package/dist/templates/copilot/prompts/check-cross-layer.prompt.md +157 -0
- package/dist/templates/copilot/prompts/check.prompt.md +97 -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 +108 -0
- package/dist/templates/cursor/agents/trellis-implement.md +109 -0
- package/dist/templates/cursor/agents/trellis-research.md +136 -0
- package/dist/templates/cursor/hooks.json +30 -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 +101 -0
- package/dist/templates/droid/droids/trellis-implement.md +102 -0
- package/dist/templates/droid/droids/trellis-research.md +137 -0
- package/dist/templates/droid/index.d.ts +15 -0
- package/dist/templates/droid/index.d.ts.map +1 -0
- package/dist/templates/droid/index.js +15 -0
- package/dist/templates/droid/index.js.map +1 -0
- package/dist/templates/droid/settings.json +59 -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 +101 -0
- package/dist/templates/gemini/agents/trellis-implement.md +102 -0
- package/dist/templates/gemini/agents/trellis-research.md +136 -0
- package/dist/templates/gemini/index.d.ts +13 -0
- package/dist/templates/gemini/index.d.ts.map +1 -0
- package/dist/templates/gemini/index.js +13 -0
- package/dist/templates/gemini/index.js.map +1 -0
- package/dist/templates/gemini/settings.json +28 -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/index.d.ts +18 -0
- package/dist/templates/kiro/index.d.ts.map +1 -0
- package/dist/templates/kiro/index.js +18 -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 +259 -0
- package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +595 -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/opencode/agents/trellis-check.md +116 -0
- package/dist/templates/opencode/agents/trellis-implement.md +118 -0
- package/dist/templates/opencode/agents/trellis-research.md +145 -0
- package/dist/templates/opencode/lib/session-utils.js +521 -0
- package/dist/templates/opencode/lib/trellis-context.js +381 -0
- package/dist/templates/opencode/package.json +5 -0
- package/dist/templates/opencode/plugins/inject-subagent-context.js +513 -0
- package/dist/templates/opencode/plugins/inject-workflow-state.js +159 -0
- package/dist/templates/opencode/plugins/session-start.js +101 -0
- package/dist/templates/pi/agents/trellis-check.md +36 -0
- package/dist/templates/pi/agents/trellis-implement.md +41 -0
- package/dist/templates/pi/agents/trellis-research.md +25 -0
- package/dist/templates/pi/extensions/trellis/index.ts.txt +1174 -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 +21 -0
- package/dist/templates/qoder/agents/trellis-check.md +102 -0
- package/dist/templates/qoder/agents/trellis-implement.md +103 -0
- package/dist/templates/qoder/agents/trellis-research.md +137 -0
- package/dist/templates/qoder/index.d.ts +15 -0
- package/dist/templates/qoder/index.d.ts.map +1 -0
- package/dist/templates/qoder/index.js +15 -0
- package/dist/templates/qoder/index.js.map +1 -0
- package/dist/templates/qoder/settings.json +47 -0
- package/dist/templates/shared-hooks/index.d.ts +50 -0
- package/dist/templates/shared-hooks/index.d.ts.map +1 -0
- package/dist/templates/shared-hooks/index.js +89 -0
- package/dist/templates/shared-hooks/index.js.map +1 -0
- package/dist/templates/shared-hooks/inject-shell-session-context.py +183 -0
- package/dist/templates/shared-hooks/inject-subagent-context.py +771 -0
- package/dist/templates/shared-hooks/inject-workflow-state.py +363 -0
- package/dist/templates/shared-hooks/session-start.py +827 -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 +60 -0
- package/dist/templates/template-utils.js.map +1 -0
- package/dist/templates/trellis/config.yaml +90 -0
- package/dist/templates/trellis/gitignore.txt +32 -0
- package/dist/templates/trellis/index.d.ts +52 -0
- package/dist/templates/trellis/index.d.ts.map +1 -0
- package/dist/templates/trellis/index.js +97 -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 +547 -0
- package/dist/templates/trellis/scripts/common/__init__.py +92 -0
- package/dist/templates/trellis/scripts/common/active_task.py +626 -0
- package/dist/templates/trellis/scripts/common/cli_adapter.py +811 -0
- package/dist/templates/trellis/scripts/common/config.py +445 -0
- package/dist/templates/trellis/scripts/common/developer.py +190 -0
- package/dist/templates/trellis/scripts/common/git.py +31 -0
- package/dist/templates/trellis/scripts/common/git_context.py +106 -0
- package/dist/templates/trellis/scripts/common/io.py +37 -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 +447 -0
- package/dist/templates/trellis/scripts/common/safe_commit.py +285 -0
- package/dist/templates/trellis/scripts/common/session_context.py +821 -0
- package/dist/templates/trellis/scripts/common/task_context.py +223 -0
- package/dist/templates/trellis/scripts/common/task_queue.py +188 -0
- package/dist/templates/trellis/scripts/common/task_store.py +698 -0
- package/dist/templates/trellis/scripts/common/task_utils.py +274 -0
- package/dist/templates/trellis/scripts/common/tasks.py +112 -0
- package/dist/templates/trellis/scripts/common/trellis_config.py +131 -0
- package/dist/templates/trellis/scripts/common/types.py +110 -0
- package/dist/templates/trellis/scripts/common/workflow_phase.py +212 -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 +500 -0
- package/dist/templates/trellis/tasks/.gitkeep +0 -0
- package/dist/templates/trellis/workflow.md +690 -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 +280 -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/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 +203 -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/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 +150 -0
- package/dist/utils/template-fetcher.d.ts.map +1 -0
- package/dist/utils/template-fetcher.js +907 -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 +334 -0
- package/dist/utils/template-hash.js.map +1 -0
- package/dist/utils/uninstall-scrubbers.d.ts +66 -0
- package/dist/utils/uninstall-scrubbers.d.ts.map +1 -0
- package/dist/utils/uninstall-scrubbers.js +342 -0
- package/dist/utils/uninstall-scrubbers.js.map +1 -0
- package/package.json +90 -0
|
@@ -0,0 +1,1174 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
3
|
+
import { delimiter, dirname, join, resolve } from "node:path";
|
|
4
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
5
|
+
|
|
6
|
+
type JsonObject = Record<string, unknown>;
|
|
7
|
+
type TextContent = { type: "text"; text: string };
|
|
8
|
+
|
|
9
|
+
interface PiToolResult {
|
|
10
|
+
content: TextContent[];
|
|
11
|
+
details?: JsonObject;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface PiExtensionContext {
|
|
15
|
+
hasUI?: boolean;
|
|
16
|
+
sessionManager?: {
|
|
17
|
+
getSessionId?: () => string;
|
|
18
|
+
getSessionFile?: () => string | undefined;
|
|
19
|
+
};
|
|
20
|
+
ui?: {
|
|
21
|
+
notify?: (message: string, type?: "info" | "warning" | "error") => void;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface PiBeforeAgentStartEvent {
|
|
26
|
+
systemPrompt?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface PiContextEvent {
|
|
30
|
+
messages?: unknown[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface PiToolCallEvent {
|
|
34
|
+
toolName?: string;
|
|
35
|
+
input?: JsonObject;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface SubagentInput {
|
|
39
|
+
agent?: string;
|
|
40
|
+
prompt?: string;
|
|
41
|
+
mode?: "single" | "parallel" | "chain";
|
|
42
|
+
prompts?: string[];
|
|
43
|
+
model?: string;
|
|
44
|
+
thinking?: ThinkingLevel;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
|
48
|
+
|
|
49
|
+
interface AgentConfig {
|
|
50
|
+
model?: string;
|
|
51
|
+
thinking?: ThinkingLevel;
|
|
52
|
+
// Parsed for pi-subagents-compatible agent files; Pi CLI has no documented fallback-model flag to pass through here.
|
|
53
|
+
fallbackModels: string[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface AgentDefinition {
|
|
57
|
+
content: string;
|
|
58
|
+
config: AgentConfig;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface PiRunConfig {
|
|
62
|
+
model?: string;
|
|
63
|
+
thinking?: ThinkingLevel;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const TRELLIS_AGENT_JSONL: Record<string, string> = {
|
|
67
|
+
"trellis-implement": "implement.jsonl",
|
|
68
|
+
implement: "implement.jsonl",
|
|
69
|
+
"trellis-check": "check.jsonl",
|
|
70
|
+
check: "check.jsonl",
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
function findProjectRoot(startDir: string): string {
|
|
74
|
+
let current = resolve(startDir);
|
|
75
|
+
while (true) {
|
|
76
|
+
if (
|
|
77
|
+
existsSync(join(current, ".trellis")) ||
|
|
78
|
+
existsSync(join(current, ".pi"))
|
|
79
|
+
) {
|
|
80
|
+
return current;
|
|
81
|
+
}
|
|
82
|
+
const parent = dirname(current);
|
|
83
|
+
if (parent === current) return resolve(startDir);
|
|
84
|
+
current = parent;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function readText(path: string): string {
|
|
89
|
+
try {
|
|
90
|
+
return readFileSync(path, "utf-8");
|
|
91
|
+
} catch {
|
|
92
|
+
return "";
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function splitMarkdownFrontmatter(content: string): {
|
|
97
|
+
frontmatter: string;
|
|
98
|
+
body: string;
|
|
99
|
+
} {
|
|
100
|
+
const normalized = content.replace(/^\uFEFF/, "");
|
|
101
|
+
const match = normalized.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
|
|
102
|
+
return match
|
|
103
|
+
? { frontmatter: match[1] ?? "", body: normalized.slice(match[0].length) }
|
|
104
|
+
: { frontmatter: "", body: normalized };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function stripMarkdownFrontmatter(content: string): string {
|
|
108
|
+
return splitMarkdownFrontmatter(content).body.trimStart();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function isJsonObject(value: unknown): value is JsonObject {
|
|
112
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function stringValue(value: unknown): string | null {
|
|
116
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const THINKING_LEVELS = [
|
|
120
|
+
"off",
|
|
121
|
+
"minimal",
|
|
122
|
+
"low",
|
|
123
|
+
"medium",
|
|
124
|
+
"high",
|
|
125
|
+
"xhigh",
|
|
126
|
+
] as const satisfies readonly ThinkingLevel[];
|
|
127
|
+
const THINKING_SUFFIX_RE = /:(?:off|minimal|low|medium|high|xhigh)$/i;
|
|
128
|
+
|
|
129
|
+
function normalizeThinking(value: unknown): ThinkingLevel | undefined {
|
|
130
|
+
const raw = stringValue(value)?.toLowerCase();
|
|
131
|
+
if (!raw) return undefined;
|
|
132
|
+
return THINKING_LEVELS.includes(raw as ThinkingLevel)
|
|
133
|
+
? (raw as ThinkingLevel)
|
|
134
|
+
: undefined;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function parseFrontmatterScalar(value: string): string | null {
|
|
138
|
+
const trimmed = value.trim();
|
|
139
|
+
if (
|
|
140
|
+
!trimmed ||
|
|
141
|
+
trimmed === "|" ||
|
|
142
|
+
trimmed === ">" ||
|
|
143
|
+
trimmed === "[]" ||
|
|
144
|
+
trimmed === "null" ||
|
|
145
|
+
trimmed === "~"
|
|
146
|
+
) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
if (
|
|
150
|
+
(trimmed.startsWith('"') && trimmed.endsWith('"')) ||
|
|
151
|
+
(trimmed.startsWith("'") && trimmed.endsWith("'"))
|
|
152
|
+
) {
|
|
153
|
+
return trimmed.slice(1, -1).trim() || null;
|
|
154
|
+
}
|
|
155
|
+
return trimmed;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function parseInlineList(value: string): string[] {
|
|
159
|
+
const trimmed = value.trim();
|
|
160
|
+
if (!trimmed || trimmed === "[]") return [];
|
|
161
|
+
const body =
|
|
162
|
+
trimmed.startsWith("[") && trimmed.endsWith("]")
|
|
163
|
+
? trimmed.slice(1, -1)
|
|
164
|
+
: trimmed;
|
|
165
|
+
return body
|
|
166
|
+
.split(",")
|
|
167
|
+
.map((item) => parseFrontmatterScalar(item))
|
|
168
|
+
.filter((item): item is string => !!item);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function readIndentedList(
|
|
172
|
+
lines: string[],
|
|
173
|
+
startIndex: number,
|
|
174
|
+
): { values: string[]; nextIndex: number } {
|
|
175
|
+
const values: string[] = [];
|
|
176
|
+
let index = startIndex + 1;
|
|
177
|
+
while (index < lines.length) {
|
|
178
|
+
const line = lines[index] ?? "";
|
|
179
|
+
if (/^[A-Za-z][A-Za-z0-9_-]*\s*:/.test(line)) break;
|
|
180
|
+
const item = line.match(/^\s*-\s*(.*)$/);
|
|
181
|
+
if (item) {
|
|
182
|
+
const scalar = parseFrontmatterScalar(item[1] ?? "");
|
|
183
|
+
if (scalar) values.push(scalar);
|
|
184
|
+
}
|
|
185
|
+
index += 1;
|
|
186
|
+
}
|
|
187
|
+
return { values, nextIndex: index - 1 };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function parseAgentConfig(content: string): AgentConfig {
|
|
191
|
+
const config: AgentConfig = { fallbackModels: [] };
|
|
192
|
+
const { frontmatter } = splitMarkdownFrontmatter(content);
|
|
193
|
+
const lines = frontmatter.split(/\r?\n/);
|
|
194
|
+
|
|
195
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
196
|
+
const match = (lines[index] ?? "").match(
|
|
197
|
+
/^([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.*)$/,
|
|
198
|
+
);
|
|
199
|
+
if (!match) continue;
|
|
200
|
+
|
|
201
|
+
const key = match[1] ?? "";
|
|
202
|
+
const value = match[2] ?? "";
|
|
203
|
+
if (key === "model") {
|
|
204
|
+
config.model = parseFrontmatterScalar(value) ?? undefined;
|
|
205
|
+
} else if (key === "thinking") {
|
|
206
|
+
config.thinking = normalizeThinking(parseFrontmatterScalar(value));
|
|
207
|
+
} else if (key === "fallbackModels" || key === "fallback_models") {
|
|
208
|
+
if (value.trim()) {
|
|
209
|
+
config.fallbackModels = parseInlineList(value);
|
|
210
|
+
} else {
|
|
211
|
+
const result = readIndentedList(lines, index);
|
|
212
|
+
config.fallbackModels = result.values;
|
|
213
|
+
index = result.nextIndex;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return config;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function modelHasThinkingSuffix(model: string): boolean {
|
|
222
|
+
return THINKING_SUFFIX_RE.test(model.trim());
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function buildPiModelArgs(config: PiRunConfig): string[] {
|
|
226
|
+
const model = stringValue(config.model);
|
|
227
|
+
const thinking = normalizeThinking(config.thinking);
|
|
228
|
+
if (model) {
|
|
229
|
+
return [
|
|
230
|
+
"--model",
|
|
231
|
+
thinking && !modelHasThinkingSuffix(model)
|
|
232
|
+
? `${model}:${thinking}`
|
|
233
|
+
: model,
|
|
234
|
+
];
|
|
235
|
+
}
|
|
236
|
+
return thinking ? ["--thinking", thinking] : [];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function resolveSubagentRunConfig(
|
|
240
|
+
input: SubagentInput,
|
|
241
|
+
agentConfig: AgentConfig,
|
|
242
|
+
): PiRunConfig {
|
|
243
|
+
return {
|
|
244
|
+
model: stringValue(input.model) ?? agentConfig.model,
|
|
245
|
+
thinking: normalizeThinking(input.thinking) ?? agentConfig.thinking,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function sanitizeKey(raw: string): string {
|
|
250
|
+
return raw
|
|
251
|
+
.trim()
|
|
252
|
+
.replace(/[^A-Za-z0-9._-]+/g, "_")
|
|
253
|
+
.replace(/^[._-]+|[._-]+$/g, "")
|
|
254
|
+
.slice(0, 160);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function hashValue(raw: string): string {
|
|
258
|
+
return createHash("sha256").update(raw).digest("hex").slice(0, 24);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
interface PiInvocation {
|
|
262
|
+
command: string;
|
|
263
|
+
argsPrefix: string[];
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const PI_CLI_JS_SEGMENTS = [
|
|
267
|
+
"node_modules",
|
|
268
|
+
"@mariozechner",
|
|
269
|
+
"pi-coding-agent",
|
|
270
|
+
"dist",
|
|
271
|
+
"cli.js",
|
|
272
|
+
];
|
|
273
|
+
const MAX_SUBAGENT_STDOUT_BYTES = 8 * 1024 * 1024;
|
|
274
|
+
const MAX_SUBAGENT_STDERR_BYTES = 1024 * 1024;
|
|
275
|
+
|
|
276
|
+
// Nested agents can emit unbounded output; keep the tail so diagnostics survive without growing memory indefinitely.
|
|
277
|
+
class BoundedBufferCollector {
|
|
278
|
+
private chunks: Buffer[] = [];
|
|
279
|
+
private length = 0;
|
|
280
|
+
private truncatedBytes = 0;
|
|
281
|
+
|
|
282
|
+
constructor(private readonly maxBytes: number) {}
|
|
283
|
+
|
|
284
|
+
append(chunk: Buffer): void {
|
|
285
|
+
const data = chunk;
|
|
286
|
+
if (data.length >= this.maxBytes) {
|
|
287
|
+
this.truncatedBytes += this.length + data.length - this.maxBytes;
|
|
288
|
+
this.chunks = [data.subarray(data.length - this.maxBytes)];
|
|
289
|
+
this.length = this.maxBytes;
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
this.chunks.push(data);
|
|
294
|
+
this.length += data.length;
|
|
295
|
+
|
|
296
|
+
while (this.length > this.maxBytes) {
|
|
297
|
+
const first = this.chunks[0];
|
|
298
|
+
if (!first) break;
|
|
299
|
+
const overflow = this.length - this.maxBytes;
|
|
300
|
+
if (first.length <= overflow) {
|
|
301
|
+
this.chunks.shift();
|
|
302
|
+
this.length -= first.length;
|
|
303
|
+
this.truncatedBytes += first.length;
|
|
304
|
+
} else {
|
|
305
|
+
this.chunks[0] = first.subarray(overflow);
|
|
306
|
+
this.length -= overflow;
|
|
307
|
+
this.truncatedBytes += overflow;
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
toString(): string {
|
|
314
|
+
const body = Buffer.concat(this.chunks, this.length).toString("utf-8");
|
|
315
|
+
return this.truncatedBytes
|
|
316
|
+
? `[${this.truncatedBytes} bytes truncated]\n${body}`
|
|
317
|
+
: body;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function isExistingFile(path: string): boolean {
|
|
322
|
+
try {
|
|
323
|
+
return statSync(path).isFile();
|
|
324
|
+
} catch {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function uniqueStrings(values: string[]): string[] {
|
|
330
|
+
const seen = new Set<string>();
|
|
331
|
+
const unique: string[] = [];
|
|
332
|
+
for (const value of values) {
|
|
333
|
+
if (!value || seen.has(value)) continue;
|
|
334
|
+
seen.add(value);
|
|
335
|
+
unique.push(value);
|
|
336
|
+
}
|
|
337
|
+
return unique;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function candidatePiCliJsPaths(): string[] {
|
|
341
|
+
const candidates: string[] = [];
|
|
342
|
+
|
|
343
|
+
for (const arg of process.argv) {
|
|
344
|
+
if (/pi-coding-agent[\\/]dist[\\/]cli\.js$/i.test(arg)) {
|
|
345
|
+
candidates.push(resolve(arg));
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const npmPrefix =
|
|
350
|
+
stringValue(process.env.npm_config_prefix) ??
|
|
351
|
+
stringValue(process.env.NPM_CONFIG_PREFIX);
|
|
352
|
+
if (npmPrefix) {
|
|
353
|
+
candidates.push(join(npmPrefix, ...PI_CLI_JS_SEGMENTS));
|
|
354
|
+
candidates.push(join(npmPrefix, "lib", ...PI_CLI_JS_SEGMENTS));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const appData = stringValue(process.env.APPDATA);
|
|
358
|
+
if (appData) {
|
|
359
|
+
candidates.push(join(appData, "npm", ...PI_CLI_JS_SEGMENTS));
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const pathValue = process.env.PATH ?? process.env.Path ?? "";
|
|
363
|
+
for (const pathEntry of pathValue.split(delimiter)) {
|
|
364
|
+
const entry = pathEntry.trim();
|
|
365
|
+
if (!entry) continue;
|
|
366
|
+
candidates.push(join(entry, ...PI_CLI_JS_SEGMENTS));
|
|
367
|
+
candidates.push(join(dirname(entry), ...PI_CLI_JS_SEGMENTS));
|
|
368
|
+
candidates.push(join(dirname(entry), "lib", ...PI_CLI_JS_SEGMENTS));
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return uniqueStrings(candidates);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function resolvePiInvocation(): PiInvocation {
|
|
375
|
+
const envCli = stringValue(process.env.TRELLIS_PI_CLI_JS);
|
|
376
|
+
if (envCli) {
|
|
377
|
+
const cliJs = resolve(envCli);
|
|
378
|
+
if (!isExistingFile(cliJs)) {
|
|
379
|
+
throw new Error(`TRELLIS_PI_CLI_JS points to a missing file: ${cliJs}`);
|
|
380
|
+
}
|
|
381
|
+
return { command: process.execPath, argsPrefix: [cliJs] };
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
for (const cliJs of candidatePiCliJsPaths()) {
|
|
385
|
+
if (isExistingFile(cliJs)) {
|
|
386
|
+
return { command: process.execPath, argsPrefix: [cliJs] };
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return { command: "pi", argsPrefix: [] };
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function createProcessContextKey(projectRoot: string): string {
|
|
394
|
+
return `pi_process_${hashValue(
|
|
395
|
+
[projectRoot, process.pid, Date.now(), randomBytes(8).toString("hex")].join(
|
|
396
|
+
":",
|
|
397
|
+
),
|
|
398
|
+
)}`;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function callString(
|
|
402
|
+
callback: (() => string | undefined) | undefined,
|
|
403
|
+
): string | null {
|
|
404
|
+
if (!callback) return null;
|
|
405
|
+
try {
|
|
406
|
+
return stringValue(callback());
|
|
407
|
+
} catch {
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function lookupString(data: unknown, keys: string[]): string | null {
|
|
413
|
+
if (!isJsonObject(data)) return null;
|
|
414
|
+
for (const key of keys) {
|
|
415
|
+
const value = stringValue(data[key]);
|
|
416
|
+
if (value) return value;
|
|
417
|
+
}
|
|
418
|
+
for (const nestedKey of [
|
|
419
|
+
"input",
|
|
420
|
+
"properties",
|
|
421
|
+
"event",
|
|
422
|
+
"hook_input",
|
|
423
|
+
"hookInput",
|
|
424
|
+
]) {
|
|
425
|
+
const nested = data[nestedKey];
|
|
426
|
+
const value = lookupString(nested, keys);
|
|
427
|
+
if (value) return value;
|
|
428
|
+
}
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function extractTextContent(content: unknown): string {
|
|
433
|
+
if (typeof content === "string") return content;
|
|
434
|
+
if (!Array.isArray(content)) return "";
|
|
435
|
+
|
|
436
|
+
return content
|
|
437
|
+
.map((block) => {
|
|
438
|
+
if (!isJsonObject(block)) return "";
|
|
439
|
+
return block.type === "text" && typeof block.text === "string"
|
|
440
|
+
? block.text
|
|
441
|
+
: "";
|
|
442
|
+
})
|
|
443
|
+
.join("");
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function extractFinalAssistantText(output: string): string | null {
|
|
447
|
+
let finalText = "";
|
|
448
|
+
|
|
449
|
+
for (const line of output.split(/\r?\n/)) {
|
|
450
|
+
const trimmed = line.trim();
|
|
451
|
+
if (!trimmed) continue;
|
|
452
|
+
|
|
453
|
+
try {
|
|
454
|
+
const event = JSON.parse(trimmed) as JsonObject;
|
|
455
|
+
const message = isJsonObject(event.message) ? event.message : null;
|
|
456
|
+
if (message?.role !== "assistant") continue;
|
|
457
|
+
|
|
458
|
+
const text = extractTextContent(message.content);
|
|
459
|
+
if (text) finalText = text;
|
|
460
|
+
} catch {
|
|
461
|
+
// Pi can print non-JSON diagnostics around structured output; keep scanning.
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
return finalText || null;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function formatPiOutput(stdout: string, stderr: string): string {
|
|
469
|
+
return extractFinalAssistantText(stdout) ?? (stdout || stderr);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function normalizeTaskRef(raw: string): string | null {
|
|
473
|
+
let normalized = raw.trim().replace(/\\/g, "/");
|
|
474
|
+
if (!normalized) return null;
|
|
475
|
+
while (normalized.startsWith("./")) normalized = normalized.slice(2);
|
|
476
|
+
if (normalized.startsWith("tasks/")) normalized = `.trellis/${normalized}`;
|
|
477
|
+
return normalized;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function taskRefToDir(projectRoot: string, taskRef: string): string {
|
|
481
|
+
if (taskRef.startsWith("/")) return taskRef;
|
|
482
|
+
if (taskRef.startsWith(".trellis/")) return join(projectRoot, taskRef);
|
|
483
|
+
return join(projectRoot, ".trellis", "tasks", taskRef);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function sessionFileHasCurrentTask(path: string): boolean {
|
|
487
|
+
try {
|
|
488
|
+
const context = JSON.parse(readText(path)) as JsonObject;
|
|
489
|
+
return !!normalizeTaskRef(stringValue(context.current_task) ?? "");
|
|
490
|
+
} catch {
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function activeRuntimeContextKeys(projectRoot: string): string[] {
|
|
496
|
+
const sessionsDir = join(projectRoot, ".trellis", ".runtime", "sessions");
|
|
497
|
+
try {
|
|
498
|
+
return readdirSync(sessionsDir, { withFileTypes: true })
|
|
499
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
|
|
500
|
+
.map((entry) => entry.name.slice(0, -".json".length))
|
|
501
|
+
.filter((key) =>
|
|
502
|
+
sessionFileHasCurrentTask(join(sessionsDir, `${key}.json`)),
|
|
503
|
+
);
|
|
504
|
+
} catch {
|
|
505
|
+
return [];
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
function adoptExistingContextKey(
|
|
510
|
+
projectRoot: string,
|
|
511
|
+
contextKey: string,
|
|
512
|
+
): string {
|
|
513
|
+
const sessionsDir = join(projectRoot, ".trellis", ".runtime", "sessions");
|
|
514
|
+
if (sessionFileHasCurrentTask(join(sessionsDir, `${contextKey}.json`))) {
|
|
515
|
+
return contextKey;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const keys = activeRuntimeContextKeys(projectRoot);
|
|
519
|
+
const processKeys = keys.filter((key) => key.startsWith("pi_process_"));
|
|
520
|
+
const candidates = processKeys.length ? processKeys : keys;
|
|
521
|
+
return candidates.length === 1 ? candidates[0] : contextKey;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function resolveContextKey(
|
|
525
|
+
input: unknown,
|
|
526
|
+
ctx?: PiExtensionContext,
|
|
527
|
+
fallback?: string | null,
|
|
528
|
+
): string | null {
|
|
529
|
+
const override = stringValue(process.env.TRELLIS_CONTEXT_ID);
|
|
530
|
+
if (override) return sanitizeKey(override) || hashValue(override);
|
|
531
|
+
|
|
532
|
+
const sessionId =
|
|
533
|
+
callString(ctx?.sessionManager?.getSessionId) ??
|
|
534
|
+
stringValue(process.env.PI_SESSION_ID) ??
|
|
535
|
+
stringValue(process.env.PI_SESSIONID) ??
|
|
536
|
+
lookupString(input, ["session_id", "sessionId", "sessionID"]);
|
|
537
|
+
if (sessionId) return `pi_${sanitizeKey(sessionId) || hashValue(sessionId)}`;
|
|
538
|
+
|
|
539
|
+
const transcriptPath =
|
|
540
|
+
callString(ctx?.sessionManager?.getSessionFile) ??
|
|
541
|
+
lookupString(input, ["transcript_path", "transcriptPath", "transcript"]);
|
|
542
|
+
if (transcriptPath) return `pi_transcript_${hashValue(transcriptPath)}`;
|
|
543
|
+
|
|
544
|
+
return fallback ?? null;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function readCurrentTask(
|
|
548
|
+
projectRoot: string,
|
|
549
|
+
platformInput?: unknown,
|
|
550
|
+
ctx?: PiExtensionContext,
|
|
551
|
+
contextKeyOverride?: string | null,
|
|
552
|
+
): string | null {
|
|
553
|
+
const contextKey =
|
|
554
|
+
contextKeyOverride ?? resolveContextKey(platformInput, ctx);
|
|
555
|
+
if (contextKey) {
|
|
556
|
+
try {
|
|
557
|
+
const rawContext = readText(
|
|
558
|
+
join(
|
|
559
|
+
projectRoot,
|
|
560
|
+
".trellis",
|
|
561
|
+
".runtime",
|
|
562
|
+
"sessions",
|
|
563
|
+
`${contextKey}.json`,
|
|
564
|
+
),
|
|
565
|
+
);
|
|
566
|
+
const context = JSON.parse(rawContext) as JsonObject;
|
|
567
|
+
const taskRef = normalizeTaskRef(stringValue(context.current_task) ?? "");
|
|
568
|
+
if (taskRef) return taskRefToDir(projectRoot, taskRef);
|
|
569
|
+
} catch {
|
|
570
|
+
// Missing or malformed session context means no active task.
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
return null;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function readJsonlFiles(
|
|
578
|
+
projectRoot: string,
|
|
579
|
+
taskDir: string,
|
|
580
|
+
jsonlName: string,
|
|
581
|
+
): string {
|
|
582
|
+
const jsonlPath = join(taskDir, jsonlName);
|
|
583
|
+
const lines = readText(jsonlPath).split(/\r?\n/);
|
|
584
|
+
const chunks: string[] = [];
|
|
585
|
+
|
|
586
|
+
for (const line of lines) {
|
|
587
|
+
const trimmed = line.trim();
|
|
588
|
+
if (!trimmed) continue;
|
|
589
|
+
try {
|
|
590
|
+
const row = JSON.parse(trimmed) as JsonObject;
|
|
591
|
+
const file = typeof row.file === "string" ? row.file : "";
|
|
592
|
+
if (!file) continue;
|
|
593
|
+
const content = readText(join(projectRoot, file));
|
|
594
|
+
if (content) {
|
|
595
|
+
chunks.push(`## ${file}\n\n${content}`);
|
|
596
|
+
}
|
|
597
|
+
} catch {
|
|
598
|
+
// Seed rows and malformed lines must not block sub-agent startup.
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
return chunks.join("\n\n---\n\n");
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
function buildTrellisContext(
|
|
606
|
+
projectRoot: string,
|
|
607
|
+
agent: string,
|
|
608
|
+
platformInput?: unknown,
|
|
609
|
+
ctx?: PiExtensionContext,
|
|
610
|
+
contextKey?: string | null,
|
|
611
|
+
): string {
|
|
612
|
+
const taskDir = readCurrentTask(projectRoot, platformInput, ctx, contextKey);
|
|
613
|
+
if (!taskDir) {
|
|
614
|
+
return "No active Trellis task found. Read .trellis/ before proceeding.";
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
const prd = readText(join(taskDir, "prd.md"));
|
|
618
|
+
const design = readText(join(taskDir, "design.md"));
|
|
619
|
+
const implementPlan = readText(join(taskDir, "implement.md"));
|
|
620
|
+
const jsonlName = TRELLIS_AGENT_JSONL[agent] ?? "";
|
|
621
|
+
const specContext = jsonlName
|
|
622
|
+
? readJsonlFiles(projectRoot, taskDir, jsonlName)
|
|
623
|
+
: "";
|
|
624
|
+
|
|
625
|
+
return [
|
|
626
|
+
"## Trellis Task Context",
|
|
627
|
+
`Task directory: ${taskDir}`,
|
|
628
|
+
"",
|
|
629
|
+
"### prd.md",
|
|
630
|
+
prd || "(missing)",
|
|
631
|
+
design ? "\n### design.md\n" + design : "",
|
|
632
|
+
implementPlan ? "\n### implement.md\n" + implementPlan : "",
|
|
633
|
+
specContext ? "\n### Curated Spec / Research Context\n" + specContext : "",
|
|
634
|
+
].join("\n");
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// ---------------------------------------------------------------------------
|
|
638
|
+
// Workflow-state breadcrumb (TypeScript port of the shared workflow-state
|
|
639
|
+
// hook used by class-1 platforms).
|
|
640
|
+
//
|
|
641
|
+
// Pi is extension-backed and MUST NOT receive Python hook scripts under .pi/.
|
|
642
|
+
// We therefore parse `.trellis/workflow.md` `[workflow-state:STATUS]...
|
|
643
|
+
// [/workflow-state:STATUS]` blocks directly in TypeScript and emit the
|
|
644
|
+
// per-turn `<workflow-state>` breadcrumb in `before_agent_start` and `input`.
|
|
645
|
+
// Tag regex mirrors the shared parser so the breadcrumb body stays
|
|
646
|
+
// byte-identical with hook-driven platforms.
|
|
647
|
+
// ---------------------------------------------------------------------------
|
|
648
|
+
|
|
649
|
+
const WORKFLOW_STATE_TAG_RE =
|
|
650
|
+
/\[workflow-state:([A-Za-z0-9_-]+)\]\s*\n([\s\S]*?)\n\s*\[\/workflow-state:\1\]/g;
|
|
651
|
+
|
|
652
|
+
function loadWorkflowBreadcrumbs(projectRoot: string): Record<string, string> {
|
|
653
|
+
const workflow = readText(join(projectRoot, ".trellis", "workflow.md"));
|
|
654
|
+
if (!workflow) return {};
|
|
655
|
+
const result: Record<string, string> = {};
|
|
656
|
+
for (const match of workflow.matchAll(WORKFLOW_STATE_TAG_RE)) {
|
|
657
|
+
const status = match[1] ?? "";
|
|
658
|
+
const body = (match[2] ?? "").trim();
|
|
659
|
+
if (status && body) result[status] = body;
|
|
660
|
+
}
|
|
661
|
+
return result;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
function readActiveTaskStatus(
|
|
665
|
+
projectRoot: string,
|
|
666
|
+
taskDir: string,
|
|
667
|
+
): { taskId: string; status: string } | null {
|
|
668
|
+
try {
|
|
669
|
+
const data = JSON.parse(
|
|
670
|
+
readText(join(taskDir, "task.json")),
|
|
671
|
+
) as JsonObject;
|
|
672
|
+
const status = stringValue(data.status);
|
|
673
|
+
if (!status) return null;
|
|
674
|
+
const id = stringValue(data.id) ?? taskDir.split(/[\\/]/).pop() ?? "";
|
|
675
|
+
return { taskId: id, status };
|
|
676
|
+
} catch {
|
|
677
|
+
return null;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
function buildWorkflowStateBreadcrumb(
|
|
682
|
+
projectRoot: string,
|
|
683
|
+
contextKey: string | null,
|
|
684
|
+
): string {
|
|
685
|
+
const templates = loadWorkflowBreadcrumbs(projectRoot);
|
|
686
|
+
const taskDir = readCurrentTask(
|
|
687
|
+
projectRoot,
|
|
688
|
+
undefined,
|
|
689
|
+
undefined,
|
|
690
|
+
contextKey,
|
|
691
|
+
);
|
|
692
|
+
let header: string;
|
|
693
|
+
let lookupKey: string;
|
|
694
|
+
if (!taskDir) {
|
|
695
|
+
header = "Status: no_task";
|
|
696
|
+
lookupKey = "no_task";
|
|
697
|
+
} else {
|
|
698
|
+
const info = readActiveTaskStatus(projectRoot, taskDir);
|
|
699
|
+
if (!info) {
|
|
700
|
+
header = "Status: no_task";
|
|
701
|
+
lookupKey = "no_task";
|
|
702
|
+
} else {
|
|
703
|
+
header = `Task: ${info.taskId} (${info.status})`;
|
|
704
|
+
lookupKey = info.status;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
const body = templates[lookupKey] ?? "Refer to workflow.md for current step.";
|
|
708
|
+
return `<workflow-state>\n${header}\n${body}\n</workflow-state>`;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// ---------------------------------------------------------------------------
|
|
712
|
+
// Session overview (developer / git branch / active tasks)
|
|
713
|
+
//
|
|
714
|
+
// Spawns `python3 .trellis/scripts/get_context.py` (the same script other
|
|
715
|
+
// platform session-start hooks invoke) to keep developer/git/active-task
|
|
716
|
+
// summary byte-identical with class-1 platforms. Failure is non-fatal — we
|
|
717
|
+
// emit an empty overview rather than block the conversation.
|
|
718
|
+
// ---------------------------------------------------------------------------
|
|
719
|
+
|
|
720
|
+
const SESSION_OVERVIEW_TIMEOUT_MS = 5000;
|
|
721
|
+
|
|
722
|
+
function pythonExecutable(): string {
|
|
723
|
+
const override = stringValue(process.env.TRELLIS_PYTHON);
|
|
724
|
+
if (override) return override;
|
|
725
|
+
return process.platform === "win32" ? "python" : "python3";
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
function buildSessionOverview(
|
|
729
|
+
projectRoot: string,
|
|
730
|
+
contextKey: string | null,
|
|
731
|
+
): string {
|
|
732
|
+
const script = join(projectRoot, ".trellis", "scripts", "get_context.py");
|
|
733
|
+
if (!isExistingFile(script)) return "";
|
|
734
|
+
try {
|
|
735
|
+
const result = spawnSync(pythonExecutable(), [script], {
|
|
736
|
+
cwd: projectRoot,
|
|
737
|
+
env: contextKey
|
|
738
|
+
? { ...process.env, TRELLIS_CONTEXT_ID: contextKey }
|
|
739
|
+
: process.env,
|
|
740
|
+
encoding: "utf-8",
|
|
741
|
+
timeout: SESSION_OVERVIEW_TIMEOUT_MS,
|
|
742
|
+
windowsHide: true,
|
|
743
|
+
});
|
|
744
|
+
if (result.status !== 0) return "";
|
|
745
|
+
const stdout = (result.stdout ?? "").trim();
|
|
746
|
+
if (!stdout) return "";
|
|
747
|
+
return `<session-overview>\n${stdout}\n</session-overview>`;
|
|
748
|
+
} catch {
|
|
749
|
+
return "";
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// Per-turn cache so input + before_agent_start in the same turn don't double-spawn.
|
|
754
|
+
class TurnContextCache {
|
|
755
|
+
private key: string | null = null;
|
|
756
|
+
private timestamp = 0;
|
|
757
|
+
private workflowState = "";
|
|
758
|
+
private sessionOverview = "";
|
|
759
|
+
// Refresh window: per-turn injections that fire close together share a
|
|
760
|
+
// single python3 spawn; anything older than this re-runs the resolver.
|
|
761
|
+
private static readonly TTL_MS = 1500;
|
|
762
|
+
|
|
763
|
+
get(
|
|
764
|
+
projectRoot: string,
|
|
765
|
+
contextKey: string | null,
|
|
766
|
+
): { workflowState: string; sessionOverview: string } {
|
|
767
|
+
const now = Date.now();
|
|
768
|
+
if (this.key === contextKey && now - this.timestamp < TurnContextCache.TTL_MS) {
|
|
769
|
+
return {
|
|
770
|
+
workflowState: this.workflowState,
|
|
771
|
+
sessionOverview: this.sessionOverview,
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
this.workflowState = buildWorkflowStateBreadcrumb(projectRoot, contextKey);
|
|
775
|
+
this.sessionOverview = buildSessionOverview(projectRoot, contextKey);
|
|
776
|
+
this.key = contextKey;
|
|
777
|
+
this.timestamp = now;
|
|
778
|
+
return {
|
|
779
|
+
workflowState: this.workflowState,
|
|
780
|
+
sessionOverview: this.sessionOverview,
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
// ---------------------------------------------------------------------------
|
|
786
|
+
// Sub-agent dispatch protocol snippet (registered with the `subagent` tool).
|
|
787
|
+
// Mirrors the [workflow-state:in_progress] dispatch protocol text in
|
|
788
|
+
// trellis/workflow.md so the AI sees the same `Active task: <path>` rule
|
|
789
|
+
// whether it reads workflow.md, the per-turn breadcrumb, or the tool prompt.
|
|
790
|
+
// ---------------------------------------------------------------------------
|
|
791
|
+
|
|
792
|
+
const SUBAGENT_DISPATCH_PROTOCOL = `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. No exceptions. On class-2 platforms (codex / copilot / gemini / qoder) the sub-agent depends on this line because there is no hook to inject task context. On class-1 platforms (claude / cursor / opencode / kiro / codebuddy / droid) and on Pi, the line is the canonical fallback when hook/extension injection misses. trellis-research uses the line to know which {task_dir}/research/ to write into.
|
|
793
|
+
|
|
794
|
+
Wrong: prompt: "implement the new feature"
|
|
795
|
+
Correct: prompt: "Active task: .trellis/tasks/05-09-pi-workflow-state-injection\\n\\nImplement the new feature ..."`;
|
|
796
|
+
|
|
797
|
+
function normalizeAgentName(agent: string): string {
|
|
798
|
+
return agent.startsWith("trellis-") ? agent : `trellis-${agent}`;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
function readAgentDefinition(
|
|
802
|
+
projectRoot: string,
|
|
803
|
+
agent: string,
|
|
804
|
+
): AgentDefinition {
|
|
805
|
+
const normalized = agent.startsWith("trellis-") ? agent : `trellis-${agent}`;
|
|
806
|
+
const raw = readText(join(projectRoot, ".pi", "agents", `${normalized}.md`));
|
|
807
|
+
return {
|
|
808
|
+
content: stripMarkdownFrontmatter(raw),
|
|
809
|
+
config: parseAgentConfig(raw),
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
function commandStartsWithTrellisContext(command: string): boolean {
|
|
814
|
+
const trimmed = command.trimStart();
|
|
815
|
+
return (
|
|
816
|
+
/^export\s+TRELLIS_CONTEXT_ID=/.test(trimmed) ||
|
|
817
|
+
/^TRELLIS_CONTEXT_ID=/.test(trimmed) ||
|
|
818
|
+
/^env\s+.*\bTRELLIS_CONTEXT_ID=/.test(trimmed)
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
function shellQuote(value: string): string {
|
|
823
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
function injectTrellisContextIntoBash(
|
|
827
|
+
event: unknown,
|
|
828
|
+
contextKey: string,
|
|
829
|
+
): boolean {
|
|
830
|
+
const toolCall = event as PiToolCallEvent;
|
|
831
|
+
if (toolCall.toolName !== "bash" || !isJsonObject(toolCall.input)) {
|
|
832
|
+
return false;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
const rawCommand = toolCall.input.command;
|
|
836
|
+
if (typeof rawCommand !== "string" || !rawCommand.trim()) {
|
|
837
|
+
return false;
|
|
838
|
+
}
|
|
839
|
+
if (commandStartsWithTrellisContext(rawCommand)) {
|
|
840
|
+
return false;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
toolCall.input.command = `export TRELLIS_CONTEXT_ID=${shellQuote(contextKey)}; ${rawCommand}`;
|
|
844
|
+
return true;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
function runPi(
|
|
848
|
+
projectRoot: string,
|
|
849
|
+
prompt: string,
|
|
850
|
+
runConfig: PiRunConfig,
|
|
851
|
+
contextKey?: string | null,
|
|
852
|
+
signal?: AbortSignal,
|
|
853
|
+
): Promise<string> {
|
|
854
|
+
return new Promise((resolvePromise, reject) => {
|
|
855
|
+
if (signal?.aborted) {
|
|
856
|
+
reject(new Error("pi subagent cancelled"));
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
const invocation = resolvePiInvocation();
|
|
861
|
+
const modelArgs = buildPiModelArgs(runConfig);
|
|
862
|
+
const child = spawn(
|
|
863
|
+
invocation.command,
|
|
864
|
+
[
|
|
865
|
+
...invocation.argsPrefix,
|
|
866
|
+
"--mode",
|
|
867
|
+
"text",
|
|
868
|
+
...modelArgs,
|
|
869
|
+
"-p",
|
|
870
|
+
"--no-session",
|
|
871
|
+
],
|
|
872
|
+
{
|
|
873
|
+
cwd: projectRoot,
|
|
874
|
+
env: contextKey
|
|
875
|
+
? { ...process.env, TRELLIS_CONTEXT_ID: contextKey }
|
|
876
|
+
: process.env,
|
|
877
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
878
|
+
windowsHide: true,
|
|
879
|
+
},
|
|
880
|
+
);
|
|
881
|
+
|
|
882
|
+
const stdout = new BoundedBufferCollector(MAX_SUBAGENT_STDOUT_BYTES);
|
|
883
|
+
const stderr = new BoundedBufferCollector(MAX_SUBAGENT_STDERR_BYTES);
|
|
884
|
+
let settled = false;
|
|
885
|
+
let aborted = false;
|
|
886
|
+
|
|
887
|
+
const abortChild = (): void => {
|
|
888
|
+
aborted = true;
|
|
889
|
+
child.kill();
|
|
890
|
+
};
|
|
891
|
+
|
|
892
|
+
const cleanup = (): void => {
|
|
893
|
+
signal?.removeEventListener("abort", abortChild);
|
|
894
|
+
};
|
|
895
|
+
|
|
896
|
+
const fail = (error: Error): void => {
|
|
897
|
+
if (settled) return;
|
|
898
|
+
settled = true;
|
|
899
|
+
cleanup();
|
|
900
|
+
reject(error);
|
|
901
|
+
};
|
|
902
|
+
|
|
903
|
+
const succeed = (value: string): void => {
|
|
904
|
+
if (settled) return;
|
|
905
|
+
settled = true;
|
|
906
|
+
cleanup();
|
|
907
|
+
resolvePromise(value);
|
|
908
|
+
};
|
|
909
|
+
|
|
910
|
+
signal?.addEventListener("abort", abortChild, { once: true });
|
|
911
|
+
|
|
912
|
+
child.stdout?.on("data", (chunk: Buffer) => stdout.append(chunk));
|
|
913
|
+
child.stderr?.on("data", (chunk: Buffer) => stderr.append(chunk));
|
|
914
|
+
child.stdin?.on("error", (error: Error & { code?: string }) => {
|
|
915
|
+
if (!aborted && error.code !== "EPIPE") fail(error);
|
|
916
|
+
});
|
|
917
|
+
child.on("error", fail);
|
|
918
|
+
child.on("close", (code) => {
|
|
919
|
+
const out = stdout.toString();
|
|
920
|
+
const err = stderr.toString();
|
|
921
|
+
if (aborted) {
|
|
922
|
+
fail(new Error("pi subagent cancelled"));
|
|
923
|
+
} else if (code === 0) {
|
|
924
|
+
succeed(formatPiOutput(out, err));
|
|
925
|
+
} else {
|
|
926
|
+
fail(
|
|
927
|
+
new Error(err || out || `pi exited with code ${code ?? "unknown"}`),
|
|
928
|
+
);
|
|
929
|
+
}
|
|
930
|
+
});
|
|
931
|
+
|
|
932
|
+
child.stdin?.end(prompt);
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
function buildSubagentPrompt(
|
|
937
|
+
projectRoot: string,
|
|
938
|
+
input: SubagentInput,
|
|
939
|
+
contextKey?: string | null,
|
|
940
|
+
agentName?: string,
|
|
941
|
+
agentDefinition?: AgentDefinition,
|
|
942
|
+
): string {
|
|
943
|
+
const normalized =
|
|
944
|
+
agentName ?? normalizeAgentName(input.agent ?? "trellis-implement");
|
|
945
|
+
const definition =
|
|
946
|
+
agentDefinition ?? readAgentDefinition(projectRoot, normalized);
|
|
947
|
+
const context = buildTrellisContext(
|
|
948
|
+
projectRoot,
|
|
949
|
+
normalized,
|
|
950
|
+
input,
|
|
951
|
+
undefined,
|
|
952
|
+
contextKey,
|
|
953
|
+
);
|
|
954
|
+
const prompt = input.prompt ?? "";
|
|
955
|
+
|
|
956
|
+
return [
|
|
957
|
+
"## Trellis Agent Definition",
|
|
958
|
+
definition.content || "(missing agent definition)",
|
|
959
|
+
"",
|
|
960
|
+
context,
|
|
961
|
+
"",
|
|
962
|
+
"## Delegated Task",
|
|
963
|
+
prompt,
|
|
964
|
+
].join("\n");
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
async function runSubagent(
|
|
968
|
+
projectRoot: string,
|
|
969
|
+
input: SubagentInput,
|
|
970
|
+
contextKey?: string | null,
|
|
971
|
+
signal?: AbortSignal,
|
|
972
|
+
): Promise<string> {
|
|
973
|
+
const agentName = normalizeAgentName(input.agent ?? "trellis-implement");
|
|
974
|
+
const agentDefinition = readAgentDefinition(projectRoot, agentName);
|
|
975
|
+
const runConfig = resolveSubagentRunConfig(input, agentDefinition.config);
|
|
976
|
+
const mode = input.mode ?? "single";
|
|
977
|
+
if (mode === "parallel") {
|
|
978
|
+
const prompts = input.prompts ?? (input.prompt ? [input.prompt] : []);
|
|
979
|
+
const outputs = await Promise.all(
|
|
980
|
+
prompts.map((prompt) =>
|
|
981
|
+
runPi(
|
|
982
|
+
projectRoot,
|
|
983
|
+
buildSubagentPrompt(
|
|
984
|
+
projectRoot,
|
|
985
|
+
{ ...input, prompt },
|
|
986
|
+
contextKey,
|
|
987
|
+
agentName,
|
|
988
|
+
agentDefinition,
|
|
989
|
+
),
|
|
990
|
+
runConfig,
|
|
991
|
+
contextKey,
|
|
992
|
+
signal,
|
|
993
|
+
),
|
|
994
|
+
),
|
|
995
|
+
);
|
|
996
|
+
return outputs.join("\n\n---\n\n");
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
if (mode === "chain") {
|
|
1000
|
+
let previous = "";
|
|
1001
|
+
const prompts = input.prompts ?? (input.prompt ? [input.prompt] : []);
|
|
1002
|
+
for (const prompt of prompts) {
|
|
1003
|
+
previous = await runPi(
|
|
1004
|
+
projectRoot,
|
|
1005
|
+
buildSubagentPrompt(
|
|
1006
|
+
projectRoot,
|
|
1007
|
+
{
|
|
1008
|
+
...input,
|
|
1009
|
+
prompt: previous
|
|
1010
|
+
? `${prompt}\n\nPrevious output:\n${previous}`
|
|
1011
|
+
: prompt,
|
|
1012
|
+
},
|
|
1013
|
+
contextKey,
|
|
1014
|
+
agentName,
|
|
1015
|
+
agentDefinition,
|
|
1016
|
+
),
|
|
1017
|
+
runConfig,
|
|
1018
|
+
contextKey,
|
|
1019
|
+
signal,
|
|
1020
|
+
);
|
|
1021
|
+
}
|
|
1022
|
+
return previous;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
return runPi(
|
|
1026
|
+
projectRoot,
|
|
1027
|
+
buildSubagentPrompt(
|
|
1028
|
+
projectRoot,
|
|
1029
|
+
input,
|
|
1030
|
+
contextKey,
|
|
1031
|
+
agentName,
|
|
1032
|
+
agentDefinition,
|
|
1033
|
+
),
|
|
1034
|
+
runConfig,
|
|
1035
|
+
contextKey,
|
|
1036
|
+
signal,
|
|
1037
|
+
);
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
export default function trellisExtension(pi: {
|
|
1041
|
+
registerTool?: (tool: JsonObject) => void;
|
|
1042
|
+
on?: (
|
|
1043
|
+
event: string,
|
|
1044
|
+
handler: (event: unknown, ctx?: PiExtensionContext) => unknown,
|
|
1045
|
+
) => void;
|
|
1046
|
+
cwd?: string;
|
|
1047
|
+
}): void {
|
|
1048
|
+
const projectRoot = findProjectRoot(pi.cwd ?? process.cwd());
|
|
1049
|
+
const processContextKey = createProcessContextKey(projectRoot);
|
|
1050
|
+
let currentContextKey: string | null = null;
|
|
1051
|
+
const turnContextCache = new TurnContextCache();
|
|
1052
|
+
|
|
1053
|
+
const buildPerTurnInjection = (contextKey: string | null): string => {
|
|
1054
|
+
const { workflowState, sessionOverview } = turnContextCache.get(
|
|
1055
|
+
projectRoot,
|
|
1056
|
+
contextKey,
|
|
1057
|
+
);
|
|
1058
|
+
return [workflowState, sessionOverview].filter(Boolean).join("\n\n");
|
|
1059
|
+
};
|
|
1060
|
+
|
|
1061
|
+
const getContextKey = (input?: unknown, ctx?: PiExtensionContext): string => {
|
|
1062
|
+
const resolvedContextKey = resolveContextKey(
|
|
1063
|
+
input,
|
|
1064
|
+
ctx,
|
|
1065
|
+
currentContextKey ?? processContextKey,
|
|
1066
|
+
);
|
|
1067
|
+
currentContextKey = adoptExistingContextKey(
|
|
1068
|
+
projectRoot,
|
|
1069
|
+
resolvedContextKey ?? processContextKey,
|
|
1070
|
+
);
|
|
1071
|
+
return currentContextKey;
|
|
1072
|
+
};
|
|
1073
|
+
|
|
1074
|
+
pi.registerTool?.({
|
|
1075
|
+
name: "subagent",
|
|
1076
|
+
label: "Subagent",
|
|
1077
|
+
description: "Run a Trellis project sub-agent with active task context.",
|
|
1078
|
+
promptSnippet: SUBAGENT_DISPATCH_PROTOCOL,
|
|
1079
|
+
promptGuidelines: SUBAGENT_DISPATCH_PROTOCOL,
|
|
1080
|
+
parameters: {
|
|
1081
|
+
type: "object",
|
|
1082
|
+
properties: {
|
|
1083
|
+
agent: {
|
|
1084
|
+
type: "string",
|
|
1085
|
+
description:
|
|
1086
|
+
"Agent name, such as trellis-implement or trellis-check.",
|
|
1087
|
+
},
|
|
1088
|
+
prompt: {
|
|
1089
|
+
type: "string",
|
|
1090
|
+
description: "Task prompt for the sub-agent.",
|
|
1091
|
+
},
|
|
1092
|
+
mode: {
|
|
1093
|
+
type: "string",
|
|
1094
|
+
enum: ["single", "parallel", "chain"],
|
|
1095
|
+
description: "Delegation mode.",
|
|
1096
|
+
},
|
|
1097
|
+
prompts: {
|
|
1098
|
+
type: "array",
|
|
1099
|
+
items: { type: "string" },
|
|
1100
|
+
description: "Prompts for parallel or chain mode.",
|
|
1101
|
+
},
|
|
1102
|
+
model: {
|
|
1103
|
+
type: "string",
|
|
1104
|
+
description:
|
|
1105
|
+
"Optional Pi model override for the child sub-agent process.",
|
|
1106
|
+
},
|
|
1107
|
+
thinking: {
|
|
1108
|
+
type: "string",
|
|
1109
|
+
enum: ["off", "minimal", "low", "medium", "high", "xhigh"],
|
|
1110
|
+
description:
|
|
1111
|
+
"Optional Pi thinking level override for the child sub-agent process.",
|
|
1112
|
+
},
|
|
1113
|
+
},
|
|
1114
|
+
required: ["prompt"],
|
|
1115
|
+
},
|
|
1116
|
+
execute: async (
|
|
1117
|
+
_toolCallId: string,
|
|
1118
|
+
input: SubagentInput,
|
|
1119
|
+
_signal?: AbortSignal,
|
|
1120
|
+
_onUpdate?: (partialResult: PiToolResult) => void,
|
|
1121
|
+
ctx?: PiExtensionContext,
|
|
1122
|
+
): Promise<PiToolResult> => {
|
|
1123
|
+
const contextKey = getContextKey(input, ctx);
|
|
1124
|
+
const output = await runSubagent(projectRoot, input, contextKey, _signal);
|
|
1125
|
+
return {
|
|
1126
|
+
content: [{ type: "text", text: output }],
|
|
1127
|
+
details: {
|
|
1128
|
+
agent: input.agent ?? "trellis-implement",
|
|
1129
|
+
mode: input.mode ?? "single",
|
|
1130
|
+
},
|
|
1131
|
+
};
|
|
1132
|
+
},
|
|
1133
|
+
});
|
|
1134
|
+
|
|
1135
|
+
pi.on?.("session_start", (event, ctx) => {
|
|
1136
|
+
getContextKey(event, ctx);
|
|
1137
|
+
ctx?.ui?.notify?.(
|
|
1138
|
+
"Trellis project context is available. Use /trellis-continue to resume the current task.",
|
|
1139
|
+
"info",
|
|
1140
|
+
);
|
|
1141
|
+
});
|
|
1142
|
+
pi.on?.("before_agent_start", (event, ctx) => {
|
|
1143
|
+
const contextKey = getContextKey(event, ctx);
|
|
1144
|
+
const current = (event as PiBeforeAgentStartEvent).systemPrompt ?? "";
|
|
1145
|
+
const context = buildTrellisContext(
|
|
1146
|
+
projectRoot,
|
|
1147
|
+
"trellis-implement",
|
|
1148
|
+
event,
|
|
1149
|
+
ctx,
|
|
1150
|
+
contextKey,
|
|
1151
|
+
);
|
|
1152
|
+
const perTurn = buildPerTurnInjection(contextKey);
|
|
1153
|
+
return {
|
|
1154
|
+
systemPrompt: [current, context, perTurn].filter(Boolean).join("\n\n"),
|
|
1155
|
+
};
|
|
1156
|
+
});
|
|
1157
|
+
pi.on?.("context", (event, ctx) => {
|
|
1158
|
+
getContextKey(event, ctx);
|
|
1159
|
+
const messages = (event as PiContextEvent).messages;
|
|
1160
|
+
return Array.isArray(messages) ? { messages } : undefined;
|
|
1161
|
+
});
|
|
1162
|
+
pi.on?.("input", (event, ctx) => {
|
|
1163
|
+
const contextKey = getContextKey(event, ctx);
|
|
1164
|
+
const additionalContext = buildPerTurnInjection(contextKey);
|
|
1165
|
+
return additionalContext
|
|
1166
|
+
? { action: "continue", additionalContext, systemPrompt: additionalContext }
|
|
1167
|
+
: { action: "continue" };
|
|
1168
|
+
});
|
|
1169
|
+
pi.on?.("tool_call", (event, ctx) => {
|
|
1170
|
+
const contextKey = getContextKey(event, ctx);
|
|
1171
|
+
injectTrellisContextIntoBash(event, contextKey);
|
|
1172
|
+
return undefined;
|
|
1173
|
+
});
|
|
1174
|
+
}
|