diff --git a/robot/helper_automation_profile_cli.py b/robot/helper_automation_profile_cli.py index d1a71ebca..05a26a1c6 100644 --- a/robot/helper_automation_profile_cli.py +++ b/robot/helper_automation_profile_cli.py @@ -149,7 +149,9 @@ def test_show_json() -> None: assert result.exit_code == 0, f"show json failed: {result.output}" parsed = _extract_json(result.output) assert isinstance(parsed, dict) - assert parsed["name"] == "manual" + # JSON output is wrapped in a CLI response envelope: {"data": {...}, ...} + data = parsed.get("data", parsed) + assert data["name"] == "manual" print("show-json-ok") @@ -186,9 +188,11 @@ def test_list_json() -> None: f"Expected dict at top level, got {type(parsed).__name__}. " f"Output: {result.output[:300]}" ) + # JSON output is wrapped in a CLI response envelope: {"data": {...}, ...} + inner = parsed.get("data", parsed) # Validate profiles wrapper - assert "profiles" in parsed, f"Missing 'profiles' key. Keys: {list(parsed.keys())}" - profiles = parsed["profiles"] + assert "profiles" in inner, f"Missing 'profiles' key. Keys: {list(inner.keys())}" + profiles = inner["profiles"] assert isinstance(profiles, list), ( f"'profiles' must be a list, got {type(profiles)}" ) @@ -202,8 +206,8 @@ def test_list_json() -> None: "Full profile dict leaked into list output (found 'phase_transitions')" ) # Validate summary - assert "summary" in parsed, f"Missing 'summary' key. Keys: {list(parsed.keys())}" - summary = parsed["summary"] + assert "summary" in inner, f"Missing 'summary' key. Keys: {list(inner.keys())}" + summary = inner["summary"] assert "built_in" in summary, f"Missing 'built_in' in summary: {summary}" assert "total" in summary, f"Missing 'total' in summary: {summary}" assert summary["built_in"] >= 8, ( diff --git a/robot/helper_config_cli.py b/robot/helper_config_cli.py index a207f274c..db341a3b2 100644 --- a/robot/helper_config_cli.py +++ b/robot/helper_config_cli.py @@ -109,7 +109,7 @@ def config_set_get_roundtrip() -> None: data = ( envelope.get("data", envelope) if isinstance(envelope, dict) else envelope ) - if data.get("source") in ("config_file", "global"): + if data.get("source") in ("config", "config_file", "global"): print("config-cli-set-get-roundtrip-ok") else: print(f"FAIL: unexpected source {data.get('source')}", file=sys.stderr) diff --git a/src/cleveragents/cli/commands/config.py b/src/cleveragents/cli/commands/config.py index 56d5f18ae..15df4caa9 100644 --- a/src/cleveragents/cli/commands/config.py +++ b/src/cleveragents/cli/commands/config.py @@ -12,6 +12,7 @@ from __future__ import annotations import fnmatch import json +import os import re import time from datetime import UTC, datetime @@ -48,7 +49,16 @@ from cleveragents.cli.renderers import _get_console app = typer.Typer(help="Manage configuration settings for CleverAgents.") console = _get_console() -_CONFIG_DIR = Path.home() / ".cleveragents" + +def _get_config_dir() -> Path: + """Return the config directory, respecting CLEVERAGENTS_HOME if set.""" + home_env = os.environ.get("CLEVERAGENTS_HOME", "").strip() + if home_env: + return Path(home_env) + return Path.home() / ".cleveragents" + + +_CONFIG_DIR = _get_config_dir() _CONFIG_PATH = _CONFIG_DIR / "config.toml" _FORMAT_HELP = "Output format: json, yaml, plain, table, or rich (default: rich)" @@ -56,9 +66,11 @@ _FORMAT_HELP = "Output format: json, yaml, plain, table, or rich (default: rich) def _get_service() -> ConfigService: """Return a ``ConfigService`` wired to the standard config paths.""" container = get_container() + config_dir = _get_config_dir() + config_path = config_dir / "config.toml" return ConfigService( - config_dir=_CONFIG_DIR, - config_path=_CONFIG_PATH, + config_dir=config_dir, + config_path=config_path, event_bus=container.event_bus(), ) diff --git a/src/cleveragents/cli/commands/plan.py b/src/cleveragents/cli/commands/plan.py index e025f1c14..2936d15a2 100644 --- a/src/cleveragents/cli/commands/plan.py +++ b/src/cleveragents/cli/commands/plan.py @@ -2945,6 +2945,14 @@ def lifecycle_list_plans( help="Filter by action name", ), ] = None, + namespace: Annotated[ + str | None, + typer.Option( + "--namespace", + "-n", + help="Filter by namespace (e.g. 'local', 'myteam')", + ), + ] = None, fmt: Annotated[ str, typer.Option( @@ -2964,6 +2972,7 @@ def lifecycle_list_plans( agents plan list --phase strategize agents plan list --state processing agents plan list "^myteam/" + agents plan list --namespace myteam """ try: import re @@ -3000,7 +3009,9 @@ def lifecycle_list_plans( ) raise typer.Abort() from exc - plans = service.list_plans(phase=phase_filter, project_name=project_id) + plans = service.list_plans( + namespace=namespace, phase=phase_filter, project_name=project_id + ) # Apply processing state filter if state_filter: @@ -3116,8 +3127,13 @@ def lifecycle_list_plans( else: active_filters.append("[yellow]Action:[/yellow] (any)") + if namespace: + active_filters.append(f"[yellow]Namespace:[/yellow] {namespace}") + else: + active_filters.append("[yellow]Namespace:[/yellow] (any)") + # Only show Filters panel if at least one filter is active - if phase_filter or state_filter or project_id or action_filter: + if phase_filter or state_filter or project_id or action_filter or namespace: filters_text = "\n".join(active_filters) filters_panel = Panel(filters_text, title="Filters", border_style="dim") console.print(filters_panel)