diff --git a/hermes_cli/sessions_cmd.py b/hermes_cli/sessions_cmd.py index 37b95fd7b6..c08e91449f 100644 --- a/hermes_cli/sessions_cmd.py +++ b/hermes_cli/sessions_cmd.py @@ -73,6 +73,17 @@ def _export_dir(output) -> Path: return Path(output).expanduser() if output and output != "-" else get_hermes_home() / "session-exports" +def _output_file_in_dir(output, default_name: str): + """Single-file exports accept a directory too (``--help`` calls the positional a path, and md/qmd take + one): an existing directory, or one spelled with a trailing separator, means ``/``.""" + if not output or output == "-": + return output + if output.endswith(("/", os.sep)) or os.path.isdir(output): + os.makedirs(output, exist_ok=True) + return os.path.join(output, default_name) + return output + + def _write_output(output, text, summary) -> None: """Write to stdout when *output* is empty or ``-``; else to the file + print *summary*.""" if not output or output == "-": @@ -375,6 +386,10 @@ def _export_flat(kind, args, collect): return sessions = collect() if sessions is not None: + from hermes_cli.session_export import default_save_filename + name = (default_save_filename(sessions[0].get("id", ""), args.format) if len(sessions) == 1 + else f"hermes_sessions.{args.format}") + args.output = _output_file_in_dir(args.output, name) _write_output(args.output, *render(args, sessions)) @@ -421,6 +436,7 @@ def _export_trace(db, args, filters): if not jsonl: print(f"No transcript to export for session '{ids[0]}'.") return + args.output = _output_file_in_dir(args.output, f"{ids[0]}.trace.jsonl") _write_output(args.output, jsonl, f"Exported 1 session trace to {args.output}") else: out_dir = _export_dir(args.output) diff --git a/hermes_cli/subcommands/sessions.py b/hermes_cli/subcommands/sessions.py index 8782c2e051..e235c20a9e 100644 --- a/hermes_cli/subcommands/sessions.py +++ b/hermes_cli/subcommands/sessions.py @@ -68,9 +68,10 @@ def build_sessions_parser(subparsers, *, cmd_sessions: Callable) -> None: sessions_export = sessions_subparsers.add_parser( "export", help="Export sessions to JSONL, Markdown, or QMD") - sessions_export.add_argument("output", nargs="?", - help="Output path. JSONL: file path (use - for stdout, required). " - "md/qmd: output directory (default: /session-exports)") + sessions_export.add_argument("output", nargs="?", metavar="OUTPUT", + help="Where to write. jsonl/html/trace: a file path, or a directory (existing, or ending in /) " + "to write a default-named file into; - for stdout (jsonl/trace only; jsonl requires OUTPUT). " + "md/qmd: a directory, one file per session (default: /session-exports)") sessions_export.add_argument( "--format", choices=["jsonl", "md", "qmd", "html", "trace"], default="jsonl", help="Export format (default: jsonl). 'trace' emits Claude Code JSONL " diff --git a/tests/hermes_cli/test_sessions_export_output_dir.py b/tests/hermes_cli/test_sessions_export_output_dir.py index bbfedb5572..0eaf01df85 100644 --- a/tests/hermes_cli/test_sessions_export_output_dir.py +++ b/tests/hermes_cli/test_sessions_export_output_dir.py @@ -40,4 +40,4 @@ def test_jsonl_export_to_file_path_is_unchanged(monkeypatch, tmp_path): _export(monkeypatch, str(target)) assert json.loads(target.read_text(encoding="utf-8"))["id"] == "sess-123" - assert sorted(p.name for p in tmp_path.iterdir()) == ["one.jsonl"] + assert target.is_file() and not (tmp_path / "hermes_session_sess-123.jsonl").exists() diff --git a/website/docs/user-guide/sessions.md b/website/docs/user-guide/sessions.md index e17fc4964e..5b8d70245c 100644 --- a/website/docs/user-guide/sessions.md +++ b/website/docs/user-guide/sessions.md @@ -366,6 +366,10 @@ hermes sessions export telegram-history.jsonl --source telegram # Export a single session hermes sessions export session.jsonl --session-id 20250305_091523_a1b2c3d4 +# Point at a directory (existing, or ending in /) and the file is named for you: +# ~/exports/hermes_session_20250305_091523_a1b2c3d4.jsonl +hermes sessions export ~/exports/ --session-id 20250305_091523_a1b2c3d4 + # Redact API keys/tokens/credentials from the exported content hermes sessions export backup.jsonl --redact ```