omo's omo-agent-toolkit worktree-sweep (their PR #7151) added three capabilities our hermes worktree command lacked: - --json on list and prune: machine-readable audit/result payloads so scripts and agents can consume verdicts without scraping table output. - --older-than DAYS: an age floor that only ever RESTRICTS reaping (young-but-reapable trees are kept); it never widens eligibility, so the existing safety invariants are untouched. - External-tree visibility: linked worktrees registered outside .worktrees/ are now reported read-only in the audit (branch, locked, missing) instead of being invisible, and registrations whose directory has vanished are dropped via git worktree prune (metadata only, no files touched) during prune. Not ported: omo's ancestor-of-default-branch merge test (our git cherry patch-equivalence is strictly stronger under rebase/squash merges), and their hardcoded external-root exclusion list (we exclude by location: everything outside .worktrees/ is hands-off). Tests: 9 new contracts in tests/hermes_cli/test_worktree_gc.py (age gate restrict-only, external trees never reaped, stale-registration prune dry-run/real, JSON shapes, negative --older-than rejected). Live E2E on a scratch repo verified all three flags end to end.
54 lines
2.7 KiB
Python
54 lines
2.7 KiB
Python
"""``hermes worktree`` subcommand parser."""
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
def build_worktree_parser(subparsers) -> None:
|
|
"""Attach the ``worktree`` subcommand to ``subparsers``."""
|
|
worktree_parser = subparsers.add_parser(
|
|
"worktree", help="Audit and reclaim accumulated git worktrees and merged branches",
|
|
description="Attended reclaim for the .worktrees/ directory hermes -w sessions "
|
|
"accumulate. Never deletes uncommitted tracked changes, unique "
|
|
"unpushed commits, or in-use trees; untracked-only scratch is "
|
|
"archived to ~/.hermes/archive/worktree-prune/ before removal. See: "
|
|
"https://hermes-agent.nousresearch.com/docs/user-guide/cli#worktree-cleanup")
|
|
worktree_subparsers = worktree_parser.add_subparsers(dest="worktree_action")
|
|
worktree_list = worktree_subparsers.add_parser(
|
|
"list", aliases=["ls", "audit"],
|
|
help="Classify every tree: age, size, verdict, reason (default action)")
|
|
worktree_list.add_argument("--repo", help="Repo root (default: current repo)")
|
|
worktree_list.add_argument(
|
|
"--json", action="store_true",
|
|
help="Machine-readable audit output (trees, external trees, branches)")
|
|
worktree_list.add_argument(
|
|
"--older-than", type=float, metavar="DAYS", dest="older_than",
|
|
help="Treat reapable trees younger than DAYS as keep")
|
|
worktree_prune = worktree_subparsers.add_parser(
|
|
"prune", help="Remove safe trees and delete fully-merged local branches")
|
|
worktree_prune.add_argument("--repo", help="Repo root (default: current repo)")
|
|
worktree_prune.add_argument(
|
|
"--json", action="store_true",
|
|
help="Machine-readable result (actions taken/planned, preserved trees)")
|
|
worktree_prune.add_argument(
|
|
"--older-than", type=float, metavar="DAYS", dest="older_than",
|
|
help="Only reap trees idle for at least DAYS days (safety gates still apply)")
|
|
worktree_prune.add_argument(
|
|
"--dry-run", action="store_true", help="Show the plan without changing anything")
|
|
worktree_prune.add_argument(
|
|
"--trees-only", action="store_true",
|
|
help="Only remove worktrees; leave local branches alone")
|
|
worktree_prune.add_argument(
|
|
"--branches-only", action="store_true",
|
|
help="Only delete merged local branches; leave worktrees alone")
|
|
|
|
def _dispatch_worktree(_args):
|
|
from hermes_cli.worktree_cmd import cmd_worktree
|
|
|
|
# argparse aliases set dest to the literal typed string ("ls"/"audit").
|
|
action = getattr(_args, "worktree_action", None)
|
|
if action in ("ls", "audit"):
|
|
_args.worktree_action = "list"
|
|
return cmd_worktree(_args)
|
|
|
|
worktree_parser.set_defaults(func=_dispatch_worktree)
|