From 469c8f45ab9cdfd4094c44cc283fdeb2458043d0 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Wed, 2 Sep 2026 20:48:06 -0700 Subject: [PATCH] refactor(hermes_cli): inline bind-board workdir sync, compact export/save renderers --- hermes_cli/projects_cmd.py | 62 +++++++++++------------------------- hermes_cli/session_export.py | 54 +++++++++---------------------- 2 files changed, 34 insertions(+), 82 deletions(-) diff --git a/hermes_cli/projects_cmd.py b/hermes_cli/projects_cmd.py index 382bd4aa4f..0b1ff36d13 100644 --- a/hermes_cli/projects_cmd.py +++ b/hermes_cli/projects_cmd.py @@ -9,9 +9,7 @@ import sys from hermes_cli import projects_db as pdb -def build_parser( - parent_subparsers: argparse._SubParsersAction, -) -> argparse.ArgumentParser: +def build_parser(parent_subparsers: argparse._SubParsersAction) -> argparse.ArgumentParser: """Attach the ``project`` subcommand tree. Returns the top parser.""" parser = parent_subparsers.add_parser( "project", @@ -83,7 +81,6 @@ def projects_command(args: argparse.Namespace) -> int: file=sys.stderr, ) return 0 - handler = _HANDLERS.get(action) if handler is None: print(f"Unknown project action: {action}", file=sys.stderr) @@ -138,23 +135,14 @@ def _print_project(proj) -> None: if proj.folders: print(" folders:") for f in proj.folders: - mark = " *" if f.is_primary else " " - label = f" ({f.label})" if f.label else "" - print(f" {mark} {f.path}{label}") + print(f" {' *' if f.is_primary else ' '} {f.path}{f' ({f.label})' if f.label else ''}") @_db_command def _cmd_create(args, conn) -> int: pid = pdb.create_project( - conn, - name=args.name, - slug=args.slug, - folders=args.folders, - primary_path=args.primary, - description=args.description, - icon=args.icon, - color=args.color, - board_slug=args.board, + conn, name=args.name, slug=args.slug, folders=args.folders, primary_path=args.primary, + description=args.description, icon=args.icon, color=args.color, board_slug=args.board, ) if args.use: pdb.set_active(conn, pid) @@ -170,16 +158,13 @@ def _cmd_create(args, conn) -> int: @_db_command def _cmd_list(args, conn) -> int: active = pdb.get_active_id(conn) - projs = pdb.list_projects( - conn, include_archived=getattr(args, "include_archived", False) - ) + projs = pdb.list_projects(conn, include_archived=getattr(args, "include_archived", False)) if not projs: print("No projects yet. Create one with `hermes project create `.") return 0 for p in projs: - marker = "*" if p.id == active else " " flags = " (archived)" if p.archived else "" - print(f"{marker} {p.slug:<24} {p.name}{flags} [{len(p.folders)} folder(s)]") + print(f"{'*' if p.id == active else ' '} {p.slug:<24} {p.name}{flags} [{len(p.folders)} folder(s)]") return 0 @@ -214,10 +199,7 @@ def _cmd_rename(args, conn, proj) -> int: @_with_project def _cmd_set_primary(args, conn, proj) -> int: if not pdb.set_primary(conn, proj.id, args.path): - return _err( - f"'{args.path}' is not a folder of {proj.slug}; " - f"add it first with `hermes project add-folder`." - ) + return _err(f"'{args.path}' is not a folder of {proj.slug}; add it first with `hermes project add-folder`.") print(f"Set primary of {proj.slug} -> {args.path}") return 0 @@ -251,28 +233,22 @@ def _flag_command(op: str, verb: str): @_with_project def _cmd_bind_board(args, conn, proj) -> int: pdb.update_project(conn, proj.id, board_slug=args.board) - if args.board.strip(): - print(f"Bound {proj.slug} -> board {args.board}") - _sync_board_default_workdir(proj, args.board) - else: + if not args.board.strip(): print(f"Unbound board from {proj.slug}") + return 0 + print(f"Bound {proj.slug} -> board {args.board}") + if proj.primary_path: # best-effort: point the bound board's default_workdir at the primary repo + try: + from hermes_cli import kanban_db as kb + + slug = kb._normalize_board_slug(args.board) + if slug and (slug == kb.DEFAULT_BOARD or kb.board_exists(slug)): + kb.write_board_metadata(slug, default_workdir=proj.primary_path) + except Exception: + pass return 0 -def _sync_board_default_workdir(proj, board_slug: str) -> None: - """Best-effort: point the bound board's default_workdir at the primary repo.""" - if not proj.primary_path: - return - try: - from hermes_cli import kanban_db as kb - - slug = kb._normalize_board_slug(board_slug) - if slug and (slug == kb.DEFAULT_BOARD or kb.board_exists(slug)): - kb.write_board_metadata(slug, default_workdir=proj.primary_path) - except Exception: - pass - - _HANDLERS = { "create": _cmd_create, "list": _cmd_list, diff --git a/hermes_cli/session_export.py b/hermes_cli/session_export.py index d44bab6324..ec8336d2c3 100644 --- a/hermes_cli/session_export.py +++ b/hermes_cli/session_export.py @@ -37,12 +37,7 @@ def normalize_export_only(only: Optional[str]) -> Optional[ExportOnly]: raise ValueError(f"Unsupported session export filter: {only}") -def render_sessions_export( - sessions: Iterable[Dict[str, Any]], - *, - fmt: str = "jsonl", - only: Optional[str] = None, -) -> str: +def render_sessions_export(sessions: Iterable[Dict[str, Any]], *, fmt: str = "jsonl", only: Optional[str] = None) -> str: """Render exported sessions in a stable, reusable format. ``fmt=jsonl`` with no filter keeps the legacy shape (one full session object per line); @@ -66,17 +61,13 @@ def render_sessions_export( if not session_list: lines += ["_No user prompts found._", ""] return _finish_markdown(lines) + heading = lambda session: f"Session: {_heading_text(_session_title_or_id(session))}" # noqa: E731 return _finish_markdown(_render_sessions_markdown( - session_list, "Hermes sessions export", - lambda session: f"Session: {_heading_text(_session_title_or_id(session))}", - lambda session: f"Session: {_heading_text(_session_title_or_id(session))}", - _append_session_messages, + session_list, "Hermes sessions export", heading, heading, _append_session_messages, )) -def export_record_count( - sessions: Iterable[Dict[str, Any]], *, only: Optional[str] = None -) -> Tuple[int, str]: +def export_record_count(sessions: Iterable[Dict[str, Any]], *, only: Optional[str] = None) -> Tuple[int, str]: """Return ``(count, noun)`` for status messages after an export.""" session_list = list(sessions) if normalize_export_only(only) == "user-prompts": @@ -84,9 +75,7 @@ def export_record_count( return len(session_list), "session" -def iter_user_prompt_records( - sessions: Iterable[Dict[str, Any]] -) -> Iterator[Dict[str, Any]]: +def iter_user_prompt_records(sessions: Iterable[Dict[str, Any]]) -> Iterator[Dict[str, Any]]: """Yield one normalized record for each user-authored prompt.""" for session in sessions: session_id = str(session.get("id") or session.get("session_id") or "") @@ -124,33 +113,25 @@ def _render_sessions_markdown(sessions, multi_title, single_heading, multi_headi return lines -def _append_prompt_records( - lines: List[str], session: Dict[str, Any], *, heading_level: int -) -> None: +def _append_prompt_records(lines: List[str], session: Dict[str, Any], *, heading_level: int) -> None: prompts = list(iter_user_prompt_records([session])) if not prompts: lines += ["_No user prompts found._", ""] return marker = "#" * heading_level for prompt in prompts: - timestamp = prompt.get("created_at") or "timestamp unavailable" - lines.append(f"{marker} {prompt['index']}. {timestamp}") + lines.append(f"{marker} {prompt['index']}. {prompt.get('created_at') or 'timestamp unavailable'}") if (message_id := prompt.get("message_id")) is not None: lines += [f"Message ID: `{message_id}`", ""] lines += [str(prompt.get("text") or ""), ""] -def _append_session_messages( - lines: List[str], session: Dict[str, Any], *, heading_level: int -) -> None: +def _append_session_messages(lines: List[str], session: Dict[str, Any], *, heading_level: int) -> None: marker = "#" * heading_level - visible_messages = [ - message for message in _messages(session) if message.get("role") != "system" - ] + visible_messages = [message for message in _messages(session) if message.get("role") != "system"] if not visible_messages: lines += ["_No messages found._", ""] return - for message in visible_messages: role = str(message.get("role") or "unknown") timestamp = _format_timestamp(message.get("timestamp")) @@ -163,9 +144,9 @@ def _append_session_messages( f"
{html_escape(tool_name)}", "", _fenced_text(text), "", "
", "", ] - continue - label = {"user": "User", "assistant": "Assistant"}.get(role, role.title()) - lines += [f"{marker} {label}{suffix}", "", text, ""] + else: + label = {"user": "User", "assistant": "Assistant"}.get(role, role.title()) + lines += [f"{marker} {label}{suffix}", "", text, ""] def _messages(session: Dict[str, Any]) -> List[Dict[str, Any]]: @@ -185,8 +166,7 @@ def _content_part_text(part: Any) -> str: return part if isinstance(part, dict): for key in ("text", "content"): - value = part.get(key) - if isinstance(value, str): + if isinstance(value := part.get(key), str): return value return json.dumps(part, ensure_ascii=False, sort_keys=True) return str(part) @@ -273,9 +253,7 @@ def normalize_save_format(fmt: Optional[str]) -> str: """Map a user-typed /save format token to a canonical format.""" token = (fmt or "json").strip().lower() if token not in _SAVE_FORMAT_ALIASES: - raise ValueError( - f"Unknown format {token!r} — expected one of: json, md, html" - ) + raise ValueError(f"Unknown format {token!r} — expected one of: json, md, html") return _SAVE_FORMAT_ALIASES[token] @@ -302,7 +280,5 @@ def render_session_for_save(session: Dict[str, Any], fmt: str) -> str: def default_save_filename(session_id: str, fmt: str) -> str: """Default filename for a /save export of the given session.""" - safe_id = "".join( - ch for ch in str(session_id) if ch.isalnum() or ch in ("-", "_") - ) or "session" + safe_id = "".join(ch for ch in str(session_id) if ch.isalnum() or ch in ("-", "_")) or "session" return f"hermes_session_{safe_id}.{fmt}"