mirror of
https://github.com/codeflash-ai/codeflash.git
synced 2026-05-04 18:25:17 +00:00
fix: output structured XML errors in subagent mode
When codeflash runs with --subagent (e.g., via the Claude Code plugin), exit_with_message() now outputs <codeflash-error> XML to stdout instead of Rich panel text. This lets the calling agent parse errors programmatically rather than receiving unstructured text. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
1e92f3d2ed
commit
bc7a5bf4bb
2 changed files with 37 additions and 1 deletions
|
|
@ -17,7 +17,7 @@ import tomlkit
|
||||||
|
|
||||||
from codeflash.cli_cmds.console import logger, paneled_text
|
from codeflash.cli_cmds.console import logger, paneled_text
|
||||||
from codeflash.code_utils.config_parser import find_pyproject_toml, get_all_closest_config_files
|
from codeflash.code_utils.config_parser import find_pyproject_toml, get_all_closest_config_files
|
||||||
from codeflash.lsp.helpers import is_LSP_enabled
|
from codeflash.lsp.helpers import is_LSP_enabled, is_subagent_mode
|
||||||
|
|
||||||
_INVALID_CHARS_NT = {"<", ">", ":", '"', "|", "?", "*"}
|
_INVALID_CHARS_NT = {"<", ">", ":", '"', "|", "?", "*"}
|
||||||
|
|
||||||
|
|
@ -458,6 +458,11 @@ def exit_with_message(message: str, *, error_on_exit: bool = False) -> None:
|
||||||
if is_LSP_enabled():
|
if is_LSP_enabled():
|
||||||
logger.error(message)
|
logger.error(message)
|
||||||
return
|
return
|
||||||
|
if is_subagent_mode():
|
||||||
|
from xml.sax.saxutils import escape
|
||||||
|
|
||||||
|
sys.stdout.write(f"<codeflash-error>{escape(message)}</codeflash-error>\n")
|
||||||
|
sys.exit(1 if error_on_exit else 0)
|
||||||
paneled_text(message, panel_args={"style": "red"})
|
paneled_text(message, panel_args={"style": "red"})
|
||||||
|
|
||||||
sys.exit(1 if error_on_exit else 0)
|
sys.exit(1 if error_on_exit else 0)
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import pytest
|
||||||
|
|
||||||
from codeflash.code_utils.code_utils import (
|
from codeflash.code_utils.code_utils import (
|
||||||
cleanup_paths,
|
cleanup_paths,
|
||||||
|
exit_with_message,
|
||||||
file_name_from_test_module_name,
|
file_name_from_test_module_name,
|
||||||
file_path_from_module_name,
|
file_path_from_module_name,
|
||||||
get_all_function_names,
|
get_all_function_names,
|
||||||
|
|
@ -751,3 +752,33 @@ class MyClass:
|
||||||
"""
|
"""
|
||||||
result = validate_python_code(code)
|
result = validate_python_code(code)
|
||||||
assert result == code
|
assert result == code
|
||||||
|
|
||||||
|
|
||||||
|
class TestExitWithMessageSubagent:
|
||||||
|
@patch("codeflash.code_utils.code_utils.is_subagent_mode", return_value=True)
|
||||||
|
def test_outputs_structured_xml_in_subagent_mode(self, _mock_subagent: MagicMock, capsys: pytest.CaptureFixture[str]) -> None:
|
||||||
|
with pytest.raises(SystemExit) as exc_info:
|
||||||
|
exit_with_message("Something went wrong", error_on_exit=True)
|
||||||
|
assert exc_info.value.code == 1
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert "<codeflash-error>" in captured.out
|
||||||
|
assert "Something went wrong" in captured.out
|
||||||
|
assert "</codeflash-error>" in captured.out
|
||||||
|
|
||||||
|
@patch("codeflash.code_utils.code_utils.is_subagent_mode", return_value=True)
|
||||||
|
def test_escapes_xml_special_chars(self, _mock_subagent: MagicMock, capsys: pytest.CaptureFixture[str]) -> None:
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
exit_with_message('File <foo> & "bar" not found', error_on_exit=True)
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert "<foo>" in captured.out
|
||||||
|
assert "&" in captured.out
|
||||||
|
|
||||||
|
@patch("codeflash.code_utils.code_utils.is_subagent_mode", return_value=False)
|
||||||
|
@patch("codeflash.code_utils.code_utils.is_LSP_enabled", return_value=False)
|
||||||
|
def test_no_xml_when_not_subagent(
|
||||||
|
self, _mock_lsp: MagicMock, _mock_subagent: MagicMock, capsys: pytest.CaptureFixture[str]
|
||||||
|
) -> None:
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
exit_with_message("Normal error", error_on_exit=True)
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert "<codeflash-error>" not in captured.out
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue