diff --git a/dev-vcs/llm-git/files/llm-git-5.4.0-pipe-mode-stdout.patch b/dev-vcs/llm-git/files/llm-git-5.4.0-pipe-mode-stdout.patch new file mode 100644 index 0000000000..7485ba87ce --- /dev/null +++ b/dev-vcs/llm-git/files/llm-git-5.4.0-pipe-mode-stdout.patch @@ -0,0 +1,58 @@ +From https://github.com/can1357/llm-git/pull/40 + +Keep pipe-mode stdout to the commit message only: the LLM spend line and the +smart-truncation notice are status output, so they go to stderr when stdout is +not a terminal. + +diff --git a/lgit/cli.py b/lgit/cli.py +--- a/lgit/cli.py ++++ b/lgit/cli.py +@@ -383,7 +383,7 @@ def _print_llm_spend() -> None: + line = f"LLM cost: ${spend.cost_usd:.4f} ({tokens})" + if spend.saved_usd > 0: + line += f", saved ${spend.saved_usd:.4f} via cache" +- print(style.dim(line)) ++ style.status(style.dim(line)) + + + def _compact_count(count: int) -> str: +@@ -585,7 +585,7 @@ async def _generate_standard_workflow( + if use_map_reduce: + analysis_diff = diff + elif len(diff) > int(config.max_diff_length): +- print(style.warning(f"Applying smart truncation (diff size: {len(diff)} characters)")) ++ style.status(style.warning(f"Applying smart truncation (diff size: {len(diff)} characters)")) + analysis_diff = smart_truncate_diff(diff, int(config.max_diff_length), config, token_counter) + else: + analysis_diff = diff +diff --git a/tests/test_cli.py b/tests/test_cli.py +--- a/tests/test_cli.py ++++ b/tests/test_cli.py +@@ -97,6 +97,28 @@ def test_emit_commit_hash_only_prints_to_stdout_when_piped( + assert capsys.readouterr().out == "" + + ++def test_dry_run_pipe_stdout_carries_only_the_commit_message( ++ capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch ++) -> None: ++ """Pipe-mode stdout must be the commit message only; LLM spend goes to stderr.""" ++ from lgit import pricing, style ++ from lgit.models import ConventionalCommit ++ ++ monkeypatch.setattr(style, "_PIPE_MODE", True) ++ pricing.reset_session() ++ pricing.record_usage("claude-haiku-4-5", pricing.TokenUsage(input_tokens=100, output_tokens=20)) ++ ++ message = ConventionalCommit.from_raw( ++ commit_type="feat", scope="sandbox", summary="added mock feature", body=["Added a sample feature."] ++ ) ++ cli._print_message(message, title="Generated Commit Message") ++ cli._print_llm_spend() ++ ++ captured = capsys.readouterr() ++ assert captured.out == message.format_commit_message() ++ assert "LLM cost" in captured.err ++ ++ + def test_trace_output_flag_enables_file_profiling() -> None: + args = _args("--trace-output", "profile.jsonl", "--dry-run") diff --git a/dev-vcs/llm-git/llm-git-5.4.0-r1.ebuild b/dev-vcs/llm-git/llm-git-5.4.0-r1.ebuild new file mode 100644 index 0000000000..51c9cc10ab --- /dev/null +++ b/dev-vcs/llm-git/llm-git-5.4.0-r1.ebuild @@ -0,0 +1,44 @@ +# Copyright 2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +DISTUTILS_USE_PEP517=hatchling +PYTHON_COMPAT=( python3_14 ) + +inherit distutils-r1 + +DESCRIPTION="AI-powered git commit message generator using LLMs via OpenAI-compat APIs" +HOMEPAGE="https://github.com/can1357/llm-git" +SRC_URI="https://github.com/can1357/${PN}/archive/v${PV}/${P}.tar.gz" + +LICENSE="MIT" +SLOT="0" +KEYWORDS="~amd64" + +PATCHES=( "${FILESDIR}/${PN}-${PV}-pipe-mode-stdout.patch" ) + +RDEPEND=" + >=dev-python/httpx2-2.4.0[${PYTHON_USEDEP}] + dev-python/blake3[${PYTHON_USEDEP}] + dev-python/jinja2[${PYTHON_USEDEP}] + dev-vcs/git +" + +BDEPEND="test? ( dev-vcs/git )" + +EPYTEST_PLUGINS=() +distutils_enable_tests pytest + +src_prepare() { + sed -i \ + -e 's/^import httpx$/import httpx2 as httpx/' \ + lgit/{api,cli,tokens}.py tests/test_pricing.py || die + sed -i -e 's/"httpx"/"httpx2"/' pyproject.toml || die + distutils-r1_src_prepare +} + +python_test() { + git init -q || die + epytest +}