mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-29 06:31:32 +00:00
feat(kanban): add --sort option to 'hermes kanban list'
Salvages #25745 by @LizerAIDev. Adds --sort {created,created-desc, priority,priority-desc,status,assignee,title,updated} to 'hermes kanban list'. Validated against VALID_SORT_ORDERS map; invalid values raise ValueError. Default behaviour (priority DESC, created ASC) is unchanged when --sort is omitted.
This commit is contained in:
parent
206f595f66
commit
a846e500b0
3 changed files with 64 additions and 1 deletions
|
|
@ -324,6 +324,12 @@ def build_parser(parent_subparsers: argparse._SubParsersAction) -> argparse.Argu
|
|||
p_list.add_argument("--archived", action="store_true",
|
||||
help="Include archived tasks")
|
||||
p_list.add_argument("--json", action="store_true")
|
||||
p_list.add_argument(
|
||||
"--sort",
|
||||
default=None,
|
||||
choices=sorted(kb.VALID_SORT_ORDERS.keys()),
|
||||
help="Sort order for listed tasks (default: priority)",
|
||||
)
|
||||
|
||||
# --- show ---
|
||||
p_show = sub.add_parser("show", help="Show a task with comments + events")
|
||||
|
|
@ -1220,6 +1226,7 @@ def _cmd_list(args: argparse.Namespace) -> int:
|
|||
status=args.status,
|
||||
tenant=args.tenant,
|
||||
include_archived=args.archived,
|
||||
order_by=getattr(args, "sort", None),
|
||||
)
|
||||
if getattr(args, "json", False):
|
||||
print(json.dumps([_task_to_dict(t) for t in tasks], indent=2, ensure_ascii=False))
|
||||
|
|
|
|||
|
|
@ -1531,6 +1531,20 @@ def get_task(conn: sqlite3.Connection, task_id: str) -> Optional[Task]:
|
|||
return Task.from_row(row) if row else None
|
||||
|
||||
|
||||
# Canonical sort-order mappings for ``hermes kanban list --sort``.
|
||||
# Each value is a raw SQL fragment appended after ``ORDER BY``.
|
||||
VALID_SORT_ORDERS: dict[str, str] = {
|
||||
"created": "created_at ASC, id ASC",
|
||||
"created-desc": "created_at DESC, id DESC",
|
||||
"priority": "priority DESC, created_at ASC",
|
||||
"priority-desc": "priority ASC, created_at ASC",
|
||||
"status": "status ASC, created_at ASC",
|
||||
"assignee": "assignee ASC, created_at ASC",
|
||||
"title": "title ASC, id ASC",
|
||||
"updated": "started_at DESC NULLS LAST, created_at DESC",
|
||||
}
|
||||
|
||||
|
||||
def list_tasks(
|
||||
conn: sqlite3.Connection,
|
||||
*,
|
||||
|
|
@ -1539,6 +1553,7 @@ def list_tasks(
|
|||
tenant: Optional[str] = None,
|
||||
include_archived: bool = False,
|
||||
limit: Optional[int] = None,
|
||||
order_by: Optional[str] = None,
|
||||
) -> list[Task]:
|
||||
query = "SELECT * FROM tasks WHERE 1=1"
|
||||
params: list[Any] = []
|
||||
|
|
@ -1555,7 +1570,15 @@ def list_tasks(
|
|||
params.append(tenant)
|
||||
if not include_archived and status != "archived":
|
||||
query += " AND status != 'archived'"
|
||||
query += " ORDER BY priority DESC, created_at ASC"
|
||||
if order_by is not None:
|
||||
order_by = order_by.strip().lower()
|
||||
if order_by not in VALID_SORT_ORDERS:
|
||||
raise ValueError(
|
||||
f"order_by must be one of {sorted(VALID_SORT_ORDERS.keys())}"
|
||||
)
|
||||
query += f" ORDER BY {VALID_SORT_ORDERS[order_by]}"
|
||||
else:
|
||||
query += " ORDER BY priority DESC, created_at ASC"
|
||||
if limit:
|
||||
query += f" LIMIT {int(limit)}"
|
||||
rows = conn.execute(query, params).fetchall()
|
||||
|
|
|
|||
|
|
@ -726,6 +726,39 @@ def test_delete_archived_task_rejects_non_archived_rows(kanban_home):
|
|||
assert kb.get_task(conn, tid) is not None
|
||||
|
||||
|
||||
def test_list_tasks_order_by(kanban_home):
|
||||
with kb.connect() as conn:
|
||||
# Create tasks with different titles and priorities
|
||||
t_a = kb.create_task(conn, title="alpha", priority=1)
|
||||
t_b = kb.create_task(conn, title="beta", priority=2)
|
||||
t_c = kb.create_task(conn, title="gamma", priority=1)
|
||||
|
||||
# Default sort: priority DESC, created ASC
|
||||
default = kb.list_tasks(conn)
|
||||
assert [t.id for t in default] == [t_b, t_a, t_c]
|
||||
|
||||
# Sort by title ASC
|
||||
by_title = kb.list_tasks(conn, order_by="title")
|
||||
assert [t.id for t in by_title] == [t_a, t_b, t_c]
|
||||
|
||||
# Sort by assignee
|
||||
kb.assign_task(conn, t_a, "alice")
|
||||
kb.assign_task(conn, t_b, "bob")
|
||||
kb.assign_task(conn, t_c, "alice")
|
||||
by_assignee = kb.list_tasks(conn, order_by="assignee")
|
||||
# alice's tasks first (alphabetically), then bob's
|
||||
assignees = [t.assignee for t in by_assignee]
|
||||
assert assignees[:2] == ["alice", "alice"]
|
||||
assert assignees[2] == "bob"
|
||||
|
||||
# Invalid sort order raises ValueError
|
||||
try:
|
||||
kb.list_tasks(conn, order_by="bogus")
|
||||
assert False, "Should have raised ValueError"
|
||||
except ValueError as e:
|
||||
assert "order_by must be one of" in str(e)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Comments / events / worker context
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue