Skip to content

Prevent Markdown backtick over-escaping when creating GitHub issues, PRs, and comments #442

Description

@williamthorsen

Problem

When agents create GitHub issues, PRs, or comments with Markdown bodies, the body is written via a bash heredoc:

gh issue create --body "$(cat <<'EOF2'
...
EOF2
)"

Agents reflexively backslash-escape backticks inside the heredoc, producing \ + ` pairs. GitHub renders the backslash literally — inline code `foo` becomes \`foo\`, and ```ts code fences become \`\`\`ts. Tickets, PRs, and comments ship with broken Markdown and the developer has to fix each one by hand.

Inside a single-quoted heredoc (<<'EOF'), bash performs no expansion — backticks are already literal and need no escaping. The over-escape is a habit carried from double-quoted strings and command substitution, where backticks do need escaping. Every body routed through bash is at risk.

This happened five times in a single recent session (codeassembly#440, codeassembly#441, devtools.afg#346, devtools.afg#347, devtools.afg#348), each requiring re-fetch, manual fix, and push-back.

Context

Four call sites in the installed skills route Markdown bodies through bash:

  • create-ticket step 5: gh issue create --body "{body}" — initial ticket body
  • create-ticket step 7: gh issue comment --body "{plan comment}" — plan attachment
  • create-gh-pr step 2: gh pr create --body "{body}" — PR body
  • wrap-up Phase 3 step 3: gh issue comment --body "{insight}" — per-insight post

create-bitbucket-pr is not affected — it uses MCP/REST tooling rather than bash.

The root cause is structural: as long as the body is interpolated into a shell command string, an agent can insert a spurious \ and the bug reappears. Warnings in Constraints sections treat the symptom; only removing the shell interpolation removes the class.

Solution

Adopt a single pattern across all four call sites: write the body to a temp file via the Write tool (no bash involved), then invoke gh ... --body-file {path}. The Write tool operates on raw strings, so there is no shell context in which backtick escaping could feel necessary.

Temp path convention: $TMPDIR/gh-body-{timestamp}.md. Rely on OS-managed scratch; no explicit cleanup.

Shared convention doc: add packages/agents/content/skills/_data/gh-body-file.md describing the pattern and rationale once. Each of the four affected skills replaces its inline gh ... --body "..." invocation with a brief step that references the shared doc.

Rejected alternatives (documented so they are not re-proposed):

  • Post-create verification that re-fetches and scans for ``` sequences — redundant once the heredoc is gone; adds latency and prose without catching new classes of bug.
  • A "never escape Markdown" note in individual skill Constraints sections — treats the symptom. The heredoc it warns about no longer exists after this change.
  • Changes to create-bitbucket-pr — the skill does not use bash for body content; the escape bug cannot arise there.

Acceptance criteria

  • packages/agents/content/skills/_data/gh-body-file.md exists and describes the write-then---body-file pattern, the temp path convention, and the rationale (including why it replaces the heredoc approach).
  • create-ticket step 5 uses --body-file for gh issue create and references the shared doc.
  • create-ticket step 7 uses --body-file for gh issue comment (plan attachment) and references the shared doc.
  • create-gh-pr step 2 uses --body-file for gh pr create and references the shared doc.
  • wrap-up Phase 3 step 3 uses --body-file for gh issue comment (per-insight post) and references the shared doc.
  • No skill SKILL.md contains an inline gh ... --body "..." invocation for a Markdown body (verifiable via rg '\bgh (issue|pr) (create|comment).*--body "' returning no hits in packages/agents/content/skills).
  • create-bitbucket-pr is unchanged.

Metadata

Metadata

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions