From cd34966b4fca20bac4ae65362934ab3fdbe8a3b6 Mon Sep 17 00:00:00 2001 From: kuyua9 <948423378@qq.com> Date: Fri, 15 May 2026 00:36:19 +0800 Subject: [PATCH] fix: save comparison HTML artifacts --- skills/last30days/scripts/last30days.py | 26 ++++++++++++++++++++++--- tests/test_html_render.py | 20 +++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/skills/last30days/scripts/last30days.py b/skills/last30days/scripts/last30days.py index d8cb7cf..9a9b1c7 100644 --- a/skills/last30days/scripts/last30days.py +++ b/skills/last30days/scripts/last30days.py @@ -97,11 +97,13 @@ def save_output( save_dir: str, suffix: str = "", synthesis_md: str | None = None, + topic_override: str | None = None, + rendered_content: str | None = None, ) -> Path: from datetime import datetime path = Path(save_dir).expanduser().resolve() path.mkdir(parents=True, exist_ok=True) - slug = slugify(report.topic) + slug = slugify(topic_override or report.topic) extension = "json" if emit == "json" else "html" if emit == "html" else "md" raw_label = "raw-html" if emit == "html" else "raw" suffix_part = f"-{suffix}" if suffix else "" @@ -110,7 +112,9 @@ def save_output( out_path = path / f"{slug}-{raw_label}{suffix_part}-{datetime.now().strftime('%Y-%m-%d')}.{extension}" # Markdown saves keep the complete debug artifact. JSON and HTML preserve # their requested wire format so file extensions match their content. - if emit in {"json", "html"}: + if rendered_content is not None: + content = rendered_content + elif emit in {"json", "html"}: content = emit_output(report, emit, synthesis_md=synthesis_md) else: content = render.render_full(report) @@ -171,6 +175,10 @@ def emit_comparison_output( raise SystemExit(f"Unsupported emit mode: {emit}") +def comparison_topic(entity_reports: list[tuple[str, schema.Report]]) -> str: + return " vs ".join(label for label, _ in entity_reports) + + def compute_save_path_display(save_dir: str, topic: str, suffix: str, emit: str) -> str: """Compute the user-friendly save path string that will be shown in the footer. @@ -875,8 +883,13 @@ def main() -> int: fun_level = config.get("FUN_LEVEL", "medium").lower() footer_save_path = None if args.save_dir: + save_topic = ( + comparison_topic(entity_reports) + if entity_reports and args.emit == "html" + else report.topic + ) footer_save_path = compute_save_path_display( - args.save_dir, report.topic, args.save_suffix or "", args.emit + args.save_dir, save_topic, args.save_suffix or "", args.emit ) # Signal to render_compact whether pre-research flags were supplied. @@ -911,12 +924,19 @@ def main() -> int: ) if args.save_dir: # Save the main topic's raw file (single-entity or comparison main). + save_topic = ( + comparison_topic(entity_reports) + if entity_reports and args.emit == "html" + else None + ) save_path = save_output( report, args.emit, args.save_dir, suffix=args.save_suffix or "", synthesis_md=synthesis_md, + topic_override=save_topic, + rendered_content=rendered if entity_reports and args.emit == "html" else None, ) sys.stderr.write(f"[last30days] Saved output to {save_path}\n") # Competitor / vs-mode: also save a per-entity raw file for each peer. diff --git a/tests/test_html_render.py b/tests/test_html_render.py index 780884d..70a32cf 100644 --- a/tests/test_html_render.py +++ b/tests/test_html_render.py @@ -277,6 +277,26 @@ class HtmlCliIntegrationTests(unittest.TestCase): path = cli.compute_save_path_display("/tmp", report.topic, "v3", "html") self.assertTrue(path.endswith("/ai-agent-frameworks-raw-html-v3.html")) + def test_save_output_can_persist_comparison_html(self): + reports = [ + ("OpenClaw", _report("OpenClaw", ["Containers"])), + ("Hermes", _report("Hermes", ["Memory"])), + ] + rendered = cli.emit_comparison_output(reports, "html") + with tempfile.TemporaryDirectory() as tmpdir: + path = cli.save_output( + reports[0][1], + "html", + tmpdir, + topic_override=cli.comparison_topic(reports), + rendered_content=rendered, + ) + self.assertEqual("openclaw-vs-hermes-raw-html.html", path.name) + saved = path.read_text(encoding="utf-8") + self.assertIn("last30days · OpenClaw vs Hermes", saved) + self.assertIn("comparing 2: OpenClaw, Hermes", saved) + self.assertNotIn("last30days · OpenClaw", saved) + if __name__ == "__main__": unittest.main()