@blulotus/trellis 0.7.0 → 0.7.2
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/README.md +2 -2
- package/dist/cli/index.js +2 -29
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/init.d.ts +0 -22
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +5 -12
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mem.d.ts +2 -2
- package/dist/commands/mem.d.ts.map +1 -1
- package/dist/commands/mem.js +7 -40
- package/dist/commands/mem.js.map +1 -1
- package/dist/commands/uninstall.d.ts +1 -1
- package/dist/commands/uninstall.d.ts.map +1 -1
- package/dist/commands/uninstall.js +4 -34
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +2 -28
- package/dist/commands/update.js.map +1 -1
- package/dist/configurators/index.d.ts.map +1 -1
- package/dist/configurators/index.js +7 -73
- package/dist/configurators/index.js.map +1 -1
- package/dist/configurators/shared.d.ts +18 -70
- package/dist/configurators/shared.d.ts.map +1 -1
- package/dist/configurators/shared.js +20 -299
- package/dist/configurators/shared.js.map +1 -1
- package/dist/configurators/workflow.d.ts +0 -14
- package/dist/configurators/workflow.d.ts.map +1 -1
- package/dist/configurators/workflow.js +1 -37
- package/dist/configurators/workflow.js.map +1 -1
- package/dist/migrations/manifests/0.1.9.json +0 -12
- package/dist/migrations/manifests/0.2.14.json +0 -84
- package/dist/migrations/manifests/0.3.0-beta.0.json +0 -87
- package/dist/migrations/manifests/0.3.4.json +1 -13
- package/dist/migrations/manifests/0.4.0-beta.1.json +24 -176
- package/dist/migrations/manifests/0.5.0-beta.0.json +67 -1203
- package/dist/migrations/manifests/0.5.0-beta.15.json +0 -96
- package/dist/migrations/manifests/0.5.0-beta.5.json +0 -168
- package/dist/migrations/manifests/0.5.0-beta.9.json +0 -20
- package/dist/migrations/manifests/0.6.0-beta.23.json +0 -66
- package/dist/migrations/manifests/0.6.3.json +1 -16
- package/dist/migrations/manifests/0.6.6.json +1 -9
- package/dist/migrations/manifests/0.7.0.json +9 -0
- package/dist/migrations/manifests/0.7.1.json +9 -0
- package/dist/migrations/manifests/0.7.2.json +9 -0
- package/dist/migrations/manifests/0.8.0.json +9 -0
- package/dist/templates/codex/hooks/session-start.py +37 -13
- package/dist/templates/codex/hooks.json +5 -0
- package/dist/templates/common/bundled-skills/trellis-meta/SKILL.md +5 -5
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-agents.md +0 -10
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-hooks.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-skills-or-commands.md +1 -13
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/overview.md +2 -2
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/bundled-skills.md +9 -28
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/generated-files.md +5 -5
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/overview.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/agents.md +1 -17
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/hooks-and-settings.md +4 -12
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/overview.md +6 -6
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/platform-map.md +5 -75
- package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/skills-and-commands.md +0 -16
- package/dist/templates/common/bundled-skills/trellis-session-insight/SKILL.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-session-insight/references/cli-quick-reference.md +2 -3
- package/dist/templates/common/commands/continue.md +24 -4
- package/dist/templates/common/commands/finish-work.md +19 -9
- package/dist/templates/common/commands/start.md +38 -29
- package/dist/templates/extract.d.ts +0 -10
- package/dist/templates/extract.d.ts.map +1 -1
- package/dist/templates/extract.js +0 -24
- package/dist/templates/extract.js.map +1 -1
- package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md.txt +7 -9
- package/dist/templates/markdown/spec/guides/cross-layer-thinking-guide.md.txt +4 -8
- package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +5 -5
- package/dist/templates/shared-hooks/index.d.ts +11 -43
- package/dist/templates/shared-hooks/index.d.ts.map +1 -1
- package/dist/templates/shared-hooks/index.js +9 -86
- package/dist/templates/shared-hooks/index.js.map +1 -1
- package/dist/templates/shared-hooks/inject-spec-context.py +1 -1
- package/dist/templates/shared-hooks/inject-subagent-context.py +23 -85
- package/dist/templates/shared-hooks/inject-workflow-state.py +56 -86
- package/dist/templates/shared-hooks/session-start.py +76 -98
- package/dist/templates/trellis/config.yaml +8 -8
- package/dist/templates/trellis/index.d.ts +1 -4
- package/dist/templates/trellis/index.d.ts.map +1 -1
- package/dist/templates/trellis/index.js +1 -5
- package/dist/templates/trellis/index.js.map +1 -1
- package/dist/templates/trellis/scripts/add_session.py +12 -11
- package/dist/templates/trellis/scripts/common/__init__.py +2 -0
- package/dist/templates/trellis/scripts/common/active_task.py +80 -152
- package/dist/templates/trellis/scripts/common/config.py +7 -7
- package/dist/templates/trellis/scripts/common/git_context.py +1 -1
- package/dist/templates/trellis/scripts/common/session_context.py +2 -2
- package/dist/templates/trellis/scripts/common/spec_inject.py +2 -2
- package/dist/templates/trellis/scripts/common/task_store.py +8 -28
- package/dist/templates/trellis/scripts/common/workflow_phase.py +4 -8
- package/dist/templates/trellis/scripts/get_context.py +2 -2
- package/dist/templates/trellis/scripts/init_developer.py +1 -1
- package/dist/templates/trellis/scripts/task.py +90 -57
- package/dist/templates/trellis/workflow.md +44 -66
- package/dist/types/ai-tools.d.ts +10 -10
- package/dist/types/ai-tools.d.ts.map +1 -1
- package/dist/types/ai-tools.js +3 -434
- package/dist/types/ai-tools.js.map +1 -1
- package/dist/utils/uninstall-scrubbers.d.ts +3 -18
- package/dist/utils/uninstall-scrubbers.d.ts.map +1 -1
- package/dist/utils/uninstall-scrubbers.js +24 -154
- package/dist/utils/uninstall-scrubbers.js.map +1 -1
- package/package.json +2 -2
- package/dist/configurators/antigravity.d.ts +0 -7
- package/dist/configurators/antigravity.d.ts.map +0 -1
- package/dist/configurators/antigravity.js +0 -11
- package/dist/configurators/antigravity.js.map +0 -1
- package/dist/configurators/codebuddy.d.ts +0 -10
- package/dist/configurators/codebuddy.d.ts.map +0 -1
- package/dist/configurators/codebuddy.js +0 -24
- package/dist/configurators/codebuddy.js.map +0 -1
- package/dist/configurators/copilot.d.ts +0 -14
- package/dist/configurators/copilot.d.ts.map +0 -1
- package/dist/configurators/copilot.js +0 -46
- package/dist/configurators/copilot.js.map +0 -1
- package/dist/configurators/cursor.d.ts +0 -10
- package/dist/configurators/cursor.d.ts.map +0 -1
- package/dist/configurators/cursor.js +0 -23
- package/dist/configurators/cursor.js.map +0 -1
- package/dist/configurators/devin.d.ts +0 -8
- package/dist/configurators/devin.d.ts.map +0 -1
- package/dist/configurators/devin.js +0 -12
- package/dist/configurators/devin.js.map +0 -1
- package/dist/configurators/droid.d.ts +0 -10
- package/dist/configurators/droid.d.ts.map +0 -1
- package/dist/configurators/droid.js +0 -24
- package/dist/configurators/droid.js.map +0 -1
- package/dist/configurators/dsh.d.ts +0 -26
- package/dist/configurators/dsh.d.ts.map +0 -1
- package/dist/configurators/dsh.js +0 -76
- package/dist/configurators/dsh.js.map +0 -1
- package/dist/configurators/gemini.d.ts +0 -18
- package/dist/configurators/gemini.d.ts.map +0 -1
- package/dist/configurators/gemini.js +0 -39
- package/dist/configurators/gemini.js.map +0 -1
- package/dist/configurators/grok.d.ts +0 -17
- package/dist/configurators/grok.d.ts.map +0 -1
- package/dist/configurators/grok.js +0 -38
- package/dist/configurators/grok.js.map +0 -1
- package/dist/configurators/kilo.d.ts +0 -7
- package/dist/configurators/kilo.d.ts.map +0 -1
- package/dist/configurators/kilo.js +0 -11
- package/dist/configurators/kilo.js.map +0 -1
- package/dist/configurators/kimi.d.ts +0 -26
- package/dist/configurators/kimi.d.ts.map +0 -1
- package/dist/configurators/kimi.js +0 -74
- package/dist/configurators/kimi.js.map +0 -1
- package/dist/configurators/kiro.d.ts +0 -11
- package/dist/configurators/kiro.d.ts.map +0 -1
- package/dist/configurators/kiro.js +0 -30
- package/dist/configurators/kiro.js.map +0 -1
- package/dist/configurators/omp.d.ts +0 -6
- package/dist/configurators/omp.d.ts.map +0 -1
- package/dist/configurators/omp.js +0 -28
- package/dist/configurators/omp.js.map +0 -1
- package/dist/configurators/opencode.d.ts +0 -5
- package/dist/configurators/opencode.d.ts.map +0 -1
- package/dist/configurators/opencode.js +0 -84
- package/dist/configurators/opencode.js.map +0 -1
- package/dist/configurators/pi.d.ts +0 -5
- package/dist/configurators/pi.d.ts.map +0 -1
- package/dist/configurators/pi.js +0 -39
- package/dist/configurators/pi.js.map +0 -1
- package/dist/configurators/qoder.d.ts +0 -12
- package/dist/configurators/qoder.d.ts.map +0 -1
- package/dist/configurators/qoder.js +0 -30
- package/dist/configurators/qoder.js.map +0 -1
- package/dist/configurators/reasonix.d.ts +0 -17
- package/dist/configurators/reasonix.d.ts.map +0 -1
- package/dist/configurators/reasonix.js +0 -37
- package/dist/configurators/reasonix.js.map +0 -1
- package/dist/configurators/snow.d.ts +0 -25
- package/dist/configurators/snow.d.ts.map +0 -1
- package/dist/configurators/snow.js +0 -69
- package/dist/configurators/snow.js.map +0 -1
- package/dist/configurators/trae.d.ts +0 -16
- package/dist/configurators/trae.d.ts.map +0 -1
- package/dist/configurators/trae.js +0 -34
- package/dist/configurators/trae.js.map +0 -1
- package/dist/configurators/zcode.d.ts +0 -23
- package/dist/configurators/zcode.d.ts.map +0 -1
- package/dist/configurators/zcode.js +0 -63
- package/dist/configurators/zcode.js.map +0 -1
- package/dist/templates/codebuddy/agents/trellis-check.md +0 -115
- package/dist/templates/codebuddy/agents/trellis-implement.md +0 -115
- package/dist/templates/codebuddy/agents/trellis-research.md +0 -137
- package/dist/templates/codebuddy/index.d.ts +0 -15
- package/dist/templates/codebuddy/index.d.ts.map +0 -1
- package/dist/templates/codebuddy/index.js +0 -15
- package/dist/templates/codebuddy/index.js.map +0 -1
- package/dist/templates/codebuddy/settings.json +0 -69
- package/dist/templates/copilot/copilot-instructions.md +0 -11
- package/dist/templates/copilot/hooks/session-start.py +0 -564
- package/dist/templates/copilot/hooks.json +0 -19
- package/dist/templates/copilot/index.d.ts +0 -28
- package/dist/templates/copilot/index.d.ts.map +0 -1
- package/dist/templates/copilot/index.js +0 -61
- package/dist/templates/copilot/index.js.map +0 -1
- package/dist/templates/copilot/prompts/before-dev.prompt.md +0 -39
- package/dist/templates/copilot/prompts/brainstorm.prompt.md +0 -173
- package/dist/templates/copilot/prompts/break-loop.prompt.md +0 -129
- package/dist/templates/copilot/prompts/check-cross-layer.prompt.md +0 -170
- package/dist/templates/copilot/prompts/check.prompt.md +0 -110
- package/dist/templates/copilot/prompts/create-command.prompt.md +0 -116
- package/dist/templates/copilot/prompts/finish-work.prompt.md +0 -99
- package/dist/templates/copilot/prompts/integrate-skill.prompt.md +0 -223
- package/dist/templates/copilot/prompts/onboard.prompt.md +0 -362
- package/dist/templates/copilot/prompts/parallel.prompt.md +0 -204
- package/dist/templates/copilot/prompts/record-session.prompt.md +0 -66
- package/dist/templates/copilot/prompts/start.prompt.md +0 -63
- package/dist/templates/copilot/prompts/update-spec.prompt.md +0 -358
- package/dist/templates/cursor/agents/trellis-check.md +0 -114
- package/dist/templates/cursor/agents/trellis-implement.md +0 -114
- package/dist/templates/cursor/agents/trellis-research.md +0 -136
- package/dist/templates/cursor/hooks.json +0 -24
- package/dist/templates/cursor/index.d.ts +0 -13
- package/dist/templates/cursor/index.d.ts.map +0 -1
- package/dist/templates/cursor/index.js +0 -13
- package/dist/templates/cursor/index.js.map +0 -1
- package/dist/templates/droid/droids/trellis-check.md +0 -107
- package/dist/templates/droid/droids/trellis-implement.md +0 -107
- package/dist/templates/droid/droids/trellis-research.md +0 -137
- package/dist/templates/droid/index.d.ts +0 -15
- package/dist/templates/droid/index.d.ts.map +0 -1
- package/dist/templates/droid/index.js +0 -15
- package/dist/templates/droid/index.js.map +0 -1
- package/dist/templates/droid/settings.json +0 -69
- package/dist/templates/dsh/DSH.md +0 -80
- package/dist/templates/dsh/agents/trellis-check.md +0 -99
- package/dist/templates/dsh/agents/trellis-implement.md +0 -106
- package/dist/templates/dsh/agents/trellis-research.md +0 -134
- package/dist/templates/dsh/index.d.ts +0 -22
- package/dist/templates/dsh/index.d.ts.map +0 -1
- package/dist/templates/dsh/index.js +0 -27
- package/dist/templates/dsh/index.js.map +0 -1
- package/dist/templates/gemini/agents/trellis-check.md +0 -107
- package/dist/templates/gemini/agents/trellis-implement.md +0 -107
- package/dist/templates/gemini/agents/trellis-research.md +0 -136
- package/dist/templates/gemini/index.d.ts +0 -13
- package/dist/templates/gemini/index.d.ts.map +0 -1
- package/dist/templates/gemini/index.js +0 -13
- package/dist/templates/gemini/index.js.map +0 -1
- package/dist/templates/gemini/settings.json +0 -40
- package/dist/templates/grok/agents/trellis-check.md +0 -106
- package/dist/templates/grok/agents/trellis-implement.md +0 -121
- package/dist/templates/grok/agents/trellis-research.md +0 -146
- package/dist/templates/grok/index.d.ts +0 -16
- package/dist/templates/grok/index.d.ts.map +0 -1
- package/dist/templates/grok/index.js +0 -19
- package/dist/templates/grok/index.js.map +0 -1
- package/dist/templates/kimi/agents/trellis-check.md +0 -104
- package/dist/templates/kimi/agents/trellis-implement.md +0 -120
- package/dist/templates/kimi/agents/trellis-research.md +0 -144
- package/dist/templates/kimi/index.d.ts +0 -20
- package/dist/templates/kimi/index.d.ts.map +0 -1
- package/dist/templates/kimi/index.js +0 -23
- package/dist/templates/kimi/index.js.map +0 -1
- package/dist/templates/kiro/agents/trellis-check.json +0 -26
- package/dist/templates/kiro/agents/trellis-implement.json +0 -26
- package/dist/templates/kiro/agents/trellis-research.json +0 -30
- package/dist/templates/kiro/agents/trellis.json +0 -34
- package/dist/templates/kiro/hooks/trellis-workflow-state.kiro.hook +0 -14
- package/dist/templates/kiro/index.d.ts +0 -32
- package/dist/templates/kiro/index.d.ts.map +0 -1
- package/dist/templates/kiro/index.js +0 -28
- package/dist/templates/kiro/index.js.map +0 -1
- package/dist/templates/omp/agents/trellis-check.md +0 -41
- package/dist/templates/omp/agents/trellis-implement.md +0 -55
- package/dist/templates/omp/agents/trellis-research.md +0 -30
- package/dist/templates/omp/extensions/trellis/index.ts.txt +0 -592
- package/dist/templates/omp/index.d.ts +0 -4
- package/dist/templates/omp/index.d.ts.map +0 -1
- package/dist/templates/omp/index.js +0 -9
- package/dist/templates/omp/index.js.map +0 -1
- package/dist/templates/opencode/agents/trellis-check.md +0 -122
- package/dist/templates/opencode/agents/trellis-implement.md +0 -123
- package/dist/templates/opencode/agents/trellis-research.md +0 -145
- package/dist/templates/opencode/lib/context-visibility.js +0 -86
- package/dist/templates/opencode/lib/session-utils.js +0 -526
- package/dist/templates/opencode/lib/trellis-context.js +0 -627
- package/dist/templates/opencode/package.json +0 -5
- package/dist/templates/opencode/plugins/inject-spec-context.js +0 -121
- package/dist/templates/opencode/plugins/inject-subagent-context.js +0 -569
- package/dist/templates/opencode/plugins/inject-workflow-state.js +0 -340
- package/dist/templates/opencode/plugins/session-start.js +0 -74
- package/dist/templates/pi/agents/trellis-check.md +0 -37
- package/dist/templates/pi/agents/trellis-implement.md +0 -52
- package/dist/templates/pi/agents/trellis-research.md +0 -25
- package/dist/templates/pi/extensions/trellis/index.ts.txt +0 -2033
- package/dist/templates/pi/index.d.ts +0 -5
- package/dist/templates/pi/index.d.ts.map +0 -1
- package/dist/templates/pi/index.js +0 -12
- package/dist/templates/pi/index.js.map +0 -1
- package/dist/templates/pi/settings.json +0 -9
- package/dist/templates/qoder/agents/trellis-check.md +0 -108
- package/dist/templates/qoder/agents/trellis-implement.md +0 -108
- package/dist/templates/qoder/agents/trellis-research.md +0 -137
- package/dist/templates/qoder/index.d.ts +0 -15
- package/dist/templates/qoder/index.d.ts.map +0 -1
- package/dist/templates/qoder/index.js +0 -15
- package/dist/templates/qoder/index.js.map +0 -1
- package/dist/templates/qoder/settings.json +0 -59
- package/dist/templates/reasonix/agents/trellis-check.md +0 -36
- package/dist/templates/reasonix/agents/trellis-implement.md +0 -51
- package/dist/templates/reasonix/index.d.ts +0 -13
- package/dist/templates/reasonix/index.d.ts.map +0 -1
- package/dist/templates/reasonix/index.js +0 -16
- package/dist/templates/reasonix/index.js.map +0 -1
- package/dist/templates/shared-hooks/inject-shell-session-context.py +0 -292
- package/dist/templates/snow/SNOW.md +0 -86
- package/dist/templates/snow/agents/trellis-check.md +0 -136
- package/dist/templates/snow/agents/trellis-implement.md +0 -158
- package/dist/templates/snow/agents/trellis-research.md +0 -114
- package/dist/templates/snow/hooks/beforeSubAgentStart.json +0 -15
- package/dist/templates/snow/hooks/onSessionStart.json +0 -15
- package/dist/templates/snow/hooks/onUserMessage.json +0 -15
- package/dist/templates/snow/hooks/write-trellis-context.py +0 -644
- package/dist/templates/snow/index.d.ts +0 -26
- package/dist/templates/snow/index.d.ts.map +0 -1
- package/dist/templates/snow/index.js +0 -36
- package/dist/templates/snow/index.js.map +0 -1
- package/dist/templates/trae/agents/trellis-check.md +0 -108
- package/dist/templates/trae/agents/trellis-implement.md +0 -108
- package/dist/templates/trae/agents/trellis-research.md +0 -137
- package/dist/templates/trae/hooks.json +0 -39
- package/dist/templates/trae/index.d.ts +0 -15
- package/dist/templates/trae/index.d.ts.map +0 -1
- package/dist/templates/trae/index.js +0 -15
- package/dist/templates/trae/index.js.map +0 -1
- package/dist/templates/trellis/gitattributes.txt +0 -9
- package/dist/templates/trellis/scripts/common/cli_adapter.py +0 -982
- package/dist/templates/zcode/agents/trellis-check.md +0 -110
- package/dist/templates/zcode/agents/trellis-implement.md +0 -116
- package/dist/templates/zcode/agents/trellis-research.md +0 -133
- package/dist/templates/zcode/config.json +0 -52
- package/dist/templates/zcode/index.d.ts +0 -25
- package/dist/templates/zcode/index.d.ts.map +0 -1
- package/dist/templates/zcode/index.js +0 -29
- package/dist/templates/zcode/index.js.map +0 -1
|
@@ -90,26 +90,14 @@ def find_repo_root(start_path: str) -> str | None:
|
|
|
90
90
|
def _detect_platform(input_data: dict) -> str | None:
|
|
91
91
|
if _hook_event_name(input_data) == "SubagentStart":
|
|
92
92
|
return "codex"
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
#
|
|
96
|
-
#
|
|
97
|
-
#
|
|
98
|
-
#
|
|
99
|
-
# match the session file `task.py start` wrote under the host's real name,
|
|
100
|
-
# so the sub-agent starts with no task context while the pointer exists on
|
|
101
|
-
# disk. Same fix as inject-workflow-state.py and session-start.py; this
|
|
102
|
-
# third copy was missed when those two were corrected.
|
|
93
|
+
# CLAUDE_PROJECT_DIR is a compatibility alias other hosts may also set, so
|
|
94
|
+
# it is checked last — a vendor-specific key always wins. Detecting the
|
|
95
|
+
# wrong host would build the context key `claude_<their-session-id>`, which
|
|
96
|
+
# never matches the session file `task.py start` wrote under the host's
|
|
97
|
+
# real name, so the sub-agent would start with no task context while the
|
|
98
|
+
# pointer exists on disk.
|
|
103
99
|
env_map = {
|
|
104
|
-
"
|
|
105
|
-
"CURSOR_PROJECT_DIR": "cursor",
|
|
106
|
-
"CODEBUDDY_PROJECT_DIR": "codebuddy",
|
|
107
|
-
"FACTORY_PROJECT_DIR": "droid",
|
|
108
|
-
"GEMINI_PROJECT_DIR": "gemini",
|
|
109
|
-
"QODER_PROJECT_DIR": "qoder",
|
|
110
|
-
"KIRO_PROJECT_DIR": "kiro",
|
|
111
|
-
"COPILOT_PROJECT_DIR": "copilot",
|
|
112
|
-
"TRAE_PROJECT_DIR": "trae",
|
|
100
|
+
"CODEX_PROJECT_DIR": "codex",
|
|
113
101
|
# Last: the shared alias, only meaningful once no vendor key matched.
|
|
114
102
|
"CLAUDE_PROJECT_DIR": "claude",
|
|
115
103
|
}
|
|
@@ -119,20 +107,8 @@ def _detect_platform(input_data: dict) -> str | None:
|
|
|
119
107
|
script_parts = set(Path(sys.argv[0]).parts)
|
|
120
108
|
if ".claude" in script_parts:
|
|
121
109
|
return "claude"
|
|
122
|
-
if ".
|
|
123
|
-
return "
|
|
124
|
-
if ".gemini" in script_parts:
|
|
125
|
-
return "gemini"
|
|
126
|
-
if ".qoder" in script_parts:
|
|
127
|
-
return "qoder"
|
|
128
|
-
if ".codebuddy" in script_parts:
|
|
129
|
-
return "codebuddy"
|
|
130
|
-
if ".factory" in script_parts:
|
|
131
|
-
return "droid"
|
|
132
|
-
if ".kiro" in script_parts:
|
|
133
|
-
return "kiro"
|
|
134
|
-
if ".zcode" in script_parts:
|
|
135
|
-
return "zcode"
|
|
110
|
+
if ".codex" in script_parts:
|
|
111
|
+
return "codex"
|
|
136
112
|
return None
|
|
137
113
|
|
|
138
114
|
|
|
@@ -888,8 +864,8 @@ def _handle_codex_subagent_start(input_data: dict) -> None:
|
|
|
888
864
|
if not subagent_type or not parent_session_id:
|
|
889
865
|
return
|
|
890
866
|
|
|
891
|
-
# Payload cwd first, then our own —
|
|
892
|
-
#
|
|
867
|
+
# Payload cwd first, then our own — a host may report "/" for every hook
|
|
868
|
+
# event. See inject-workflow-state.py.
|
|
893
869
|
repo_root = None
|
|
894
870
|
for candidate in (_string_value(input_data.get("cwd")), os.getcwd()):
|
|
895
871
|
if not candidate:
|
|
@@ -938,11 +914,11 @@ def _handle_codex_subagent_start(input_data: dict) -> None:
|
|
|
938
914
|
|
|
939
915
|
|
|
940
916
|
def _extract_subagent_name(value: Any) -> str:
|
|
941
|
-
"""Extract a sub-agent name from
|
|
917
|
+
"""Extract a sub-agent name from the shapes hook JSON can carry.
|
|
942
918
|
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
919
|
+
A plain string is the common case; a host may instead nest the name as
|
|
920
|
+
``{"custom": {"name": "..."}}`` or
|
|
921
|
+
``{"type": {"case": "custom", "value": {"name": "..."}}}``.
|
|
946
922
|
"""
|
|
947
923
|
direct = _string_value(value)
|
|
948
924
|
if direct:
|
|
@@ -1013,14 +989,8 @@ def _parse_hook_input(input_data: dict) -> tuple[str, str, dict]:
|
|
|
1013
989
|
"""Parse hook input across different platform formats.
|
|
1014
990
|
|
|
1015
991
|
Returns (subagent_type, original_prompt, tool_input).
|
|
1016
|
-
Handles:
|
|
1017
|
-
|
|
1018
|
-
- CodeBuddy: tool_name=task (IDE) or Task (CLI), tool_input.subagent_name
|
|
1019
|
-
- Cursor: tool_name=Task|Subagent, tool_input.subagent_type
|
|
1020
|
-
- Copilot CLI: toolName=task (camelCase key, lowercase value)
|
|
1021
|
-
- ZCode: toolName=Agent, toolInput/tool_input.subagent_type
|
|
1022
|
-
- Gemini CLI: tool_name IS the agent name (BeforeTool matcher already filtered)
|
|
1023
|
-
- Kiro: agentSpawn hook, agent_name field at top level
|
|
992
|
+
Handles the Claude Code shape: tool_name=Task|Agent with
|
|
993
|
+
tool_input.subagent_type (camelCase key spellings accepted too).
|
|
1024
994
|
"""
|
|
1025
995
|
tool_input = input_data.get("tool_input", {})
|
|
1026
996
|
if not isinstance(tool_input, dict):
|
|
@@ -1037,21 +1007,6 @@ def _parse_hook_input(input_data: dict) -> tuple[str, str, dict]:
|
|
|
1037
1007
|
tool_input,
|
|
1038
1008
|
)
|
|
1039
1009
|
|
|
1040
|
-
# Kiro: agentSpawn hook passes agent_name at top level
|
|
1041
|
-
agent_name = input_data.get("agent_name", "")
|
|
1042
|
-
if agent_name:
|
|
1043
|
-
return agent_name, tool_input.get("prompt", input_data.get("prompt", "")), tool_input
|
|
1044
|
-
|
|
1045
|
-
# Gemini CLI: BeforeTool where tool_name IS the agent name
|
|
1046
|
-
# (matcher already ensured it's one of our agents)
|
|
1047
|
-
if tool_name in AGENTS_ALL:
|
|
1048
|
-
return tool_name, tool_input.get("prompt", ""), tool_input
|
|
1049
|
-
|
|
1050
|
-
# Copilot CLI: toolName field (camelCase), value might be the agent name
|
|
1051
|
-
tool_name_camel = input_data.get("toolName", "")
|
|
1052
|
-
if tool_name_camel in AGENTS_ALL:
|
|
1053
|
-
return tool_name_camel, input_data.get("toolArgs", ""), tool_input
|
|
1054
|
-
|
|
1055
1010
|
return "", "", tool_input
|
|
1056
1011
|
|
|
1057
1012
|
|
|
@@ -1140,32 +1095,15 @@ def main():
|
|
|
1140
1095
|
if not context:
|
|
1141
1096
|
sys.exit(0)
|
|
1142
1097
|
|
|
1143
|
-
# Return updated input
|
|
1144
|
-
# include multiple formats. ZCode is stricter; live probing confirmed the
|
|
1145
|
-
# nested Claude-compatible shape below reaches the sub-agent prompt.
|
|
1098
|
+
# Return updated input in the Claude Code PreToolUse shape.
|
|
1146
1099
|
updated = {**tool_input, "prompt": new_prompt}
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
"
|
|
1150
|
-
|
|
1151
|
-
"permissionDecision": "allow",
|
|
1152
|
-
"updatedInput": updated,
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
else:
|
|
1156
|
-
output = {
|
|
1157
|
-
# Claude Code / Qoder / CodeBuddy / Droid format
|
|
1158
|
-
"hookSpecificOutput": {
|
|
1159
|
-
"hookEventName": "PreToolUse",
|
|
1160
|
-
"permissionDecision": "allow",
|
|
1161
|
-
"updatedInput": updated,
|
|
1162
|
-
},
|
|
1163
|
-
# Cursor format
|
|
1164
|
-
"permission": "allow",
|
|
1165
|
-
"updated_input": updated,
|
|
1166
|
-
# Gemini format
|
|
1100
|
+
output = {
|
|
1101
|
+
"hookSpecificOutput": {
|
|
1102
|
+
"hookEventName": "PreToolUse",
|
|
1103
|
+
"permissionDecision": "allow",
|
|
1167
1104
|
"updatedInput": updated,
|
|
1168
1105
|
}
|
|
1106
|
+
}
|
|
1169
1107
|
|
|
1170
1108
|
print(json.dumps(output, ensure_ascii=False))
|
|
1171
1109
|
sys.exit(0)
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""Trellis per-turn breadcrumb hook (UserPromptSubmit / BeforeAgent equivalent).
|
|
3
3
|
|
|
4
|
-
Runs on every user prompt
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
4
|
+
Runs on every user prompt, but emits nothing until the session opts into the
|
|
5
|
+
Trellis workflow by invoking `trellis-start`, `trellis-continue` or
|
|
6
|
+
`trellis-finish-work` (each runs `task.py engage`). Once engaged, it resolves the
|
|
7
|
+
active task through Trellis' session-aware active task resolver and emits a short
|
|
8
|
+
<workflow-state> block reminding the main AI what task is active and its expected
|
|
9
|
+
flow.
|
|
10
|
+
|
|
11
|
+
The emitted ``hookEventName`` field is ``UserPromptSubmit`` (Claude Code
|
|
12
|
+
naming, also accepted by Codex wiring). ``_detect_platform`` reports which
|
|
13
|
+
host is running so the payload shape can stay host-aware.
|
|
13
14
|
Breadcrumb text is pulled exclusively from the resolved workflow file's
|
|
14
15
|
[workflow-state:STATUS] tag blocks — the active task may select a
|
|
15
16
|
per-task variant (`.trellis/workflows/<id>.md` via task.json `workflow`),
|
|
@@ -20,14 +21,10 @@ missing or a tag is absent, the breadcrumb degrades to a generic
|
|
|
20
21
|
the broken state instead of the hook silently masking it.
|
|
21
22
|
|
|
22
23
|
Which platforms register this hook is decided by SHARED_HOOKS_BY_PLATFORM
|
|
23
|
-
in templates/shared-hooks/index.ts — currently Claude
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
writer puts that map on disk at init time. Kiro wires this via the CLI
|
|
28
|
-
custom agent's ``hooks.userPromptSubmit`` and the IDE ``.kiro.hook``
|
|
29
|
-
``promptSubmit`` event; its output branch emits a plain-text breadcrumb
|
|
30
|
-
(Kiro adds hook stdout directly to the conversation context).
|
|
24
|
+
in templates/shared-hooks/index.ts — currently Claude Code and Codex. That
|
|
25
|
+
table is the source of truth; each listed platform's
|
|
26
|
+
collect<Platform>Templates() pulls this file into its template map through
|
|
27
|
+
collectSharedHooks(), and a single writer puts that map on disk at init time.
|
|
31
28
|
|
|
32
29
|
Silent exit 0 cases (no output):
|
|
33
30
|
- No .trellis/ directory found (not a Trellis project)
|
|
@@ -67,14 +64,6 @@ if sys.platform.startswith("win"):
|
|
|
67
64
|
from typing import Optional
|
|
68
65
|
|
|
69
66
|
|
|
70
|
-
# Bootstrap notice for Codex while the session has no active task. Codex does not
|
|
71
|
-
# get the full SessionStart overview; this short reminder points the main session
|
|
72
|
-
# at the start skill once and leaves the per-turn state block compact.
|
|
73
|
-
CODEX_NO_TASK_BOOTSTRAP_NOTICE = """<trellis-bootstrap>
|
|
74
|
-
If you have not already loaded Trellis context this session, read the `trellis-start` skill once.
|
|
75
|
-
</trellis-bootstrap>"""
|
|
76
|
-
|
|
77
|
-
|
|
78
67
|
# ---------------------------------------------------------------------------
|
|
79
68
|
# CWD-robust Trellis root discovery (fixes hook-path-robustness for this hook)
|
|
80
69
|
# ---------------------------------------------------------------------------
|
|
@@ -98,26 +87,13 @@ def find_trellis_root(start: Path) -> Optional[Path]:
|
|
|
98
87
|
# ---------------------------------------------------------------------------
|
|
99
88
|
|
|
100
89
|
def _detect_platform(input_data: dict) -> str | None:
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
#
|
|
106
|
-
# the context key becomes `claude_<their-session-id>`. That key does not
|
|
107
|
-
# match the session file `task.py start` wrote under the host's real name,
|
|
108
|
-
# so every turn reports no_task while the pointer exists on disk.
|
|
109
|
-
# Observed on CodeBuddy IDE 4.10.4: session file `codebuddy_ae54840e….json`
|
|
110
|
-
# alongside marker `update-check-claude_ae54840e….marker`, same id.
|
|
90
|
+
# CLAUDE_PROJECT_DIR is a compatibility alias other hosts may also set, so
|
|
91
|
+
# it is checked last — a vendor-specific key always wins. Detecting the
|
|
92
|
+
# wrong host would build the context key `claude_<their-session-id>`, which
|
|
93
|
+
# never matches the session file `task.py start` wrote under the host's
|
|
94
|
+
# real name, and every turn would report no_task while the pointer exists.
|
|
111
95
|
env_map = {
|
|
112
|
-
"
|
|
113
|
-
"CURSOR_PROJECT_DIR": "cursor",
|
|
114
|
-
"CODEBUDDY_PROJECT_DIR": "codebuddy",
|
|
115
|
-
"FACTORY_PROJECT_DIR": "droid",
|
|
116
|
-
"GEMINI_PROJECT_DIR": "gemini",
|
|
117
|
-
"QODER_PROJECT_DIR": "qoder",
|
|
118
|
-
"KIRO_PROJECT_DIR": "kiro",
|
|
119
|
-
"COPILOT_PROJECT_DIR": "copilot",
|
|
120
|
-
"TRAE_PROJECT_DIR": "trae",
|
|
96
|
+
"CODEX_PROJECT_DIR": "codex",
|
|
121
97
|
# Last: the shared alias, only meaningful once no vendor key matched.
|
|
122
98
|
"CLAUDE_PROJECT_DIR": "claude",
|
|
123
99
|
}
|
|
@@ -127,24 +103,8 @@ def _detect_platform(input_data: dict) -> str | None:
|
|
|
127
103
|
script_parts = set(Path(sys.argv[0]).parts)
|
|
128
104
|
if ".claude" in script_parts:
|
|
129
105
|
return "claude"
|
|
130
|
-
if ".cursor" in script_parts:
|
|
131
|
-
return "cursor"
|
|
132
106
|
if ".codex" in script_parts:
|
|
133
107
|
return "codex"
|
|
134
|
-
if ".gemini" in script_parts:
|
|
135
|
-
return "gemini"
|
|
136
|
-
if ".qoder" in script_parts:
|
|
137
|
-
return "qoder"
|
|
138
|
-
if ".codebuddy" in script_parts:
|
|
139
|
-
return "codebuddy"
|
|
140
|
-
if ".factory" in script_parts:
|
|
141
|
-
return "droid"
|
|
142
|
-
if ".kiro" in script_parts:
|
|
143
|
-
return "kiro"
|
|
144
|
-
if ".trae" in script_parts:
|
|
145
|
-
return "trae"
|
|
146
|
-
if ".zcode" in script_parts:
|
|
147
|
-
return "zcode"
|
|
148
108
|
return None
|
|
149
109
|
|
|
150
110
|
|
|
@@ -157,6 +117,30 @@ def _resolve_active_task(root: Path, input_data: dict):
|
|
|
157
117
|
return resolve_active_task(root, input_data, platform=_detect_platform(input_data))
|
|
158
118
|
|
|
159
119
|
|
|
120
|
+
def _is_session_engaged(root: Path, input_data: dict) -> bool:
|
|
121
|
+
"""Whether this session opted into the Trellis workflow.
|
|
122
|
+
|
|
123
|
+
A project too old to have `is_session_engaged` (hooks updated, `.trellis/scripts`
|
|
124
|
+
not) raises ImportError. Reporting False then would silently stop the per-turn
|
|
125
|
+
breadcrumb — the one channel that enforces the planning and commit gates — so
|
|
126
|
+
the stale-scripts case keeps the old always-inject behavior and says why.
|
|
127
|
+
"""
|
|
128
|
+
scripts_dir = root / ".trellis" / "scripts"
|
|
129
|
+
if str(scripts_dir) not in sys.path:
|
|
130
|
+
sys.path.insert(0, str(scripts_dir))
|
|
131
|
+
try:
|
|
132
|
+
from common.active_task import is_session_engaged # type: ignore[import-not-found]
|
|
133
|
+
except ImportError:
|
|
134
|
+
print(
|
|
135
|
+
"trellis inject-workflow-state: .trellis/scripts predates opt-in "
|
|
136
|
+
"engagement; injecting unconditionally. Run `trellis update`.",
|
|
137
|
+
file=sys.stderr,
|
|
138
|
+
)
|
|
139
|
+
return True
|
|
140
|
+
|
|
141
|
+
return is_session_engaged(root, input_data, platform=_detect_platform(input_data))
|
|
142
|
+
|
|
143
|
+
|
|
160
144
|
def get_active_task(root: Path, input_data: dict) -> Optional[tuple[str, str, str]]:
|
|
161
145
|
"""Return (task_id, status, source) from the current active task."""
|
|
162
146
|
active = _resolve_active_task(root, input_data)
|
|
@@ -400,8 +384,8 @@ def build_breadcrumb(
|
|
|
400
384
|
def _load_hook_input() -> dict:
|
|
401
385
|
"""Read hook JSON without trusting host runners to close stdin.
|
|
402
386
|
|
|
403
|
-
|
|
404
|
-
|
|
387
|
+
A hook runner can leave stdin open while sending no payload. A plain
|
|
388
|
+
`json.load(sys.stdin)` then blocks forever.
|
|
405
389
|
Normal hook runners write the complete JSON payload and close stdin, so the
|
|
406
390
|
short daemon read preserves that path while failing closed to `{}` for
|
|
407
391
|
non-piping hosts.
|
|
@@ -443,6 +427,12 @@ def main() -> int:
|
|
|
443
427
|
if root is None:
|
|
444
428
|
return 0 # not a Trellis project
|
|
445
429
|
|
|
430
|
+
# Opt-in gate, ahead of every read below: a session that never invoked an
|
|
431
|
+
# entry point gets no breadcrumb, and does not pay the task-resolution and
|
|
432
|
+
# workflow.md reads either.
|
|
433
|
+
if not _is_session_engaged(root, data):
|
|
434
|
+
return 0
|
|
435
|
+
|
|
446
436
|
config = _read_trellis_config(root)
|
|
447
437
|
if prompt_has_skip_keyword(data.get("prompt", ""), _resolve_skip_keyword(config)):
|
|
448
438
|
return 0 # user opted out of the per-turn breadcrumb for this turn
|
|
@@ -451,8 +441,8 @@ def main() -> int:
|
|
|
451
441
|
platform = _detect_platform(data)
|
|
452
442
|
task = get_active_task(root, data)
|
|
453
443
|
if task is None:
|
|
454
|
-
#
|
|
455
|
-
#
|
|
444
|
+
# Engaged but no active task: either the entry point has not created one
|
|
445
|
+
# yet, or `task.py finish` / archive cleared the pointer mid-session.
|
|
456
446
|
no_task_key = resolve_breadcrumb_key("no_task", platform, config)
|
|
457
447
|
breadcrumb = build_breadcrumb(
|
|
458
448
|
None, "no_task", templates, breadcrumb_key=no_task_key
|
|
@@ -465,31 +455,11 @@ def main() -> int:
|
|
|
465
455
|
task_id, status, templates, source_for_breadcrumb, breadcrumb_key=status_key
|
|
466
456
|
)
|
|
467
457
|
if platform == "codex":
|
|
468
|
-
|
|
469
|
-
if task is None:
|
|
470
|
-
parts.append(CODEX_NO_TASK_BOOTSTRAP_NOTICE)
|
|
471
|
-
parts.append(_codex_mode_banner(config))
|
|
472
|
-
parts.append(breadcrumb)
|
|
473
|
-
breadcrumb = "\n\n".join(parts)
|
|
474
|
-
|
|
475
|
-
# Kiro (CLI userPromptSubmit / IDE promptSubmit) adds a hook's stdout
|
|
476
|
-
# directly to the conversation context — no JSON envelope. Emit the bare
|
|
477
|
-
# breadcrumb text. Conditionally isolated: all other platforms keep the
|
|
478
|
-
# hookSpecificOutput JSON path below unchanged.
|
|
479
|
-
if platform == "kiro":
|
|
480
|
-
print(breadcrumb)
|
|
481
|
-
return 0
|
|
482
|
-
|
|
483
|
-
# Gemini CLI 0.40.x rejects "UserPromptSubmit" — its per-turn event is
|
|
484
|
-
# named "BeforeAgent". Other platforms (Claude/Cursor/Qoder/CodeBuddy/
|
|
485
|
-
# Droid/Codex/Copilot) accept the original Claude-style name.
|
|
486
|
-
hook_event_name = (
|
|
487
|
-
"BeforeAgent" if platform == "gemini" else "UserPromptSubmit"
|
|
488
|
-
)
|
|
458
|
+
breadcrumb = f"{_codex_mode_banner(config)}\n\n{breadcrumb}"
|
|
489
459
|
|
|
490
460
|
output = {
|
|
491
461
|
"hookSpecificOutput": {
|
|
492
|
-
"hookEventName":
|
|
462
|
+
"hookEventName": "UserPromptSubmit",
|
|
493
463
|
"additionalContext": breadcrumb,
|
|
494
464
|
}
|
|
495
465
|
}
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
"""
|
|
4
4
|
Session Start Hook - Inject structured context
|
|
5
|
+
|
|
6
|
+
Emits nothing until the session opts into the Trellis workflow by invoking
|
|
7
|
+
`trellis-start`, `trellis-continue` or `trellis-finish-work` (each runs
|
|
8
|
+
`task.py engage`). The session-identity env bridge still runs unconditionally —
|
|
9
|
+
it is plumbing, not context, and `task.py engage` depends on it.
|
|
5
10
|
"""
|
|
6
11
|
from __future__ import annotations
|
|
7
12
|
|
|
@@ -67,38 +72,27 @@ def _normalize_windows_shell_path(path_str: str) -> str:
|
|
|
67
72
|
return path_str
|
|
68
73
|
|
|
69
74
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
_FIRST_REPLY_NOTICE_TAIL = """Choose the acknowledgment language in this order:
|
|
74
|
-
1. Use the language of the user's current request (the user message that triggered this reply).
|
|
75
|
-
2. If that request has no clear natural language, use an explicitly established project communication language.
|
|
76
|
-
3. If neither provides a language, output the language-neutral fallback exactly: `Trellis SessionStart ✓`.
|
|
77
|
-
Continue directly with the user's request after the acknowledgment.
|
|
78
|
-
The acknowledgment must not alter the language used for the remainder of the response.
|
|
79
|
-
This notice is one-shot: do not repeat it after the first visible assistant reply in this session.
|
|
80
|
-
</first-reply-notice>"""
|
|
81
|
-
|
|
82
|
-
FIRST_REPLY_NOTICE = f"{_FIRST_REPLY_NOTICE_HEAD}\n{_FIRST_REPLY_NOTICE_TAIL}"
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def _build_first_reply_notice(update_hint: str | None) -> str:
|
|
86
|
-
"""First-reply notice, carrying the Trellis update reminder when there is one.
|
|
75
|
+
def _build_update_notice(update_hint: str | None) -> str:
|
|
76
|
+
"""Escalate a pending Trellis update into the visible reply.
|
|
87
77
|
|
|
88
78
|
The reminder has to reach the *user*, not just the model's context — a line
|
|
89
79
|
buried in SessionStart context is exactly how the update step kept getting
|
|
90
|
-
skipped.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
80
|
+
skipped. So it gets an explicit "relay this out loud" instruction rather
|
|
81
|
+
than a plain state line.
|
|
82
|
+
|
|
83
|
+
Emits nothing when no update is pending, which is the normal case: the block
|
|
84
|
+
exists only on the sessions that have something to report. Replaces the old
|
|
85
|
+
<first-reply-notice>, whose other job — making the AI announce that Trellis
|
|
86
|
+
context loaded — died with opt-in engagement, where the user invoked the
|
|
87
|
+
entry point and its output is the acknowledgment.
|
|
95
88
|
"""
|
|
96
89
|
if not update_hint:
|
|
97
|
-
return
|
|
90
|
+
return ""
|
|
98
91
|
return (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
f"{
|
|
92
|
+
"<trellis-maintenance-notice>\n"
|
|
93
|
+
"Relay this Trellis maintenance notice on its own line in your next "
|
|
94
|
+
f"visible reply, verbatim: {update_hint}\n"
|
|
95
|
+
"</trellis-maintenance-notice>\n\n"
|
|
102
96
|
)
|
|
103
97
|
|
|
104
98
|
|
|
@@ -160,15 +154,7 @@ def should_skip_injection() -> bool:
|
|
|
160
154
|
return True
|
|
161
155
|
non_interactive_vars = [
|
|
162
156
|
"CLAUDE_NON_INTERACTIVE",
|
|
163
|
-
"
|
|
164
|
-
"CODEBUDDY_NON_INTERACTIVE",
|
|
165
|
-
"FACTORY_NON_INTERACTIVE",
|
|
166
|
-
"CURSOR_NON_INTERACTIVE",
|
|
167
|
-
"GEMINI_NON_INTERACTIVE",
|
|
168
|
-
"KIRO_NON_INTERACTIVE",
|
|
169
|
-
"COPILOT_NON_INTERACTIVE",
|
|
170
|
-
"TRAE_NON_INTERACTIVE",
|
|
171
|
-
"ZCODE_NON_INTERACTIVE",
|
|
157
|
+
"CODEX_NON_INTERACTIVE",
|
|
172
158
|
]
|
|
173
159
|
return any(os.environ.get(var) == "1" for var in non_interactive_vars)
|
|
174
160
|
|
|
@@ -216,26 +202,13 @@ def _format_git_state(repo_root: Path) -> str:
|
|
|
216
202
|
|
|
217
203
|
|
|
218
204
|
def _detect_platform(input_data: dict) -> str | None:
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
#
|
|
222
|
-
#
|
|
223
|
-
#
|
|
224
|
-
# the context key becomes `claude_<their-session-id>`. That key does not
|
|
225
|
-
# match the session file `task.py start` wrote under the host's real name,
|
|
226
|
-
# so every turn reports no_task while the pointer exists on disk.
|
|
227
|
-
# Observed on CodeBuddy IDE 4.10.4: session file `codebuddy_ae54840e….json`
|
|
228
|
-
# alongside marker `update-check-claude_ae54840e….marker`, same id.
|
|
205
|
+
# CLAUDE_PROJECT_DIR is a compatibility alias other hosts may also set, so
|
|
206
|
+
# it is checked last — a vendor-specific key always wins. Detecting the
|
|
207
|
+
# wrong host would build the context key `claude_<their-session-id>`, which
|
|
208
|
+
# never matches the session file `task.py start` wrote under the host's
|
|
209
|
+
# real name, and every turn would report no_task while the pointer exists.
|
|
229
210
|
env_map = {
|
|
230
|
-
"
|
|
231
|
-
"CURSOR_PROJECT_DIR": "cursor",
|
|
232
|
-
"CODEBUDDY_PROJECT_DIR": "codebuddy",
|
|
233
|
-
"FACTORY_PROJECT_DIR": "droid",
|
|
234
|
-
"GEMINI_PROJECT_DIR": "gemini",
|
|
235
|
-
"QODER_PROJECT_DIR": "qoder",
|
|
236
|
-
"KIRO_PROJECT_DIR": "kiro",
|
|
237
|
-
"COPILOT_PROJECT_DIR": "copilot",
|
|
238
|
-
"TRAE_PROJECT_DIR": "trae",
|
|
211
|
+
"CODEX_PROJECT_DIR": "codex",
|
|
239
212
|
# Last: the shared alias, only meaningful once no vendor key matched.
|
|
240
213
|
"CLAUDE_PROJECT_DIR": "claude",
|
|
241
214
|
}
|
|
@@ -245,24 +218,8 @@ def _detect_platform(input_data: dict) -> str | None:
|
|
|
245
218
|
script_parts = set(Path(sys.argv[0]).parts)
|
|
246
219
|
if ".claude" in script_parts:
|
|
247
220
|
return "claude"
|
|
248
|
-
if ".cursor" in script_parts:
|
|
249
|
-
return "cursor"
|
|
250
221
|
if ".codex" in script_parts:
|
|
251
222
|
return "codex"
|
|
252
|
-
if ".gemini" in script_parts:
|
|
253
|
-
return "gemini"
|
|
254
|
-
if ".qoder" in script_parts:
|
|
255
|
-
return "qoder"
|
|
256
|
-
if ".codebuddy" in script_parts:
|
|
257
|
-
return "codebuddy"
|
|
258
|
-
if ".factory" in script_parts:
|
|
259
|
-
return "droid"
|
|
260
|
-
if ".kiro" in script_parts:
|
|
261
|
-
return "kiro"
|
|
262
|
-
if ".trae" in script_parts:
|
|
263
|
-
return "trae"
|
|
264
|
-
if ".zcode" in script_parts:
|
|
265
|
-
return "zcode"
|
|
266
223
|
return None
|
|
267
224
|
|
|
268
225
|
|
|
@@ -275,6 +232,35 @@ def _resolve_context_key(trellis_dir: Path, input_data: dict) -> str | None:
|
|
|
275
232
|
return resolve_context_key(input_data, platform=_detect_platform(input_data))
|
|
276
233
|
|
|
277
234
|
|
|
235
|
+
def _is_session_engaged(trellis_dir: Path, input_data: dict) -> bool:
|
|
236
|
+
"""Whether this session opted into the Trellis workflow.
|
|
237
|
+
|
|
238
|
+
A project too old to have `is_session_engaged` (pre-opt-in `.trellis/scripts`,
|
|
239
|
+
e.g. an install that upgraded the hooks but not the scripts) raises ImportError
|
|
240
|
+
here. Reporting False then would make Trellis look uninstalled on every
|
|
241
|
+
session with no way to diagnose it, so the stale-scripts case keeps the old
|
|
242
|
+
always-inject behavior and says why on stderr.
|
|
243
|
+
"""
|
|
244
|
+
scripts_dir = trellis_dir / "scripts"
|
|
245
|
+
if str(scripts_dir) not in sys.path:
|
|
246
|
+
sys.path.insert(0, str(scripts_dir))
|
|
247
|
+
try:
|
|
248
|
+
from common.active_task import is_session_engaged # type: ignore[import-not-found]
|
|
249
|
+
except ImportError:
|
|
250
|
+
print(
|
|
251
|
+
"trellis session-start: .trellis/scripts predates opt-in engagement; "
|
|
252
|
+
"injecting context unconditionally. Run `trellis update`.",
|
|
253
|
+
file=sys.stderr,
|
|
254
|
+
)
|
|
255
|
+
return True
|
|
256
|
+
|
|
257
|
+
return is_session_engaged(
|
|
258
|
+
trellis_dir.parent,
|
|
259
|
+
input_data,
|
|
260
|
+
platform=_detect_platform(input_data),
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
|
|
278
264
|
def _persist_context_key_for_bash(context_key: str | None) -> None:
|
|
279
265
|
"""Expose Trellis session identity to later Claude Code Bash commands.
|
|
280
266
|
|
|
@@ -853,15 +839,7 @@ def main():
|
|
|
853
839
|
# Try platform-specific env vars, hook cwd, fallback to cwd
|
|
854
840
|
project_dir_env_vars = [
|
|
855
841
|
"CLAUDE_PROJECT_DIR",
|
|
856
|
-
"
|
|
857
|
-
"CODEBUDDY_PROJECT_DIR",
|
|
858
|
-
"FACTORY_PROJECT_DIR",
|
|
859
|
-
"CURSOR_PROJECT_DIR",
|
|
860
|
-
"GEMINI_PROJECT_DIR",
|
|
861
|
-
"KIRO_PROJECT_DIR",
|
|
862
|
-
"COPILOT_PROJECT_DIR",
|
|
863
|
-
"TRAE_PROJECT_DIR",
|
|
864
|
-
"ZCODE_PROJECT_DIR",
|
|
842
|
+
"CODEX_PROJECT_DIR",
|
|
865
843
|
]
|
|
866
844
|
project_dir = None
|
|
867
845
|
for var in project_dir_env_vars:
|
|
@@ -876,6 +854,21 @@ def main():
|
|
|
876
854
|
context_key = _resolve_context_key(trellis_dir, hook_input)
|
|
877
855
|
_persist_context_key_for_bash(context_key)
|
|
878
856
|
|
|
857
|
+
# Context injection is opt-in. Everything below this point costs tokens, and
|
|
858
|
+
# a session that never invoked an entry point (`trellis-start` /
|
|
859
|
+
# `trellis-continue` / `trellis-finish-work`) is not doing Trellis work — an
|
|
860
|
+
# in-flight task belonging to some other window is not consent.
|
|
861
|
+
#
|
|
862
|
+
# The env bridge above runs FIRST and unconditionally: it is the only channel
|
|
863
|
+
# by which session identity reaches `task.py` in a Bash child on Claude Code,
|
|
864
|
+
# so gating it would leave `task.py engage` unable to write the very flag
|
|
865
|
+
# this check reads.
|
|
866
|
+
#
|
|
867
|
+
# An engaged session still re-enters here on /clear and /compact, which is
|
|
868
|
+
# the point: compaction drops the workflow context a long task depends on.
|
|
869
|
+
if not _is_session_engaged(trellis_dir, hook_input):
|
|
870
|
+
sys.exit(0)
|
|
871
|
+
|
|
879
872
|
# Load config for scope filtering and legacy detection
|
|
880
873
|
is_mono, packages, scope_config, task_pkg, default_pkg = _load_trellis_config(
|
|
881
874
|
trellis_dir,
|
|
@@ -892,8 +885,7 @@ Trellis compact SessionStart context. Use it to orient the session; load details
|
|
|
892
885
|
</session-context>
|
|
893
886
|
|
|
894
887
|
""")
|
|
895
|
-
output.write(
|
|
896
|
-
output.write("\n\n")
|
|
888
|
+
output.write(_build_update_notice(_resolve_update_hint(trellis_dir, context_key)))
|
|
897
889
|
|
|
898
890
|
# Legacy migration warning
|
|
899
891
|
legacy_warning = _check_legacy_spec(trellis_dir, is_mono, packages)
|
|
@@ -937,29 +929,15 @@ Context loaded. Follow <task-status>. Load workflow/spec/task details only when
|
|
|
937
929
|
|
|
938
930
|
context_text = output.getvalue()
|
|
939
931
|
|
|
940
|
-
# Kiro (CLI trellis agent agentSpawn) adds a hook's stdout directly to the
|
|
941
|
-
# conversation context — no JSON envelope. Emit the bare overview text.
|
|
942
|
-
# Conditionally isolated: all other platforms keep the JSON path below.
|
|
943
|
-
if _detect_platform(hook_input) == "kiro":
|
|
944
|
-
print(context_text, flush=True)
|
|
945
|
-
return
|
|
946
|
-
|
|
947
|
-
platform = _detect_platform(hook_input)
|
|
948
932
|
result: dict[str, object] = {
|
|
949
|
-
# Claude Code /
|
|
950
|
-
# ZCode format.
|
|
933
|
+
# Claude Code / Codex format.
|
|
951
934
|
"hookSpecificOutput": {
|
|
952
935
|
"hookEventName": "SessionStart",
|
|
953
936
|
"additionalContext": context_text,
|
|
954
937
|
},
|
|
938
|
+
# Snake_case alias for hosts that read the top-level key instead.
|
|
939
|
+
"additional_context": context_text,
|
|
955
940
|
}
|
|
956
|
-
# Cursor sessionStart format (top-level snake_case per Cursor docs).
|
|
957
|
-
# ZCode reads BOTH `hookSpecificOutput.additionalContext` and top-level
|
|
958
|
-
# `additional_context` without deduplication, so emitting both keys would
|
|
959
|
-
# duplicate the context in the conversation. Keep the previous shared output
|
|
960
|
-
# shape for every other platform.
|
|
961
|
-
if platform != "zcode":
|
|
962
|
-
result["additional_context"] = context_text
|
|
963
941
|
|
|
964
942
|
# Output JSON - stdout is already configured for UTF-8
|
|
965
943
|
print(json.dumps(result, ensure_ascii=False), flush=True)
|