From 1ff2fa5dcf67d179e66994f7651f3c1bd62fd2f1 Mon Sep 17 00:00:00 2001 From: DaTweety <129504390+DaTweety@users.noreply.github.com> Date: Sat, 17 Oct 2026 21:35:07 +0200 Subject: [PATCH] qet MCP: keep the edit script's report lines when cutting long output _run_qet() kept only the last 400 000 characters of a run's output. A large qet_edit (a whole multi-folio drawing in one call, some 2000 ops) logs more than that, and the cut took the capability report the script makes first: the tool then answered "the binary never ran the script" although QElectroTech had run it and saved the result. Lines the script reports through its marker are now kept whole wherever they stand; only the other output is cut to the tail. Co-Authored-By: Claude Opus 5.5 --- misc/qet-mcp/qet_mcp.py | 19 ++++++++++++++++++- misc/qet-mcp/test_qet_mcp.py | 17 +++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/misc/qet-mcp/qet_mcp.py b/misc/qet-mcp/qet_mcp.py index 7de720d62..528f9cdf4 100755 --- a/misc/qet-mcp/qet_mcp.py +++ b/misc/qet-mcp/qet_mcp.py @@ -1072,6 +1072,23 @@ def _user_collections() -> dict: return found +def _keep_tail(text: str, tail: int) -> str: + """The last `tail` characters of a run's output, but every line an edit + script reported through its marker kept whole, wherever it stands. + + A large qet_edit logs far more than `tail` characters; cut from the + front, it lost the capability report the script makes first, and the + tool then said the binary had never run the script.""" + if len(text) <= tail: + return text + marked = [ln for ln in text.splitlines() if "QETEDIT " in ln] + rest = text[-tail:] + if not marked: + return rest + kept = set(rest.splitlines()) + return "\n".join([ln for ln in marked if ln not in kept] + [rest]) + + def _run_qet(binary: str, args: list[str], timeout: int = 180, elements_dir: str | None = None, script: str | None = None, tail: int = 4000, @@ -1150,7 +1167,7 @@ def _run_qet(binary: str, args: list[str], timeout: int = 180, "hint": "a modal dialog during load will hang a headless " "run; check the project's format version"} result = {"ok": p.returncode == 0, "exit_code": p.returncode, - "stdout": p.stdout[-tail:], "stderr": p.stderr[-tail:]} + "stdout": _keep_tail(p.stdout, tail), "stderr": _keep_tail(p.stderr, tail)} # QElectroTech refuses --run when scripting is switched off, which # it is by default. Its own message is clear but French, and it # names a settings dialog that nobody driving this server is diff --git a/misc/qet-mcp/test_qet_mcp.py b/misc/qet-mcp/test_qet_mcp.py index 3ada780c3..65406d889 100644 --- a/misc/qet-mcp/test_qet_mcp.py +++ b/misc/qet-mcp/test_qet_mcp.py @@ -6626,6 +6626,23 @@ class ZippedProjects(unittest.TestCase): self.assertNotEqual(*rotated[0]["orientation"]) +class TestKeepTail(unittest.TestCase): + """A large edit logs more than the kept tail; the script's own report + lines must survive the cut, or qet_edit says the script never ran.""" + + def test_short_output_is_untouched(self): + self.assertEqual(m._keep_tail("a\nb", 100), "a\nb") + + def test_marker_lines_survive_the_cut(self): + first = 'QETEDIT {"kind": "capabilities", "missing": []}' + text = first + "\n" + "x" * 1000 + "\n" + 'QETEDIT {"kind": "save", "result": true}' + kept = m._keep_tail(text, 100) + self.assertIn(first, kept) + self.assertIn('"kind": "save"', kept) + self.assertEqual(kept.count('"kind": "save"'), 1) + self.assertEqual(m._parse_script_output(kept).get("missing_methods"), []) + + if __name__ == "__main__": print(f"binary : {BINARY or '(none: integration tests skipped)'}") print(f"elements : {ELEMENTS or '(none)'}")