@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.
Files changed (210) hide show
  1. package/README.md +60 -95
  2. package/dist/cli/index.js +7 -0
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/commands/init.d.ts +13 -0
  5. package/dist/commands/init.d.ts.map +1 -1
  6. package/dist/commands/init.js +474 -210
  7. package/dist/commands/init.js.map +1 -1
  8. package/dist/commands/update.d.ts.map +1 -1
  9. package/dist/commands/update.js +295 -54
  10. package/dist/commands/update.js.map +1 -1
  11. package/dist/configurators/antigravity.d.ts.map +1 -1
  12. package/dist/configurators/antigravity.js +2 -8
  13. package/dist/configurators/antigravity.js.map +1 -1
  14. package/dist/configurators/claude.d.ts.map +1 -1
  15. package/dist/configurators/claude.js +4 -10
  16. package/dist/configurators/claude.js.map +1 -1
  17. package/dist/configurators/codebuddy.d.ts.map +1 -1
  18. package/dist/configurators/codebuddy.js +3 -3
  19. package/dist/configurators/codebuddy.js.map +1 -1
  20. package/dist/configurators/codex.d.ts.map +1 -1
  21. package/dist/configurators/codex.js +5 -13
  22. package/dist/configurators/codex.js.map +1 -1
  23. package/dist/configurators/copilot.d.ts.map +1 -1
  24. package/dist/configurators/copilot.js +5 -19
  25. package/dist/configurators/copilot.js.map +1 -1
  26. package/dist/configurators/cursor.d.ts.map +1 -1
  27. package/dist/configurators/cursor.js +3 -3
  28. package/dist/configurators/cursor.js.map +1 -1
  29. package/dist/configurators/droid.d.ts.map +1 -1
  30. package/dist/configurators/droid.js +3 -3
  31. package/dist/configurators/droid.js.map +1 -1
  32. package/dist/configurators/gemini.d.ts.map +1 -1
  33. package/dist/configurators/gemini.js +3 -5
  34. package/dist/configurators/gemini.js.map +1 -1
  35. package/dist/configurators/index.d.ts.map +1 -1
  36. package/dist/configurators/index.js +44 -55
  37. package/dist/configurators/index.js.map +1 -1
  38. package/dist/configurators/kilo.d.ts.map +1 -1
  39. package/dist/configurators/kilo.js +2 -8
  40. package/dist/configurators/kilo.js.map +1 -1
  41. package/dist/configurators/kiro.d.ts.map +1 -1
  42. package/dist/configurators/kiro.js +3 -3
  43. package/dist/configurators/kiro.js.map +1 -1
  44. package/dist/configurators/opencode.d.ts.map +1 -1
  45. package/dist/configurators/opencode.js +7 -4
  46. package/dist/configurators/opencode.js.map +1 -1
  47. package/dist/configurators/pi.d.ts +3 -0
  48. package/dist/configurators/pi.d.ts.map +1 -0
  49. package/dist/configurators/pi.js +44 -0
  50. package/dist/configurators/pi.js.map +1 -0
  51. package/dist/configurators/qoder.d.ts +7 -6
  52. package/dist/configurators/qoder.d.ts.map +1 -1
  53. package/dist/configurators/qoder.js +18 -12
  54. package/dist/configurators/qoder.js.map +1 -1
  55. package/dist/configurators/shared.d.ts +30 -6
  56. package/dist/configurators/shared.d.ts.map +1 -1
  57. package/dist/configurators/shared.js +65 -15
  58. package/dist/configurators/shared.js.map +1 -1
  59. package/dist/configurators/windsurf.d.ts.map +1 -1
  60. package/dist/configurators/windsurf.js +2 -8
  61. package/dist/configurators/windsurf.js.map +1 -1
  62. package/dist/constants/paths.d.ts +2 -0
  63. package/dist/constants/paths.d.ts.map +1 -1
  64. package/dist/constants/paths.js +2 -0
  65. package/dist/constants/paths.js.map +1 -1
  66. package/dist/migrations/manifests/0.5.0-beta.0.json +2 -0
  67. package/dist/migrations/manifests/0.5.0-beta.10.json +9 -0
  68. package/dist/migrations/manifests/0.5.0-beta.11.json +9 -0
  69. package/dist/migrations/manifests/0.5.0-beta.12.json +9 -0
  70. package/dist/migrations/manifests/0.5.0-beta.13.json +9 -0
  71. package/dist/migrations/manifests/0.5.0-beta.14.json +9 -0
  72. package/dist/migrations/manifests/0.5.0-beta.15.json +116 -0
  73. package/dist/migrations/manifests/0.5.0-beta.16.json +9 -0
  74. package/dist/migrations/manifests/0.5.0-beta.17.json +9 -0
  75. package/dist/migrations/manifests/0.5.0-beta.18.json +9 -0
  76. package/dist/migrations/manifests/0.5.0-beta.19.json +9 -0
  77. package/dist/migrations/manifests/0.5.0-beta.5.json +2 -0
  78. package/dist/migrations/manifests/0.5.0-beta.9.json +48 -0
  79. package/dist/migrations/manifests/0.5.0-rc.0.json +9 -0
  80. package/dist/templates/claude/agents/trellis-research.md +1 -1
  81. package/dist/templates/claude/settings.json +0 -4
  82. package/dist/templates/codebuddy/agents/trellis-research.md +1 -1
  83. package/dist/templates/codex/agents/trellis-research.toml +3 -2
  84. package/dist/templates/codex/hooks/session-start.py +126 -26
  85. package/dist/templates/codex/skills/finish-work/SKILL.md +41 -109
  86. package/dist/templates/codex/skills/start/SKILL.md +12 -9
  87. package/dist/templates/common/bundled-skills/trellis-meta/SKILL.md +73 -0
  88. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/add-project-local-conventions.md +83 -0
  89. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-agents.md +54 -0
  90. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-context-loading.md +81 -0
  91. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-hooks.md +57 -0
  92. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-skills-or-commands.md +78 -0
  93. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-spec-structure.md +83 -0
  94. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-task-lifecycle.md +90 -0
  95. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-workflow.md +64 -0
  96. package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/overview.md +55 -0
  97. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/context-injection.md +68 -0
  98. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/generated-files.md +80 -0
  99. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/overview.md +51 -0
  100. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/spec-system.md +102 -0
  101. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/task-system.md +101 -0
  102. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/workflow.md +75 -0
  103. package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/workspace-memory.md +71 -0
  104. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/agents.md +79 -0
  105. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/hooks-and-settings.md +69 -0
  106. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/overview.md +59 -0
  107. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/platform-map.md +74 -0
  108. package/dist/templates/common/bundled-skills/trellis-meta/references/platform-files/skills-and-commands.md +83 -0
  109. package/dist/templates/common/commands/continue.md +9 -5
  110. package/dist/templates/common/commands/finish-work.md +34 -10
  111. package/dist/templates/common/index.d.ts +22 -2
  112. package/dist/templates/common/index.d.ts.map +1 -1
  113. package/dist/templates/common/index.js +53 -4
  114. package/dist/templates/common/index.js.map +1 -1
  115. package/dist/templates/common/skills/brainstorm.md +50 -4
  116. package/dist/templates/copilot/hooks/session-start.py +127 -30
  117. package/dist/templates/copilot/prompts/finish-work.prompt.md +44 -112
  118. package/dist/templates/copilot/prompts/start.prompt.md +12 -9
  119. package/dist/templates/cursor/agents/trellis-check.md +1 -1
  120. package/dist/templates/cursor/agents/trellis-implement.md +1 -1
  121. package/dist/templates/cursor/agents/trellis-research.md +2 -2
  122. package/dist/templates/cursor/hooks.json +7 -1
  123. package/dist/templates/droid/droids/trellis-research.md +1 -1
  124. package/dist/templates/extract.d.ts +6 -0
  125. package/dist/templates/extract.d.ts.map +1 -1
  126. package/dist/templates/extract.js +14 -0
  127. package/dist/templates/extract.js.map +1 -1
  128. package/dist/templates/gemini/agents/trellis-research.md +1 -1
  129. package/dist/templates/kiro/agents/trellis-research.json +1 -1
  130. package/dist/templates/markdown/agents.md +19 -12
  131. package/dist/templates/markdown/gitignore.txt +3 -0
  132. package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md.txt +24 -0
  133. package/dist/templates/opencode/agents/trellis-check.md +1 -1
  134. package/dist/templates/opencode/agents/trellis-implement.md +7 -4
  135. package/dist/templates/opencode/agents/trellis-research.md +2 -2
  136. package/dist/templates/opencode/lib/trellis-context.js +100 -13
  137. package/dist/templates/opencode/plugins/inject-subagent-context.js +70 -5
  138. package/dist/templates/opencode/plugins/inject-workflow-state.js +38 -44
  139. package/dist/templates/opencode/plugins/session-start.js +76 -31
  140. package/dist/templates/pi/agents/trellis-check.md +28 -0
  141. package/dist/templates/pi/agents/trellis-implement.md +33 -0
  142. package/dist/templates/pi/agents/trellis-research.md +25 -0
  143. package/dist/templates/pi/extensions/trellis/index.ts.txt +997 -0
  144. package/dist/templates/pi/index.d.ts +5 -0
  145. package/dist/templates/pi/index.d.ts.map +1 -0
  146. package/dist/templates/pi/index.js +12 -0
  147. package/dist/templates/pi/index.js.map +1 -0
  148. package/dist/templates/pi/settings.json +12 -0
  149. package/dist/templates/qoder/agents/trellis-research.md +1 -1
  150. package/dist/templates/shared-hooks/index.d.ts +31 -0
  151. package/dist/templates/shared-hooks/index.d.ts.map +1 -1
  152. package/dist/templates/shared-hooks/index.js +59 -0
  153. package/dist/templates/shared-hooks/index.js.map +1 -1
  154. package/dist/templates/shared-hooks/inject-shell-session-context.py +180 -0
  155. package/dist/templates/shared-hooks/inject-subagent-context.py +156 -27
  156. package/dist/templates/shared-hooks/inject-workflow-state.py +85 -92
  157. package/dist/templates/shared-hooks/session-start.py +232 -36
  158. package/dist/templates/trellis/config.yaml +6 -0
  159. package/dist/templates/trellis/gitignore.txt +3 -0
  160. package/dist/templates/trellis/index.d.ts +1 -1
  161. package/dist/templates/trellis/index.d.ts.map +1 -1
  162. package/dist/templates/trellis/index.js +2 -2
  163. package/dist/templates/trellis/index.js.map +1 -1
  164. package/dist/templates/trellis/scripts/common/__init__.py +8 -0
  165. package/dist/templates/trellis/scripts/common/active_task.py +593 -0
  166. package/dist/templates/trellis/scripts/common/cli_adapter.py +72 -14
  167. package/dist/templates/trellis/scripts/common/paths.py +61 -58
  168. package/dist/templates/trellis/scripts/common/session_context.py +12 -0
  169. package/dist/templates/trellis/scripts/common/task_context.py +27 -194
  170. package/dist/templates/trellis/scripts/common/task_store.py +102 -26
  171. package/dist/templates/trellis/scripts/common/tasks.py +4 -1
  172. package/dist/templates/trellis/scripts/common/types.py +0 -2
  173. package/dist/templates/trellis/scripts/common/workflow_phase.py +15 -3
  174. package/dist/templates/trellis/scripts/task.py +99 -34
  175. package/dist/templates/trellis/workflow.md +332 -64
  176. package/dist/types/ai-tools.d.ts +12 -3
  177. package/dist/types/ai-tools.d.ts.map +1 -1
  178. package/dist/types/ai-tools.js +29 -0
  179. package/dist/types/ai-tools.js.map +1 -1
  180. package/dist/utils/file-writer.d.ts.map +1 -1
  181. package/dist/utils/file-writer.js +7 -2
  182. package/dist/utils/file-writer.js.map +1 -1
  183. package/dist/utils/posix.d.ts +13 -0
  184. package/dist/utils/posix.d.ts.map +1 -0
  185. package/dist/utils/posix.js +15 -0
  186. package/dist/utils/posix.js.map +1 -0
  187. package/dist/utils/project-detector.d.ts +2 -0
  188. package/dist/utils/project-detector.d.ts.map +1 -1
  189. package/dist/utils/project-detector.js +120 -11
  190. package/dist/utils/project-detector.js.map +1 -1
  191. package/dist/utils/task-json.d.ts +46 -0
  192. package/dist/utils/task-json.d.ts.map +1 -0
  193. package/dist/utils/task-json.js +49 -0
  194. package/dist/utils/task-json.js.map +1 -0
  195. package/dist/utils/template-fetcher.d.ts +22 -6
  196. package/dist/utils/template-fetcher.d.ts.map +1 -1
  197. package/dist/utils/template-fetcher.js +405 -27
  198. package/dist/utils/template-fetcher.js.map +1 -1
  199. package/dist/utils/template-hash.d.ts +22 -3
  200. package/dist/utils/template-hash.d.ts.map +1 -1
  201. package/dist/utils/template-hash.js +99 -19
  202. package/dist/utils/template-hash.js.map +1 -1
  203. package/package.json +7 -7
  204. package/dist/templates/markdown/spec/backend/directory-structure.md +0 -292
  205. package/dist/templates/markdown/spec/backend/index.md +0 -40
  206. package/dist/templates/markdown/spec/backend/script-conventions.md +0 -742
  207. package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md +0 -118
  208. package/dist/templates/markdown/spec/guides/cross-platform-thinking-guide.md +0 -394
  209. package/dist/templates/shared-hooks/statusline.py +0 -218
  210. 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":"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"}
@@ -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
- Read `.trellis/.current-task` → task directory (e.g. `.trellis/tasks/04-17-foo/`). If empty or missing, ask the user where to write output; do NOT guess.
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\nRead `.trellis/.current-task` → task directory (e.g. `.trellis/tasks/04-17-foo/`). If empty or missing, 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",
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
- Use the `/trellis:start` command when starting a new session to:
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
- Use `@/.trellis/` to learn:
12
- - Development workflow (`workflow.md`)
13
- - Project structure guidelines (`spec/`)
14
- - Developer workspace (`workspace/`)
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 you're using Codex, project-scoped helpers may also live in:
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
- Keep this managed block so 'trellis update' can refresh the instructions.
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 -->
@@ -7,6 +7,9 @@
7
7
  # Current task pointer (local state)
8
8
  .current-task
9
9
 
10
+ # Session/window scoped runtime state
11
+ .runtime/
12
+
10
13
  # Agent runtime files (in worktree)
11
14
  .agent-log
12
15
  .session-id
@@ -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. Read `.trellis/.current-task` → get task directory (e.g., `.trellis/tasks/xxx`)
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. Read `.trellis/.current-task` → get task directory (e.g., `.trellis/tasks/xxx`)
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
- - If `path` is a file Read it
28
- - If `path` is a directory → Read all `.md` files in it
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: deny
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. Read `.trellis/.current-task` → get task directory (if exists)
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
- * Get current task directory from .trellis/.current-task
46
- */
47
- getCurrentTask() {
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 currentTaskPath = join(this.directory, ".trellis", ".current-task")
50
- if (!existsSync(currentTaskPath)) {
51
- return null
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 subagentType = args.subagent_type
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:", subagentType)
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
- // Read current task
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 active
9
- * and its expected flow. Breadcrumb text is pulled from the project's
10
- * workflow.md [workflow-state:STATUS] tag blocks (single source of
11
- * truth for users who fork the Trellis workflow), with hardcoded
12
- * fallbacks so the hook never breaks when workflow.md is missing or
13
- * malformed.
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 (.trellis/.current-task missing or stale)
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
- * Returns {status: body}. Missing tags fall back to hardcoded defaults.
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 result
45
+ if (!existsSync(workflowPath)) return {}
60
46
  let content
61
47
  try {
62
48
  content = readFileSync(workflowPath, "utf-8")
63
49
  } catch {
64
- return result
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 taskRef = ctx.getCurrentTask()
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)) return null
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 (templates or fallback) → detailed body
98
- * - Unknown status generic "refer to workflow.md"
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
- const header = id === null ? `Status: ${status}` : `Task: ${id} (${status})`
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(