diff --git a/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-context b/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-context index aabee9fc..9eb5ae2a 100755 --- a/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-context +++ b/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-context @@ -1648,7 +1648,9 @@ def cli(argv): sys.exit(code) import contextlib, io, json buf = io.StringIO() - with contextlib.redirect_stdout(buf): code = main(argv) + with contextlib.redirect_stdout(buf): + try: code = main(argv) + except SystemExit as e: code = e.code if isinstance(e.code, int) else (0 if e.code is None else 1) # a refusal keeps its prose RESULT['prose'] = buf.getvalue().rstrip('\n').split('\n') ax_evidence.annotate('context', RESULT, repo) print(json.dumps(RESULT, indent=2)) diff --git a/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-path b/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-path index 741eaa4d..03a1e04e 100755 --- a/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-path +++ b/plugins/axiomcode/skills/axiomcode/scripts/axiomcode-path @@ -2359,8 +2359,12 @@ if __name__ == '__main__': sys.exit(code) # the prose goes to a buffer, not to the caller: one code path produces both, and `prose` is # carried in the document so a machine answer is never LESS than the human one. + # a refusal (an endpoint nothing declares, a bad argument) leaves answer() through SystemExit: catch it inside + # the redirect, or the JSON and the prose it carries are both lost and the caller sees nothing at all buf = io.StringIO() - with contextlib.redirect_stdout(buf): code = answer() + with contextlib.redirect_stdout(buf): + try: code = answer() + except SystemExit as e: code = e.code if isinstance(e.code, int) else (0 if e.code is None else 1) RESULT['prose'] = buf.getvalue().rstrip('\n').split('\n') ax_evidence.annotate('path', RESULT, (RESULT.get('query') or {}).get('repo') or '.') print(json.dumps(RESULT, indent=2)) diff --git a/tests/cases/python/a-refused-endpoint-is-said/case.json b/tests/cases/python/a-refused-endpoint-is-said/case.json new file mode 100644 index 00000000..5c9b3461 --- /dev/null +++ b/tests/cases/python/a-refused-endpoint-is-said/case.json @@ -0,0 +1,45 @@ +{ + "lang": "python", + "src": "src", + "checks": [ + { + "why": "path with an endpoint nothing declares says so; the refusal reached the caller as nothing at all once the front door asked for --json", + "run": [ + "path", + "main", + "NoSuchName" + ], + "expect_error": true, + "want": [ + "nothing named 'NoSuchName'" + ], + "env": { + "AXIOMCODE_FRONT": "1" + } + }, + { + "why": "the same refusal from the raw verb", + "run": [ + "path", + "main", + "NoSuchName" + ], + "expect_error": true, + "want": [ + "nothing named 'NoSuchName'" + ] + }, + { + "why": "control: a path between two declared functions still answers", + "run": [ + "path", + "main", + "helper" + ], + "want": [ + "main \u2192 helper", + "\u2192 [known_edge" + ] + } + ] +} diff --git a/tests/cases/python/a-refused-endpoint-is-said/src/app.py b/tests/cases/python/a-refused-endpoint-is-said/src/app.py new file mode 100644 index 00000000..36024cc8 --- /dev/null +++ b/tests/cases/python/a-refused-endpoint-is-said/src/app.py @@ -0,0 +1,6 @@ +def helper(): + return 1 + + +def main(): + return helper()