feat(cli): sessions list --workspace filter + Workspace column

`hermes sessions list --workspace <needle>` filters to one workspace (git repo
root or project dir, matched by path substring or basename) and adds a
Workspace column. The column only appears once at least one listed session
carries a workspace, so all-unbound listings render exactly as before.

Co-authored-by: Cary Palmer <palmer@dugoutfantasy.com>
This commit is contained in:
Brooklyn Nicholson 2026-07-12 05:08:03 -04:00
parent 602fe1c15d
commit 0c4aed2499

View file

@ -13514,6 +13514,12 @@ def main():
sessions_list.add_argument(
"--limit", type=int, default=20, help="Max sessions to show"
)
sessions_list.add_argument(
"--workspace",
metavar="NEEDLE",
help="Only sessions in one workspace: a git repo root or project dir "
"(matched by path substring or basename).",
)
def _add_session_filter_args(p, default_older_help):
p.add_argument(
@ -13840,13 +13846,58 @@ def main():
_exclude = None if _source else ["tool"]
if action == "list":
from hermes_state import workspace_key as _ws_key
sessions = db.list_sessions_rich(
source=args.source, exclude_sources=_exclude, limit=args.limit
)
# Workspace filter: match a session by its workspace key (git repo
# root, else cwd) — path substring or exact basename.
_ws_filter = (getattr(args, "workspace", None) or "").strip()
if _ws_filter:
_needle = _ws_filter.lower()
def _in_workspace(s):
key = (_ws_key(s) or "").lower()
return bool(key) and (
_needle in key or _needle == os.path.basename(key.rstrip("/\\"))
)
sessions = [s for s in sessions if _in_workspace(s)]
if not sessions:
print("No sessions found.")
return
# Short workspace label: the repo/dir basename, "—" when unbound. The
# Workspace column only appears once at least one session carries one
# (or when filtering), so all-unbound listings read as before.
def _ws_label(s):
key = _ws_key(s)
return (os.path.basename(key.rstrip("/\\")) or key) if key else ""
has_ws = bool(_ws_filter) or any(_ws_key(s) for s in sessions)
has_titles = any(s.get("title") for s in sessions)
if has_ws:
if has_titles:
print(f"{'Title':<28} {'Workspace':<18} {'Last Active':<13} {'ID'}")
print("" * 110)
else:
print(f"{'Preview':<38} {'Workspace':<18} {'Last Active':<13} {'Src':<6} {'ID'}")
print("" * 100)
for s in sessions:
last_active = _relative_time(s.get("last_active"))
ws = _ws_label(s)[:16]
if has_titles:
title = (s.get("title") or "")[:26]
print(f"{title:<28} {ws:<18} {last_active:<13} {s['id']}")
else:
preview = s.get("preview", "")[:36]
print(f"{preview:<38} {ws:<18} {last_active:<13} {s['source']:<6} {s['id']}")
return
if has_titles:
print(f"{'Title':<32} {'Preview':<40} {'Last Active':<13} {'ID'}")
print("" * 110)