feat: adding more support for command-level flags, also including workarounds for getting raw SDK info

This commit is contained in:
ajspig 2026-03-11 14:37:30 -04:00
parent d3342ab466
commit a881313212
7 changed files with 277 additions and 78 deletions

View File

@ -21,10 +21,15 @@ add_common_options(app)
def list_conclusions(
observer: Optional[str] = typer.Option(None, "--observer", help="Observer peer ID"),
observed: Optional[str] = typer.Option(None, "--observed", help="Observed peer ID"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""List conclusions."""
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer)
client, config = get_client()
if not observer:
@ -33,13 +38,13 @@ def list_conclusions(
print_error("NO_PEER", "Observer peer ID required. Use --observer or set default peer.")
raise typer.Exit(1)
peer = client.peer(observer)
p = client.peer(observer)
try:
if observed:
scope = peer.conclusions_of(observed)
scope = p.conclusions_of(observed)
else:
scope = peer.conclusions
scope = p.conclusions
conclusions = list(scope.list())
items = [
@ -63,10 +68,15 @@ def search(
observer: Optional[str] = typer.Option(None, "--observer", help="Observer peer ID"),
observed: Optional[str] = typer.Option(None, "--observed", help="Observed peer ID"),
top_k: int = typer.Option(10, help="Max results"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Semantic search over conclusions."""
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer)
client, config = get_client()
if not observer:
@ -75,13 +85,13 @@ def search(
print_error("NO_PEER", "Observer peer ID required. Use --observer or set default peer.")
raise typer.Exit(1)
peer = client.peer(observer)
p = client.peer(observer)
try:
if observed:
scope = peer.conclusions_of(observed)
scope = p.conclusions_of(observed)
else:
scope = peer.conclusions
scope = p.conclusions
results = scope.query(query, top_k=top_k)
items = [
@ -104,11 +114,16 @@ def create(
content: str = typer.Argument(help="Conclusion content or JSON payload"),
observer: Optional[str] = typer.Option(None, "--observer", help="Observer peer ID"),
observed: Optional[str] = typer.Option(None, "--observed", help="Observed peer ID"),
session_id: Optional[str] = typer.Option(None, "--session", help="Session context"),
session_id: Optional[str] = typer.Option(None, "--session", "-s", help="Session context"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Create a conclusion."""
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer)
client, config = get_client()
if not observer:
@ -124,13 +139,13 @@ def create(
except (json.JSONDecodeError, AttributeError):
pass
peer = client.peer(observer)
p = client.peer(observer)
try:
if observed:
scope = peer.conclusions_of(observed)
scope = p.conclusions_of(observed)
else:
scope = peer.conclusions
scope = p.conclusions
result = scope.create(content, session_id=session_id)
print_result({
@ -150,10 +165,15 @@ def delete(
observer: Optional[str] = typer.Option(None, "--observer", help="Observer peer ID"),
observed: Optional[str] = typer.Option(None, "--observed", help="Observed peer ID"),
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Delete a conclusion."""
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer)
validate_resource_id(conclusion_id, "conclusion")
client, config = get_client()
@ -166,13 +186,13 @@ def delete(
if not yes:
typer.confirm(f"Delete conclusion '{conclusion_id}'?", abort=True)
peer = client.peer(observer)
p = client.peer(observer)
try:
if observed:
scope = peer.conclusions_of(observed)
scope = p.conclusions_of(observed)
else:
scope = peer.conclusions
scope = p.conclusions
scope.delete(conclusion_id)
status(f"Conclusion '{conclusion_id}' deleted")

View File

@ -47,13 +47,17 @@ def generate(
admin: bool = typer.Option(False, "--admin", help="Generate admin key"),
expires: str = typer.Option("90d", "--expires", help="Expiry duration (e.g., 30d, 24h)"),
no_expire: bool = typer.Option(False, "--no-expire", help="No expiration (use with caution)"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Generate a scoped JWT key.
Requires an admin JWT in config. Generated keys are scoped down from admin.
"""
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_resolved_config
handle_cmd_flags(json_output=json_output)
config = get_resolved_config()
if not config.api_key:

View File

@ -2,12 +2,13 @@
from __future__ import annotations
import hashlib
from typing import Optional
import typer
from honcho_cli.commands.workspace import _handle_error
from honcho_cli.output import print_result
from honcho_cli.output import print_result, status
from honcho_cli.validation import validate_resource_id
from honcho_cli.common import add_common_options
@ -21,34 +22,62 @@ def list_messages(
session_id: Optional[str] = typer.Argument(None, help="Session ID (uses default if omitted)"),
last: int = typer.Option(20, "--last", help="Number of recent messages"),
reverse: bool = typer.Option(False, "--reverse", help="Reverse order"),
brief: bool = typer.Option(False, "--brief", help="Show only IDs, peer, token count, and created_at (no content)"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
session: Optional[str] = typer.Option(None, "--session", "-s", help="Override session ID"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""List messages in a session."""
from honcho_cli.commands.session import _get_session_id
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace, session=session)
sid = _get_session_id(session_id)
client, config = get_client()
session = client.session(sid)
sess = client.session(sid)
try:
msgs = list(session.messages())
msgs = list(sess.messages())
if not reverse:
msgs = msgs[-last:]
else:
msgs = msgs[:last]
items = [
{
"id": m.id,
"peer_id": m.peer_id,
"content": m.content,
"token_count": m.token_count,
"metadata": m.metadata,
"created_at": str(m.created_at),
}
for m in msgs
]
print_result(items, columns=["id", "peer_id", "content", "created_at"], title="Messages")
# Detect duplicate content
content_hashes: dict[str, list[str]] = {}
for m in msgs:
h = hashlib.md5(m.content.encode()).hexdigest()
content_hashes.setdefault(h, []).append(m.id)
dupes = {h: ids for h, ids in content_hashes.items() if len(ids) > 1}
if dupes:
dupe_count = sum(len(ids) - 1 for ids in dupes.values())
status(f"Warning: {dupe_count} duplicate message(s) detected (identical content, different IDs)")
if brief:
items = [
{
"id": m.id,
"peer_id": m.peer_id,
"token_count": m.token_count,
"created_at": str(m.created_at),
}
for m in msgs
]
print_result(items, columns=["id", "peer_id", "token_count", "created_at"], title="Messages")
else:
items = [
{
"id": m.id,
"peer_id": m.peer_id,
"content": m.content,
"token_count": m.token_count,
"metadata": m.metadata,
"created_at": str(m.created_at),
}
for m in msgs
]
print_result(items, columns=["id", "peer_id", "content", "created_at"], title="Messages")
except Exception as e:
_handle_error(e, "message", "list")
@ -56,20 +85,24 @@ def list_messages(
@app.command("get")
def get_message(
message_id: str = typer.Argument(help="Message ID"),
session_id: Optional[str] = typer.Option(None, "--session", help="Session ID"),
session_id: Optional[str] = typer.Option(None, "--session", "-s", help="Session ID"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Get a single message by ID."""
from honcho_cli.commands.session import _get_session_id
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace)
validate_resource_id(message_id, "message")
sid = _get_session_id(session_id)
client, config = get_client()
try:
# Use raw HTTP to get a single message
session = client.session(sid)
msgs = list(session.messages())
sess = client.session(sid)
msgs = list(sess.messages())
msg = next((m for m in msgs if m.id == message_id), None)
if msg is None:

View File

@ -30,22 +30,28 @@ def _get_peer_id(peer_id: str | None) -> str:
@app.command("list")
def list_peers() -> None:
def list_peers(
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""List all peers in the workspace."""
from honcho_cli.commands.workspace import _compact_config, _raw_list
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace)
client, config = get_client()
try:
peers = list(client.peers())
raw_peers = _raw_list(client.peers())
items = [
{
"id": p.id,
"metadata": getattr(p, "metadata", {}),
"configuration": _config_to_dict(p.configuration) if getattr(p, "configuration", None) else None,
"created_at": str(getattr(p, "created_at", "")),
"metadata": p.metadata,
"configuration": _compact_config(_config_to_dict(p.configuration)) if p.configuration else None,
"created_at": str(p.created_at),
}
for p in peers
for p in raw_peers
]
print_result(items, columns=["id", "metadata", "created_at"], title="Peers")
except Exception as e:
@ -55,18 +61,23 @@ def list_peers() -> None:
@app.command()
def inspect(
peer_id: Optional[str] = typer.Argument(None, help="Peer ID (uses default if omitted)"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Inspect a peer: card, session count, recent conclusions."""
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer)
pid = _get_peer_id(peer_id)
client, config = get_client()
peer = client.peer(pid)
p = client.peer(pid)
try:
card = peer.get_card()
sessions = list(peer.sessions())
conclusions = list(peer.conclusions.list())
card = p.get_card()
sessions = list(p.sessions())
conclusions = list(p.conclusions.list())
result = {
"id": pid,
@ -88,16 +99,21 @@ def inspect(
def card(
peer_id: Optional[str] = typer.Argument(None, help="Peer ID (uses default if omitted)"),
target: Optional[str] = typer.Option(None, help="Target peer for relationship card"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Get raw peer card content."""
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer)
pid = _get_peer_id(peer_id)
client, config = get_client()
peer = client.peer(pid)
p = client.peer(pid)
try:
result = peer.get_card(target=target)
result = p.get_card(target=target)
print_result({"peer_id": pid, "target": target, "card": result})
except Exception as e:
_handle_error(e, "peer", pid)
@ -109,16 +125,21 @@ def chat(
peer_id: Optional[str] = typer.Option(None, help="Peer ID (uses default if omitted)"),
target: Optional[str] = typer.Option(None, help="Target peer for perspective"),
session: Optional[str] = typer.Option(None, help="Session context"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Query the dialectic about a peer."""
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer)
pid = _get_peer_id(peer_id)
client, config = get_client()
peer = client.peer(pid)
p = client.peer(pid)
try:
response = peer.chat(query, target=target, session=session)
response = p.chat(query, target=target, session=session)
print_result({"peer_id": pid, "query": query, "response": response})
except Exception as e:
_handle_error(e, "peer", pid)
@ -129,16 +150,21 @@ def search(
query: str = typer.Argument(help="Search query"),
peer_id: Optional[str] = typer.Option(None, help="Peer ID (uses default if omitted)"),
limit: int = typer.Option(10, help="Max results"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Search a peer's messages."""
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer)
pid = _get_peer_id(peer_id)
client, config = get_client()
peer = client.peer(pid)
p = client.peer(pid)
try:
results = peer.search(query, limit=limit)
results = p.search(query, limit=limit)
items = [
{
"id": m.id,

View File

@ -31,28 +31,34 @@ def _get_session_id(session_id: str | None) -> str:
@app.command("list")
def list_sessions(
peer_id: Optional[str] = typer.Option(None, "--peer", help="Filter by peer"),
peer_id: Optional[str] = typer.Option(None, "--peer", "-p", help="Filter by peer"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
session: Optional[str] = typer.Option(None, "--session", "-s", help="Override session ID"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""List sessions in the workspace."""
from honcho_cli.commands.workspace import _raw_list
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace, session=session)
client, config = get_client()
try:
if peer_id:
peer = client.peer(peer_id)
sessions = list(peer.sessions())
raw_sessions = _raw_list(peer.sessions())
else:
sessions = list(client.sessions())
raw_sessions = _raw_list(client.sessions())
items = [
{
"id": s.id,
"is_active": getattr(s, "is_active", None),
"metadata": getattr(s, "metadata", {}),
"created_at": str(getattr(s, "created_at", "")),
"is_active": s.is_active,
"metadata": s.metadata,
"created_at": str(s.created_at),
}
for s in sessions
for s in raw_sessions
]
print_result(items, columns=["id", "is_active", "metadata", "created_at"], title="Sessions")
except Exception as e:
@ -62,20 +68,28 @@ def list_sessions(
@app.command()
def inspect(
session_id: Optional[str] = typer.Argument(None, help="Session ID (uses default if omitted)"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
session: Optional[str] = typer.Option(None, "--session", "-s", help="Override session ID"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Inspect a session: peers, message count, summaries, config."""
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace, session=session)
sid = _get_session_id(session_id)
client, config = get_client()
session = client.session(sid)
sess = client.session(sid)
try:
peers = session.peers()
messages = list(session.messages())
summaries = session.summaries()
sess_config = session.get_configuration()
peers = sess.peers()
messages = list(sess.messages())
summaries = sess.summaries()
sess_config = sess.get_configuration()
from honcho_cli.commands.workspace import _compact_config
raw_config = _config_to_dict(sess_config) if sess_config else None
result = {
"id": sid,
"peers": [{"id": p.id} for p in peers],
@ -84,7 +98,7 @@ def inspect(
"short": summaries.short_summary if hasattr(summaries, "short_summary") else None,
"long": summaries.long_summary if hasattr(summaries, "long_summary") else None,
},
"configuration": _config_to_dict(sess_config) if sess_config else None,
"configuration": _compact_config(raw_config) if isinstance(raw_config, dict) else raw_config,
}
print_result(result)
except Exception as e:
@ -96,16 +110,21 @@ def messages(
session_id: Optional[str] = typer.Argument(None, help="Session ID (uses default if omitted)"),
last: int = typer.Option(20, "--last", help="Number of recent messages"),
reverse: bool = typer.Option(False, "--reverse", help="Reverse order (oldest first)"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
session: Optional[str] = typer.Option(None, "--session", "-s", help="Override session ID"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""List recent messages in a session."""
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace, session=session)
sid = _get_session_id(session_id)
client, config = get_client()
session = client.session(sid)
sess = client.session(sid)
try:
msgs = list(session.messages())
msgs = list(sess.messages())
if not reverse:
msgs = msgs[-last:]
else:
@ -131,16 +150,21 @@ def context(
session_id: Optional[str] = typer.Argument(None, help="Session ID (uses default if omitted)"),
tokens: Optional[int] = typer.Option(None, help="Token budget"),
summary: bool = typer.Option(True, help="Include summary"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
session: Optional[str] = typer.Option(None, "--session", "-s", help="Override session ID"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Get session context (what an agent would see)."""
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace, session=session)
sid = _get_session_id(session_id)
client, config = get_client()
session = client.session(sid)
sess = client.session(sid)
try:
ctx = session.context(tokens=tokens, summary=summary)
ctx = sess.context(tokens=tokens, summary=summary)
result = ctx.__dict__ if hasattr(ctx, "__dict__") else ctx
print_result(result)
except Exception as e:
@ -150,16 +174,21 @@ def context(
@app.command()
def summaries(
session_id: Optional[str] = typer.Argument(None, help="Session ID (uses default if omitted)"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
session: Optional[str] = typer.Option(None, "--session", "-s", help="Override session ID"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Get session summaries (short + long)."""
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace, session=session)
sid = _get_session_id(session_id)
client, config = get_client()
session = client.session(sid)
sess = client.session(sid)
try:
s = session.summaries()
s = sess.summaries()
result = {
"session_id": sid,
"short_summary": s.short_summary if hasattr(s, "short_summary") else None,

View File

@ -1,4 +1,4 @@
"""Workspace commands: inspect, delete, search, queue-status."""
"""Workspace commands: list, inspect, delete, search, queue-status."""
from __future__ import annotations
@ -26,13 +26,55 @@ def _get_workspace_id(workspace_id: str | None) -> str:
return validate_resource_id(wid, "workspace")
def _raw_list(page) -> list:
"""Collect all raw API response items across all pages of a SyncPage."""
items = list(page._raw_items)
while page.has_next_page():
page = page.get_next_page()
if page is None:
break
items.extend(page._raw_items)
return items
def _compact_config(config_dict: dict) -> dict | str:
"""Return '(defaults)' if all config values are None, else the dict."""
if all(v is None for v in config_dict.values()):
return "(defaults)"
return config_dict
@app.command("list")
def list_workspaces(
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""List all accessible workspaces."""
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output)
client, config = get_client()
try:
workspaces = list(client.workspaces())
items = [{"id": w} for w in workspaces]
print_result(items, columns=["id"], title="Workspaces")
except Exception as e:
_handle_error(e, "workspace", "list")
@app.command()
def inspect(
workspace_id: Optional[str] = typer.Argument(None, help="Workspace ID (uses default if omitted)"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Inspect a workspace: peers, sessions, config."""
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace)
wid = _get_workspace_id(workspace_id)
client, config = get_client()
@ -44,17 +86,24 @@ def inspect(
ws_config = client.get_configuration()
ws_metadata = client.get_metadata()
peers = list(client.peers())
sessions = list(client.sessions())
# Use raw API response objects to get all fields (created_at, is_active)
raw_peers = _raw_list(client.peers())
raw_sessions = _raw_list(client.sessions())
result = {
"workspace_id": wid,
"metadata": ws_metadata,
"configuration": _config_to_dict(ws_config),
"peer_count": len(peers),
"session_count": len(sessions),
"peers": [{"id": p.id, "metadata": getattr(p, "metadata", {})} for p in peers[:20]],
"sessions": [{"id": s.id, "is_active": getattr(s, "is_active", None), "metadata": getattr(s, "metadata", {})} for s in sessions[:20]],
"configuration": _compact_config(_config_to_dict(ws_config)),
"peer_count": len(raw_peers),
"session_count": len(raw_sessions),
"peers": [
{"id": p.id, "metadata": p.metadata, "created_at": str(p.created_at)}
for p in raw_peers[:20]
],
"sessions": [
{"id": s.id, "is_active": s.is_active, "metadata": s.metadata, "created_at": str(s.created_at)}
for s in raw_sessions[:20]
],
}
print_result(result)
except Exception as e:
@ -66,10 +115,14 @@ def delete(
workspace_id: str = typer.Argument(help="Workspace ID to delete"),
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation"),
dry_run: bool = typer.Option(False, "--dry-run", help="Show what would be deleted without deleting"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Delete a workspace. Destructive — requires --yes or interactive confirm."""
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output)
validate_resource_id(workspace_id, "workspace")
client, config = get_client()
client = _with_workspace(client, workspace_id)
@ -100,11 +153,16 @@ def delete(
def search(
query: str = typer.Argument(help="Search query"),
workspace_id: Optional[str] = typer.Option(None, help="Workspace ID"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
limit: int = typer.Option(10, help="Max results"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Search messages across workspace."""
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace)
wid = _get_workspace_id(workspace_id)
client, config = get_client()
@ -128,13 +186,18 @@ def search(
@app.command("queue-status")
def queue_status(
workspace_id: Optional[str] = typer.Option(None, help="Workspace ID"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
observer: Optional[str] = typer.Option(None, help="Filter by observer peer"),
sender: Optional[str] = typer.Option(None, help="Filter by sender peer"),
session: Optional[str] = typer.Option(None, help="Filter by session"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Get queue processing status."""
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace)
_get_workspace_id(workspace_id)
client, config = get_client()

View File

@ -1,4 +1,11 @@
"""Shared callback for subcommand groups to accept global-style flags."""
"""Shared callback and command-level flag helpers.
Flags like --json, -w, -p, -s work in TWO positions:
1. Group-level (before subcommand): honcho workspace --json list
2. Command-level (after subcommand): honcho workspace list --json -w granola
Both positions are idempotent if set at group level, the command-level is a no-op.
"""
from __future__ import annotations
@ -9,10 +16,28 @@ import typer
from honcho_cli.output import set_json_mode, set_quiet_mode
def handle_cmd_flags(
json_output: bool = False,
workspace: str | None = None,
peer: str | None = None,
session: str | None = None,
) -> None:
"""Apply command-level flags. Idempotent if already set by group callback."""
if json_output:
set_json_mode(True)
from honcho_cli.main import _global_overrides
if workspace:
_global_overrides["workspace"] = workspace
if peer:
_global_overrides["peer"] = peer
if session:
_global_overrides["session"] = session
def add_common_options(app: typer.Typer) -> None:
"""Add a callback to a sub-app that accepts --json, --quiet, -w, -p, -s."""
# Allow flags like --json after subcommands (e.g., `honcho workspace inspect granola --json`)
app.info.context_settings = {"allow_interspersed_args": True}
@app.callback(invoke_without_command=True)
def _callback(
@ -28,7 +53,6 @@ def add_common_options(app: typer.Typer) -> None:
if quiet:
set_quiet_mode(True)
# Import here to avoid circular imports
from honcho_cli.main import _global_overrides
if workspace: