Repository navigation
Add --porcelain flag for clean, pipeable commit-message output #6
Description
Problem or motivation
commitbee is designed for interactive terminal use. While the LLM generates a response, its raw JSON is streamed to the terminal token-by-token, spinners and progress lines flow alongside it, and COMMITBEE_LOG=debug adds tracing on top. The final sanitized commit message only appears at the end, on top of all that noise. There is no clean way today to get just the commit message out of commitbee — every piping attempt either leaks noise through or has to silence legitimate error messages with 2>/dev/null.
Concrete use cases that are blocked today:
- Piping the generated message into another tool — a validator like
commitlint, a preview viewer, a formatter, a custom shell script. - Editor / IDE integrations (VS Code, JetBrains, nvim, etc.) that capture stdout and insert into their commit-message buffer.
- Populating the commit-message file from a
prepare-commit-msghook without the current2>/dev/nullworkaround that also hides real errors. - Higher-level automation (release-notes pipelines, AI-review chains, etc.) that expects exactly one clean string on stdout.
Example usage once shipped:
# Pipe the generated message through any tool — validator, formatter, preview
commitbee --porcelain | commitlint
commitbee --porcelain | bat -l gitcommit
# Use from any shell script
commitbee --porcelain | your-script.sh
# Populate git's commit-msg file from a prepare-commit-msg hook
# ($1 is the path git passes to the hook)
commitbee --porcelain > "$1"The existing workaround — commitbee --dry-run --yes 2>/dev/null — is undiscoverable, isn't a documented contract, and silences legitimate errors the user would otherwise want to see.
Note on the original phrasing: the live token stream users see today is JSON (the LLM returns a JSON object and commitbee streams it token-by-token before the sanitizer turns it into a Conventional Commit message). The final stdout contract under
--porcelainis the sanitized plain-text commit message — the user-facing artifact — not the raw LLM JSON.
Proposed solution
Add a new top-level flag --porcelain that puts commitbee into a machine-readable output mode.
stdout contract:
- stdout contains exactly the final sanitized commit message followed by a single
\n. - UTF-8, no BOM, no ANSI escape sequences, no terminal hyperlinks.
- On any error, stdout is empty; process exits non-zero.
What --porcelain silences:
- The live-streamed LLM JSON response (the main source of noise today).
- All progress indicators and spinners.
- All info / warning / status lines.
- All tracing output, regardless of
RUST_LOG/COMMITBEE_LOG. - ANSI color in all styled output, including error reports.
- Terminal hyperlinks in error reports.
What --porcelain forces non-interactive:
- Implies
--dry-run— no commit is created; the message is printed and the process exits. - Implies
--no-split— split-commit UI is interactive-only. - The Edit / Refine / Cancel review menu is unreachable under
--dry-run. - Disables the interactive
--allow-secretsconfirmation — with--allow-secretspassed, porcelain falls through to the non-interactive "fail closed" branch if secrets are detected.
Incompatibilities (rejected at argument-parse time with exit code 2):
| Combination | Reason |
|---|---|
--porcelain --yes |
--yes commits for real; --porcelain only generates and prints. Mutually exclusive output destinations. |
--porcelain --clipboard |
stdout contract vs. clipboard destination |
--porcelain --show-prompt |
swallowing a debug flag silently would be deceptive |
--porcelain --verbose |
same reasoning |
--porcelain -n <N> |
multi-candidate UI is pointless without a picker |
--porcelain <subcommand> |
--porcelain is only meaningful for the default generate flow; it does not apply to config, doctor, completions, hook, init, set-key, get-key, or eval |
Errors still flow to stderr (color and hyperlinks disabled under porcelain). The process exits non-zero, so downstream tools detect failure without parsing stdout.
Stability boundary:
- The structural contract (one sanitized commit message on stdout, trailing
\n, UTF-8, no decoration) is stable from v0.7.0 onward. - The content of the message (wording, capitalization, type/scope choices) depends on the configured LLM and is not stable across prompts, models, or commitbee versions.
Acceptance criteria
-
commitbee --porcelainwrites exactly<message>\nto stdout on a successful generation. -
commitbee --porcelainwrites zero bytes to stderr on success, regardless ofRUST_LOG,COMMITBEE_LOG, or TTY state. -
commitbee --porcelainexits 0 on success and non-zero on any failure (no staged changes, LLM unreachable, secrets detected, cancelled, etc.). -
commitbee --porcelaincombined with any of--yes,--clipboard,--show-prompt,--verbose,-n <N>, or a subcommand exits with an argument-parse error (exit code 2) and empty stdout. -
commitbee --porcelain --allow-secretswith staged secrets does not hang on interactive stdin; it fails closed with a non-zero exit and empty stdout. -
commitbee --porcelainpiped to a non-TTY consumer produces identical output to TTY (no spinner bleed-through, no ANSI). - Integration tests in
tests/porcelain.rscover each of the above usingassert_cmd+wiremock+tempfilewithenv_clear(). - A structural lint test walks
src/and fails ifprintln!/print!appears outside a pinned allowlist of known emission sites (prevents future stdout leaks).
Alternatives considered
--dry-runalone: prints the final message to stdout but still streams the live LLM response and progress lines, and can drop into the interactive review menu in a TTY. Not a stable machine-readable contract.- Shell redirection (
commitbee --dry-run --yes 2>/dev/null): the current workaround, but silences legitimate errors, is undiscoverable, and isn't a documented contract. - Parsing or grepping commitbee's current stdout: fragile; breaks whenever output formatting changes.
--just-body(original suggestion): ambiguous — a Conventional Commit message has both a subject and a body, so "just body" reads as "only the body paragraph, omitting the subject." Rejected.--format=raw|jsonas a single knob: semantically awkward because--format=rawimplies a format is being chosen when the intent is the absence of formatting. Structured JSON output is a separate future concern — see Related.
Area
CLI / UX
Related
- Future structured output: a separate
--format=<json|…>modifier is expected to land when structured JSON output becomes a feature. It will be orthogonal to--porcelain— one controls stdout cleanliness, the other controls output shape.--porcelainalone will always mean "plain-text commit message." - Milestone: v0.7.0
Issue originally reported by @yegor256; description refined by the maintainer.
Activity
[-]`--just-body` would be nice to have[/-][+]Add --porcelain flag for clean, pipeable commit-message output[/+]Sephyi commentedon Apr 19, 2026
Thanks for raising this, @yegor256 — this genuinely wasn't on my radar, but it's a real gap.
I've edited your original issue description and title in place to reflect the implementation I've been planning out (flag name, stdout contract, edge cases around subcommands, secret handling, etc.). If any of the refined version doesn't match what you originally had in mind, please flag it and I'll adjust — don't want to accidentally drift from your actual intent.
I’ll likely implement this feature within the next 24 hours on the development branch.
yegor256 commentedon Apr 19, 2026
@Sephyi looks perfect, waiting for the implementation
feat: add --porcelain flag for machine-readable commit output
Sephyi commentedon Apr 19, 2026
@yegor256 pushed to the development branch. If you don't already have CommitBee installed from there:
Also covered in Docs Getting Started under Install → Development Version.
Just let me know if you encounter any issues.
Known follow-ups already on the radar (tracked here so I don’t forget):
yegor256 commentedon Apr 19, 2026
@Sephyi I'll wait until it's released
Sephyi commentedon May 3, 2026
@yegor256, I apologize for the delay. I genuinely want to ensure that all the issues raised in #3 are fully addressed in the next release. I’m not sure when it will be ready, but I thought of it and wanted to post a quick comment.