From a416e61ef9758c7f55bd44328e2e9cd75b148a6b Mon Sep 17 00:00:00 2001 From: Rafael Ramdani <173571271+slophisticated@users.noreply.github.com> Date: Sat, 17 Oct 2026 02:18:44 +0700 Subject: [PATCH] fix(cli): disable ANSI colors for redirected output by default --- README.md | 3 +++ src/diffnote/cli.py | 10 +++++++--- src/diffnote/render.py | 5 ++++- tests/test_cli.py | 21 +++++++++++++++++++++ 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2b6200b..d6d6c2b 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,9 @@ diffnote --json # machine-readable, for CI diffnote --llm # add a prose summary using local Ollama (optional) ``` +Terminal output uses colors when stdout is a terminal and disables them for pipes or +redirection. Use `--color` to force ANSI colors or `--no-color` to disable them. + That's the whole workflow. No config file, no sign-up, no telemetry. ## Features diff --git a/src/diffnote/cli.py b/src/diffnote/cli.py index 20961be..6d0fd7b 100644 --- a/src/diffnote/cli.py +++ b/src/diffnote/cli.py @@ -30,8 +30,12 @@ def build_parser(): help="Print a GitHub PR-body markdown.") fmt.add_argument("--json", action="store_true", help="Print machine-readable JSON.") - fmt.add_argument("--no-color", action="store_true", - help="Disable ANSI colors.") + color = fmt.add_mutually_exclusive_group() + color.add_argument("--color", action="store_true", dest="use_color", + help="Force ANSI colors, even when output is redirected.") + color.add_argument("--no-color", action="store_false", dest="use_color", + help="Disable ANSI colors.") + p.set_defaults(use_color=None) fmt.add_argument("--llm", action="store_true", help="Enhance with a local Ollama model (needs Ollama running).") return p @@ -87,7 +91,7 @@ def main(argv=None): elif args.markdown: print(render.render_markdown(summary, llm_prose)) else: - print(render.render_terminal(summary, use_color=not args.no_color, llm_prose=llm_prose)) + print(render.render_terminal(summary, use_color=args.use_color, llm_prose=llm_prose)) return 0 diff --git a/src/diffnote/render.py b/src/diffnote/render.py index f073605..0b234d4 100644 --- a/src/diffnote/render.py +++ b/src/diffnote/render.py @@ -1,6 +1,7 @@ """Output renderers: terminal (ANSI), markdown (PR body), json.""" import json as _json +import sys RESET = "\033[0m" BOLD = "\033[1m" @@ -16,7 +17,9 @@ def _c(text, code, use_color): return f"{code}{text}{RESET}" if use_color else text -def render_terminal(summary, use_color=True, llm_prose=None): +def render_terminal(summary, use_color=None, llm_prose=None): + if use_color is None: + use_color = sys.stdout.isatty() out = [] bar = "─" * 42 out.append(_c("diffnote", BOLD + CYAN, use_color) + diff --git a/tests/test_cli.py b/tests/test_cli.py index 1cc13d6..62201d7 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -3,6 +3,8 @@ import subprocess import tempfile +import pytest + from diffnote import cli @@ -58,6 +60,25 @@ def test_json_output(capsys): assert "title" in data +@pytest.mark.parametrize("is_tty,args,has_color", [ + (True, [], True), + (False, [], False), + (True, ["--color"], True), + (False, ["--color"], True), + (True, ["--no-color"], False), + (False, ["--no-color"], False), +]) +def test_terminal_color_selection(capsys, monkeypatch, is_tty, args, has_color): + monkeypatch.chdir(_make_dirty_repo()) + monkeypatch.setattr(cli.sys.stdout, "isatty", lambda: is_tty) + + assert cli.main(args) == 0 + + out = capsys.readouterr().out + assert "files changed" in out + assert ("\033[" in out) is has_color + + def test_markdown_output(capsys): import os d = _make_dirty_repo()