@mindfoldhq/trellis 0.5.0-beta.8 → 0.5.0-rc.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/README.md +60 -95
- package/dist/cli/index.js +7 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/init.d.ts +13 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +474 -210
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +295 -54
- package/dist/commands/update.js.map +1 -1
- package/dist/configurators/antigravity.d.ts.map +1 -1
- package/dist/configurators/antigravity.js +2 -8
- package/dist/configurators/antigravity.js.map +1 -1
- package/dist/configurators/claude.d.ts.map +1 -1
- package/dist/configurators/claude.js +4 -10
- package/dist/configurators/claude.js.map +1 -1
- package/dist/configurators/codebuddy.d.ts.map +1 -1
- package/dist/configurators/codebuddy.js +3 -3
- package/dist/configurators/codebuddy.js.map +1 -1
- package/dist/configurators/codex.d.ts.map +1 -1
- package/dist/configurators/codex.js +5 -13
- package/dist/configurators/codex.js.map +1 -1
- package/dist/configurators/copilot.d.ts.map +1 -1
- package/dist/configurators/copilot.js +5 -19
- package/dist/configurators/copilot.js.map +1 -1
- package/dist/configurators/cursor.d.ts.map +1 -1
- package/dist/configurators/cursor.js +3 -3
- package/dist/configurators/cursor.js.map +1 -1
- package/dist/configurators/droid.d.ts.map +1 -1
- package/dist/configurators/droid.js +3 -3
- package/dist/configurators/droid.js.map +1 -1
- package/dist/configurators/gemini.d.ts.map +1 -1
- package/dist/configurators/gemini.js +3 -5
- package/dist/configurators/gemini.js.map +1 -1
- package/dist/configurators/index.d.ts.map +1 -1
- package/dist/configurators/index.js +44 -55
- package/dist/configurators/index.js.map +1 -1
- package/dist/configurators/kilo.d.ts.map +1 -1
- package/dist/configurators/kilo.js +2 -8
- package/dist/configurators/kilo.js.map +1 -1
- package/dist/configurators/kiro.d.ts.map +1 -1
- package/dist/configurators/kiro.js +3 -3
- package/dist/configurators/kiro.js.map +1 -1
- package/dist/configurators/opencode.d.ts.map +1 -1
- package/dist/configurators/opencode.js +7 -4
- package/dist/configurators/opencode.js.map +1 -1
- package/dist/configurators/pi.d.ts +3 -0
- package/dist/configurators/pi.d.ts.map +1 -0
- package/dist/configurators/pi.js +44 -0
- package/dist/configurators/pi.js.map +1 -0
- package/dist/configurators/qoder.d.ts +7 -6
- package/dist/configurators/qoder.d.ts.map +1 -1
- package/dist/configurators/qoder.js +18 -12
- package/dist/configurators/qoder.js.map +1 -1
- package/dist/configurators/shared.d.ts +30 -6
- package/dist/configurators/shared.d.ts.map +1 -1
- package/dist/configurators/shared.js +65 -15
- package/dist/configurators/shared.js.map +1 -1
- package/dist/configurators/windsurf.d.ts.map +1 -1
- package/dist/configurators/windsurf.js +2 -8
- package/dist/configurators/windsurf.js.map +1 -1
- package/dist/constants/paths.d.ts +2 -0
- package/dist/constants/paths.d.ts.map +1 -1
- package/dist/constants/paths.js +2 -0
- package/dist/constants/paths.js.map +1 -1
- package/dist/migrations/manifests/0.5.0-beta.0.json +2 -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.5.json +2 -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/templates/claude/agents/trellis-research.md +1 -1
- package/dist/templates/claude/settings.json +0 -4
- package/dist/templates/codebuddy/agents/trellis-research.md +1 -1
- package/dist/templates/codex/agents/trellis-research.toml +3 -2
- package/dist/templates/codex/hooks/session-start.py +126 -26
- package/dist/templates/codex/skills/finish-work/SKILL.md +41 -109
- package/dist/templates/codex/skills/start/SKILL.md +12 -9
- 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 +81 -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 +64 -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 +101 -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 +79 -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 +9 -5
- package/dist/templates/common/commands/finish-work.md +34 -10
- package/dist/templates/common/index.d.ts +22 -2
- package/dist/templates/common/index.d.ts.map +1 -1
- package/dist/templates/common/index.js +53 -4
- package/dist/templates/common/index.js.map +1 -1
- package/dist/templates/common/skills/brainstorm.md +50 -4
- package/dist/templates/copilot/hooks/session-start.py +127 -30
- package/dist/templates/copilot/prompts/finish-work.prompt.md +44 -112
- package/dist/templates/copilot/prompts/start.prompt.md +12 -9
- package/dist/templates/cursor/agents/trellis-check.md +1 -1
- package/dist/templates/cursor/agents/trellis-implement.md +1 -1
- package/dist/templates/cursor/agents/trellis-research.md +2 -2
- package/dist/templates/cursor/hooks.json +7 -1
- package/dist/templates/droid/droids/trellis-research.md +1 -1
- package/dist/templates/extract.d.ts +6 -0
- package/dist/templates/extract.d.ts.map +1 -1
- package/dist/templates/extract.js +14 -0
- package/dist/templates/extract.js.map +1 -1
- package/dist/templates/gemini/agents/trellis-research.md +1 -1
- package/dist/templates/kiro/agents/trellis-research.json +1 -1
- package/dist/templates/markdown/agents.md +19 -12
- package/dist/templates/markdown/gitignore.txt +3 -0
- package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +24 -0
- package/dist/templates/opencode/agents/trellis-check.md +1 -1
- package/dist/templates/opencode/agents/trellis-implement.md +7 -4
- package/dist/templates/opencode/agents/trellis-research.md +2 -2
- package/dist/templates/opencode/lib/trellis-context.js +100 -13
- package/dist/templates/opencode/plugins/inject-subagent-context.js +70 -5
- package/dist/templates/opencode/plugins/inject-workflow-state.js +38 -44
- package/dist/templates/opencode/plugins/session-start.js +76 -31
- package/dist/templates/pi/agents/trellis-check.md +28 -0
- package/dist/templates/pi/agents/trellis-implement.md +33 -0
- package/dist/templates/pi/agents/trellis-research.md +25 -0
- package/dist/templates/pi/extensions/trellis/index.ts.txt +997 -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 +12 -0
- package/dist/templates/qoder/agents/trellis-research.md +1 -1
- package/dist/templates/shared-hooks/index.d.ts +31 -0
- package/dist/templates/shared-hooks/index.d.ts.map +1 -1
- package/dist/templates/shared-hooks/index.js +59 -0
- package/dist/templates/shared-hooks/index.js.map +1 -1
- package/dist/templates/shared-hooks/inject-shell-session-context.py +180 -0
- package/dist/templates/shared-hooks/inject-subagent-context.py +156 -27
- package/dist/templates/shared-hooks/inject-workflow-state.py +85 -92
- package/dist/templates/shared-hooks/session-start.py +232 -36
- package/dist/templates/trellis/config.yaml +6 -0
- package/dist/templates/trellis/gitignore.txt +3 -0
- package/dist/templates/trellis/index.d.ts +1 -1
- package/dist/templates/trellis/index.d.ts.map +1 -1
- package/dist/templates/trellis/index.js +2 -2
- package/dist/templates/trellis/index.js.map +1 -1
- package/dist/templates/trellis/scripts/common/__init__.py +8 -0
- package/dist/templates/trellis/scripts/common/active_task.py +593 -0
- package/dist/templates/trellis/scripts/common/cli_adapter.py +72 -14
- package/dist/templates/trellis/scripts/common/paths.py +61 -58
- package/dist/templates/trellis/scripts/common/session_context.py +12 -0
- package/dist/templates/trellis/scripts/common/task_context.py +27 -194
- package/dist/templates/trellis/scripts/common/task_store.py +102 -26
- package/dist/templates/trellis/scripts/common/tasks.py +4 -1
- package/dist/templates/trellis/scripts/common/types.py +0 -2
- package/dist/templates/trellis/scripts/common/workflow_phase.py +15 -3
- package/dist/templates/trellis/scripts/task.py +99 -34
- package/dist/templates/trellis/workflow.md +332 -64
- package/dist/types/ai-tools.d.ts +12 -3
- package/dist/types/ai-tools.d.ts.map +1 -1
- package/dist/types/ai-tools.js +29 -0
- package/dist/types/ai-tools.js.map +1 -1
- package/dist/utils/file-writer.d.ts.map +1 -1
- package/dist/utils/file-writer.js +7 -2
- package/dist/utils/file-writer.js.map +1 -1
- 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 +2 -0
- package/dist/utils/project-detector.d.ts.map +1 -1
- package/dist/utils/project-detector.js +120 -11
- package/dist/utils/project-detector.js.map +1 -1
- package/dist/utils/task-json.d.ts +46 -0
- package/dist/utils/task-json.d.ts.map +1 -0
- package/dist/utils/task-json.js +49 -0
- package/dist/utils/task-json.js.map +1 -0
- package/dist/utils/template-fetcher.d.ts +22 -6
- package/dist/utils/template-fetcher.d.ts.map +1 -1
- package/dist/utils/template-fetcher.js +405 -27
- package/dist/utils/template-fetcher.js.map +1 -1
- package/dist/utils/template-hash.d.ts +22 -3
- package/dist/utils/template-hash.d.ts.map +1 -1
- package/dist/utils/template-hash.js +99 -19
- package/dist/utils/template-hash.js.map +1 -1
- package/package.json +7 -7
- package/dist/templates/markdown/spec/backend/directory-structure.md +0 -292
- package/dist/templates/markdown/spec/backend/index.md +0 -40
- package/dist/templates/markdown/spec/backend/script-conventions.md +0 -742
- package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md +0 -118
- package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md +0 -394
- package/dist/templates/shared-hooks/statusline.py +0 -218
- package/dist/templates/trellis/scripts/create_bootstrap.py +0 -298
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract.js","sourceRoot":"","sources":["../../src/templates/extract.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"extract.js","sourceRoot":"","sources":["../../src/templates/extract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAE/D,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAI3C;;GAEG;AACH,MAAM,UAAU,sBAAsB;IACpC,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IACrD,IAAI,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAChC,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,MAAM,IAAI,KAAK,CACb,4EAA4E,CAC7E,CAAC;AACJ,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,oBAAoB;IAClC,OAAO,sBAAsB,EAAE,CAAC;AAClC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB;IACnC,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACpD,IAAI,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAChC,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,MAAM,IAAI,KAAK,CACb,0EAA0E,CAC3E,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,uBAAuB;IACrC,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IACtD,IAAI,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAChC,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,MAAM,IAAI,KAAK,CACb,8EAA8E,CAC/E,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAChD,IAAI,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAChC,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,MAAM,IAAI,KAAK,CACb,kEAAkE,CACnE,CAAC;AACJ,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,eAAe;IAC7B,OAAO,iBAAiB,EAAE,CAAC;AAC7B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,YAAoB;IAClD,MAAM,WAAW,GAAG,oBAAoB,EAAE,CAAC;IAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;IACtD,OAAO,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AAC5C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAC1B,QAA0B,EAC1B,QAAgB;IAEhB,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC9D,OAAO,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,YAAoB;IAC7C,OAAO,eAAe,CAAC,WAAW,YAAY,EAAE,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,YAAoB;IAC/C,OAAO,eAAe,CAAC,YAAY,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,OAAO,YAAY,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;AAC5C,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,eAAuB,EACvB,QAAgB,EAChB,OAAkC;IAElC,MAAM,WAAW,GAAG,oBAAoB,EAAE,CAAC;IAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;IACxD,MAAM,gBAAgB,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AACrD,CAAC;AAED,KAAK,UAAU,gBAAgB,CAC7B,GAAW,EACX,IAAY,EACZ,OAAkC;IAElC,SAAS,CAAC,IAAI,CAAC,CAAC;IAEhB,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACxC,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAElC,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YACvB,MAAM,gBAAgB,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;QACrD,CAAC;aAAM,CAAC;YACN,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAClD,MAAM,YAAY,GAChB,OAAO,EAAE,UAAU,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;YAC1E,MAAM,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -29,7 +29,7 @@ Conversations get compacted; files don't. Every research output MUST end up as a
|
|
|
29
29
|
|
|
30
30
|
### Step 1: Resolve Current Task
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
Run `python3 ./.trellis/scripts/task.py current --source` → active task path. If no active task is set, ask the user where to write output; do NOT guess.
|
|
33
33
|
|
|
34
34
|
Ensure `{TASK_DIR}/research/` exists:
|
|
35
35
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trellis-research",
|
|
3
3
|
"description": "Code and tech search expert. Persists findings to {TASK_DIR}/research/. No writes outside that directory.",
|
|
4
|
-
"instructions": "# Research Agent\n\nYou are the Research Agent in the Trellis workflow.\n\n## Core Principle\n\n**You do one thing: find, explain, and PERSIST information.**\n\nConversations get compacted; files don't. Every research output MUST end up as a file under `{TASK_DIR}/research/`. Returning findings only through the chat reply is a failure — the caller cannot read them next session.\n\n---\n\n## Core Responsibilities\n\n1. **Internal Search** — locate files/components, understand code logic, discover patterns (Glob, Grep, Read)\n2. **External Search** — library docs, API references, best practices (web search)\n3. **Persist** — write each research topic to `{TASK_DIR}/research/<topic>.md`\n4. **Report** — return file paths + one-line summaries to the main agent (not full content)\n\n---\n\n## Workflow\n\n### Step 1: Resolve Current Task\n\
|
|
4
|
+
"instructions": "# Research Agent\n\nYou are the Research Agent in the Trellis workflow.\n\n## Core Principle\n\n**You do one thing: find, explain, and PERSIST information.**\n\nConversations get compacted; files don't. Every research output MUST end up as a file under `{TASK_DIR}/research/`. Returning findings only through the chat reply is a failure — the caller cannot read them next session.\n\n---\n\n## Core Responsibilities\n\n1. **Internal Search** — locate files/components, understand code logic, discover patterns (Glob, Grep, Read)\n2. **External Search** — library docs, API references, best practices (web search)\n3. **Persist** — write each research topic to `{TASK_DIR}/research/<topic>.md`\n4. **Report** — return file paths + one-line summaries to the main agent (not full content)\n\n---\n\n## Workflow\n\n### Step 1: Resolve Current Task\n\nRun `python3 ./.trellis/scripts/task.py current --source` → active task path. If no active task is set, ask the user where to write output; do NOT guess.\n\nEnsure `{TASK_DIR}/research/` exists:\n\n```bash\nmkdir -p <TASK_DIR>/research\n```\n\n### Step 2: Understand Search Request\n\nClassify: internal / external / mixed. Determine scope (global / specific directory) and expected shape (file list / pattern notes / tech comparison).\n\n### Step 3: Execute Search\n\nRun independent searches in parallel (Glob + Grep + web) for efficiency.\n\n### Step 4: Persist Each Topic\n\nFor each distinct research topic, Write a markdown file at `{TASK_DIR}/research/<topic-slug>.md`. Use the File Format below.\n\n### Step 5: Report to Main Agent\n\nReply with ONLY:\n\n- List of files written (paths relative to repo root)\n- One-line summary per file\n- Any critical caveats that the main agent needs to know right now\n\nDo NOT paste full research content into the reply. The files are the contract.\n\n---\n\n## Scope Limits (Strict)\n\n### Write ALLOWED\n\n- `{TASK_DIR}/research/*.md` — your own output\n- Creating `{TASK_DIR}/research/` if it doesn't exist (via `mkdir -p`)\n\n### Write FORBIDDEN\n\n- Code files (`src/`, `lib/`, …)\n- Spec files (`.trellis/spec/`) — main agent should use `update-spec` skill instead\n- `.trellis/scripts/`, `.trellis/workflow.md`, platform config (`.claude/`, `.cursor/`, etc.)\n- Other task directories\n- Any git operation (commit / push / branch / merge)\n\nIf the user asks you to edit code, decline and suggest spawning `implement` instead.\n\n---\n\n## File Format\n\nEach `{TASK_DIR}/research/<topic>.md` should follow:\n\n```markdown\n# Research: <topic>\n\n- **Query**: <original query>\n- **Scope**: <internal / external / mixed>\n- **Date**: <YYYY-MM-DD>\n\n## Findings\n\n### Files Found\n\n| File Path | Description |\n|---|---|\n| `src/services/xxx.ts` | Main implementation |\n| `src/types/xxx.ts` | Type definitions |\n\n### Code Patterns\n\n<describe patterns, cite file:line>\n\n### External References\n\n- [Library X docs](url) — <why relevant, version constraints>\n\n### Related Specs\n\n- `.trellis/spec/xxx.md` — <description>\n\n## Caveats / Not Found\n\n<anything incomplete or uncertain>\n```\n\n---\n\n## Guidelines\n\n### DO\n\n- Provide specific file paths and line numbers\n- Quote actual code snippets\n- Persist every topic to its own file\n- Return file paths in your reply, not the full content\n- Mark \"not found\" explicitly when searches come up empty\n\n### DON'T\n\n- Don't write code or modify files outside `{TASK_DIR}/research/`\n- Don't guess uncertain info\n- Don't paste full research text into the reply (files are the deliverable)\n- Don't propose improvements or critique implementation (that's not your role)\n",
|
|
5
5
|
"tools": [
|
|
6
6
|
"read",
|
|
7
7
|
"write",
|
|
@@ -3,20 +3,27 @@
|
|
|
3
3
|
|
|
4
4
|
These instructions are for AI assistants working in this project.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
- Initialize your developer identity
|
|
8
|
-
- Understand current project context
|
|
9
|
-
- Read relevant guidelines
|
|
6
|
+
This project is managed by Trellis. The working knowledge you need lives under `.trellis/`:
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
8
|
+
- `.trellis/workflow.md` — development phases, when to create tasks, skill routing
|
|
9
|
+
- `.trellis/spec/` — package- and layer-scoped coding guidelines (read before writing code in a given layer)
|
|
10
|
+
- `.trellis/workspace/` — per-developer journals and session traces
|
|
11
|
+
- `.trellis/tasks/` — active and archived tasks (PRDs, research, jsonl context)
|
|
15
12
|
|
|
16
|
-
If
|
|
17
|
-
- `.agents/skills/` for reusable Trellis skills
|
|
18
|
-
- `.codex/agents/` for optional custom subagents
|
|
13
|
+
If a Trellis command is available on your platform (e.g. `/trellis:finish-work`, `/trellis:continue`), prefer it over manual steps. Not every platform exposes every command.
|
|
19
14
|
|
|
20
|
-
|
|
15
|
+
If you're using Codex or another agent-capable tool, additional project-scoped helpers may live in:
|
|
16
|
+
- `.agents/skills/` — reusable Trellis skills
|
|
17
|
+
- `.codex/agents/` — optional custom subagents
|
|
18
|
+
|
|
19
|
+
## Subagents
|
|
20
|
+
|
|
21
|
+
- ALWAYS wait for all subagents to complete before yielding.
|
|
22
|
+
- Spawn subagents automatically when:
|
|
23
|
+
- Parallelizable work (e.g., install + verify, npm test + typecheck, multiple tasks from plan)
|
|
24
|
+
- Long-running or blocking tasks where a worker can run independently.
|
|
25
|
+
- Isolation for risky changes or checks
|
|
26
|
+
|
|
27
|
+
Managed by Trellis. Edits outside this block are preserved; edits inside may be overwritten by a future `trellis update`.
|
|
21
28
|
|
|
22
29
|
<!-- TRELLIS:END -->
|
|
@@ -69,6 +69,30 @@ subprocess.run(["python3", "other_script.py"])
|
|
|
69
69
|
subprocess.run([sys.executable, "other_script.py"])
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
**Rule 4**: Don't assume the Python version your AI CLI uses matches your shell's `python3`. Your terminal may resolve `python3` → 3.11 (via homebrew/pyenv), but AI CLI hosts often spawn hook subprocesses with a minimal PATH that resolves `python3` → the system Python (3.9 on macOS). Any `.py` file run as an AI-CLI hook must be written for the lowest plausible Python version.
|
|
73
|
+
|
|
74
|
+
Concrete failure: PEP 604 union syntax (`str | None`) requires Python 3.10+. If your hook file uses it, start with `from __future__ import annotations` so annotations become lazy strings and work on Python 3.7+:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
#!/usr/bin/env python3
|
|
78
|
+
"""My hook."""
|
|
79
|
+
from __future__ import annotations # REQUIRED for PEP 604 annotations
|
|
80
|
+
|
|
81
|
+
def handler(x: str | None) -> dict | None: # OK — lazy annotation
|
|
82
|
+
...
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
# BAD — crashes on Python < 3.10:
|
|
87
|
+
# TypeError: unsupported operand type(s) for |: 'type' and 'NoneType'
|
|
88
|
+
def handler(x: str | None) -> dict | None:
|
|
89
|
+
...
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Note: `from __future__ import annotations` only covers **annotations**. Runtime expressions like `isinstance(x, int | str)` still require Python 3.10+. Avoid them in hook scripts.
|
|
93
|
+
|
|
94
|
+
Applies to anything the AI CLI executes as a hook: `match/case` statements (3.10+), `tomllib` (3.11+), `ExceptionGroup` / `except*` (3.11+) — all crash on older Python regardless of `__future__`.
|
|
95
|
+
|
|
72
96
|
### 2. Path Handling
|
|
73
97
|
|
|
74
98
|
| Assumption | macOS/Linux | Windows |
|
|
@@ -21,7 +21,7 @@ You are the Check Agent in the Trellis workflow.
|
|
|
21
21
|
|
|
22
22
|
Otherwise, load context yourself:
|
|
23
23
|
|
|
24
|
-
1.
|
|
24
|
+
1. Run `python3 ./.trellis/scripts/task.py current --source` → get active task directory and source (e.g., `Current task: .trellis/tasks/xxx`)
|
|
25
25
|
2. Read `{task_dir}/check.jsonl`
|
|
26
26
|
3. For each entry in JSONL:
|
|
27
27
|
- If `path` is a file → Read it
|
|
@@ -21,14 +21,17 @@ You are the Implement Agent in the Trellis workflow.
|
|
|
21
21
|
|
|
22
22
|
Otherwise, load context yourself:
|
|
23
23
|
|
|
24
|
-
1.
|
|
24
|
+
1. Run `python3 ./.trellis/scripts/task.py current --source` → get active task directory and source (e.g., `Current task: .trellis/tasks/xxx`)
|
|
25
25
|
2. Read `{task_dir}/implement.jsonl`
|
|
26
|
-
3. For each entry in JSONL:
|
|
27
|
-
-
|
|
28
|
-
- If `
|
|
26
|
+
3. For each entry in JSONL (JSON object per line):
|
|
27
|
+
- Skip rows without a `"file"` field (e.g. `{"_example": "..."}` seed rows)
|
|
28
|
+
- If `file` points at a file → Read it
|
|
29
|
+
- If `file` ends with `/` (directory) → Read all `.md` files in it
|
|
29
30
|
4. Read `{task_dir}/prd.md` for requirements
|
|
30
31
|
5. Read `{task_dir}/info.md` for technical design (if exists)
|
|
31
32
|
|
|
33
|
+
**If `implement.jsonl` has no curated entries (only a seed row, or the file is missing)**: read `prd.md` to understand the task domain, then decide which specs apply based on `.trellis/spec/` layout. You can list available specs with `python3 ./.trellis/scripts/get_context.py --mode packages`. Do not block on the missing jsonl — proceed with prd-only context plus your own spec judgment.
|
|
34
|
+
|
|
32
35
|
Then proceed with the workflow below using the loaded context.
|
|
33
36
|
|
|
34
37
|
---
|
|
@@ -6,7 +6,7 @@ permission:
|
|
|
6
6
|
read: allow
|
|
7
7
|
write: deny
|
|
8
8
|
edit: deny
|
|
9
|
-
bash:
|
|
9
|
+
bash: allow
|
|
10
10
|
glob: allow
|
|
11
11
|
grep: allow
|
|
12
12
|
mcp__exa__*: allow
|
|
@@ -22,7 +22,7 @@ You are the Research Agent in the Trellis workflow.
|
|
|
22
22
|
|
|
23
23
|
Otherwise, if task-specific research is needed:
|
|
24
24
|
|
|
25
|
-
1.
|
|
25
|
+
1. Run `python3 ./.trellis/scripts/task.py current --source` → get active task directory and source (if set)
|
|
26
26
|
2. For each entry in JSONL (if task dir exists):
|
|
27
27
|
- If `path` is a file → Read it
|
|
28
28
|
- If `path` is a directory → Read all `.md` files in it
|
|
@@ -9,6 +9,8 @@ import { existsSync, readFileSync, appendFileSync, readdirSync } from "fs"
|
|
|
9
9
|
import { isAbsolute, join } from "path"
|
|
10
10
|
import { platform } from "os"
|
|
11
11
|
import { execSync } from "child_process"
|
|
12
|
+
import { createHash } from "crypto"
|
|
13
|
+
import process from "process"
|
|
12
14
|
|
|
13
15
|
const PYTHON_CMD = platform() === "win32" ? "python" : "python3"
|
|
14
16
|
// Debug logging
|
|
@@ -24,6 +26,43 @@ function debugLog(prefix, ...args) {
|
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
28
|
|
|
29
|
+
function stringValue(value) {
|
|
30
|
+
return typeof value === "string" && value.trim() ? value.trim() : null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function sanitizeKey(raw) {
|
|
34
|
+
const safe = raw.trim().replace(/[^A-Za-z0-9._-]+/g, "_").replace(/^[._-]+|[._-]+$/g, "")
|
|
35
|
+
return safe ? safe.slice(0, 160) : ""
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function hashValue(raw) {
|
|
39
|
+
return createHash("sha256").update(raw).digest("hex").slice(0, 24)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function lookupString(data, keys) {
|
|
43
|
+
if (!data || typeof data !== "object") return null
|
|
44
|
+
for (const key of keys) {
|
|
45
|
+
const value = stringValue(data[key])
|
|
46
|
+
if (value) return value
|
|
47
|
+
}
|
|
48
|
+
for (const nestedKey of ["input", "properties", "event", "hook_input", "hookInput"]) {
|
|
49
|
+
const nested = data[nestedKey]
|
|
50
|
+
if (nested && typeof nested === "object") {
|
|
51
|
+
const value = lookupString(nested, keys)
|
|
52
|
+
if (value) return value
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function buildContextKey(platformName, kind, value) {
|
|
59
|
+
if (kind === "transcript") {
|
|
60
|
+
return `${platformName}_transcript_${hashValue(value)}`
|
|
61
|
+
}
|
|
62
|
+
const safeValue = sanitizeKey(value)
|
|
63
|
+
return safeValue ? `${platformName}_${safeValue}` : `${platformName}_${hashValue(value)}`
|
|
64
|
+
}
|
|
65
|
+
|
|
27
66
|
/**
|
|
28
67
|
* Trellis Context Manager
|
|
29
68
|
*/
|
|
@@ -41,23 +80,67 @@ export class TrellisContext {
|
|
|
41
80
|
return existsSync(join(this.directory, ".trellis"))
|
|
42
81
|
}
|
|
43
82
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
83
|
+
getContextKey(platformInput = null) {
|
|
84
|
+
const override = stringValue(process.env.TRELLIS_CONTEXT_ID)
|
|
85
|
+
if (override) {
|
|
86
|
+
return sanitizeKey(override) || hashValue(override)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const runID = stringValue(process.env.OPENCODE_RUN_ID)
|
|
90
|
+
if (runID) return buildContextKey("opencode", "session", runID)
|
|
91
|
+
|
|
92
|
+
const input = platformInput && typeof platformInput === "object" ? platformInput : null
|
|
93
|
+
if (!input) return null
|
|
94
|
+
|
|
95
|
+
const sessionID = lookupString(input, ["session_id", "sessionId", "sessionID"])
|
|
96
|
+
if (sessionID) return buildContextKey("opencode", "session", sessionID)
|
|
97
|
+
|
|
98
|
+
const conversationID = lookupString(input, ["conversation_id", "conversationId", "conversationID"])
|
|
99
|
+
if (conversationID) return buildContextKey("opencode", "conversation", conversationID)
|
|
100
|
+
|
|
101
|
+
const transcriptPath = lookupString(input, ["transcript_path", "transcriptPath", "transcript"])
|
|
102
|
+
if (transcriptPath) return buildContextKey("opencode", "transcript", transcriptPath)
|
|
103
|
+
|
|
104
|
+
return null
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
readContext(contextKey) {
|
|
48
108
|
try {
|
|
49
|
-
const
|
|
50
|
-
if (!existsSync(
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
const taskRef = readFileSync(currentTaskPath, "utf-8").trim()
|
|
54
|
-
const normalized = this.normalizeTaskRef(taskRef)
|
|
55
|
-
return normalized || null
|
|
109
|
+
const contextPath = join(this.directory, ".trellis", ".runtime", "sessions", `${contextKey}.json`)
|
|
110
|
+
if (!existsSync(contextPath)) return null
|
|
111
|
+
return JSON.parse(readFileSync(contextPath, "utf-8"))
|
|
56
112
|
} catch {
|
|
57
113
|
return null
|
|
58
114
|
}
|
|
59
115
|
}
|
|
60
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Get active task from session runtime context.
|
|
119
|
+
*/
|
|
120
|
+
getActiveTask(platformInput = null) {
|
|
121
|
+
const contextKey = this.getContextKey(platformInput)
|
|
122
|
+
if (!contextKey) {
|
|
123
|
+
return { taskPath: null, source: "none", stale: false }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const context = this.readContext(contextKey)
|
|
127
|
+
const taskRef = this.normalizeTaskRef(context?.current_task || "")
|
|
128
|
+
if (taskRef) {
|
|
129
|
+
const taskDir = this.resolveTaskDir(taskRef)
|
|
130
|
+
return {
|
|
131
|
+
taskPath: taskRef,
|
|
132
|
+
source: `session:${contextKey}`,
|
|
133
|
+
stale: !taskDir || !existsSync(taskDir),
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return { taskPath: null, source: "none", stale: false }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
getCurrentTask(platformInput = null) {
|
|
141
|
+
return this.getActiveTask(platformInput).taskPath
|
|
142
|
+
}
|
|
143
|
+
|
|
61
144
|
normalizeTaskRef(taskRef) {
|
|
62
145
|
if (!taskRef) {
|
|
63
146
|
return ""
|
|
@@ -115,13 +198,17 @@ export class TrellisContext {
|
|
|
115
198
|
return this.readFile(join(this.directory, relativePath))
|
|
116
199
|
}
|
|
117
200
|
|
|
118
|
-
runScript(scriptPath, cwd = null) {
|
|
201
|
+
runScript(scriptPath, cwd = null, contextKey = null) {
|
|
119
202
|
try {
|
|
120
203
|
const result = execSync(`${PYTHON_CMD} "${scriptPath}"`, {
|
|
121
204
|
cwd: cwd || this.directory,
|
|
122
205
|
timeout: 10000,
|
|
123
206
|
encoding: "utf-8",
|
|
124
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
207
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
208
|
+
env: {
|
|
209
|
+
...process.env,
|
|
210
|
+
...(contextKey ? { TRELLIS_CONTEXT_ID: contextKey } : {}),
|
|
211
|
+
},
|
|
125
212
|
})
|
|
126
213
|
return result || ""
|
|
127
214
|
} catch {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* global process */
|
|
1
2
|
/**
|
|
2
3
|
* Trellis Context Injection Plugin
|
|
3
4
|
*
|
|
@@ -255,12 +256,66 @@ ${originalPrompt}
|
|
|
255
256
|
return templates[agentType] || originalPrompt
|
|
256
257
|
}
|
|
257
258
|
|
|
259
|
+
function shellQuote(value) {
|
|
260
|
+
return `'${String(value).replace(/'/g, "'\\''")}'`
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function powershellQuote(value) {
|
|
264
|
+
return `'${String(value).replace(/'/g, "''")}'`
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function buildTrellisContextPrefix(contextKey, hostPlatform = process.platform) {
|
|
268
|
+
if (hostPlatform === "win32") {
|
|
269
|
+
// OpenCode's Windows Bash tool runs through PowerShell, not a POSIX shell.
|
|
270
|
+
return `$env:TRELLIS_CONTEXT_ID = ${powershellQuote(contextKey)}; `
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return `export TRELLIS_CONTEXT_ID=${shellQuote(contextKey)}; `
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function getBashCommandKey(args) {
|
|
277
|
+
if (!args || typeof args !== "object") return null
|
|
278
|
+
if (typeof args.command === "string") return "command"
|
|
279
|
+
if (typeof args.cmd === "string") return "cmd"
|
|
280
|
+
return null
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function commandStartsWithTrellisContext(command) {
|
|
284
|
+
const firstCommand = command.trimStart().split(/[;&|]/, 1)[0].trimStart()
|
|
285
|
+
return (
|
|
286
|
+
/^TRELLIS_CONTEXT_ID\s*=/.test(firstCommand) ||
|
|
287
|
+
/^export\s+TRELLIS_CONTEXT_ID\s*=/.test(firstCommand) ||
|
|
288
|
+
/^env\s+(?:[^\s=]+\s+)*TRELLIS_CONTEXT_ID\s*=/.test(firstCommand) ||
|
|
289
|
+
/^\$env:TRELLIS_CONTEXT_ID\s*=/i.test(firstCommand)
|
|
290
|
+
)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* OpenCode TUI may not expose OPENCODE_RUN_ID to Bash. The plugin hook still
|
|
295
|
+
* receives session identity, so inject it into Bash commands before execution.
|
|
296
|
+
*/
|
|
297
|
+
function injectTrellisContextIntoBash(ctx, input, output, hostPlatform) {
|
|
298
|
+
const args = output?.args
|
|
299
|
+
const commandKey = getBashCommandKey(args)
|
|
300
|
+
if (!commandKey) return false
|
|
301
|
+
|
|
302
|
+
const command = args[commandKey]
|
|
303
|
+
if (!command.trim()) return false
|
|
304
|
+
if (commandStartsWithTrellisContext(command)) return false
|
|
305
|
+
|
|
306
|
+
const contextKey = ctx.getContextKey(input)
|
|
307
|
+
if (!contextKey) return false
|
|
308
|
+
|
|
309
|
+
args[commandKey] = `${buildTrellisContextPrefix(contextKey, hostPlatform)}${command}`
|
|
310
|
+
return true
|
|
311
|
+
}
|
|
312
|
+
|
|
258
313
|
// OpenCode plugin factory: `export default async (input) => hooks`.
|
|
259
314
|
// OpenCode 1.2.x iterates every module export and invokes it as a function
|
|
260
315
|
// (packages/opencode/src/plugin/index.ts — `for ([_, fn] of Object.entries(mod)) await fn(input)`);
|
|
261
316
|
// the previous `{ id, server }` object shape failed with
|
|
262
317
|
// `TypeError: fn is not a function` in 1.2.x.
|
|
263
|
-
export default async ({ directory }) => {
|
|
318
|
+
export default async ({ directory, platform: hostPlatform = process.platform }) => {
|
|
264
319
|
const ctx = new TrellisContext(directory)
|
|
265
320
|
debugLog("inject", "Plugin loaded, directory:", directory)
|
|
266
321
|
|
|
@@ -270,6 +325,13 @@ export default async ({ directory }) => {
|
|
|
270
325
|
debugLog("inject", "tool.execute.before called, tool:", input?.tool)
|
|
271
326
|
|
|
272
327
|
const toolName = input?.tool?.toLowerCase()
|
|
328
|
+
if (toolName === "bash") {
|
|
329
|
+
if (injectTrellisContextIntoBash(ctx, input, output, hostPlatform)) {
|
|
330
|
+
debugLog("inject", "Injected TRELLIS_CONTEXT_ID into Bash command")
|
|
331
|
+
}
|
|
332
|
+
return
|
|
333
|
+
}
|
|
334
|
+
|
|
273
335
|
if (toolName !== "task") {
|
|
274
336
|
return
|
|
275
337
|
}
|
|
@@ -277,21 +339,24 @@ export default async ({ directory }) => {
|
|
|
277
339
|
const args = output?.args
|
|
278
340
|
if (!args) return
|
|
279
341
|
|
|
280
|
-
const
|
|
342
|
+
const rawSubagentType = args.subagent_type
|
|
343
|
+
// Strip "trellis-" prefix added by v0.5.0-beta.5 agent rename migration
|
|
344
|
+
const subagentType = (rawSubagentType || "").replace(/^trellis-/, "")
|
|
281
345
|
const originalPrompt = args.prompt || ""
|
|
282
346
|
|
|
283
|
-
debugLog("inject", "Task tool called, subagent_type:",
|
|
347
|
+
debugLog("inject", "Task tool called, subagent_type:", rawSubagentType)
|
|
284
348
|
|
|
285
349
|
if (!AGENTS_ALL.includes(subagentType)) {
|
|
286
350
|
debugLog("inject", "Skipping - unsupported subagent_type")
|
|
287
351
|
return
|
|
288
352
|
}
|
|
289
353
|
|
|
290
|
-
//
|
|
291
|
-
const taskDir = ctx.getCurrentTask()
|
|
354
|
+
// Resolve active task through session runtime context.
|
|
355
|
+
const taskDir = ctx.getCurrentTask(input)
|
|
292
356
|
|
|
293
357
|
// Agents requiring task directory
|
|
294
358
|
if (AGENTS_REQUIRE_TASK.includes(subagentType)) {
|
|
359
|
+
// subagentType is already stripped of "trellis-" prefix above
|
|
295
360
|
if (!taskDir) {
|
|
296
361
|
debugLog("inject", "Skipping - no current task")
|
|
297
362
|
return
|
|
@@ -5,19 +5,21 @@
|
|
|
5
5
|
* Per-turn UserPromptSubmit equivalent for OpenCode.
|
|
6
6
|
*
|
|
7
7
|
* On every chat.message, if a Trellis task is active, inject a short
|
|
8
|
-
* <workflow-state> breadcrumb reminding the main AI what task is
|
|
9
|
-
* and its expected flow. Breadcrumb text is pulled
|
|
10
|
-
* workflow.md [workflow-state:STATUS] tag blocks
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
8
|
+
* <workflow-state> breadcrumb reminding the main AI what task is
|
|
9
|
+
* active and its expected flow. Breadcrumb text is pulled exclusively
|
|
10
|
+
* from the project's workflow.md [workflow-state:STATUS] tag blocks —
|
|
11
|
+
* workflow.md is the single source of truth. There are no fallback
|
|
12
|
+
* tables in this plugin: when workflow.md is missing or a tag is
|
|
13
|
+
* absent, the breadcrumb degrades to a generic
|
|
14
|
+
* "Refer to workflow.md for current step." line so users see (and fix)
|
|
15
|
+
* the broken state instead of the plugin silently masking it.
|
|
14
16
|
*
|
|
15
|
-
* Unlike session-start, this plugin does NOT dedupe — breadcrumb
|
|
17
|
+
* Unlike session-start, this plugin does NOT dedupe — the breadcrumb
|
|
16
18
|
* should surface on every turn so long conversations don't drift.
|
|
17
19
|
*
|
|
18
20
|
* Silently skips when:
|
|
19
21
|
* - No .trellis/ directory
|
|
20
|
-
* - No active task
|
|
22
|
+
* - No active task in the session runtime context
|
|
21
23
|
* - task.json malformed or missing status
|
|
22
24
|
*/
|
|
23
25
|
|
|
@@ -29,40 +31,25 @@ import { TrellisContext, debugLog } from "../lib/trellis-context.js"
|
|
|
29
31
|
// (so "in-review" / "blocked-by-team" work alongside "in_progress").
|
|
30
32
|
const TAG_RE = /\[workflow-state:([A-Za-z0-9_-]+)\]\s*\n([\s\S]*?)\n\s*\[\/workflow-state:\1\]/g
|
|
31
33
|
|
|
32
|
-
// Hardcoded defaults for built-in Trellis statuses. Used when workflow.md
|
|
33
|
-
// is missing, malformed, or lacks the tag for this status.
|
|
34
|
-
//
|
|
35
|
-
// `no_task` is a pseudo-status emitted when .current-task is missing — keeps
|
|
36
|
-
// the Next-Action reminder flowing per-turn even without an active task.
|
|
37
|
-
const FALLBACK_BREADCRUMBS = {
|
|
38
|
-
no_task:
|
|
39
|
-
"No active task. If the user describes multi-step work, load " +
|
|
40
|
-
"trellis-brainstorm skill to clarify requirements and create a task " +
|
|
41
|
-
"via `python3 ./.trellis/scripts/task.py create`. Simple one-off " +
|
|
42
|
-
"questions or trivial edits don't need a task — just answer directly.",
|
|
43
|
-
planning:
|
|
44
|
-
"Complete prd.md via trellis-brainstorm skill; then run task.py start.",
|
|
45
|
-
in_progress:
|
|
46
|
-
"Flow: implement → check → update-spec → finish\n" +
|
|
47
|
-
"Check conversation history + git status to determine current step; do NOT skip check.",
|
|
48
|
-
completed:
|
|
49
|
-
"User commits changes; then run task.py archive.",
|
|
50
|
-
}
|
|
51
|
-
|
|
52
34
|
/**
|
|
53
35
|
* Parse workflow.md for [workflow-state:STATUS] blocks.
|
|
54
|
-
*
|
|
36
|
+
*
|
|
37
|
+
* Returns {status: body}. workflow.md is the single source of truth —
|
|
38
|
+
* there are no fallback tables here. Missing tags (or a missing /
|
|
39
|
+
* unreadable workflow.md) fall back to a generic line in
|
|
40
|
+
* buildBreadcrumb so users see the broken state and fix workflow.md
|
|
41
|
+
* rather than the plugin silently masking it.
|
|
55
42
|
*/
|
|
56
43
|
function loadBreadcrumbs(directory) {
|
|
57
|
-
const result = { ...FALLBACK_BREADCRUMBS }
|
|
58
44
|
const workflowPath = join(directory, ".trellis", "workflow.md")
|
|
59
|
-
if (!existsSync(workflowPath)) return
|
|
45
|
+
if (!existsSync(workflowPath)) return {}
|
|
60
46
|
let content
|
|
61
47
|
try {
|
|
62
48
|
content = readFileSync(workflowPath, "utf-8")
|
|
63
49
|
} catch {
|
|
64
|
-
return
|
|
50
|
+
return {}
|
|
65
51
|
}
|
|
52
|
+
const result = {}
|
|
66
53
|
for (const match of content.matchAll(TAG_RE)) {
|
|
67
54
|
const status = match[1]
|
|
68
55
|
const body = match[2].trim()
|
|
@@ -74,11 +61,14 @@ function loadBreadcrumbs(directory) {
|
|
|
74
61
|
/**
|
|
75
62
|
* Get (taskId, status) from active task, or null if no active task.
|
|
76
63
|
*/
|
|
77
|
-
function getActiveTask(ctx) {
|
|
78
|
-
const
|
|
64
|
+
function getActiveTask(ctx, platformInput = null) {
|
|
65
|
+
const active = ctx.getActiveTask(platformInput)
|
|
66
|
+
const taskRef = active.taskPath
|
|
79
67
|
if (!taskRef) return null
|
|
80
68
|
const taskDir = ctx.resolveTaskDir(taskRef)
|
|
81
|
-
if (!taskDir || !existsSync(taskDir))
|
|
69
|
+
if (active.stale || !taskDir || !existsSync(taskDir)) {
|
|
70
|
+
return { id: taskRef.split("/").pop(), status: "stale", source: active.source }
|
|
71
|
+
}
|
|
82
72
|
const taskJsonPath = join(taskDir, "task.json")
|
|
83
73
|
if (!existsSync(taskJsonPath)) return null
|
|
84
74
|
try {
|
|
@@ -86,7 +76,7 @@ function getActiveTask(ctx) {
|
|
|
86
76
|
const status = typeof data.status === "string" ? data.status : ""
|
|
87
77
|
if (!status) return null
|
|
88
78
|
const id = data.id || taskRef.split("/").pop()
|
|
89
|
-
return { id, status }
|
|
79
|
+
return { id, status, source: active.source }
|
|
90
80
|
} catch {
|
|
91
81
|
return null
|
|
92
82
|
}
|
|
@@ -94,16 +84,20 @@ function getActiveTask(ctx) {
|
|
|
94
84
|
|
|
95
85
|
/**
|
|
96
86
|
* Build the <workflow-state>...</workflow-state> block.
|
|
97
|
-
* - Known status (
|
|
98
|
-
* - Unknown status
|
|
87
|
+
* - Known status (tag present in workflow.md) → detailed body
|
|
88
|
+
* - Unknown status (no tag, or workflow.md missing) → generic
|
|
89
|
+
* "Refer to workflow.md for current step." line
|
|
99
90
|
* - no_task pseudo-status (id === null) → header omits task info
|
|
100
91
|
*/
|
|
101
|
-
function buildBreadcrumb(id, status, templates) {
|
|
92
|
+
function buildBreadcrumb(id, status, templates, source = null) {
|
|
102
93
|
let body = templates[status]
|
|
103
94
|
if (body === undefined) {
|
|
104
95
|
body = "Refer to workflow.md for current step."
|
|
105
96
|
}
|
|
106
|
-
|
|
97
|
+
let header = id === null ? `Status: ${status}` : `Task: ${id} (${status})`
|
|
98
|
+
if (source) {
|
|
99
|
+
header = `${header}\nSource: ${source}`
|
|
100
|
+
}
|
|
107
101
|
return `<workflow-state>\n${header}\n${body}\n</workflow-state>`
|
|
108
102
|
}
|
|
109
103
|
|
|
@@ -124,9 +118,9 @@ export default async ({ directory }) => {
|
|
|
124
118
|
return
|
|
125
119
|
}
|
|
126
120
|
const templates = loadBreadcrumbs(directory)
|
|
127
|
-
const task = getActiveTask(ctx)
|
|
121
|
+
const task = getActiveTask(ctx, input)
|
|
128
122
|
const breadcrumb = task
|
|
129
|
-
? buildBreadcrumb(task.id, task.status, templates)
|
|
123
|
+
? buildBreadcrumb(task.id, task.status, templates, task.source)
|
|
130
124
|
: buildBreadcrumb(null, "no_task", templates)
|
|
131
125
|
|
|
132
126
|
const parts = output?.parts || []
|
|
@@ -142,9 +136,9 @@ export default async ({ directory }) => {
|
|
|
142
136
|
debugLog(
|
|
143
137
|
"workflow-state",
|
|
144
138
|
"Injected breadcrumb for task",
|
|
145
|
-
task.id,
|
|
139
|
+
task ? task.id : "none",
|
|
146
140
|
"status",
|
|
147
|
-
task.status,
|
|
141
|
+
task ? task.status : "no_task",
|
|
148
142
|
)
|
|
149
143
|
} catch (error) {
|
|
150
144
|
debugLog(
|