fix: preserve complex Markdown in GitHub CLI bodies - #108
Conversation
Adding CLAUDE.md with task information for AI processing. This file will be removed when the task is complete. Issue: #40
Add comprehensive solution for safely passing complex markdown content with special characters to GitHub CLI commands.
## Changes
### Core Implementation
- Add `githubCli` helper object with methods for safe GitHub CLI operations
- Implement `createIssue()`, `createPullRequest()`, and `withBodyFile()` functions
- Use temporary file approach with `--body-file` parameter to avoid shell escaping issues
- Automatic cleanup of temporary files with proper error handling
### New Features
- Safe handling of backticks, variables (${var}), quotes, and special characters
- Support for all GitHub CLI options (assignee, labels, milestone, etc.)
- Production-ready error handling and file cleanup
- Memory-efficient temporary file management
### Testing & Examples
- Comprehensive test suite covering edge cases and error scenarios
- Demonstration script showing the problem and solution
- Documentation with usage examples for CI/CD workflows
### Version Bump
- Update package.json from 0.7.1 to 0.7.2
## Solves
- Issue #40: GitHub CLI with complex markdown body fails due to shell escaping
- Provides production-ready alternative to direct `--body` parameter usage
- Maintains compatibility with all existing functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
|
We should double check more cases similar like this, make sure we have test coverage similar to all our competitors, and select behavior closer to how it would behave in sh scripts or with least surprise based on best practices from competitors. If there multiple options we should allow to configure, and use closer to sh behavior by default. We also must support all our supported language versions. |
|
🤖 AI Work Session Started Starting automated work session at 2026-09-14T21:05:29.924Z The PR has been converted to draft mode while work is in progress. This comment marks the beginning of an AI work session. Please wait for the session to finish, and provide your feedback. Runtime: solve |
# Conflicts: # js/examples/github-cli-complex-body-solution.mjs # js/examples/test-gh-cli-body-issue.mjs # js/tests/github-cli-body.test.mjs # package.json # src/$.mjs
Working session summaryImplemented and finalized PR #108.
This summary was automatically extracted from the AI working session output. |
🤖 Solution Draft LogThis log file contains the complete execution trace of the AI solution draft process. 💰 Cost estimation:
📊 Context and tokens usage:
Total: (413.2K + 13.3M cached) input tokens, 53.6K output tokens, $15.524431 cost 🤖 Models used:
📎 Log file uploaded as Gist (5512KB)Now working session is ended, feel free to review and add any feedback on the solution draft. |
🎉 Auto-mergedThis pull request has been automatically merged by hive-mind.
Auto-merged by hive-mind with --auto-merge flag |
Closes #40.
Problem and reproduction
Interpolating a multiline Markdown value into
gh issue create --bodymust send one exact argument even when the body contains fenced code, inline backticks,${...}text,$HOME, command-substitution syntax, mixed quotes, operators, globs, backslashes, significant whitespace, or Unicode.The original issue used author-written quotes around the interpolation:
Running the new regression against the pre-context-aware implementation (
360b71a) reproduced the bug: unquoted interpolation passed, while the double-quoted, single-quoted, and injection-safety cases failed with shell syntax errors. The same six cases pass on this branch.Solution
gh issue createinvocation without creating a real issue.--body ${body}form, context-aware--body "${body}", and GitHub CLI's native--body-file -stdin alternative.Behavior decision
The default follows the least-surprising shell contract: an interpolated value is data and arrives as one argument, like
"$BODY"insh. The recommended form is simply:No GitHub-specific escaping helper is needed. Author-written quotes remain supported by the existing context-aware quoting behavior. Users who intentionally opt into legacy
COMMAND_STREAM_QUOTE_CONTEXT=0behavior can continue to use the unquoted interpolation form. When a file or stdin is the natural source,gh --body-fileremains available as a transport choice rather than a required workaround.The live experiment matched
sh "$BODY"for command-stream's automatic and author-quoted forms, Bun Shell, and zx. Execa is optional locally; its reviewed interpolation behavior remains represented by the repository's pinned competitor corpus. That corpus accounts for 12 JavaScript projects and 14 Rust projects at immutable revisions.Verification
node --test js/tests/github-cli-body.test.mjs— 6 passedbun test js/tests/ --timeout 10000— 1,394 passed, 6 skipped, 0 failedcargo fmt --all -- --check— passedcargo clippy --all-targets --all-features -- -D warnings— passedcargo doc --no-deps --all-features— passedcargo test --all-features --verboseand doc tests — passedmain— passedThe GitHub CLI itself documents both
--body stringand--body-file file;--body-file -reads standard input.