This issue was authored by an agent:
The website docs advertise three template variables for prompts — {{ticket}}, {{taskTitle}}, {{taskBody}} — but only {{ticket}} is actually wired up. {{taskTitle}} and
{{taskBody}} pass through verbatim into the agent prompt, which is almost certainly not what users expect.
What the docs say
website/docs/workspaces-plugin.md (lines 462-464):
- `{{ticket}}`: Replaced with task ID (if ticketMode is "required" or "optional")
- `{{taskTitle}}`: Replaced with task title from TD
- `{{taskBody}}`: Replaced with task description from TD
And again on line 645:
Link tasks to workspaces for context—agents can see task descriptions via {{taskBody}}
What actually happens
internal/plugins/workspace/template.go only matches {{ticket}} / {{ticket || 'fallback'}}:
var ticketPattern = regexp.MustCompile(`\{\{ticket(?:\s*\|\|\s*'([^']*)')?\}\}`)
func ExpandPromptTemplate(body, taskID string) string {
return ticketPattern.ReplaceAllStringFunc(body, ...)
}
There are no references to taskTitle or taskBody as template names anywhere in the Go code (verified via git grep). Anything other than {{ticket}} falls through unchanged.
Repro
- Create/edit a prompt in
~/.config/sidecar/config.json:
{
"name": "Repro",
"ticketMode": "optional",
"body": "Work on {{ticket}}.\n\nTitle: {{taskTitle}}\nBody:\n{{taskBody}}"
}
- Create a workspace, link a task with title + description, pick this prompt, choose any agent (e.g. claude or copilot).
- Inspect the launcher script the agent is actually invoked with — e.g. by tailing
~/.local/state/sidecar/<project>/<worktree>/start.sh while it exists, or by adding a
slog.Debug in writeAgentLauncher and running with --debug.
You'll see {{ticket}} substituted and {{taskTitle}} / {{taskBody}} left as literal {{taskTitle}} / {{taskBody}} strings in the prompt fed to the agent.
Why it matters
When no prompt template is selected, sidecar already does the right-ish thing: getTaskContext (internal/plugins/workspace/agent.go:776) shells out to td show --json and embeds
Task: <title>\n\n<description> for the agent. So the data is available — it just isn't exposed to prompt authors via {{taskTitle}} / {{taskBody}} even though the docs promise
it.
This means anyone writing a prompt template who follows the docs ends up shipping the literal strings {{taskTitle}} and {{taskBody}} to their agent, which is confusing both for
the user (silent failure) and the agent (random Mustache-looking tokens in the prompt).
Possible fixes (one of)
- Implement what's documented. Extend
ExpandPromptTemplate to take a small struct ({ TaskID, Title, Body string }), match {{taskTitle}} and {{taskBody}} (with || 'fallback' parity), and thread title/body in from buildAgentCommand — they're already on Worktree (TaskTitle) or fetchable via getTaskContext. Probably the better option
since {{taskBody}} is genuinely useful.
- Fix the docs. Strip
{{taskTitle}} and {{taskBody}} from website/docs/workspaces-plugin.md (lines 462-464 and the mention on line 645). Cheap, but loses functionality
the docs imply.
Happy to PR option 1 if there's appetite for it.
Refs
internal/plugins/workspace/template.go — current single-variable expander
internal/plugins/workspace/agent.go:550-552 — where ExpandPromptTemplate is invoked
internal/plugins/workspace/agent.go:776 (getTaskContext) — existing path that already pulls title+body
website/docs/workspaces-plugin.md:462-464, 645 — the over-promise
.claude/skills/create-prompt/SKILL.md — accurate, only documents {{ticket}}
This issue was authored by an agent:
The website docs advertise three template variables for prompts —
{{ticket}},{{taskTitle}},{{taskBody}}— but only{{ticket}}is actually wired up.{{taskTitle}}and{{taskBody}}pass through verbatim into the agent prompt, which is almost certainly not what users expect.What the docs say
website/docs/workspaces-plugin.md(lines 462-464):And again on line 645:
What actually happens
internal/plugins/workspace/template.goonly matches{{ticket}}/{{ticket || 'fallback'}}:There are no references to
taskTitleortaskBodyas template names anywhere in the Go code (verified viagit grep). Anything other than{{ticket}}falls through unchanged.Repro
~/.config/sidecar/config.json:{ "name": "Repro", "ticketMode": "optional", "body": "Work on {{ticket}}.\n\nTitle: {{taskTitle}}\nBody:\n{{taskBody}}" }~/.local/state/sidecar/<project>/<worktree>/start.shwhile it exists, or by adding aslog.DebuginwriteAgentLauncherand running with--debug.You'll see
{{ticket}}substituted and{{taskTitle}}/{{taskBody}}left as literal{{taskTitle}}/{{taskBody}}strings in the prompt fed to the agent.Why it matters
When no prompt template is selected, sidecar already does the right-ish thing:
getTaskContext(internal/plugins/workspace/agent.go:776) shells out totd show --jsonand embedsTask: <title>\n\n<description>for the agent. So the data is available — it just isn't exposed to prompt authors via{{taskTitle}}/{{taskBody}}even though the docs promiseit.
This means anyone writing a prompt template who follows the docs ends up shipping the literal strings
{{taskTitle}}and{{taskBody}}to their agent, which is confusing both forthe user (silent failure) and the agent (random Mustache-looking tokens in the prompt).
Possible fixes (one of)
ExpandPromptTemplateto take a small struct ({ TaskID, Title, Body string }), match{{taskTitle}}and{{taskBody}}(with|| 'fallback'parity), and thread title/body in frombuildAgentCommand— they're already onWorktree(TaskTitle) or fetchable viagetTaskContext. Probably the better optionsince
{{taskBody}}is genuinely useful.{{taskTitle}}and{{taskBody}}fromwebsite/docs/workspaces-plugin.md(lines 462-464 and the mention on line 645). Cheap, but loses functionalitythe docs imply.
Happy to PR option 1 if there's appetite for it.
Refs
internal/plugins/workspace/template.go— current single-variable expanderinternal/plugins/workspace/agent.go:550-552— whereExpandPromptTemplateis invokedinternal/plugins/workspace/agent.go:776(getTaskContext) — existing path that already pulls title+bodywebsite/docs/workspaces-plugin.md:462-464, 645— the over-promise.claude/skills/create-prompt/SKILL.md— accurate, only documents{{ticket}}