feat(system): stderr routing migration Phase 3 — 10 branches migrated (FPLAN-0033)
Migrated error()/warning() calls across 10 branches to use CLI display API which routes to stderr. 48 files modified across ai_mail, api, backup, daemon, flow, memory, prax, seedgo, spawn, trigger. Seedgo stderr_routing standard created (24th standard) with full-file scanning. System average 49% → 69%. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
154e92d13e
commit
97952124b5
@@ -33,7 +33,7 @@ _UPDATE_SECTION = None # type: ignore
|
||||
from aipass.prax.apps.modules.logger import system_logger as logger
|
||||
|
||||
# CLI services for display
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error
|
||||
|
||||
# =============================================================================
|
||||
# CONSTANTS & CONFIG
|
||||
@@ -228,14 +228,14 @@ def main():
|
||||
remaining_args = args[1:] if len(args) > 1 else []
|
||||
|
||||
if not modules:
|
||||
console.print("❌ ERROR: No modules found")
|
||||
error("No modules found")
|
||||
return 1
|
||||
|
||||
# Route command
|
||||
if route_command(command, remaining_args, modules):
|
||||
return 0
|
||||
else:
|
||||
console.print(f"❌ ERROR: Unknown command: {command}")
|
||||
error(f"Unknown command: {command}")
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -21,7 +21,7 @@ from typing import List
|
||||
from aipass.prax.apps.modules.logger import system_logger as logger
|
||||
|
||||
# CLI services for formatting
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error
|
||||
from rich.panel import Panel
|
||||
|
||||
# Import handlers
|
||||
@@ -65,7 +65,7 @@ def handle_ping(verbose: bool = False) -> bool:
|
||||
except Exception as e:
|
||||
logger.error(f"Ping failed: {e}")
|
||||
if verbose:
|
||||
console.print(f"Ping failed: {e}")
|
||||
error(f"Ping failed: {e}")
|
||||
return False
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ def handle_status() -> bool:
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Status check failed: {e}")
|
||||
console.print(f"Error getting status: {e}")
|
||||
error(f"Error getting status: {e}")
|
||||
return False
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ def handle_registry() -> bool:
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Registry read failed: {e}")
|
||||
console.print(f"Error reading registry: {e}")
|
||||
error(f"Error reading registry: {e}")
|
||||
return False
|
||||
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
from aipass.prax.apps.modules.logger import system_logger as logger
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error
|
||||
from aipass.ai_mail.apps.handlers.dispatch.status import (
|
||||
load_dispatch_log,
|
||||
check_pid_status,
|
||||
@@ -93,7 +93,7 @@ def handle_command(command: str, args: List[str]) -> bool:
|
||||
elif subcommand == "wake":
|
||||
return _orchestrate_wake(args[1:])
|
||||
else:
|
||||
console.print(f"[red]Unknown dispatch subcommand: {subcommand}[/red]")
|
||||
error(f"Unknown dispatch subcommand: {subcommand}")
|
||||
print_help()
|
||||
return False
|
||||
|
||||
@@ -175,7 +175,7 @@ def _orchestrate_wake(args: List[str]) -> bool:
|
||||
i += 1
|
||||
|
||||
if not filtered:
|
||||
console.print("[red]Missing branch argument[/red]")
|
||||
error("Missing branch argument")
|
||||
return False
|
||||
|
||||
branch_email = filtered[0]
|
||||
|
||||
@@ -28,7 +28,7 @@ _AI_MAIL_DIR = Path(__file__).resolve().parents[2]
|
||||
_REPO_ROOT = _AI_MAIL_DIR.parents[2]
|
||||
|
||||
from aipass.prax import logger
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error, success
|
||||
from aipass.trigger.apps.modules.core import trigger
|
||||
|
||||
# Handlers - business logic providers
|
||||
@@ -120,7 +120,7 @@ def handle_send(args: List[str]) -> bool:
|
||||
parsed = parse_send_args(args)
|
||||
|
||||
if parsed["mode"] == "error":
|
||||
console.print(f"[red]{parsed['error']}[/red]")
|
||||
error(parsed['error'])
|
||||
console.print(" Multiple: send @branch1 @branch2 \"Subject\" \"Message\"")
|
||||
return False
|
||||
|
||||
@@ -204,7 +204,7 @@ def _send_direct(to_branch, subject, message, auto_execute=False,
|
||||
pass
|
||||
return True
|
||||
else:
|
||||
console.print(f"[red]Failed to deliver: {error_msg}[/red]")
|
||||
error(f"Failed to deliver: {error_msg}")
|
||||
dispatch_send_error(to_branch, subject, error_msg, deliver_email_to_branch)
|
||||
return False
|
||||
except BrokenPipeError:
|
||||
@@ -212,7 +212,7 @@ def _send_direct(to_branch, subject, message, auto_execute=False,
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"[email] Send failed: {e}")
|
||||
console.print(f"[red]Error: {e}[/red]")
|
||||
error(f"Error: {e}")
|
||||
dispatch_send_error(to_branch, subject, str(e), deliver_email_to_branch)
|
||||
return False
|
||||
|
||||
@@ -226,10 +226,13 @@ def _send_broadcast(subject, message, user_info, auto_execute, no_memory_save, r
|
||||
branches, create_email_file, load_email_file, deliver_email_to_branch,
|
||||
_delivery_callback, log_operation, update_central)
|
||||
if isinstance(results, str):
|
||||
console.print("[red]Failed to load email file for broadcast[/red]")
|
||||
error("Failed to load email file for broadcast")
|
||||
return False
|
||||
for name, success, err in results:
|
||||
console.print(f" {'[green]OK[/green]' if success else '[red]FAIL[/red]'} {name}" + (f" ({err})" if not success else ""))
|
||||
for name, ok, err in results:
|
||||
if ok:
|
||||
console.print(f" [green]OK[/green] {name}")
|
||||
else:
|
||||
error(f"FAIL {name} ({err})")
|
||||
console.print(f"\nBroadcast complete: {success_count}/{total} delivered")
|
||||
return ok
|
||||
|
||||
@@ -241,7 +244,7 @@ def handle_inbox(args: List[str]) -> bool:
|
||||
first_arg = args[0] if args else None
|
||||
ok, info = resolve_inbox_target(first_arg, _REPO_ROOT, get_branch_by_email, get_current_user)
|
||||
if not ok:
|
||||
console.print(f"[red]{info['error']}[/red]")
|
||||
error(info['error'])
|
||||
return False
|
||||
|
||||
inbox_file = info["inbox_file"]
|
||||
@@ -269,7 +272,7 @@ def handle_inbox(args: List[str]) -> bool:
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"[email] Inbox view failed: {e}")
|
||||
console.print(f"[red]Error: {e}[/red]")
|
||||
error(f"Error: {e}")
|
||||
return False
|
||||
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ from typing import Any, List
|
||||
from aipass.prax.apps.modules.logger import system_logger as logger
|
||||
|
||||
# CLI services for formatted output
|
||||
from aipass.cli.apps.modules import console, header
|
||||
from aipass.cli.apps.modules import console, header, error
|
||||
from rich.panel import Panel
|
||||
from rich.table import Table
|
||||
from rich.columns import Columns
|
||||
@@ -102,9 +102,7 @@ def print_introspection():
|
||||
modules = discover_modules()
|
||||
|
||||
if not modules:
|
||||
console.print("[red]No modules discovered[/red]")
|
||||
console.print()
|
||||
console.print("[dim]Run 'python3 api.py --help' for usage information[/dim]")
|
||||
error("No modules discovered", suggestion="Run 'python3 api.py --help' for usage information")
|
||||
console.print()
|
||||
return
|
||||
|
||||
@@ -254,9 +252,7 @@ def main():
|
||||
|
||||
if not modules:
|
||||
logger.error("No modules found")
|
||||
console.print()
|
||||
console.print("[red]ERROR: No modules found[/red]")
|
||||
console.print()
|
||||
error("No modules found")
|
||||
return 1
|
||||
|
||||
# Extract command and remaining args (matching seed pattern)
|
||||
@@ -274,11 +270,7 @@ def main():
|
||||
return 0
|
||||
else:
|
||||
logger.warning(f"Unknown command: {command}")
|
||||
console.print()
|
||||
console.print(f"[red]ERROR: Unknown command: {command}[/red]")
|
||||
console.print()
|
||||
console.print("Run [dim]python3 api.py --help[/dim] for available commands")
|
||||
console.print()
|
||||
error(f"Unknown command: {command}", suggestion="Run 'python3 api.py --help' for available commands")
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -19,7 +19,7 @@ import argparse
|
||||
from pathlib import Path
|
||||
from typing import Any, List
|
||||
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error
|
||||
from aipass.prax import logger
|
||||
|
||||
# Explicit module imports (replaces dynamic discover_modules)
|
||||
@@ -172,7 +172,7 @@ def main():
|
||||
modules = get_modules()
|
||||
|
||||
if not modules:
|
||||
console.print("❌ ERROR: No modules found")
|
||||
error("No modules found")
|
||||
return 1
|
||||
|
||||
# 5. Handle 'all' command: snapshot → versioned → drive-sync
|
||||
@@ -187,7 +187,7 @@ def main():
|
||||
force=args.force, test=False, limit=0
|
||||
)
|
||||
if not route_command(snapshot_args, modules):
|
||||
console.print("[red]Snapshot failed - aborting[/red]")
|
||||
error("Snapshot failed - aborting")
|
||||
return 1
|
||||
|
||||
console.print()
|
||||
@@ -201,7 +201,7 @@ def main():
|
||||
force=args.force, test=False, limit=0
|
||||
)
|
||||
if not route_command(versioned_args, modules):
|
||||
console.print("[red]Versioned backup failed - aborting[/red]")
|
||||
error("Versioned backup failed - aborting")
|
||||
return 1
|
||||
|
||||
console.print()
|
||||
@@ -215,7 +215,7 @@ def main():
|
||||
force=args.force, test=False, limit=args.limit
|
||||
)
|
||||
if not route_command(sync_args, modules):
|
||||
console.print("[red]Drive sync failed[/red]")
|
||||
error("Drive sync failed")
|
||||
return 1
|
||||
|
||||
console.print()
|
||||
|
||||
@@ -22,7 +22,7 @@ from typing import List, Any
|
||||
from aipass.prax.apps.modules.logger import system_logger as logger
|
||||
|
||||
# Console
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error
|
||||
|
||||
def _header(text):
|
||||
console.print(f"\n[bold cyan]{'='*70}[/bold cyan]")
|
||||
@@ -186,9 +186,7 @@ def main():
|
||||
return 0
|
||||
else:
|
||||
console.print()
|
||||
console.print(f"[red]Unknown command: {command}[/red]")
|
||||
console.print()
|
||||
console.print("Run [dim]daemon --help[/dim] for available commands")
|
||||
error(f"Unknown command: {command}", suggestion="Run 'daemon --help' for available commands")
|
||||
console.print()
|
||||
return 1
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ from typing import List
|
||||
|
||||
from aipass.prax import logger
|
||||
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error as cli_error
|
||||
|
||||
def _header(text):
|
||||
console.print(f"\n[bold cyan]{'='*70}[/bold cyan]")
|
||||
@@ -31,7 +31,7 @@ def _success(text):
|
||||
console.print(f"[green]OK:[/green] {text}")
|
||||
|
||||
def _error(text):
|
||||
console.print(f"[red]ERROR:[/red] {text}")
|
||||
cli_error(text)
|
||||
|
||||
from aipass.daemon.apps.handlers.actions.actions_registry import (
|
||||
list_actions,
|
||||
|
||||
@@ -21,7 +21,7 @@ from typing import List
|
||||
|
||||
from aipass.prax import logger
|
||||
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error as cli_error
|
||||
|
||||
def _header(text):
|
||||
console.print(f"\n[bold cyan]{'='*70}[/bold cyan]")
|
||||
@@ -32,7 +32,7 @@ def _success(text):
|
||||
console.print(f"[green]OK:[/green] {text}")
|
||||
|
||||
def _error(text):
|
||||
console.print(f"[red]ERROR:[/red] {text}")
|
||||
cli_error(text)
|
||||
|
||||
# Handler imports
|
||||
from aipass.daemon.apps.handlers.schedule.task_registry import (
|
||||
@@ -178,7 +178,7 @@ def _handle_create(args: List[str]) -> bool:
|
||||
try:
|
||||
parsed = parser.parse_args(args)
|
||||
except SystemExit:
|
||||
console.print("[red]Usage: schedule create \"task\" --due <date> --to @branch [--message \"details\"][/red]")
|
||||
_error('Usage: schedule create "task" --due <date> --to @branch [--message "details"]')
|
||||
return False
|
||||
|
||||
if not parsed.task:
|
||||
|
||||
@@ -19,7 +19,7 @@ from typing import Dict, Any, List
|
||||
|
||||
from aipass.prax import logger
|
||||
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error
|
||||
|
||||
def _header(text):
|
||||
console.print(f"\n[bold cyan]{'='*70}[/bold cyan]")
|
||||
@@ -163,7 +163,7 @@ def handle_command(command: str, args: list) -> bool:
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[DAEMON] Error generating update digest: {e}", exc_info=True)
|
||||
console.print(f"[red]Error: {e}[/red]")
|
||||
error(f"Error: {e}")
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ Parent plan: `AIPass/DPLAN-003_aipass_as_operating_system_2026-03-13.md`
|
||||
|------|---------|--------|
|
||||
| `registry_discovery_map.md` | Every find_registry() call in src/aipass/ | Done |
|
||||
| `portability_audit.md` | Full investigation results (session 24) | Done |
|
||||
| `credential_model.md` | Registry credential design (UUID now, macaroons later) | Active |
|
||||
| `registry_refactor_plan.md` | Shared function design, migration steps | Pending |
|
||||
| `aipass_init_spec.md` | What `aipass init` creates, CLI design, templates | Pending |
|
||||
| `drone_help_spec.md` | `drone aipass help` module design | Pending |
|
||||
| `credential_model.md` | Registry credential design (UUID now, macaroons later) | Stage 1 Done |
|
||||
| `registry_refactor_plan.md` | Shared find_registry() design, migration steps | Pending |
|
||||
| `aipass_init_spec.md` | CLI owns init — `drone @cli aipass init` (entry point later) | Pending |
|
||||
| `aipass_help_spec.md` | CLI owns help — `drone @cli aipass help` (entry point later) | Pending |
|
||||
|
||||
@@ -152,3 +152,5 @@ Discovered during testing. Reference for future work.
|
||||
- **2026-03-14:** Drone verification added (`_verify_registry_credential` in load_registry). Drone dispatched and fixed error propagation — new `RegistryMismatchError` class separates "not found" (fallback OK) from "mismatch" (hard error). Spawn templates updated with `{{REGISTRY_ID}}` placeholder.
|
||||
- **2026-03-14:** Stage 1 complete. Credential model is end-to-end: init creates credentialed registries, drone verifies on load, spawn injects into new passports, mismatch = hard error. Remaining: drone CLI stderr surfacing (DPLAN-0031), `aipass init` CLI command (future).
|
||||
- **2026-03-14:** FPLAN-0032 executed — CLI stderr standardization Phase 1+2 done. CLI owns err_console, error()/warning() go to stderr. Drone imports from CLI instead of creating its own Console(stderr=True). Credential mismatch errors now properly surface to terminal via stderr. The stderr issue that blocked error visibility during DPLAN-003 testing is resolved.
|
||||
- **2026-03-14:** CLI confirmed as owner of credential model + project commands. CLI owns: `aipass init` (create project), `aipass help` (project help), display API (error/warning/fatal/err_console). Drone owns verification (registry_handler). Spawn owns injection (passport templates). Access via `drone @cli aipass init/help` for now; `aipass init` entry point is future icing.
|
||||
- **2026-03-14:** Seedgo `stderr_routing` standard created (24th standard). Checker bug found — was scanning all files but not displaying module/handler results. Seedgo fixed display + aggregation. Agent-verified across 3 branches. FPLAN-0033 parked for Phase 3 migration (343 error prints, 10 branches).
|
||||
|
||||
@@ -27,6 +27,20 @@
|
||||
"subject": "Build STATUS board — per-branch STATUS.local.md with Prax-managed central aggregation",
|
||||
"date_closed": "2026-03-10",
|
||||
"location": "flow"
|
||||
},
|
||||
{
|
||||
"plan_id": "FPLAN-0032",
|
||||
"type": "FPLAN",
|
||||
"subject": "CLI stderr standardization — Phase 1 add err_console to display.py",
|
||||
"date_closed": "2026-03-14",
|
||||
"location": "flow"
|
||||
},
|
||||
{
|
||||
"plan_id": "FPLAN-0031",
|
||||
"type": "FPLAN",
|
||||
"subject": "Drone stderr and error propagation — investigation and long-term fix",
|
||||
"date_closed": "2026-03-14",
|
||||
"location": "flow"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
from aipass.prax.apps.modules.logger import system_logger as logger
|
||||
|
||||
# CLI services for formatted output
|
||||
from aipass.cli.apps.modules import console, header, success, error
|
||||
from aipass.cli.apps.modules import console, header, success, error, warning
|
||||
|
||||
# =============================================================================
|
||||
# MODULE DISCOVERY
|
||||
@@ -151,7 +151,7 @@ def main():
|
||||
return 0
|
||||
else:
|
||||
console.print()
|
||||
console.print(f"[red]Unknown command: {command}[/red]")
|
||||
error(f"Unknown command: {command}")
|
||||
console.print()
|
||||
console.print("Run [dim]drone @flow --help[/dim] for available commands")
|
||||
console.print()
|
||||
@@ -287,7 +287,7 @@ def print_module_help(command: str, modules: List[Any]):
|
||||
|
||||
if not target_module:
|
||||
console.print()
|
||||
console.print(f"[red]Unknown command: {command}[/red]")
|
||||
error(f"Unknown command: {command}")
|
||||
console.print()
|
||||
console.print("Run [dim]drone @flow --help[/dim] for available commands")
|
||||
console.print()
|
||||
|
||||
@@ -34,7 +34,7 @@ from aipass.prax import logger
|
||||
from aipass.flow.apps.handlers.json import json_handler
|
||||
|
||||
# CLI services for display and error handling
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error, warning
|
||||
|
||||
# Internal: Registry handlers
|
||||
from aipass.flow.apps.handlers.registry.load_registry import load_registry
|
||||
@@ -90,7 +90,7 @@ def _display_messages(messages: List[Dict[str, Any]]):
|
||||
console.print(format_plan_error(error_text, plan_num, details=details))
|
||||
|
||||
elif msg_type == "warning":
|
||||
console.print(f"[yellow]{msg['text']}[/yellow]")
|
||||
warning(msg['text'])
|
||||
|
||||
elif msg_type == "dim":
|
||||
console.print(f"[dim]{msg['text']}[/dim]")
|
||||
@@ -102,7 +102,7 @@ def _display_messages(messages: List[Dict[str, Any]]):
|
||||
console.print(f"[green]{msg['text']}[/green]")
|
||||
|
||||
elif msg_type == "error_text":
|
||||
console.print(f"[red]{msg['text']}[/red]")
|
||||
error(msg['text'])
|
||||
|
||||
elif msg_type == "header":
|
||||
console.print(format_plan_deletion_header(msg["plan_key"], msg["plan_info"]))
|
||||
@@ -119,7 +119,7 @@ def _display_messages(messages: List[Dict[str, Any]]):
|
||||
console.print(f" * FPLAN-{plan['plan_num']}: {plan['subject']}")
|
||||
|
||||
elif msg_type == "confirm_warning":
|
||||
console.print(f"\n[bold red]WARNING: This will close all {msg['count']} plans![/bold red]")
|
||||
error(f"WARNING: This will close all {msg['count']} plans!")
|
||||
|
||||
elif msg_type == "closing_all":
|
||||
console.print(f"\n[bold]Closing all {msg['count']} plan(s)...[/bold]")
|
||||
|
||||
@@ -44,7 +44,7 @@ from aipass.prax.apps.modules.logger import system_logger as logger
|
||||
from aipass.flow.apps.handlers.json import json_handler
|
||||
|
||||
# CLI services for display
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, warning
|
||||
|
||||
# Registry handlers (cross-domain - OK for modules)
|
||||
from aipass.flow.apps.handlers.registry.load_registry import load_registry
|
||||
@@ -195,7 +195,7 @@ def create_plan(
|
||||
if msg_type == "dim":
|
||||
console.print(f"[dim]{msg['text']}[/dim]")
|
||||
elif msg_type == "warning":
|
||||
console.print(f"[yellow]{msg['text']}[/yellow]")
|
||||
warning(msg['text'])
|
||||
elif msg_type == "display":
|
||||
console.print(msg["text"])
|
||||
return ok, num, loc, tmpl, error
|
||||
|
||||
@@ -20,7 +20,7 @@ from typing import List
|
||||
|
||||
# Infrastructure imports (module does the logging)
|
||||
from aipass.prax.apps.modules.logger import system_logger as logger
|
||||
from aipass.cli.apps.modules import console, header, success, error
|
||||
from aipass.cli.apps.modules import console, header, success, error, warning
|
||||
|
||||
# Handler imports (local handlers in handlers/dplan/)
|
||||
from aipass.flow.apps.handlers.dplan.create import create_plan
|
||||
@@ -458,7 +458,7 @@ def _handle_close(args: List[str]) -> bool:
|
||||
console.print(f"[dim] Memory Bank archival running in background[/dim]")
|
||||
except Exception as e:
|
||||
logger.warning(f"[dev_flow] Failed to spawn background processing: {e}")
|
||||
console.print(f"[yellow] Background archival failed to start - will retry on next close[/yellow]")
|
||||
warning("Background archival failed to start - will retry on next close")
|
||||
|
||||
# Step 3/3: Done
|
||||
console.print(f"[dim][3/3][/dim] Finalizing...")
|
||||
@@ -476,7 +476,7 @@ def _handle_close_all() -> bool:
|
||||
open_plans = get_open_plans()
|
||||
|
||||
if not open_plans:
|
||||
console.print("\n[yellow]No open plans to close[/yellow]\n")
|
||||
warning("No open plans to close")
|
||||
return True
|
||||
|
||||
console.print(f"\n[bold yellow]Found {len(open_plans)} open plan(s) to close:[/bold yellow]")
|
||||
@@ -511,7 +511,7 @@ def _handle_close_all() -> bool:
|
||||
else:
|
||||
failure_count += 1
|
||||
logger.warning(f"[dev_flow] Failed to close DPLAN-{p['number']:03d}: {err}")
|
||||
console.print(f"[red] Failed: {err}[/red]")
|
||||
error(f" Failed: {err}")
|
||||
|
||||
# Push dashboard
|
||||
try:
|
||||
@@ -530,7 +530,7 @@ def _handle_close_all() -> bool:
|
||||
console.print(f"\n[dim]Background processing started for {success_count} plan(s)[/dim]")
|
||||
except Exception as e:
|
||||
logger.warning(f"[dev_flow] Failed to spawn background processing: {e}")
|
||||
console.print(f"\n[yellow]Background processing failed to start[/yellow]")
|
||||
warning("Background processing failed to start")
|
||||
|
||||
console.print("\n" + "=" * 60)
|
||||
console.print("[bold green]CLOSE ALL COMPLETE[/bold green]")
|
||||
|
||||
@@ -25,7 +25,7 @@ from pathlib import Path
|
||||
FLOW_ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
# External: CLI console (Rich display) and Prax logger
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error, warning
|
||||
from aipass.prax.apps.modules.logger import system_logger as logger
|
||||
|
||||
MODULE_NAME = "dplan_post_close_runner"
|
||||
@@ -58,7 +58,7 @@ def handle_command(command: str, args: list) -> bool:
|
||||
|
||||
# Run the post-close processing directly (foreground)
|
||||
if not _acquire_lock():
|
||||
console.print("[yellow]Another instance is already running[/yellow]")
|
||||
warning("Another instance is already running")
|
||||
return True
|
||||
|
||||
try:
|
||||
@@ -66,7 +66,7 @@ def handle_command(command: str, args: list) -> bool:
|
||||
console.print(f"[green]Processing complete:[/green] {result.get('processed', 0)} processed, {result.get('errors', 0)} errors")
|
||||
except Exception as e:
|
||||
logger.error(f"[{MODULE_NAME}] Background processing failed: {e}")
|
||||
console.print(f"[red]Processing failed: {e}[/red]")
|
||||
error(f"Processing failed: {e}")
|
||||
finally:
|
||||
_release_lock()
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ from aipass.prax.apps.modules.logger import system_logger as logger
|
||||
from aipass.flow.apps.handlers.json import json_handler
|
||||
|
||||
# CLI services for display
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error, warning
|
||||
|
||||
# Registry handlers
|
||||
from aipass.flow.apps.handlers.registry.load_registry import load_registry
|
||||
@@ -149,13 +149,13 @@ def list_plans(filter_type: str = "open") -> bool:
|
||||
|
||||
# Module handles display
|
||||
if result.get("empty") and result.get("success"):
|
||||
console.print("[yellow]No plans found in registry[/yellow]")
|
||||
warning("No plans found in registry")
|
||||
return True
|
||||
|
||||
if not result.get("success"):
|
||||
error_msg = result.get("error", "Unknown error")
|
||||
try:
|
||||
console.print(f"[red]ERROR: {error_msg}[/red]")
|
||||
error(f"ERROR: {error_msg}")
|
||||
except BrokenPipeError:
|
||||
pass
|
||||
return False
|
||||
@@ -204,7 +204,7 @@ def handle_command(command: str, args: List[str]) -> bool:
|
||||
if filter_arg in ["open", "closed", "all"]:
|
||||
filter_type = filter_arg
|
||||
else:
|
||||
console.print(f"[yellow]Unknown filter '{filter_arg}', defaulting to 'open'[/yellow]")
|
||||
warning(f"Unknown filter '{filter_arg}', defaulting to 'open'")
|
||||
console.print("[dim]Valid filters: open, closed, all[/dim]")
|
||||
|
||||
# STEP 2: Execute workflow
|
||||
|
||||
@@ -28,7 +28,7 @@ _PKG_ROOT = Path(__file__).resolve().parents[3] # file.py → modules/ → apps
|
||||
FLOW_ROOT = _PKG_ROOT / "flow"
|
||||
|
||||
# External: CLI console (Rich display) and Prax logger
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error, warning
|
||||
from aipass.prax.apps.modules.logger import system_logger as logger
|
||||
|
||||
MODULE_NAME = "post_close_runner"
|
||||
@@ -63,7 +63,7 @@ def handle_command(command: str, args: list) -> bool:
|
||||
|
||||
# Run the post-close processing directly (foreground)
|
||||
if not _acquire_lock():
|
||||
console.print("[yellow]Another instance is already running[/yellow]")
|
||||
warning("Another instance is already running")
|
||||
return True
|
||||
|
||||
try:
|
||||
@@ -71,7 +71,7 @@ def handle_command(command: str, args: list) -> bool:
|
||||
console.print("[green]Processing complete[/green]")
|
||||
except Exception as e:
|
||||
logger.error(f"[{MODULE_NAME}] Background processing failed: {e}")
|
||||
console.print(f"[red]Processing failed: {e}[/red]")
|
||||
error(f"Processing failed: {e}")
|
||||
finally:
|
||||
_release_lock()
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ from aipass.prax.apps.modules.logger import system_logger as logger
|
||||
from aipass.flow.apps.handlers.json import json_handler
|
||||
|
||||
# CLI services for display
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error, warning
|
||||
|
||||
# Registry handlers
|
||||
from aipass.flow.apps.handlers.registry.load_registry import load_registry
|
||||
@@ -115,11 +115,11 @@ def start_monitoring():
|
||||
# Module handles display
|
||||
status = result.get("status", "")
|
||||
if status == "already_running":
|
||||
console.print("[yellow]Monitor is already running[/yellow]")
|
||||
warning("Monitor is already running")
|
||||
elif status == "started":
|
||||
console.print(f"[green]OK[/green] {result['message']}")
|
||||
elif status == "error":
|
||||
console.print(f"[red]{result['message']}[/red]")
|
||||
error(result['message'])
|
||||
return result.get("success", False)
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ def stop_monitoring():
|
||||
if status == "stopped":
|
||||
console.print("[green]OK[/green] Monitor stopped")
|
||||
elif status == "not_running":
|
||||
console.print("[yellow]Monitor is not running[/yellow]")
|
||||
warning("Monitor is not running")
|
||||
return result.get("success", False)
|
||||
|
||||
|
||||
@@ -252,7 +252,7 @@ def handle_command(command: str, args: List[str]) -> bool:
|
||||
return True
|
||||
|
||||
else:
|
||||
console.print(f"[red]Unknown subcommand: {subcommand}[/red]")
|
||||
error(f"Unknown subcommand: {subcommand}")
|
||||
console.print()
|
||||
console.print("Available commands:")
|
||||
console.print(" • scan - One-time scan and heal registry")
|
||||
|
||||
@@ -33,7 +33,7 @@ from aipass.prax.apps.modules.logger import system_logger as logger
|
||||
from aipass.flow.apps.handlers.json import json_handler
|
||||
|
||||
# CLI services for display and error handling
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, warning
|
||||
|
||||
# Internal: Registry handlers
|
||||
from aipass.flow.apps.handlers.registry.load_registry import load_registry
|
||||
@@ -84,7 +84,7 @@ def _display_messages(messages: List[Dict[str, Any]]):
|
||||
console.print(format_restore_error(error_type, plan_key, details=details))
|
||||
|
||||
elif msg_type == "warning":
|
||||
console.print(f"[yellow]{msg['text']}[/yellow]")
|
||||
warning(msg['text'])
|
||||
|
||||
elif msg_type == "dim":
|
||||
console.print(f"[dim]{msg['text']}[/dim]")
|
||||
|
||||
@@ -30,7 +30,7 @@ from rich import box
|
||||
from rich.table import Table
|
||||
|
||||
from aipass.prax import logger
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error
|
||||
|
||||
# =============================================================================
|
||||
# INFRASTRUCTURE SETUP
|
||||
@@ -55,7 +55,7 @@ def print_introspection():
|
||||
# Discover modules
|
||||
modules = discover_modules()
|
||||
|
||||
console.print(f"[yellow]Discovered Modules:[/yellow] {len(modules)}")
|
||||
console.print(f"[bold cyan]Discovered Modules:[/bold cyan] {len(modules)}")
|
||||
console.print()
|
||||
|
||||
for module in modules:
|
||||
@@ -123,12 +123,12 @@ def print_help():
|
||||
|
||||
console.print("[bold cyan]USAGE:[/bold cyan]")
|
||||
console.print()
|
||||
console.print(" [yellow]Via Drone (recommended):[/yellow]")
|
||||
console.print(" [dim]drone @memory search \"error handling\"[/dim]")
|
||||
console.print(" [bold]Via Drone (recommended):[/bold]")
|
||||
console.print(" [dim]drone @memory search \"performance patterns\"[/dim]")
|
||||
console.print(" [dim]drone @memory status[/dim]")
|
||||
console.print(" [dim]drone @memory rollover[/dim]")
|
||||
console.print()
|
||||
console.print(" [yellow]Direct execution:[/yellow]")
|
||||
console.print(" [bold]Direct execution:[/bold]")
|
||||
console.print(" [dim]python3 -m aipass.memory.apps.memory search \"query\"[/dim]")
|
||||
console.print(" [dim]python3 -m aipass.memory.apps.memory rollover[/dim]")
|
||||
console.print()
|
||||
@@ -141,7 +141,7 @@ def print_help():
|
||||
console.print(" [cyan]--type TYPE[/cyan] Filter by memory type (observations, local)")
|
||||
console.print(" [cyan]--n N[/cyan] Number of results (default: 5)")
|
||||
console.print()
|
||||
console.print(" [yellow]Example:[/yellow]")
|
||||
console.print(" [bold]Example:[/bold]")
|
||||
console.print(" [dim]drone @memory search \"registry bugs\" --branch SEED --n 10[/dim]")
|
||||
console.print()
|
||||
console.print("-" * 70)
|
||||
@@ -149,7 +149,7 @@ def print_help():
|
||||
|
||||
console.print("[bold cyan]WATCH MODE:[/bold cyan]")
|
||||
console.print()
|
||||
console.print(" [yellow]Start memory file watcher:[/yellow]")
|
||||
console.print(" [bold]Start memory file watcher:[/bold]")
|
||||
console.print(" [dim]drone @memory watch[/dim]")
|
||||
console.print(" [dim]Monitors all branches, auto-rolls when limit exceeded[/dim]")
|
||||
console.print(" [dim]Press Ctrl+C to stop[/dim]")
|
||||
@@ -275,7 +275,7 @@ def start_watch() -> None:
|
||||
result = start_memory_watcher()
|
||||
|
||||
if not result.get('success'):
|
||||
console.print(f"[red]x Failed to start watcher: {result.get('error')}[/red]")
|
||||
error(f"Failed to start watcher: {result.get('error')}")
|
||||
return
|
||||
|
||||
console.print(f"[green]>[/green] Watching {result.get('count', 0)} branch directories")
|
||||
@@ -332,9 +332,7 @@ def main():
|
||||
return # Module handled it successfully
|
||||
else:
|
||||
console.print()
|
||||
console.print(f"[red]Unknown command: {command}[/red]")
|
||||
console.print()
|
||||
console.print("Run [dim]python3 -m aipass.memory.apps.memory --help[/dim] for available commands")
|
||||
error(f"Unknown command: {command}", suggestion="Run 'python3 -m aipass.memory.apps.memory --help' for available commands")
|
||||
console.print()
|
||||
return
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ from rich.panel import Panel
|
||||
from rich import box
|
||||
|
||||
from aipass.prax import logger
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error, warning
|
||||
|
||||
# =============================================================================
|
||||
# INFRASTRUCTURE SETUP
|
||||
@@ -139,7 +139,7 @@ def run_rollover() -> bool:
|
||||
result = _handler_execute_rollover()
|
||||
|
||||
if not result.get('success') and result.get('error'):
|
||||
console.print(f"[red]x[/red] {result['error']}")
|
||||
error(result['error'])
|
||||
return False
|
||||
|
||||
triggers_count = result.get('triggers_count', 0)
|
||||
@@ -168,9 +168,8 @@ def run_rollover() -> bool:
|
||||
|
||||
if failed:
|
||||
console.print()
|
||||
console.print("[red]Failed operations:[/red]")
|
||||
for fail in failed:
|
||||
console.print(f" [red]x[/red] {fail['trigger']} - {fail['stage']}: {fail['error']}")
|
||||
error(f"{fail['trigger']} - {fail['stage']}: {fail['error']}")
|
||||
|
||||
return success_count > 0
|
||||
|
||||
@@ -201,11 +200,11 @@ def sync_line_counts() -> None:
|
||||
if result['success']:
|
||||
console.print(f"[green]>[/green] Updated {result['updated']} files")
|
||||
if result['failed'] > 0:
|
||||
console.print(f"[yellow]![/yellow] {result['failed']} files failed:")
|
||||
for branch, mem_type, error in result.get('failures', []):
|
||||
console.print(f" [red]x[/red] {branch}.{mem_type}: {error}")
|
||||
warning(f"{result['failed']} files failed")
|
||||
for branch, mem_type, err_msg in result.get('failures', []):
|
||||
error(f"{branch}.{mem_type}: {err_msg}")
|
||||
else:
|
||||
console.print("[red]x[/red] Failed to sync line counts")
|
||||
error("Failed to sync line counts")
|
||||
|
||||
console.print()
|
||||
|
||||
@@ -235,7 +234,7 @@ def show_status() -> None:
|
||||
stats_result = detector.get_rollover_stats()
|
||||
|
||||
if not stats_result['success']:
|
||||
console.print(f"[red]x[/red] Failed to get status: {stats_result.get('error', 'Unknown error')}")
|
||||
error(f"Failed to get status: {stats_result.get('error', 'Unknown error')}")
|
||||
logger.error(f"[rollover] Failed to get status: {stats_result.get('error')}")
|
||||
return
|
||||
|
||||
@@ -249,7 +248,7 @@ def show_status() -> None:
|
||||
|
||||
# Per-branch details
|
||||
if stats['branches']:
|
||||
console.print("[yellow]Branch Details:[/yellow]")
|
||||
console.print("[bold cyan]Branch Details:[/bold cyan]")
|
||||
console.print()
|
||||
|
||||
for branch_name, branch_stats in stats['branches'].items():
|
||||
@@ -294,7 +293,7 @@ def check_triggers() -> None:
|
||||
triggers_result = detector.check_all_branches()
|
||||
|
||||
if not triggers_result['success']:
|
||||
console.print(f"[red]x[/red] Failed to check triggers: {triggers_result.get('error', 'Unknown error')}")
|
||||
error(f"Failed to check triggers: {triggers_result.get('error', 'Unknown error')}")
|
||||
logger.error(f"[rollover] Failed to check triggers: {triggers_result.get('error')}")
|
||||
return
|
||||
|
||||
@@ -304,7 +303,7 @@ def check_triggers() -> None:
|
||||
console.print("[green]>[/green] No files need rollover")
|
||||
return
|
||||
|
||||
console.print(f"[yellow]Found {len(triggers)} files ready for rollover:[/yellow]")
|
||||
console.print(f"[bold cyan]Found {len(triggers)} files ready for rollover:[/bold cyan]")
|
||||
console.print()
|
||||
|
||||
for trigger in triggers:
|
||||
|
||||
@@ -26,7 +26,7 @@ from rich.panel import Panel
|
||||
from rich import box
|
||||
|
||||
from aipass.prax import logger
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error, warning
|
||||
|
||||
# =============================================================================
|
||||
# INFRASTRUCTURE SETUP
|
||||
@@ -63,8 +63,7 @@ def handle_command(command: str, args: List[str]) -> bool:
|
||||
|
||||
if command == 'search':
|
||||
if not args:
|
||||
console.print("[red]Error:[/red] Search query required")
|
||||
console.print("Usage: search <query> [--branch BRANCH] [--type TYPE] [--n N]")
|
||||
error("Search query required", suggestion="Usage: search <query> [--branch BRANCH] [--type TYPE] [--n N]")
|
||||
return True
|
||||
|
||||
# Parse arguments
|
||||
@@ -85,7 +84,7 @@ def handle_command(command: str, args: List[str]) -> bool:
|
||||
try:
|
||||
n_results = int(args[i + 1])
|
||||
except ValueError:
|
||||
console.print(f"[red]Error:[/red] Invalid number: {args[i + 1]}")
|
||||
error(f"Invalid number: {args[i + 1]}")
|
||||
return True
|
||||
i += 2
|
||||
else:
|
||||
@@ -94,7 +93,7 @@ def handle_command(command: str, args: List[str]) -> bool:
|
||||
|
||||
query = ' '.join(query_parts)
|
||||
if not query:
|
||||
console.print("[red]Error:[/red] Search query required")
|
||||
error("Search query required")
|
||||
return True
|
||||
|
||||
show_search_results(query, branch=branch, memory_type=memory_type, n_results=n_results)
|
||||
@@ -126,7 +125,7 @@ def print_help() -> None:
|
||||
console.print()
|
||||
console.print("[bold]EXAMPLES:[/bold]")
|
||||
console.print(" # Search all branches")
|
||||
console.print(" [dim]drone @memory search \"error handling patterns\"[/dim]")
|
||||
console.print(" [dim]drone @memory search \"performance patterns\"[/dim]")
|
||||
console.print()
|
||||
console.print(" # Search specific branch")
|
||||
console.print(" [dim]drone @memory search \"registry bugs\" --branch SEED[/dim]")
|
||||
@@ -191,7 +190,7 @@ def show_search_results(
|
||||
)
|
||||
|
||||
if not result['success']:
|
||||
console.print(f"[red]x[/red] {result.get('error', 'Unknown error')}")
|
||||
error(result.get('error', 'Unknown error'))
|
||||
return False
|
||||
|
||||
collections_searched = result.get('collections_searched', 0)
|
||||
@@ -203,19 +202,11 @@ def show_search_results(
|
||||
console.print()
|
||||
|
||||
if not filtered_results and total_results == 0:
|
||||
console.print("[yellow]No matching memories found[/yellow]")
|
||||
console.print()
|
||||
console.print("[dim]Try:[/dim]")
|
||||
console.print(" * Different search terms")
|
||||
console.print(" * Broader query without filters")
|
||||
console.print(" * Check if memories have been rolled over (drone @memory status)")
|
||||
warning("No matching memories found", details="Try different search terms, broader query without filters, or check if memories have been rolled over (drone @memory status)")
|
||||
return True
|
||||
|
||||
if not filtered_results:
|
||||
console.print("[yellow]No relevant memories found[/yellow]")
|
||||
console.print()
|
||||
console.print("[dim]The search found some results but none were relevant enough (>40% similarity).[/dim]")
|
||||
console.print("[dim]Try more specific search terms related to your AIPass work.[/dim]")
|
||||
warning("No relevant memories found", details="Results found but none relevant enough (>40% similarity). Try more specific search terms.")
|
||||
return True
|
||||
|
||||
for i, item in enumerate(filtered_results, 1):
|
||||
|
||||
@@ -17,7 +17,7 @@ import sys
|
||||
from typing import List
|
||||
|
||||
from aipass.prax.apps.modules.logger import system_logger as logger
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error
|
||||
|
||||
|
||||
def print_introspection():
|
||||
@@ -102,7 +102,7 @@ def handle_command(command: str, args: List[str]) -> bool:
|
||||
if result:
|
||||
console.print("[green]✅ Pushed to all branch dashboards[/green]\n")
|
||||
else:
|
||||
console.print("[red]❌ Push failed — check logs[/red]\n")
|
||||
error("Push failed — check logs")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ from pathlib import Path
|
||||
from typing import Dict, List
|
||||
|
||||
from aipass.prax.apps.modules.logger import system_logger as logger
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error, warning
|
||||
|
||||
# Import handlers
|
||||
from aipass.prax.apps.handlers.dashboard.operations import (
|
||||
@@ -192,7 +192,7 @@ def print_status():
|
||||
try:
|
||||
branches = get_branch_paths()
|
||||
except Exception as e:
|
||||
console.print(f"[red]Error loading branches: {e}[/red]")
|
||||
error(f"Error loading branches: {e}")
|
||||
return
|
||||
|
||||
console.print()
|
||||
@@ -254,13 +254,13 @@ def _handle_refresh(args: List[str]) -> None:
|
||||
if result["status"] == "success":
|
||||
console.print(f"[green]Refreshed {result['branches_updated']} branches[/green]")
|
||||
elif result["status"] == "partial":
|
||||
console.print(f"[yellow]Refreshed {result['branches_updated']} branches, {result['branches_failed']} failed[/yellow]")
|
||||
warning(f"Refreshed {result['branches_updated']} branches, {result['branches_failed']} failed")
|
||||
for err in result.get("errors", []):
|
||||
console.print(f" [red]{err}[/red]")
|
||||
error(str(err))
|
||||
else:
|
||||
console.print(f"[red]Refresh failed[/red]")
|
||||
error("Refresh failed")
|
||||
for err in result.get("errors", []):
|
||||
console.print(f" [red]{err}[/red]")
|
||||
error(str(err))
|
||||
return
|
||||
|
||||
# @branch arg: refresh specific branch
|
||||
@@ -268,14 +268,14 @@ def _handle_refresh(args: List[str]) -> None:
|
||||
try:
|
||||
branch_path = _resolve_branch_path(args[0])
|
||||
except FileNotFoundError as e:
|
||||
console.print(f"[red]{e}[/red]")
|
||||
error(str(e))
|
||||
return
|
||||
console.print(f"[dim]Refreshing {branch_path.name.upper()} dashboard...[/dim]")
|
||||
result = refresh_single_dashboard(branch_path)
|
||||
if result["status"] == "success":
|
||||
console.print(f"[green]Refreshed {result['branch']}[/green]")
|
||||
else:
|
||||
console.print(f"[red]Failed: {result.get('error', 'unknown')}[/red]")
|
||||
error(f"Failed: {result.get('error', 'unknown')}")
|
||||
return
|
||||
|
||||
# No args: refresh current branch (detect from CWD)
|
||||
@@ -296,7 +296,7 @@ def _handle_refresh(args: List[str]) -> None:
|
||||
if result["status"] == "success":
|
||||
console.print(f"[green]Refreshed {result['branch']}[/green]")
|
||||
else:
|
||||
console.print(f"[red]Failed: {result.get('error', 'unknown')}[/red]")
|
||||
error(f"Failed: {result.get('error', 'unknown')}")
|
||||
|
||||
|
||||
def _handle_push_template(args: List[str]) -> None:
|
||||
@@ -334,9 +334,9 @@ def _handle_push_template(args: List[str]) -> None:
|
||||
|
||||
if result["errors"]:
|
||||
console.print()
|
||||
console.print(f"[red]Errors ({len(result['errors'])}):[/red]")
|
||||
error(f"Errors ({len(result['errors'])})")
|
||||
for err in result["errors"]:
|
||||
console.print(f" [red]! {err}[/red]")
|
||||
error(f"! {err}")
|
||||
|
||||
if not result["changes"] and not result["errors"]:
|
||||
console.print()
|
||||
@@ -362,13 +362,13 @@ def _handle_diff_template(args: List[str]) -> None:
|
||||
if idx + 1 < len(args):
|
||||
branch_name = args[idx + 1]
|
||||
else:
|
||||
console.print("[red]--branch requires a branch name[/red]")
|
||||
error("--branch requires a branch name")
|
||||
return
|
||||
|
||||
result = diff_dashboard_template(branch_name=branch_name)
|
||||
|
||||
if "error" in result:
|
||||
console.print(f"[red]Error: {result['error']}[/red]")
|
||||
error(result['error'])
|
||||
return
|
||||
|
||||
summary = result.get("summary", {})
|
||||
@@ -491,7 +491,7 @@ def main():
|
||||
remaining_args = args[1:]
|
||||
|
||||
if not handle_command(command, remaining_args):
|
||||
console.print(f"[red]Unknown command: {command}[/red]")
|
||||
error(f"Unknown command: {command}")
|
||||
print_help()
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import sys
|
||||
from typing import List
|
||||
|
||||
from aipass.prax.apps.modules.logger import initialize_logging_system, system_logger as logger
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error
|
||||
|
||||
|
||||
def print_introspection():
|
||||
@@ -77,7 +77,7 @@ def handle_command(command: str, args: List[str]) -> bool:
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in init command: {e}")
|
||||
console.print(f"[red]❌ ERROR: {e}[/red]")
|
||||
error(str(e))
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import sys
|
||||
from typing import List
|
||||
|
||||
from aipass.prax.apps.modules.logger import system_logger as logger
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error
|
||||
|
||||
|
||||
def print_introspection():
|
||||
@@ -71,7 +71,7 @@ def _display_audit(files: list, summary: dict) -> None:
|
||||
if summary['healthy']:
|
||||
console.print("[green] Status: HEALTHY — all logs within limits[/green]")
|
||||
else:
|
||||
console.print(f"[red] Status: {summary['oversized_count']} oversized, {summary['critical_count']} critical[/red]")
|
||||
error(f"Status: {summary['oversized_count']} oversized, {summary['critical_count']} critical")
|
||||
|
||||
# Show oversized files
|
||||
oversized = [f for f in files if f["status"] != "ok"]
|
||||
|
||||
@@ -389,7 +389,7 @@ def _interactive_loop():
|
||||
elif cmd == 'status':
|
||||
_print_status()
|
||||
else:
|
||||
console.print(f"[red]Unknown command: {cmd}[/red]")
|
||||
error(f"Unknown command: {cmd}")
|
||||
console.print("[dim]Type 'help' for available commands[/dim]")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
|
||||
@@ -17,7 +17,7 @@ from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
from aipass.prax.apps.modules.logger import enable_terminal_output, disable_terminal_output, system_logger as logger
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error
|
||||
|
||||
|
||||
def print_introspection():
|
||||
@@ -101,7 +101,7 @@ def handle_command(command: str, args: List[str]) -> bool:
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in terminal command: {e}")
|
||||
console.print(f"[red]❌ ERROR: {e}[/red]")
|
||||
error(str(e))
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ from typing import List, Callable
|
||||
from aipass.prax.apps.modules.logger import system_logger as logger
|
||||
|
||||
# CLI services
|
||||
from aipass.cli.apps.modules import console, header, success, error
|
||||
from aipass.cli.apps.modules import console, header, success, error, warning
|
||||
|
||||
# =============================================================================
|
||||
# MODULE DISCOVERY
|
||||
@@ -63,7 +63,7 @@ def discover_command_modules() -> List[Callable]:
|
||||
command_handlers.append(module.handle_command)
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"Warning: Failed to load module {module_file.name}: {e}")
|
||||
warning(f"Failed to load module {module_file.name}: {e}")
|
||||
|
||||
return command_handlers
|
||||
|
||||
@@ -121,7 +121,7 @@ def route_command(command: str, args: List[str], handlers: List[Callable]) -> bo
|
||||
if handler(command, args):
|
||||
return True
|
||||
except Exception as e:
|
||||
console.print(f"❌ ERROR: Handler failed: {e}")
|
||||
error(f"Handler failed: {e}")
|
||||
return False
|
||||
|
||||
return False
|
||||
@@ -177,14 +177,14 @@ Examples:
|
||||
handlers = discover_command_modules()
|
||||
|
||||
if not handlers:
|
||||
console.print("❌ ERROR: No command modules discovered")
|
||||
error("No command modules discovered")
|
||||
return 1
|
||||
|
||||
# Route command to appropriate handler
|
||||
if route_command(parsed_args.command, parsed_args.args, handlers):
|
||||
return 0
|
||||
else:
|
||||
console.print(f"❌ ERROR: Unknown command: {parsed_args.command}")
|
||||
error(f"Unknown command: {parsed_args.command}")
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# Stderr Routing Standard
|
||||
**Status:** Active
|
||||
**Date:** 2026-03-13
|
||||
**Origin:** FPLAN-0032 (CLI stderr standardization)
|
||||
|
||||
## What This Covers
|
||||
|
||||
Error and warning output must route to stderr via CLI display functions, not stay on stdout via raw `console.print()` with colored markup.
|
||||
|
||||
---
|
||||
|
||||
## The Problem
|
||||
|
||||
CLI `display.py` now has `err_console = Console(stderr=True)`. The functions `error()`, `warning()`, and `fatal()` route through it automatically. But branches still using `console.print('[red]Error...[/red]')` send errors to stdout — breaking piping, redirection, and machine-readable output.
|
||||
|
||||
## Correct Pattern
|
||||
|
||||
```python
|
||||
from aipass.cli.apps.modules import error, warning, fatal
|
||||
|
||||
error('Branch not found', suggestion='Check spelling') # stderr
|
||||
warning('Template mismatch', details='Expected v2') # stderr
|
||||
fatal('Config missing') # stderr + sys.exit(1)
|
||||
```
|
||||
|
||||
## Wrong Pattern
|
||||
|
||||
```python
|
||||
console.print('[red]Error: Branch not found[/red]') # stdout!
|
||||
console.print('[yellow]Warning: mismatch[/yellow]') # stdout!
|
||||
Console(stderr=True) # Don't create your own
|
||||
```
|
||||
|
||||
## What the Checker Catches
|
||||
|
||||
1. `console.print()` with `[red]`/`[bold red]` markup — use `error()` or `fatal()`
|
||||
2. `console.print()` with `[yellow]`/`[bold yellow]` markup — use `warning()`
|
||||
3. Custom `Console(stderr=True)` creation — import `err_console` from CLI
|
||||
|
||||
## CLI Display Exports
|
||||
|
||||
```python
|
||||
from aipass.cli.apps.modules import (
|
||||
error, # ❌ message + optional suggestion → stderr
|
||||
warning, # ⚠ message + optional details → stderr
|
||||
fatal, # ❌ message + sys.exit(1) → stderr
|
||||
err_console, # Raw stderr Console (rare, prefer functions above)
|
||||
)
|
||||
```
|
||||
|
||||
## Scope
|
||||
|
||||
- All branches except CLI (which defines these functions)
|
||||
- Handlers are exempt (CLI separation standard covers them separately)
|
||||
- `if __name__ == '__main__':` blocks are excluded
|
||||
|
||||
## Reference
|
||||
|
||||
- `src/aipass/cli/apps/modules/display.py` — error(), warning(), fatal() definitions
|
||||
- FPLAN-0032 — Phase 1+2: CLI stderr standardization (closed)
|
||||
- FPLAN-0033 — Phase 3: branch migration (gates on this standard)
|
||||
@@ -0,0 +1,303 @@
|
||||
# =================== AIPass ====================
|
||||
# Name: stderr_routing_check.py
|
||||
# Description: Stderr Routing Standards Checker
|
||||
# Version: 1.0.0
|
||||
# Created: 2026-03-13
|
||||
# Modified: 2026-03-13
|
||||
# =============================================
|
||||
|
||||
"""
|
||||
Stderr Routing Standards Checker
|
||||
|
||||
Validates that error/warning output uses CLI display functions (error(), warning(),
|
||||
fatal()) which route to stderr, instead of raw console.print() with red/yellow markup
|
||||
that stays on stdout.
|
||||
|
||||
CORRECT:
|
||||
from aipass.cli.apps.modules import error, warning, fatal
|
||||
error('Branch not found', suggestion='Check spelling')
|
||||
warning('Template mismatch', details='Expected v2')
|
||||
|
||||
WRONG:
|
||||
console.print('[red]Error: Branch not found[/red]')
|
||||
console.print('[yellow]Warning: mismatch[/yellow]')
|
||||
"""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Dict, List
|
||||
|
||||
|
||||
AUDIT_SCOPE = "all_files"
|
||||
|
||||
|
||||
def is_bypassed(file_path: str, standard: str, line: int | None = None, bypass_rules: list | None = None) -> bool:
|
||||
"""Check if a violation should be bypassed."""
|
||||
if not bypass_rules:
|
||||
return False
|
||||
for rule in bypass_rules:
|
||||
if rule.get('standard') and rule.get('standard') != standard:
|
||||
continue
|
||||
rule_file = rule.get('file', '')
|
||||
if rule_file and rule_file not in file_path:
|
||||
continue
|
||||
rule_lines = rule.get('lines', [])
|
||||
if rule_lines and line is not None:
|
||||
if line in rule_lines:
|
||||
return True
|
||||
elif not rule_lines:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def check_module(module_path: str, bypass_rules: list | None = None) -> Dict:
|
||||
"""
|
||||
Check if module routes error/warning output to stderr via CLI display functions.
|
||||
|
||||
Returns:
|
||||
dict: {passed, checks, score, standard}
|
||||
"""
|
||||
checks: List[Dict] = []
|
||||
path = Path(module_path)
|
||||
|
||||
if is_bypassed(module_path, 'stderr_routing', bypass_rules=bypass_rules):
|
||||
return {
|
||||
'passed': True,
|
||||
'checks': [{'name': 'Bypassed', 'passed': True, 'message': 'Standard bypassed via .seedgo/bypass.json'}],
|
||||
'score': 100,
|
||||
'standard': 'STDERR_ROUTING'
|
||||
}
|
||||
|
||||
if not path.exists():
|
||||
return {
|
||||
'passed': False,
|
||||
'checks': [{'name': 'File exists', 'passed': False, 'message': f'File not found: {module_path}'}],
|
||||
'score': 0,
|
||||
'standard': 'STDERR_ROUTING'
|
||||
}
|
||||
|
||||
# CLI branch is exempt — it defines these functions
|
||||
if '/cli/apps/' in module_path:
|
||||
return {
|
||||
'passed': True,
|
||||
'checks': [{'name': 'Stderr routing', 'passed': True, 'message': 'CLI branch exempt (defines display functions)'}],
|
||||
'score': 100,
|
||||
'standard': 'STDERR_ROUTING'
|
||||
}
|
||||
|
||||
try:
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
lines = content.split('\n')
|
||||
except Exception as e:
|
||||
return {
|
||||
'passed': False,
|
||||
'checks': [{'name': 'File readable', 'passed': False, 'message': f'Error reading file: {e}'}],
|
||||
'score': 0,
|
||||
'standard': 'STDERR_ROUTING'
|
||||
}
|
||||
|
||||
filename = path.name
|
||||
|
||||
# Check 1: console.print() with error-like red markup
|
||||
error_prints = _find_error_prints(lines, module_path, bypass_rules)
|
||||
if error_prints:
|
||||
checks.append({
|
||||
'name': 'Error output routing',
|
||||
'passed': False,
|
||||
'message': f'{filename}: {len(error_prints)} error print(s) on lines {error_prints[:5]} — use error() or fatal() instead'
|
||||
})
|
||||
elif _has_any_output(content):
|
||||
checks.append({
|
||||
'name': 'Error output routing',
|
||||
'passed': True,
|
||||
'message': 'No error-like console.print() with red markup'
|
||||
})
|
||||
|
||||
# Check 2: console.print() with warning-like yellow markup
|
||||
warning_prints = _find_warning_prints(lines, module_path, bypass_rules)
|
||||
if warning_prints:
|
||||
checks.append({
|
||||
'name': 'Warning output routing',
|
||||
'passed': False,
|
||||
'message': f'{filename}: {len(warning_prints)} warning print(s) on lines {warning_prints[:5]} — use warning() instead'
|
||||
})
|
||||
elif _has_any_output(content):
|
||||
checks.append({
|
||||
'name': 'Warning output routing',
|
||||
'passed': True,
|
||||
'message': 'No warning-like console.print() with yellow markup'
|
||||
})
|
||||
|
||||
# Check 3: Custom Console(stderr=True) — should import err_console
|
||||
custom_stderr = _find_custom_stderr_console(lines, module_path, bypass_rules)
|
||||
if custom_stderr:
|
||||
checks.append({
|
||||
'name': 'Stderr console creation',
|
||||
'passed': False,
|
||||
'message': f'{filename}: Custom Console(stderr=True) on lines {custom_stderr[:3]} — import err_console from aipass.cli.apps.modules'
|
||||
})
|
||||
|
||||
# No checks applied = no output patterns = skip
|
||||
if not checks:
|
||||
return {
|
||||
'passed': True,
|
||||
'checks': [{'name': 'Stderr routing', 'passed': True, 'message': 'No error/warning output patterns (skipped)'}],
|
||||
'score': 100,
|
||||
'standard': 'STDERR_ROUTING'
|
||||
}
|
||||
|
||||
passed_checks = sum(1 for c in checks if c['passed'])
|
||||
total_checks = len(checks)
|
||||
score = int((passed_checks / total_checks * 100)) if total_checks > 0 else 0
|
||||
|
||||
return {
|
||||
'passed': score >= 75,
|
||||
'checks': checks,
|
||||
'score': score,
|
||||
'standard': 'STDERR_ROUTING'
|
||||
}
|
||||
|
||||
|
||||
def _has_any_output(content: str) -> bool:
|
||||
"""Check if file has any console output at all."""
|
||||
return 'console.print(' in content or 'err_console.print(' in content
|
||||
|
||||
|
||||
def _in_skip_context(line: str, in_docstring: bool, in_main_block: bool) -> bool:
|
||||
"""Check if we should skip this line."""
|
||||
stripped = line.strip()
|
||||
if in_docstring or in_main_block:
|
||||
return True
|
||||
if stripped.startswith('#'):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _find_error_prints(lines: List[str], module_path: str, bypass_rules: list | None) -> List[int]:
|
||||
"""Find console.print() calls with red markup containing error-like content."""
|
||||
violations = []
|
||||
in_docstring = False
|
||||
in_main_block = False
|
||||
main_block_indent = 0
|
||||
|
||||
# Patterns: [red], [bold red], [red bold] in console.print()
|
||||
red_pattern = re.compile(r'console\.print\(.*\[(?:bold\s+)?red(?:\s+bold)?\]', re.IGNORECASE)
|
||||
# Also catch: console.print(f"❌ ..." or console.print("Error: ...")
|
||||
error_msg_pattern = re.compile(r'console\.print\([f"\']*[^)]*(?:error|failed|fatal|cannot|unable|invalid|❌)', re.IGNORECASE)
|
||||
|
||||
for i, line in enumerate(lines, 1):
|
||||
stripped = line.strip()
|
||||
current_indent = len(line) - len(line.lstrip()) if stripped else 0
|
||||
|
||||
# Track __main__ blocks
|
||||
if "if __name__ ==" in stripped and "__main__" in stripped:
|
||||
in_main_block = True
|
||||
main_block_indent = current_indent
|
||||
continue
|
||||
if in_main_block and stripped and current_indent <= main_block_indent:
|
||||
in_main_block = False
|
||||
|
||||
# Track docstrings
|
||||
for quote in ('"""', "'''"):
|
||||
if line.count(quote) % 2 == 1:
|
||||
in_docstring = not in_docstring
|
||||
|
||||
if _in_skip_context(line, in_docstring, in_main_block):
|
||||
continue
|
||||
|
||||
# Skip if in a string context (the console.print itself is in a string)
|
||||
if 'console.print(' not in stripped:
|
||||
continue
|
||||
|
||||
# Check it's not inside a string literal
|
||||
before = line.split('console.print(')[0]
|
||||
if before.count("'") % 2 == 1 or before.count('"') % 2 == 1:
|
||||
continue
|
||||
|
||||
# Skip bypassed lines
|
||||
if is_bypassed(module_path, 'stderr_routing', line=i, bypass_rules=bypass_rules):
|
||||
continue
|
||||
|
||||
# Match red markup patterns
|
||||
if red_pattern.search(stripped):
|
||||
violations.append(i)
|
||||
elif error_msg_pattern.search(stripped):
|
||||
# Only flag if it looks like an error message, not just mentioning the word
|
||||
# Must be the console.print argument, not a variable name
|
||||
violations.append(i)
|
||||
|
||||
return violations
|
||||
|
||||
|
||||
def _find_warning_prints(lines: List[str], module_path: str, bypass_rules: list | None) -> List[int]:
|
||||
"""Find console.print() calls with yellow markup containing warning-like content."""
|
||||
violations = []
|
||||
in_docstring = False
|
||||
in_main_block = False
|
||||
main_block_indent = 0
|
||||
|
||||
# Patterns: [yellow] in console.print() that look like warnings
|
||||
yellow_pattern = re.compile(r'console\.print\(.*\[(?:bold\s+)?yellow(?:\s+bold)?\]', re.IGNORECASE)
|
||||
warning_msg_pattern = re.compile(r'console\.print\([f"\']*[^)]*(?:warning|⚠)', re.IGNORECASE)
|
||||
|
||||
for i, line in enumerate(lines, 1):
|
||||
stripped = line.strip()
|
||||
current_indent = len(line) - len(line.lstrip()) if stripped else 0
|
||||
|
||||
if "if __name__ ==" in stripped and "__main__" in stripped:
|
||||
in_main_block = True
|
||||
main_block_indent = current_indent
|
||||
continue
|
||||
if in_main_block and stripped and current_indent <= main_block_indent:
|
||||
in_main_block = False
|
||||
|
||||
for quote in ('"""', "'''"):
|
||||
if line.count(quote) % 2 == 1:
|
||||
in_docstring = not in_docstring
|
||||
|
||||
if _in_skip_context(line, in_docstring, in_main_block):
|
||||
continue
|
||||
|
||||
if 'console.print(' not in stripped:
|
||||
continue
|
||||
|
||||
before = line.split('console.print(')[0]
|
||||
if before.count("'") % 2 == 1 or before.count('"') % 2 == 1:
|
||||
continue
|
||||
|
||||
if is_bypassed(module_path, 'stderr_routing', line=i, bypass_rules=bypass_rules):
|
||||
continue
|
||||
|
||||
if yellow_pattern.search(stripped):
|
||||
violations.append(i)
|
||||
elif warning_msg_pattern.search(stripped):
|
||||
violations.append(i)
|
||||
|
||||
return violations
|
||||
|
||||
|
||||
def _find_custom_stderr_console(lines: List[str], module_path: str, bypass_rules: list | None) -> List[int]:
|
||||
"""Find custom Console(stderr=True) creation — should use err_console import."""
|
||||
violations = []
|
||||
in_docstring = False
|
||||
|
||||
stderr_pattern = re.compile(r'Console\s*\(.*stderr\s*=\s*True', re.IGNORECASE)
|
||||
|
||||
for i, line in enumerate(lines, 1):
|
||||
stripped = line.strip()
|
||||
|
||||
for quote in ('"""', "'''"):
|
||||
if line.count(quote) % 2 == 1:
|
||||
in_docstring = not in_docstring
|
||||
|
||||
if in_docstring or stripped.startswith('#'):
|
||||
continue
|
||||
|
||||
if is_bypassed(module_path, 'stderr_routing', line=i, bypass_rules=bypass_rules):
|
||||
continue
|
||||
|
||||
if stderr_pattern.search(stripped):
|
||||
violations.append(i)
|
||||
|
||||
return violations
|
||||
@@ -0,0 +1,73 @@
|
||||
# =================== AIPass ====================
|
||||
# Name: stderr_routing_content.py
|
||||
# Description: Stderr Routing Standards Content Handler
|
||||
# Version: 1.0.0
|
||||
# Created: 2026-03-13
|
||||
# Modified: 2026-03-13
|
||||
# =============================================
|
||||
|
||||
"""
|
||||
Stderr Routing Standards Content Handler
|
||||
|
||||
Provides formatted stderr routing standards content.
|
||||
Module orchestrates, handler implements.
|
||||
"""
|
||||
|
||||
|
||||
def get_stderr_routing_standards() -> str:
|
||||
"""Return formatted stderr routing standards content with Rich markup."""
|
||||
lines = [
|
||||
"[bold red]STDERR ROUTING STANDARD[/bold red]",
|
||||
"",
|
||||
"[yellow]POLICY:[/yellow] Error and warning output MUST route to stderr",
|
||||
"via CLI display functions. Normal output stays on stdout.",
|
||||
"",
|
||||
"[bold cyan]WHY:[/bold cyan]",
|
||||
" CLI display.py has [dim]err_console = Console(stderr=True)[/dim].",
|
||||
" [dim]error()[/dim], [dim]warning()[/dim], and [dim]fatal()[/dim] route through it.",
|
||||
" Branches using [dim]console.print('[red]Error...[/red]')[/dim] bypass this",
|
||||
" and send errors to stdout, breaking piping and redirection.",
|
||||
"",
|
||||
"\u2500" * 70,
|
||||
"",
|
||||
"[bold cyan]CORRECT PATTERN:[/bold cyan]",
|
||||
" [dim]from aipass.cli.apps.modules import error, warning, fatal[/dim]",
|
||||
"",
|
||||
" [green]\u2713[/green] [dim]error('Branch not found', suggestion='Check spelling')[/dim]",
|
||||
" [green]\u2713[/green] [dim]warning('Template mismatch', details='Expected v2')[/dim]",
|
||||
" [green]\u2713[/green] [dim]fatal('Config missing') # stderr + sys.exit(1)[/dim]",
|
||||
"",
|
||||
"[bold cyan]WRONG PATTERN:[/bold cyan]",
|
||||
" [red]\u2717[/red] [dim]console.print('[red]Error: Branch not found[/red]')[/dim]",
|
||||
" [red]\u2717[/red] [dim]console.print('[yellow]Warning: mismatch[/yellow]')[/dim]",
|
||||
" [red]\u2717[/red] [dim]Console(stderr=True) # Don't create your own[/dim]",
|
||||
"",
|
||||
"\u2500" * 70,
|
||||
"",
|
||||
"[bold cyan]WHAT THE CHECKER CATCHES:[/bold cyan]",
|
||||
" 1. [dim]console.print()[/dim] with [red][red]/[bold red][/red] markup",
|
||||
" \u2192 Should use [dim]error()[/dim] or [dim]fatal()[/dim]",
|
||||
" 2. [dim]console.print()[/dim] with [yellow][yellow]/[bold yellow][/yellow] markup",
|
||||
" \u2192 Should use [dim]warning()[/dim]",
|
||||
" 3. Custom [dim]Console(stderr=True)[/dim] creation",
|
||||
" \u2192 Import [dim]err_console[/dim] from CLI instead",
|
||||
"",
|
||||
"[bold cyan]CLI DISPLAY EXPORTS:[/bold cyan]",
|
||||
" [dim]from aipass.cli.apps.modules import ([/dim]",
|
||||
" [dim] error, # \u2718 message + optional suggestion \u2192 stderr[/dim]",
|
||||
" [dim] warning, # \u26a0 message + optional details \u2192 stderr[/dim]",
|
||||
" [dim] fatal, # \u2718 message + sys.exit(1) \u2192 stderr[/dim]",
|
||||
" [dim] err_console, # Raw stderr Console (rare, prefer functions)[/dim]",
|
||||
" [dim])[/dim]",
|
||||
"",
|
||||
"[bold cyan]SCOPE:[/bold cyan]",
|
||||
" All branches except CLI (which defines these functions).",
|
||||
" Handlers are exempt from CLI output standards (separate concern).",
|
||||
"",
|
||||
"[bold cyan]REFERENCE:[/bold cyan]",
|
||||
" [dim]See: src/aipass/cli/apps/modules/display.py (error, warning, fatal)[/dim]",
|
||||
" [dim]See: FPLAN-0032 (Phase 1+2: CLI stderr standardization)[/dim]",
|
||||
" [dim]See: FPLAN-0033 (Phase 3: branch migration — gates on this standard)[/dim]",
|
||||
]
|
||||
|
||||
return "\n".join(lines)
|
||||
@@ -233,6 +233,16 @@ def print_branch_summary(audit_result: Dict, system_averages: Dict[str, int] | N
|
||||
for issue in violation.get('issues', []):
|
||||
console.print(f" [dim]• {issue}[/dim]")
|
||||
|
||||
# Show STDERR_ROUTING violations (console.print red/yellow → use error()/warning())
|
||||
stderr_routing_violations = audit_result.get('stderr_routing_violations', [])
|
||||
if stderr_routing_violations:
|
||||
console.print()
|
||||
console.print(f" [bold red]STDERR_ROUTING VIOLATIONS ({len(stderr_routing_violations)} files):[/bold red]")
|
||||
for violation in stderr_routing_violations:
|
||||
console.print(f" [red]✗[/red] {violation['path']} [dim](score: {violation['score']}%)[/dim]")
|
||||
for issue in violation.get('issues', []):
|
||||
console.print(f" [dim]• {issue}[/dim]")
|
||||
|
||||
# Show SHEBANG violations (no #!/... in pip packages)
|
||||
shebang_violations = audit_result.get('shebang_violations', [])
|
||||
if shebang_violations:
|
||||
|
||||
@@ -143,7 +143,14 @@ def audit_branch(branch: Dict[str, str], bypass_rules: list, pack_path: Path = N
|
||||
v, s = _run_all_files(checker, name, all_files, bypass_rules)
|
||||
all_violations[name] = v
|
||||
if s:
|
||||
scores[name] = int(sum(s) / len(s))
|
||||
avg_score = int(sum(s) / len(s))
|
||||
scores[name] = avg_score
|
||||
# Update results to reflect all-files findings
|
||||
all_failed = []
|
||||
for vi in v:
|
||||
all_failed.extend({"name": name, "passed": False, "message": iss} for iss in vi.get("issues", []))
|
||||
if all_failed:
|
||||
results[name] = {"passed": avg_score >= 75, "checks": all_failed, "score": avg_score, "standard": name.upper()}
|
||||
|
||||
# Log structure post-checks (audit-level, not in any checker)
|
||||
if "log_structure" in scores:
|
||||
|
||||
@@ -32,6 +32,7 @@ from aipass.prax import logger
|
||||
|
||||
# CLI services (display/output formatting)
|
||||
from aipass.cli import console, header
|
||||
from aipass.cli.apps.modules import error, warning
|
||||
|
||||
# JSON handler for tracking
|
||||
from aipass.seedgo.apps.handlers.json import json_handler
|
||||
@@ -204,21 +205,18 @@ def handle_command(command: str, args: List[str]) -> bool:
|
||||
# Validate pack name
|
||||
packs = _discover_packs()
|
||||
if pack_name not in packs:
|
||||
console.print(f"\n[red]Unknown pack:[/red] '{pack_name}'")
|
||||
console.print()
|
||||
console.print("[yellow]Available packs:[/yellow]")
|
||||
for name in packs:
|
||||
console.print(f" [cyan]{name}[/cyan]")
|
||||
console.print()
|
||||
console.print(f"[dim]Usage: drone @seedgo audit {next(iter(packs), '<pack>')}[/dim]")
|
||||
console.print()
|
||||
available = ", ".join(packs.keys())
|
||||
error(
|
||||
f"Unknown pack: '{pack_name}'",
|
||||
suggestion=f"Available packs: {available}. Usage: drone @seedgo audit {next(iter(packs), '<pack>')}"
|
||||
)
|
||||
return True
|
||||
|
||||
pack_path = packs[pack_name]
|
||||
|
||||
# Handle --show-bypasses mode (placeholder — bypass audit merged into audit per D11)
|
||||
if show_bypasses:
|
||||
console.print("[yellow]--show-bypasses not yet implemented in seedgo[/yellow]")
|
||||
warning("--show-bypasses not yet implemented in seedgo")
|
||||
return True
|
||||
|
||||
# =========================================================================
|
||||
|
||||
@@ -22,6 +22,7 @@ from typing import List, Any
|
||||
|
||||
from aipass.prax import logger
|
||||
from aipass.cli import console, header
|
||||
from aipass.cli.apps.modules import error
|
||||
|
||||
VERSION = "2.0.0"
|
||||
SEEDGO_ROOT = Path(__file__).parent
|
||||
@@ -228,8 +229,7 @@ def main() -> int:
|
||||
if route_command(command, remaining, modules):
|
||||
return 0
|
||||
|
||||
console.print(f"[red]Unknown command:[/red] {command}")
|
||||
console.print("Run 'seedgo --help' for usage")
|
||||
error(f"Unknown command: {command}", suggestion="Run 'seedgo --help' for usage")
|
||||
return 1
|
||||
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ All implementation logic lives in apps/handlers/delete_ops.py.
|
||||
|
||||
from aipass.prax import logger
|
||||
# CLI service: from cli.apps.modules import console (via aipass namespace)
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error, warning
|
||||
|
||||
from aipass.spawn.apps.handlers.delete_ops import delete_branch
|
||||
|
||||
@@ -65,7 +65,7 @@ def handle_delete(args: list[str]) -> int:
|
||||
Returns exit code (0=success, 1=failure).
|
||||
"""
|
||||
if not args:
|
||||
console.print("[yellow]Usage: drone @spawn delete <@branch> [--yes] [--dry-run][/yellow]")
|
||||
warning("Usage: drone @spawn delete <@branch> [--yes] [--dry-run]")
|
||||
console.print()
|
||||
console.print(" [green]@branch[/green] Branch to archive and deregister")
|
||||
console.print(" [green]--yes[/green] Skip confirmation prompt")
|
||||
@@ -79,7 +79,7 @@ def handle_delete(args: list[str]) -> int:
|
||||
targets = [a for a in args if not a.startswith("--")]
|
||||
|
||||
if not targets:
|
||||
console.print("[red]Error: specify a branch name (e.g. @api)[/red]")
|
||||
error("specify a branch name (e.g. @api)")
|
||||
return 1
|
||||
|
||||
branch_name = targets[0].lstrip("@").lower()
|
||||
@@ -92,7 +92,7 @@ def handle_delete(args: list[str]) -> int:
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.error(f"[delete] Unexpected error deleting {branch_name}: {exc}")
|
||||
console.print(f"[red]Error deleting {branch_name}: {exc}[/red]")
|
||||
error(f"Error deleting {branch_name}: {exc}")
|
||||
return 1
|
||||
|
||||
_print_summary(result, dry_run)
|
||||
@@ -113,7 +113,7 @@ def _print_summary(result: dict, dry_run: bool) -> None:
|
||||
if success:
|
||||
console.print(f"[green]Delete {mode}{branch}[/green]")
|
||||
else:
|
||||
console.print(f"[red]Delete FAILED {mode}{branch}[/red]")
|
||||
error(f"Delete FAILED {mode}{branch}")
|
||||
|
||||
archive_path = result.get("archive_path", "")
|
||||
if archive_path:
|
||||
@@ -121,8 +121,8 @@ def _print_summary(result: dict, dry_run: bool) -> None:
|
||||
|
||||
console.print(f" Registry updated: {result.get('registry_updated', False)}")
|
||||
|
||||
error = result.get("error", "")
|
||||
if error:
|
||||
console.print(f" [red]Error: {error}[/red]")
|
||||
err_msg = result.get("error", "")
|
||||
if err_msg:
|
||||
error(err_msg)
|
||||
|
||||
console.print()
|
||||
|
||||
@@ -15,7 +15,7 @@ All implementation logic lives in apps/handlers/passport_ops.py.
|
||||
import argparse
|
||||
|
||||
from aipass.prax import logger
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error, warning
|
||||
|
||||
from aipass.spawn.apps.handlers.passport_ops import grant_passport
|
||||
|
||||
@@ -58,13 +58,13 @@ def handle_passport(args: list[str]) -> int:
|
||||
Returns exit code (0=success, 1=failure).
|
||||
"""
|
||||
if not args:
|
||||
console.print("[yellow]Usage: drone @spawn passport <@dirname> [--role ...] [--purpose ...][/yellow]")
|
||||
warning("Usage: drone @spawn passport <@dirname> [--role ...] [--purpose ...]")
|
||||
console.print()
|
||||
console.print(" [dim]Grant birthright citizenship to a directory[/dim]")
|
||||
console.print()
|
||||
console.print(" [green]@dirname[/green] Directory to grant citizenship (created if needed)")
|
||||
console.print(" [yellow]--role[/yellow] Role description for the passport")
|
||||
console.print(" [yellow]--purpose[/yellow] Purpose description")
|
||||
warning("--role", details="Role description for the passport")
|
||||
warning("--purpose", details="Purpose description")
|
||||
return 1
|
||||
|
||||
parser = argparse.ArgumentParser(prog="spawn passport", add_help=False)
|
||||
@@ -96,9 +96,9 @@ def handle_passport(args: list[str]) -> int:
|
||||
console.print(f" Files: {result['files_copied']}")
|
||||
console.print(f" Registry: {'updated' if result['registry_updated'] else 'not updated'}")
|
||||
if result["validation_issues"]:
|
||||
console.print(f" [yellow]Warnings: {len(result['validation_issues'])} unreplaced placeholders[/yellow]")
|
||||
warning(f"{len(result['validation_issues'])} unreplaced placeholders")
|
||||
console.print()
|
||||
return 0
|
||||
else:
|
||||
console.print(f"[red]Error: {result['error']}[/red]")
|
||||
error(result['error'])
|
||||
return 1
|
||||
|
||||
@@ -14,7 +14,7 @@ All implementation logic lives in apps/handlers/sync_registry_ops.py.
|
||||
|
||||
from aipass.prax import logger
|
||||
# CLI service: from cli.apps.modules import console (via aipass namespace)
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error, warning
|
||||
|
||||
from aipass.spawn.apps.handlers.sync_registry_ops import sync_registry
|
||||
|
||||
@@ -64,7 +64,7 @@ def handle_sync_registry(args: list[str]) -> int:
|
||||
Returns exit code (0=success, 1=failure).
|
||||
"""
|
||||
if args and args[0] in ["--help", "-h"]:
|
||||
console.print("[yellow]Usage: drone @spawn sync-registry [--fix][/yellow]")
|
||||
warning("Usage: drone @spawn sync-registry [--fix]")
|
||||
console.print()
|
||||
console.print(" [green](no args)[/green] Report mismatches between registry and filesystem")
|
||||
console.print(" [green]--fix[/green] Auto-repair: remove stale, add unregistered")
|
||||
@@ -76,7 +76,7 @@ def handle_sync_registry(args: list[str]) -> int:
|
||||
result = sync_registry(fix=fix)
|
||||
except Exception as exc:
|
||||
logger.error(f"[sync-registry] Unexpected error: {exc}")
|
||||
console.print(f"[red]Error: {exc}[/red]")
|
||||
error(str(exc))
|
||||
return 1
|
||||
|
||||
_print_summary(result)
|
||||
@@ -106,7 +106,7 @@ def _print_summary(result: dict) -> None:
|
||||
|
||||
# Stale
|
||||
if stale:
|
||||
console.print(f" [red]Stale ({len(stale)}):[/red] [dim]registered but missing/invalid[/dim]")
|
||||
error(f"Stale ({len(stale)}): registered but missing/invalid")
|
||||
for name in sorted(stale):
|
||||
console.print(f" {name}")
|
||||
else:
|
||||
@@ -114,7 +114,7 @@ def _print_summary(result: dict) -> None:
|
||||
|
||||
# Unregistered
|
||||
if unregistered:
|
||||
console.print(f" [yellow]Unregistered ({len(unregistered)}):[/yellow] [dim]on disk but not in registry[/dim]")
|
||||
warning(f"Unregistered ({len(unregistered)}): on disk but not in registry")
|
||||
for name in sorted(unregistered):
|
||||
console.print(f" {name}")
|
||||
else:
|
||||
|
||||
@@ -14,7 +14,7 @@ All implementation logic lives in apps/handlers/sync_templates_ops.py.
|
||||
|
||||
from aipass.prax import logger
|
||||
# CLI service: from cli.apps.modules import console (via aipass namespace)
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error, warning
|
||||
|
||||
from aipass.spawn.apps.handlers.sync_templates_ops import sync_templates
|
||||
|
||||
@@ -66,7 +66,7 @@ def handle_sync_templates(args: list[str]) -> int:
|
||||
Returns exit code (0=success, 1=failure).
|
||||
"""
|
||||
if args and args[0] in ["--help", "-h"]:
|
||||
console.print("[yellow]Usage: drone @spawn sync-templates [--status|--sync|--dry-run][/yellow]")
|
||||
warning("Usage: drone @spawn sync-templates [--status|--sync|--dry-run]")
|
||||
console.print()
|
||||
console.print(" [green](no args)[/green] Report which managed template files are stale")
|
||||
console.print(" [green]--status[/green] Same as no args")
|
||||
@@ -81,7 +81,7 @@ def handle_sync_templates(args: list[str]) -> int:
|
||||
result = sync_templates(sync=sync, dry_run=dry_run)
|
||||
except Exception as exc:
|
||||
logger.error(f"[sync-templates] Unexpected error: {exc}")
|
||||
console.print(f"[red]Error: {exc}[/red]")
|
||||
error(str(exc))
|
||||
return 1
|
||||
|
||||
_print_summary(result, dry_run)
|
||||
@@ -119,7 +119,7 @@ def _print_summary(result: dict, dry_run: bool) -> None:
|
||||
console.print(f" {name}")
|
||||
|
||||
if stale:
|
||||
console.print(f" [yellow]Stale ({len(stale)}):[/yellow]")
|
||||
warning(f"Stale ({len(stale)}):")
|
||||
for name in stale:
|
||||
console.print(f" {name}")
|
||||
|
||||
@@ -129,7 +129,7 @@ def _print_summary(result: dict, dry_run: bool) -> None:
|
||||
console.print(f" {name}")
|
||||
|
||||
if errors:
|
||||
console.print(f" [red]Errors ({len(errors)}):[/red]")
|
||||
error(f"Errors ({len(errors)}):")
|
||||
for err in errors:
|
||||
console.print(f" {err}")
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ All implementation logic lives in apps/handlers/update_ops.py.
|
||||
|
||||
from aipass.prax import logger
|
||||
# CLI service: from cli.apps.modules import console (via aipass namespace)
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error, warning
|
||||
|
||||
from aipass.spawn.apps.handlers.update_ops import (
|
||||
update_branch,
|
||||
@@ -72,7 +72,7 @@ def handle_update(args: list[str]) -> int:
|
||||
Returns exit code (0=success, 1=failure).
|
||||
"""
|
||||
if not args:
|
||||
console.print("[yellow]Usage: drone @spawn update <@branch|class --all> [--dry-run] [--trace][/yellow]")
|
||||
warning("Usage: drone @spawn update <@branch|class --all> [--dry-run] [--trace]")
|
||||
console.print()
|
||||
console.print(" [green]@branch[/green] Update a single branch (uses its own class)")
|
||||
console.print(" [green]builder --all[/green] Update all builder-class branches")
|
||||
@@ -100,9 +100,7 @@ def handle_update(args: list[str]) -> int:
|
||||
if "--all" in args:
|
||||
if citizen_class is None:
|
||||
classes = ", ".join(get_available_classes())
|
||||
console.print(f"[red]Error: --all requires a citizen class[/red]")
|
||||
console.print(f"[dim]Specify a class: drone @spawn update <class> --all[/dim]")
|
||||
console.print(f"[dim]Available classes: {classes}[/dim]")
|
||||
error("--all requires a citizen class", suggestion=f"drone @spawn update <class> --all (available: {classes})")
|
||||
return 1
|
||||
|
||||
results = update_all(dry_run=dry_run, trace=trace, citizen_class=citizen_class)
|
||||
@@ -110,7 +108,7 @@ def handle_update(args: list[str]) -> int:
|
||||
return 0 if all(r.get("success") for r in results) else 1
|
||||
|
||||
if not targets:
|
||||
console.print("[red]Error: specify a branch name (e.g. @prax) or use <class> --all[/red]")
|
||||
error("specify a branch name (e.g. @prax) or use <class> --all")
|
||||
return 1
|
||||
|
||||
# Take the first target, strip leading @
|
||||
@@ -120,7 +118,7 @@ def handle_update(args: list[str]) -> int:
|
||||
result = update_branch(branch_name, dry_run=dry_run, trace=trace)
|
||||
except Exception as exc:
|
||||
logger.error(f"[update] Unexpected error updating {branch_name}: {exc}")
|
||||
console.print(f"[red]Error updating {branch_name}: {exc}[/red]")
|
||||
error(f"Error updating {branch_name}: {exc}")
|
||||
return 1
|
||||
|
||||
_print_branch_summary(result, dry_run)
|
||||
@@ -141,7 +139,7 @@ def _print_branch_summary(result: dict, dry_run: bool) -> None:
|
||||
if success:
|
||||
console.print(f"[green]Update {mode}{branch}[/green]")
|
||||
else:
|
||||
console.print(f"[red]Update FAILED {mode}{branch}[/red]")
|
||||
error(f"Update FAILED {mode}{branch}")
|
||||
|
||||
console.print(f" Additions: {result.get('additions', 0)}")
|
||||
console.print(f" Renames: {result.get('renames', 0)}")
|
||||
@@ -151,7 +149,7 @@ def _print_branch_summary(result: dict, dry_run: bool) -> None:
|
||||
|
||||
errs = result.get("errors", [])
|
||||
if errs:
|
||||
console.print(f" [red]Errors: {len(errs)}[/red]")
|
||||
error(f"Errors: {len(errs)}")
|
||||
for e in errs:
|
||||
console.print(f" - {e}")
|
||||
|
||||
@@ -223,5 +221,5 @@ def _print_all_summary(results: list[dict], dry_run: bool) -> None:
|
||||
f"!{total_skip} py-skipped")
|
||||
|
||||
if total_err:
|
||||
console.print(f" [red]{total_err} errors across all branches[/red]")
|
||||
error(f"{total_err} errors across all branches")
|
||||
console.print()
|
||||
|
||||
@@ -17,7 +17,7 @@ import sys
|
||||
import argparse
|
||||
|
||||
from aipass.prax.apps.modules.logger import system_logger as logger
|
||||
from aipass.cli.apps.modules import console, header
|
||||
from aipass.cli.apps.modules import console, header, error, warning
|
||||
|
||||
|
||||
def print_help():
|
||||
@@ -52,13 +52,13 @@ def print_help():
|
||||
console.print()
|
||||
console.print("[bold cyan]OPTIONS:[/bold cyan]")
|
||||
console.print()
|
||||
console.print(" [yellow]--role[/yellow] Agent role description")
|
||||
console.print(" [yellow]--traits[/yellow] Agent personality traits")
|
||||
console.print(" [yellow]--purpose[/yellow] Agent purpose (brief)")
|
||||
console.print(" [yellow]--template[/yellow] Custom template directory")
|
||||
console.print(" [yellow]--registry[/yellow] Path to AIPASS_REGISTRY.json")
|
||||
console.print(" [yellow]--dry-run[/yellow] Preview changes without modifying files")
|
||||
console.print(" [yellow]--trace[/yellow] Enable verbose logging")
|
||||
warning("--role", details="Agent role description")
|
||||
warning("--traits", details="Agent personality traits")
|
||||
warning("--purpose", details="Agent purpose (brief)")
|
||||
warning("--template", details="Custom template directory")
|
||||
warning("--registry", details="Path to AIPASS_REGISTRY.json")
|
||||
warning("--dry-run", details="Preview changes without modifying files")
|
||||
warning("--trace", details="Enable verbose logging")
|
||||
console.print()
|
||||
|
||||
|
||||
@@ -68,8 +68,7 @@ def handle_create(args):
|
||||
from aipass.spawn.apps.modules.core import validate_class, get_default_class
|
||||
|
||||
if not args:
|
||||
console.print("[red]Error: target path required[/red]")
|
||||
console.print("[dim]Usage: drone @spawn create [class] <target_path> [--role ...][/dim]")
|
||||
error("target path required", suggestion="drone @spawn create [class] <target_path> [--role ...]")
|
||||
return 1
|
||||
|
||||
# Check if first arg is a citizen class
|
||||
@@ -79,7 +78,7 @@ def handle_create(args):
|
||||
citizen_class = args[0]
|
||||
remaining_args = args[1:]
|
||||
if not remaining_args:
|
||||
console.print("[red]Error: target path required after class name[/red]")
|
||||
error("target path required after class name")
|
||||
return 1
|
||||
|
||||
parser = argparse.ArgumentParser(prog="spawn create", add_help=False)
|
||||
@@ -110,11 +109,11 @@ def handle_create(args):
|
||||
console.print(f" Files: {result['files_copied']}")
|
||||
console.print(f" Registry: {'updated' if result['registry_updated'] else 'not updated'}")
|
||||
if result["validation_issues"]:
|
||||
console.print(f" [yellow]Warnings: {len(result['validation_issues'])} unreplaced placeholders[/yellow]")
|
||||
warning(f"{len(result['validation_issues'])} unreplaced placeholders")
|
||||
console.print()
|
||||
return 0
|
||||
else:
|
||||
console.print(f"[red]Error: {result['error']}[/red]")
|
||||
error(result['error'])
|
||||
return 1
|
||||
|
||||
|
||||
@@ -183,12 +182,10 @@ def main():
|
||||
}
|
||||
|
||||
if command in stub_commands:
|
||||
console.print(f"[yellow]'{command}' is not yet implemented.[/yellow]")
|
||||
console.print(f"[dim]Planned: {stub_commands[command]}[/dim]")
|
||||
warning(f"'{command}' is not yet implemented.", details=f"Planned: {stub_commands[command]}")
|
||||
return 1
|
||||
|
||||
console.print(f"[red]Unknown command: {command}[/red]")
|
||||
console.print("[dim]Run 'drone @spawn --help' for available commands[/dim]")
|
||||
error(f"Unknown command: {command}", suggestion="Run 'drone @spawn --help' for available commands")
|
||||
return 1
|
||||
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@ def handle_command(command: str, args: list) -> bool:
|
||||
Returns:
|
||||
True if command was handled, False otherwise
|
||||
"""
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error
|
||||
|
||||
# Handle module-name routing (drone @trigger core <subcmd>)
|
||||
if command == "core":
|
||||
@@ -203,7 +203,7 @@ def handle_command(command: str, args: list) -> bool:
|
||||
|
||||
if command == "fire":
|
||||
if not args:
|
||||
console.print("[red]Usage: drone @trigger fire <event> [key=value ...][/red]")
|
||||
error("Usage: drone @trigger fire <event> [key=value ...]")
|
||||
return True
|
||||
event_name = args[0]
|
||||
# Parse key=value pairs from remaining args
|
||||
|
||||
@@ -147,7 +147,7 @@ def handle_command(command: str, args: list) -> bool:
|
||||
Returns:
|
||||
True if command was handled, False otherwise
|
||||
"""
|
||||
from aipass.cli.apps.modules import console
|
||||
from aipass.cli.apps.modules import console, error
|
||||
|
||||
if command != "errors":
|
||||
return False
|
||||
@@ -170,8 +170,7 @@ def handle_command(command: str, args: list) -> bool:
|
||||
if sub in routes:
|
||||
return routes[sub](console, rest)
|
||||
|
||||
console.print(f"[red]Unknown subcommand: {sub}[/red]")
|
||||
console.print("Run [dim]drone @trigger errors help[/dim] for available commands")
|
||||
error(f"Unknown subcommand: {sub}", suggestion="Run 'drone @trigger errors help' for available commands")
|
||||
return True
|
||||
|
||||
|
||||
@@ -229,16 +228,15 @@ def _cmd_list(console, args: list) -> bool:
|
||||
def _cmd_detail(console, args: list) -> bool:
|
||||
"""Show full error details for a fingerprint or ID."""
|
||||
from rich.panel import Panel
|
||||
from aipass.cli.apps.modules import error
|
||||
|
||||
if not args:
|
||||
console.print("[red]Missing error ID or fingerprint[/red]")
|
||||
console.print("Usage: drone @trigger errors detail <id_or_fingerprint>")
|
||||
error("Missing error ID or fingerprint", suggestion="Usage: drone @trigger errors detail <id_or_fingerprint>")
|
||||
return True
|
||||
|
||||
entry = _find_by_id_or_fp(args[0])
|
||||
if not entry:
|
||||
console.print(f"[red]Error not found:[/red] {args[0]}")
|
||||
console.print(" [dim]Try a fingerprint prefix, full fingerprint, or short ID[/dim]")
|
||||
error(f"Error not found: {args[0]}", suggestion="Try a fingerprint prefix, full fingerprint, or short ID")
|
||||
return True
|
||||
|
||||
st = entry.get("status", "?")
|
||||
|
||||
@@ -27,7 +27,7 @@ from typing import List, Any
|
||||
from aipass.prax.apps.modules.logger import system_logger as logger
|
||||
|
||||
# CLI services for formatted output
|
||||
from aipass.cli.apps.modules import console, header
|
||||
from aipass.cli.apps.modules import console, header, error
|
||||
|
||||
# =============================================================================
|
||||
# MODULE DISCOVERY
|
||||
@@ -209,9 +209,7 @@ def main():
|
||||
return 0
|
||||
else:
|
||||
console.print()
|
||||
console.print(f"[red]Unknown command: {command}[/red]")
|
||||
console.print()
|
||||
console.print("Run [dim]python3 trigger.py --help[/dim] for available commands")
|
||||
error(f"Unknown command: {command}", suggestion="Run 'python3 trigger.py --help' for available commands")
|
||||
console.print()
|
||||
return 1
|
||||
|
||||
|
||||
Reference in New Issue
Block a user