From 6f2b3ec8b47f9aa7b582bc22cd39c425eb2a101d Mon Sep 17 00:00:00 2001 From: CleverThis Date: Sun, 12 Apr 2026 19:19:52 +0000 Subject: [PATCH] fix(cli): restore context show rich panels ISSUES CLOSED: #6323 --- features/steps/project_context_cli_steps.py | 9 +- .../cli/commands/project_context.py | 97 +++++++++++++++++-- src/cleveragents/cli/formatting.py | 7 +- 3 files changed, 96 insertions(+), 17 deletions(-) diff --git a/features/steps/project_context_cli_steps.py b/features/steps/project_context_cli_steps.py index 80a32acb2..8362e15b7 100644 --- a/features/steps/project_context_cli_steps.py +++ b/features/steps/project_context_cli_steps.py @@ -7,11 +7,12 @@ CLI commands using an in-memory SQLite database. from __future__ import annotations import json -import yaml +from contextlib import suppress from io import StringIO from typing import Any from unittest.mock import MagicMock, patch +import yaml from behave import given, then, when # type: ignore[import-untyped] from sqlalchemy import create_engine from sqlalchemy.orm import Session, sessionmaker @@ -690,10 +691,8 @@ def step_issue6323_seed_usage(context: Any, project: str) -> None: svc._hot.clear() svc._warm.clear() svc._cold.clear() - try: - svc._budget.max_tokens_hot = 12_000 - except Exception: # pragma: no cover - fallback for unexpected attribute errors - pass + with suppress(Exception): + svc._budget.max_tokens_hot = 12_000 # pragma: no cover - fallback guard svc.store( TieredFragment( diff --git a/src/cleveragents/cli/commands/project_context.py b/src/cleveragents/cli/commands/project_context.py index 47fd942e1..4b3eb24ca 100644 --- a/src/cleveragents/cli/commands/project_context.py +++ b/src/cleveragents/cli/commands/project_context.py @@ -898,20 +898,31 @@ def context_show( command_name = "project context show" success_message = "Context policy loaded" + include_resources_display = ( + ", ".join(include_resources) if include_resources else "(all)" + ) + exclude_paths_display = ", ".join(exclude_paths) if exclude_paths else "(none)" + include_paths_display = ", ".join(include_paths) + exclude_resources_display = ", ".join(exclude_resources) + query_limit_display = str(query_limit) if query_limit is not None else "(no limit)" + summary_tokens_display = ( + str(summary_tokens) if summary_tokens is not None else "(auto)" + ) + if fmt == OutputFormat.RICH: policy_lines = [ f"[cyan]Project:[/cyan] {project}", f"[blue]View:[/blue] {active_view}", - f"[green]Include:[/green] {', '.join(include_resources) if include_resources else '(all)'}", - f"[yellow]Exclude:[/yellow] {', '.join(exclude_paths) if exclude_paths else '(none)'}", + f"[green]Include:[/green] {include_resources_display}", + f"[yellow]Exclude:[/yellow] {exclude_paths_display}", ] if include_paths: policy_lines.append( - f"[green]Include paths:[/green] {', '.join(include_paths)}" + f"[green]Include paths:[/green] {include_paths_display}" ) if exclude_resources: policy_lines.append( - f"[yellow]Exclude resources:[/yellow] {', '.join(exclude_resources)}" + f"[yellow]Exclude resources:[/yellow] {exclude_resources_display}" ) limits = structured_data["limits"] @@ -919,25 +930,80 @@ def context_show( f"[magenta]Hot Tokens:[/magenta] {limits['hot_max_tokens']:,}", f"[magenta]Warm Decisions:[/magenta] {limits['warm_max_decisions']:,}", f"[magenta]Cold Decisions:[/magenta] {limits['cold_max_decisions']:,}", - f"[cyan]Query Limit:[/cyan] {limits['query_limit'] if limits['query_limit'] is not None else '(no limit)'}", + f"[cyan]Query Limit:[/cyan] {query_limit_display}", f"[cyan]Max File Size:[/cyan] {limits['max_file_size']}", f"[cyan]Max Total Size:[/cyan] {limits['max_total_size']}", ] summarization = structured_data["summarization"] summarization_lines = [ - f"[green]Enabled:[/green] {'yes' if summarization['enabled'] else 'no'}", - f"[blue]Max Tokens:[/blue] {summarization['max_tokens'] if summarization['max_tokens'] is not None else '(auto)'}", + "[green]Enabled:[/green] yes" + if summarization["enabled"] + else "[green]Enabled:[/green] no", + f"[blue]Max Tokens:[/blue] {summary_tokens_display}", ] usage = structured_data["current_usage"] usage_lines = [ - f"[blue]Hot Context:[/blue] {usage['hot_context_tokens']:,} / {usage['hot_context_limit']:,}", - f"[blue]Warm Entries:[/blue] {usage['warm_entries']:,} / {usage['warm_limit']:,}", - f"[blue]Cold Entries:[/blue] {usage['cold_entries']:,} / {usage['cold_limit']:,}", + ( + f"[blue]Hot Context:[/blue] " + f"{usage['hot_context_tokens']:,} / {usage['hot_context_limit']:,}" + ), + ( + f"[blue]Warm Entries:[/blue] " + f"{usage['warm_entries']:,} / {usage['warm_limit']:,}" + ), + ( + f"[blue]Cold Entries:[/blue] " + f"{usage['cold_entries']:,} / {usage['cold_limit']:,}" + ), f"[green]Indexed Resources:[/green] {usage['indexed_resources']:,}", ] + raw_policy_blob = _load_policy_json(session_factory, project) or {} + execution_environment = raw_policy_blob.get("execution_environment") + env_priority = raw_policy_blob.get("execution_env_priority") + execution_lines: list[str] = [] + if execution_environment: + execution_lines.append(f"[cyan]Environment:[/cyan] {execution_environment}") + if env_priority: + execution_lines.append(f"[cyan]Priority:[/cyan] {env_priority}") + + temporal_scope = acms.get("temporal_scope", "current") + auto_refresh = "yes" if acms.get("auto_refresh", True) else "no" + strategies = acms.get("strategies") or [] + strategies_display = ", ".join(strategies) if strategies else "(default)" + default_breadth = acms.get("default_breadth", _DEFAULT_BREADTH) + default_depth = acms.get("default_depth", _DEFAULT_DEPTH) + depth_gradient = acms.get("depth_gradient") or {} + skeleton_ratio = acms.get("skeleton_ratio", _DEFAULT_SKELETON_RATIO) + + def _gradient_key(entry: tuple[Any, Any]) -> int | str: + hop, _ = entry + try: + return int(str(hop)) + except (TypeError, ValueError): + return str(hop) + + if depth_gradient: + gradient_parts = [ + f"{hop}:{value}" + for hop, value in sorted(depth_gradient.items(), key=_gradient_key) + ] + gradient_display = ", ".join(gradient_parts) + else: + gradient_display = "(none)" + + pipeline_lines = [ + f"[cyan]Temporal Scope:[/cyan] {temporal_scope}", + f"[cyan]Auto Refresh:[/cyan] {auto_refresh}", + f"[cyan]Strategies:[/cyan] {strategies_display}", + f"[cyan]Default Breadth:[/cyan] {default_breadth}", + f"[cyan]Default Depth:[/cyan] {default_depth}", + f"[cyan]Depth gradient:[/cyan] {gradient_display}", + f"[cyan]Skeleton Ratio:[/cyan] {skeleton_ratio}", + ] + console.print( Panel("\n".join(policy_lines), title="Context Policy", expand=False) ) @@ -948,6 +1014,17 @@ def context_show( console.print( Panel("\n".join(usage_lines), title="Current Usage", expand=False) ) + if execution_lines: + console.print( + Panel( + "\n".join(execution_lines), + title="Execution Environment", + expand=False, + ) + ) + console.print( + Panel("\n".join(pipeline_lines), title="ACMS Pipeline", expand=False) + ) console.print("[green]✓ OK[/green] Context policy loaded") return diff --git a/src/cleveragents/cli/formatting.py b/src/cleveragents/cli/formatting.py index bd679982b..f502e7b5c 100644 --- a/src/cleveragents/cli/formatting.py +++ b/src/cleveragents/cli/formatting.py @@ -15,10 +15,11 @@ from __future__ import annotations import json import time +from collections.abc import Mapping, Sequence from datetime import datetime from enum import Enum, StrEnum from io import StringIO -from typing import Any, Mapping, Sequence +from typing import Any import yaml from rich.console import Console @@ -315,7 +316,9 @@ def format_output( rendered = _format_plain(safe_data) message_lines = _format_plain_messages(plain_pairs) if message_lines: - rendered = (rendered + "\n" if rendered else "") + "\n".join(message_lines) + prefix = f"{rendered}\n" if rendered else "" + message_block = "\n".join(message_lines) + rendered = f"{prefix}{message_block}" sys.stdout.write(rendered + "\n") sys.stdout.flush() return ""