Problem
Body/description input is inconsistent across commands, making scripting and agentic usage painful. The current state:
| Command |
--body |
--body-file |
stdin (-) |
Editor |
Positional |
issue create |
--body |
--body-file |
via --body-file - |
yes |
no |
issue comment |
positional |
--body-file |
via --body-file - |
yes |
no |
issue edit body |
positional |
no |
no |
yes |
yes |
issue edit comment |
positional |
no |
no |
yes |
yes |
pr create |
--body |
--body-file |
via --body-file - |
yes |
no |
pr comment |
positional |
--body-file |
via --body-file - |
yes |
no |
pr edit body |
positional |
no |
no |
yes |
yes |
pr edit comment |
positional |
no |
no |
yes |
yes |
pr merge |
-m |
no |
no |
yes |
no |
release create |
-b (optional arg) |
no |
no |
yes |
no |
release edit |
-b (optional arg) |
no |
no |
yes |
no |
tag create |
-b (optional arg) |
no |
no |
yes |
no |
Pain points
-
--body-file only exists on 4 commands (issue create, issue comment, pr create, pr comment). It's missing from all edit commands, release, tag, and pr merge.
-
No stdin piping on edit commands. You can echo "body" | fj issue create "title" --body-file - but you can't pipe into fj issue edit 42 body or fj pr edit 10 body. For agentic/scripted use, this means you have to shell-escape multiline markdown into a positional argument, which is fragile.
-
Flag naming is inconsistent. --body on issues/PRs, -b on releases/tags, -m on pr merge. These all do the same thing conceptually (provide text content) but use different conventions.
-
--yes mode is broken without body text. Every command that falls through to the editor will bail with an error if --yes is set. This is correct behavior, but it means the only non-interactive path for many commands is a positional argument or --body flag, with no --body-file/stdin option.
-
Positional body args don't scale. For edit commands that accept the new body as a positional arg, passing multiline markdown (with headings, code blocks, lists) via shell argument is error-prone. A --body-file flag would be far more ergonomic.
Proposal
Add --body-file <PATH> (with stdin support via -) to every command that accepts body/description text:
issue edit body
issue edit comment
pr edit body
pr edit comment
pr merge (for merge commit message)
release create
release edit
tag create
This would make every body-accepting command usable as:
# Inline (short text)
fj issue edit 42 body "quick fix"
# From file (long markdown)
fj issue edit 42 body --body-file description.md
# From stdin (piped/heredoc)
echo "Generated description" | fj issue edit 42 body --body-file -
# From heredoc (multiline in scripts)
fj pr edit 10 body --body-file - <<'EOF'
## Summary
This PR does things.
## Checklist
- [x] Tests pass
EOF
Stretch: unify flag names
Consider aliasing -b and -m to --body everywhere for consistency, while keeping the short forms as aliases for backwards compat. Not blocking, but would reduce cognitive load.
## Problem
Body/description input is inconsistent across commands, making scripting and agentic usage painful. The current state:
| Command | `--body` | `--body-file` | stdin (`-`) | Editor | Positional |
|---------|----------|---------------|-------------|--------|------------|
| `issue create` | `--body` | `--body-file` | via `--body-file -` | yes | no |
| `issue comment` | positional | `--body-file` | via `--body-file -` | yes | no |
| `issue edit body` | positional | **no** | **no** | yes | yes |
| `issue edit comment` | positional | **no** | **no** | yes | yes |
| `pr create` | `--body` | `--body-file` | via `--body-file -` | yes | no |
| `pr comment` | positional | `--body-file` | via `--body-file -` | yes | no |
| `pr edit body` | positional | **no** | **no** | yes | yes |
| `pr edit comment` | positional | **no** | **no** | yes | yes |
| `pr merge` | `-m` | **no** | **no** | yes | no |
| `release create` | `-b` (optional arg) | **no** | **no** | yes | no |
| `release edit` | `-b` (optional arg) | **no** | **no** | yes | no |
| `tag create` | `-b` (optional arg) | **no** | **no** | yes | no |
### Pain points
1. **`--body-file` only exists on 4 commands** (issue create, issue comment, pr create, pr comment). It's missing from all edit commands, release, tag, and pr merge.
2. **No stdin piping on edit commands.** You can `echo "body" | fj issue create "title" --body-file -` but you can't pipe into `fj issue edit 42 body` or `fj pr edit 10 body`. For agentic/scripted use, this means you have to shell-escape multiline markdown into a positional argument, which is fragile.
3. **Flag naming is inconsistent.** `--body` on issues/PRs, `-b` on releases/tags, `-m` on pr merge. These all do the same thing conceptually (provide text content) but use different conventions.
4. **`--yes` mode is broken without body text.** Every command that falls through to the editor will bail with an error if `--yes` is set. This is correct behavior, but it means the *only* non-interactive path for many commands is a positional argument or `--body` flag, with no `--body-file`/stdin option.
5. **Positional body args don't scale.** For edit commands that accept the new body as a positional arg, passing multiline markdown (with headings, code blocks, lists) via shell argument is error-prone. A `--body-file` flag would be far more ergonomic.
## Proposal
Add `--body-file <PATH>` (with stdin support via `-`) to every command that accepts body/description text:
- `issue edit body`
- `issue edit comment`
- `pr edit body`
- `pr edit comment`
- `pr merge` (for merge commit message)
- `release create`
- `release edit`
- `tag create`
This would make every body-accepting command usable as:
```bash
# Inline (short text)
fj issue edit 42 body "quick fix"
# From file (long markdown)
fj issue edit 42 body --body-file description.md
# From stdin (piped/heredoc)
echo "Generated description" | fj issue edit 42 body --body-file -
# From heredoc (multiline in scripts)
fj pr edit 10 body --body-file - <<'EOF'
## Summary
This PR does things.
## Checklist
- [x] Tests pass
EOF
```
### Stretch: unify flag names
Consider aliasing `-b` and `-m` to `--body` everywhere for consistency, while keeping the short forms as aliases for backwards compat. Not blocking, but would reduce cognitive load.