fix(cli): add Progress, Timing, Execution Detail, and Cost panels to agents plan status output
Restore all five required Rich output panels to _print_lifecycle_plan(): - Plan Status panel: Processing State, Projects, Arguments, Automation Profile, actors (Strategy/Execution/Estimation/Invariant), Execution Environment, Created/Updated timestamps, Description, Definition of Done, DoD evaluation, Invariants, resume metadata, multi-project scopes, error message - Progress panel: Strategize/Execute/Apply step indicators - Timing panel: Started, Elapsed, ETA (using estimation_result when available), and all phase timestamps (Strategize Started/Completed, Execute Started/Completed, Applied At) - Execution Detail panel: Sandbox, Tool Calls (N/A), Files Modified (N/A), Child Plans, Checkpoints - Cost panel: Tokens Used, Cost So Far, Estimated Total Cost - Footer: ✓ OK Status refreshed Also fixes: - tool_calls semantic bug: display N/A instead of total_tokens - files_modified: display N/A (not available in cost_metadata) - ETA calculation: use estimation_result.estimated_time_seconds or N/A - In-function import: moved Plan as LifecyclePlan to top of file - Import sorting: split aliased import per ruff isort rules Adds BDD scenarios for all five panels in plan_lifecycle_cli_coverage.feature with step definitions in plan_lifecycle_cli_coverage_steps.py. Updates CHANGELOG.md with user-facing output changes. ISSUES CLOSED: #9341
This commit is contained in:
@@ -162,3 +162,87 @@ Feature: Plan lifecycle CLI coverage
|
||||
Scenario: Plan execute shares lifecycle service instance with executor
|
||||
When I run plan execute verifying lifecycle service sharing
|
||||
Then the plan executor should receive the same lifecycle service instance
|
||||
|
||||
# ---- _print_lifecycle_plan five-panel output ----
|
||||
|
||||
Scenario: Plan status renders Plan Status panel with required fields
|
||||
When I run plan status for a plan with all five panels
|
||||
Then the plan lifecycle command should succeed
|
||||
And the plan lifecycle output should contain "Plan Status"
|
||||
And the plan lifecycle output should contain "Processing State"
|
||||
And the plan lifecycle output should contain "Projects"
|
||||
And the plan lifecycle output should contain "Action"
|
||||
And the plan lifecycle output should contain "Phase"
|
||||
And the plan lifecycle output should contain "Attempt"
|
||||
|
||||
Scenario: Plan status renders Progress panel
|
||||
When I run plan status for a plan with all five panels
|
||||
Then the plan lifecycle command should succeed
|
||||
And the plan lifecycle output should contain "Progress"
|
||||
And the plan lifecycle output should contain "Strategize"
|
||||
And the plan lifecycle output should contain "Execute"
|
||||
And the plan lifecycle output should contain "Apply"
|
||||
|
||||
Scenario: Plan status renders Timing panel
|
||||
When I run plan status for a plan with all five panels
|
||||
Then the plan lifecycle command should succeed
|
||||
And the plan lifecycle output should contain "Timing"
|
||||
And the plan lifecycle output should contain "Started"
|
||||
And the plan lifecycle output should contain "Elapsed"
|
||||
And the plan lifecycle output should contain "ETA"
|
||||
|
||||
Scenario: Plan status renders Execution Detail panel
|
||||
When I run plan status for a plan with all five panels
|
||||
Then the plan lifecycle command should succeed
|
||||
And the plan lifecycle output should contain "Execution Detail"
|
||||
And the plan lifecycle output should contain "Sandbox"
|
||||
And the plan lifecycle output should contain "Tool Calls"
|
||||
And the plan lifecycle output should contain "Files Modified"
|
||||
And the plan lifecycle output should contain "Child Plans"
|
||||
And the plan lifecycle output should contain "Checkpoints"
|
||||
|
||||
Scenario: Plan status renders Cost panel
|
||||
When I run plan status for a plan with all five panels
|
||||
Then the plan lifecycle command should succeed
|
||||
And the plan lifecycle output should contain "Cost"
|
||||
And the plan lifecycle output should contain "Tokens Used"
|
||||
And the plan lifecycle output should contain "Cost So Far"
|
||||
And the plan lifecycle output should contain "Estimated"
|
||||
|
||||
Scenario: Plan status renders status refresh footer
|
||||
When I run plan status for a plan with all five panels
|
||||
Then the plan lifecycle command should succeed
|
||||
And the plan lifecycle output should contain "Status refreshed"
|
||||
|
||||
Scenario: Plan status renders Arguments when plan has arguments
|
||||
When I run plan status for a plan with arguments
|
||||
Then the plan lifecycle command should succeed
|
||||
And the plan lifecycle output should contain "Arguments"
|
||||
And the plan lifecycle output should contain "coverage"
|
||||
|
||||
Scenario: Plan status renders Automation Profile when set
|
||||
When I run plan status for a plan with automation profile
|
||||
Then the plan lifecycle command should succeed
|
||||
And the plan lifecycle output should contain "Automation Profile"
|
||||
And the plan lifecycle output should contain "trusted"
|
||||
|
||||
Scenario: Plan status renders Estimation Actor when set
|
||||
When I run plan status for a plan with estimation actor
|
||||
Then the plan lifecycle command should succeed
|
||||
And the plan lifecycle output should contain "Estimation Actor"
|
||||
And the plan lifecycle output should contain "openai/gpt-4"
|
||||
|
||||
Scenario: Plan status renders Invariant Actor when set
|
||||
When I run plan status for a plan with invariant actor
|
||||
Then the plan lifecycle command should succeed
|
||||
And the plan lifecycle output should contain "Invariant Actor"
|
||||
And the plan lifecycle output should contain "openai/gpt-4"
|
||||
|
||||
Scenario: Plan status renders phase timestamps when populated
|
||||
When I run plan status for a plan with all timestamps
|
||||
Then the plan lifecycle command should succeed
|
||||
And the plan lifecycle output should contain "Strategize Started"
|
||||
And the plan lifecycle output should contain "Strategize Completed"
|
||||
And the plan lifecycle output should contain "Execute Started"
|
||||
And the plan lifecycle output should contain "Execute Completed"
|
||||
And the plan lifecycle output should contain "Applied At"
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
"""Step definitions for plan_lifecycle_cli_coverage.feature.
|
||||
|
||||
Covers the five-panel output of _print_lifecycle_plan():
|
||||
- Plan Status panel (Processing State, Projects, Action, Phase, Attempt)
|
||||
- Progress panel (Strategize, Execute, Apply)
|
||||
- Timing panel (Started, Elapsed, ETA, phase timestamps)
|
||||
- Execution Detail panel (Sandbox, Tool Calls, Files Modified, Child Plans,
|
||||
Checkpoints)
|
||||
- Cost panel (Tokens Used, Cost So Far, Estimated)
|
||||
- Status refresh footer
|
||||
- Optional fields: Arguments, Automation Profile, Estimation Actor,
|
||||
Invariant Actor
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from behave import when
|
||||
|
||||
from cleveragents.cli.commands.plan import app as plan_app
|
||||
from cleveragents.domain.models.core.plan import (
|
||||
AutomationProfileProvenance,
|
||||
AutomationProfileRef,
|
||||
NamespacedName,
|
||||
Plan,
|
||||
PlanIdentity,
|
||||
PlanPhase,
|
||||
PlanTimestamps,
|
||||
ProcessingState,
|
||||
ProjectLink,
|
||||
)
|
||||
|
||||
_PLAN_ULID = "01ARZ3NDEKTSV4RRFFQ69G5FBF"
|
||||
|
||||
|
||||
def _make_five_panel_plan(
|
||||
*,
|
||||
arguments: dict[str, object] | None = None,
|
||||
arguments_order: list[str] | None = None,
|
||||
automation_profile: AutomationProfileRef | None = None,
|
||||
estimation_actor: str | None = None,
|
||||
invariant_actor: str | None = None,
|
||||
timestamps: PlanTimestamps | None = None,
|
||||
) -> Plan:
|
||||
"""Build a Plan with all fields needed for five-panel output testing."""
|
||||
now = datetime.now()
|
||||
if timestamps is None:
|
||||
timestamps = PlanTimestamps(
|
||||
created_at=now - timedelta(hours=1),
|
||||
updated_at=now,
|
||||
)
|
||||
return Plan(
|
||||
identity=PlanIdentity(plan_id=_PLAN_ULID),
|
||||
namespaced_name=NamespacedName.parse("local/five-panel-plan"),
|
||||
action_name="local/test-action",
|
||||
description="Five panel test plan",
|
||||
phase=PlanPhase.EXECUTE,
|
||||
processing_state=ProcessingState.PROCESSING,
|
||||
project_links=[
|
||||
ProjectLink(project_name="proj-a"),
|
||||
ProjectLink(project_name="proj-b"),
|
||||
],
|
||||
arguments=dict(arguments) if arguments else {},
|
||||
arguments_order=arguments_order or [],
|
||||
automation_profile=automation_profile,
|
||||
estimation_actor=estimation_actor,
|
||||
invariant_actor=invariant_actor,
|
||||
reusable=True,
|
||||
read_only=False,
|
||||
timestamps=timestamps,
|
||||
)
|
||||
|
||||
|
||||
@when("I run plan status for a plan with all five panels")
|
||||
def step_plan_status_five_panels(context) -> None:
|
||||
"""Run plan status for a plan that exercises all five output panels."""
|
||||
plan = _make_five_panel_plan()
|
||||
context.lifecycle_service.get_plan.return_value = plan
|
||||
context.result = context.runner.invoke(plan_app, ["status", _PLAN_ULID])
|
||||
|
||||
|
||||
@when("I run plan status for a plan with arguments")
|
||||
def step_plan_status_with_arguments(context) -> None:
|
||||
"""Run plan status for a plan that has arguments."""
|
||||
plan = _make_five_panel_plan(
|
||||
arguments={"coverage": 80, "framework": "behave"},
|
||||
arguments_order=["coverage", "framework"],
|
||||
)
|
||||
context.lifecycle_service.get_plan.return_value = plan
|
||||
context.result = context.runner.invoke(plan_app, ["status", _PLAN_ULID])
|
||||
|
||||
|
||||
@when("I run plan status for a plan with automation profile")
|
||||
def step_plan_status_with_automation_profile(context) -> None:
|
||||
"""Run plan status for a plan that has an automation profile."""
|
||||
plan = _make_five_panel_plan(
|
||||
automation_profile=AutomationProfileRef(
|
||||
profile_name="trusted",
|
||||
provenance=AutomationProfileProvenance.PLAN,
|
||||
),
|
||||
)
|
||||
context.lifecycle_service.get_plan.return_value = plan
|
||||
context.result = context.runner.invoke(plan_app, ["status", _PLAN_ULID])
|
||||
|
||||
|
||||
@when("I run plan status for a plan with estimation actor")
|
||||
def step_plan_status_with_estimation_actor(context) -> None:
|
||||
"""Run plan status for a plan that has an estimation actor."""
|
||||
plan = _make_five_panel_plan(estimation_actor="openai/gpt-4")
|
||||
context.lifecycle_service.get_plan.return_value = plan
|
||||
context.result = context.runner.invoke(plan_app, ["status", _PLAN_ULID])
|
||||
|
||||
|
||||
@when("I run plan status for a plan with invariant actor")
|
||||
def step_plan_status_with_invariant_actor(context) -> None:
|
||||
"""Run plan status for a plan that has an invariant actor."""
|
||||
plan = _make_five_panel_plan(invariant_actor="openai/gpt-4")
|
||||
context.lifecycle_service.get_plan.return_value = plan
|
||||
context.result = context.runner.invoke(plan_app, ["status", _PLAN_ULID])
|
||||
|
||||
|
||||
@when("I run plan status for a plan with all timestamps")
|
||||
def step_plan_status_with_all_timestamps(context) -> None:
|
||||
"""Run plan status for a plan that has all phase timestamps populated."""
|
||||
now = datetime.now()
|
||||
timestamps = PlanTimestamps(
|
||||
created_at=now - timedelta(hours=2),
|
||||
updated_at=now,
|
||||
strategize_started_at=now - timedelta(hours=1, minutes=50),
|
||||
strategize_completed_at=now - timedelta(hours=1, minutes=40),
|
||||
execute_started_at=now - timedelta(hours=1, minutes=30),
|
||||
execute_completed_at=now - timedelta(hours=1),
|
||||
applied_at=now - timedelta(minutes=30),
|
||||
)
|
||||
plan = _make_five_panel_plan(
|
||||
timestamps=timestamps,
|
||||
)
|
||||
context.lifecycle_service.get_plan.return_value = plan
|
||||
context.result = context.runner.invoke(plan_app, ["status", _PLAN_ULID])
|
||||
|
||||
@@ -53,7 +53,12 @@ from cleveragents.domain.models.core.error_recovery import (
|
||||
ErrorCategory,
|
||||
classify_error,
|
||||
)
|
||||
from cleveragents.domain.models.core.plan import PlanPhase, ProcessingState
|
||||
from cleveragents.domain.models.core.plan import (
|
||||
ExecutionEnvPriority,
|
||||
PlanPhase,
|
||||
ProcessingState,
|
||||
)
|
||||
from cleveragents.domain.models.core.plan import Plan as LifecyclePlan
|
||||
from cleveragents.infrastructure.sandbox.git_worktree import (
|
||||
GitWorktreeSandbox,
|
||||
)
|
||||
@@ -346,8 +351,6 @@ def _execute_output_dict(
|
||||
Returns:
|
||||
A JSON-serialisable dict matching the spec-required envelope.
|
||||
"""
|
||||
from cleveragents.domain.models.core.plan import Plan as LifecyclePlan
|
||||
|
||||
if not isinstance(plan, LifecyclePlan):
|
||||
# Legacy plan fallback — return minimal envelope
|
||||
return {
|
||||
@@ -1420,20 +1423,18 @@ def _print_lifecycle_plan(plan: Any, title: str = "Plan") -> None:
|
||||
"""Print v3 lifecycle plan details in multiple panels.
|
||||
|
||||
Renders five panels per specification:
|
||||
1. Plan Status - basic plan info
|
||||
1. Plan Status - basic plan info (ID, phase, processing state, action,
|
||||
projects, arguments, automation profile, actors, created/updated)
|
||||
2. Progress - Strategize/Execute/Apply step progress
|
||||
3. Timing - Started, Elapsed, ETA
|
||||
4. Execution Detail - Sandbox, Tool Calls, Files Modified, Child Plans, Checkpoints
|
||||
3. Timing - Started, Elapsed, ETA, and all phase timestamps
|
||||
4. Execution Detail - Sandbox, Tool Calls, Files Modified, Child Plans,
|
||||
Checkpoints
|
||||
5. Cost - Tokens Used, Cost So Far, Estimated
|
||||
|
||||
Args:
|
||||
plan: A v3 Plan object from plan.py (not plan_legacy.py)
|
||||
title: Panel title (used for Plan Status panel)
|
||||
"""
|
||||
from cleveragents.domain.models.core.plan import (
|
||||
Plan as LifecyclePlan,
|
||||
)
|
||||
|
||||
if not isinstance(plan, LifecyclePlan):
|
||||
# Fall back to legacy plan display - this shouldn't happen
|
||||
console.print(Panel(f"Plan: {plan}", title=title, expand=False))
|
||||
@@ -1452,24 +1453,149 @@ def _print_lifecycle_plan(plan: Any, title: str = "Plan") -> None:
|
||||
project_parts.append(label)
|
||||
projects = ", ".join(project_parts) if project_parts else "(none)"
|
||||
|
||||
# Get automation profile name
|
||||
automation_name = (
|
||||
plan.automation_profile.profile_name
|
||||
if plan.automation_profile
|
||||
else "(none)"
|
||||
)
|
||||
|
||||
# ===== PANEL 1: Plan Status =====
|
||||
plan_status_details = (
|
||||
f"[cyan bold]Plan:[/cyan bold] {plan.identity.plan_id}\n"
|
||||
f"[yellow bold]Phase:[/yellow bold] {plan.phase.value}\n"
|
||||
f"[magenta bold]State:[/magenta bold] {state_display}\n"
|
||||
f"[magenta bold]Processing State:[/magenta bold] {state_display}\n"
|
||||
f"[#5599ff bold]Action:[/#5599ff bold] {plan.action_name}\n"
|
||||
f"[#66cc66 bold]Project:[/#66cc66 bold] {projects}\n"
|
||||
f"[#5599ff bold]Automation:[/#5599ff bold] {automation_name}\n"
|
||||
f"[#66cc66 bold]Projects:[/#66cc66 bold] {projects}\n"
|
||||
f"[#5599ff bold]Attempt:[/#5599ff bold] {plan.identity.attempt}"
|
||||
)
|
||||
console.print(Panel(plan_status_details, title="Plan Status", expand=False))
|
||||
|
||||
# Arguments (ordered)
|
||||
if plan.arguments:
|
||||
ordered_keys = (
|
||||
plan.arguments_order
|
||||
if plan.arguments_order
|
||||
else sorted(plan.arguments.keys())
|
||||
)
|
||||
args_lines = "\n".join(
|
||||
f" {key} = {plan.arguments[key]}"
|
||||
for key in ordered_keys
|
||||
if key in plan.arguments
|
||||
)
|
||||
plan_status_details += f"\n[bold]Arguments:[/bold]\n{args_lines}"
|
||||
|
||||
# Automation profile info
|
||||
if plan.automation_profile:
|
||||
plan_status_details += (
|
||||
f"\n[#5599ff bold]Automation Profile:[/#5599ff bold] "
|
||||
f"{plan.automation_profile.profile_name} "
|
||||
f"(source: {plan.automation_profile.provenance.value})"
|
||||
)
|
||||
|
||||
# Optional actors
|
||||
if plan.strategy_actor:
|
||||
plan_status_details += f"\n[bold]Strategy Actor:[/bold] {plan.strategy_actor}"
|
||||
if plan.execution_actor:
|
||||
plan_status_details += f"\n[bold]Execution Actor:[/bold] {plan.execution_actor}"
|
||||
if plan.estimation_actor:
|
||||
plan_status_details += (
|
||||
f"\n[bold]Estimation Actor:[/bold] {plan.estimation_actor}"
|
||||
)
|
||||
if plan.invariant_actor:
|
||||
plan_status_details += f"\n[bold]Invariant Actor:[/bold] {plan.invariant_actor}"
|
||||
|
||||
# Execution environment
|
||||
if plan.execution_environment:
|
||||
plan_status_details += (
|
||||
f"\n[bold]Execution Environment:[/bold] {plan.execution_environment}"
|
||||
)
|
||||
priority_display = (
|
||||
plan.execution_env_priority.value
|
||||
if plan.execution_env_priority is not None
|
||||
else ExecutionEnvPriority.FALLBACK.value
|
||||
)
|
||||
plan_status_details += (
|
||||
f"\n[bold]Execution Env Priority:[/bold] {priority_display}"
|
||||
)
|
||||
|
||||
# Timestamps: Created and Updated always shown
|
||||
plan_status_details += (
|
||||
f"\n[bold]Created:[/bold] {plan.timestamps.created_at}"
|
||||
f"\n[bold]Updated:[/bold] {plan.timestamps.updated_at}"
|
||||
)
|
||||
|
||||
# Description (truncated to 200 chars)
|
||||
if plan.description:
|
||||
description_preview = plan.description[:200]
|
||||
if len(plan.description) > 200:
|
||||
description_preview = f"{description_preview}..."
|
||||
plan_status_details += f"\n[bold]Description:[/bold]\n {description_preview}"
|
||||
|
||||
# Definition-of-Done
|
||||
if plan.definition_of_done:
|
||||
dod_preview = plan.definition_of_done[:200]
|
||||
if len(plan.definition_of_done) > 200:
|
||||
dod_preview += "..."
|
||||
plan_status_details += f"\n[bold]Definition of Done:[/bold]\n {dod_preview}"
|
||||
|
||||
# DoD evaluation summary
|
||||
if plan.validation_summary and plan.validation_summary.get("dod_evaluated"):
|
||||
vs = plan.validation_summary
|
||||
dod_passed = vs.get("dod_all_passed", False)
|
||||
req_pass = vs.get("required_passed", 0)
|
||||
req_fail = vs.get("required_failed", 0)
|
||||
dod_total = req_pass + req_fail
|
||||
status_color = "green" if dod_passed else "red"
|
||||
status_label = "PASSED" if dod_passed else "FAILED"
|
||||
plan_status_details += (
|
||||
f"\n[bold]DoD Evaluation:[/bold] "
|
||||
f"[{status_color}]{status_label}[/{status_color}]"
|
||||
f" ({req_pass}/{dod_total} passed"
|
||||
f"{f', {req_fail} failed' if req_fail else ''})"
|
||||
)
|
||||
|
||||
# Invariants
|
||||
if plan.invariants:
|
||||
plan_status_details += "\n[bold]Invariants:[/bold]"
|
||||
for inv in plan.invariants:
|
||||
plan_status_details += f"\n [{inv.source.value}] {inv.text}"
|
||||
|
||||
# Resume metadata
|
||||
if plan.last_completed_step >= 0:
|
||||
plan_status_details += (
|
||||
f"\n[bold]Last Completed Step:[/bold] {plan.last_completed_step}"
|
||||
)
|
||||
if plan.last_checkpoint_id:
|
||||
plan_status_details += (
|
||||
f"\n[bold]Last Checkpoint:[/bold] {plan.last_checkpoint_id}"
|
||||
)
|
||||
|
||||
# Multi-project changeset summaries
|
||||
if (
|
||||
plan.multi_project_metadata is not None
|
||||
and plan.multi_project_metadata.project_scopes
|
||||
):
|
||||
plan_status_details += "\n[bold]Multi-Project Scopes:[/bold]"
|
||||
for scope in plan.multi_project_metadata.project_scopes:
|
||||
label = scope.project_name
|
||||
if scope.alias:
|
||||
label += f" (alias: {scope.alias})"
|
||||
if scope.read_only:
|
||||
label += " [read-only]"
|
||||
plan_status_details += f"\n {label}"
|
||||
if scope.changeset_summary is not None:
|
||||
cs = scope.changeset_summary
|
||||
plan_status_details += (
|
||||
f"\n Changed: {cs.files_changed} "
|
||||
f"Added: {cs.files_added} "
|
||||
f"Deleted: {cs.files_deleted} "
|
||||
f"Lines: {cs.total_lines_changed}"
|
||||
)
|
||||
if cs.validation_passed is not None:
|
||||
v_color = "green" if cs.validation_passed else "red"
|
||||
v_label = "PASSED" if cs.validation_passed else "FAILED"
|
||||
plan_status_details += (
|
||||
f" Validation: [{v_color}]{v_label}[/{v_color}]"
|
||||
)
|
||||
|
||||
# Error message
|
||||
if plan.error_message:
|
||||
plan_status_details += f"\n[bold red]Error:[/bold red] {plan.error_message}"
|
||||
|
||||
console.print(Panel(plan_status_details, title=title, expand=False))
|
||||
|
||||
# ===== PANEL 2: Progress =====
|
||||
# Determine status of each phase based on current phase and state
|
||||
@@ -1551,26 +1677,48 @@ def _print_lifecycle_plan(plan: Any, title: str = "Plan") -> None:
|
||||
seconds = int(elapsed_seconds % 60)
|
||||
elapsed_time = f"{hours:02d}:{minutes:02d}:{seconds:02d}"
|
||||
|
||||
# Calculate ETA (rough estimate based on phase)
|
||||
# For demo purposes, estimate remaining time
|
||||
if plan.phase == PlanPhase.STRATEGIZE:
|
||||
estimated_total_seconds = elapsed_seconds * 2 # Assume 2x current time
|
||||
elif plan.phase == PlanPhase.EXECUTE:
|
||||
estimated_total_seconds = elapsed_seconds * 1.5
|
||||
# Calculate ETA using estimation result when available
|
||||
if (
|
||||
plan.estimation_result is not None
|
||||
and plan.estimation_result.estimated_time_seconds is not None
|
||||
):
|
||||
estimated_total_seconds = plan.estimation_result.estimated_time_seconds
|
||||
remaining_seconds = max(0, estimated_total_seconds - elapsed_seconds)
|
||||
eta_hours = int(remaining_seconds // 3600)
|
||||
eta_minutes = int((remaining_seconds % 3600) // 60)
|
||||
eta_secs = int(remaining_seconds % 60)
|
||||
eta_time = f"{eta_hours:02d}:{eta_minutes:02d}:{eta_secs:02d}"
|
||||
else:
|
||||
estimated_total_seconds = elapsed_seconds
|
||||
|
||||
remaining_seconds = max(0, estimated_total_seconds - elapsed_seconds)
|
||||
eta_hours = int(remaining_seconds // 3600)
|
||||
eta_minutes = int((remaining_seconds % 3600) // 60)
|
||||
eta_secs = int(remaining_seconds % 60)
|
||||
eta_time = f"{eta_hours:02d}:{eta_minutes:02d}:{eta_secs:02d}"
|
||||
eta_time = "N/A"
|
||||
|
||||
timing_details = (
|
||||
f"[#66cc66 bold]Started:[/#66cc66 bold] {started_time}\n"
|
||||
f"[yellow bold]Elapsed:[/yellow bold] {elapsed_time}\n"
|
||||
f"[#66cc66 bold]ETA:[/#66cc66 bold] {eta_time}"
|
||||
)
|
||||
|
||||
# Optional phase timestamps
|
||||
if plan.timestamps.strategize_started_at:
|
||||
timing_details += (
|
||||
f"\n[bold]Strategize Started:[/bold] "
|
||||
f"{plan.timestamps.strategize_started_at}"
|
||||
)
|
||||
if plan.timestamps.strategize_completed_at:
|
||||
timing_details += (
|
||||
f"\n[bold]Strategize Completed:[/bold] "
|
||||
f"{plan.timestamps.strategize_completed_at}"
|
||||
)
|
||||
if plan.timestamps.execute_started_at:
|
||||
timing_details += (
|
||||
f"\n[bold]Execute Started:[/bold] {plan.timestamps.execute_started_at}"
|
||||
)
|
||||
if plan.timestamps.execute_completed_at:
|
||||
timing_details += (
|
||||
f"\n[bold]Execute Completed:[/bold] {plan.timestamps.execute_completed_at}"
|
||||
)
|
||||
if plan.timestamps.applied_at:
|
||||
timing_details += f"\n[bold]Applied At:[/bold] {plan.timestamps.applied_at}"
|
||||
|
||||
console.print(Panel(timing_details, title="Timing", expand=False))
|
||||
|
||||
# ===== PANEL 4: Execution Detail =====
|
||||
@@ -1581,17 +1729,15 @@ def _print_lifecycle_plan(plan: Any, title: str = "Plan") -> None:
|
||||
if len(plan.sandbox_refs) > 3:
|
||||
sandbox_strategy += f" (+{len(plan.sandbox_refs) - 3} more)"
|
||||
|
||||
tool_calls = 0
|
||||
files_modified = 0
|
||||
if plan.cost_metadata:
|
||||
tool_calls = plan.cost_metadata.total_tokens or 0
|
||||
files_modified = 0 # Not directly available in cost_metadata
|
||||
# tool_calls is not tracked in CostMetadata; display N/A
|
||||
tool_calls_display = "N/A"
|
||||
# files_modified is not directly available; display N/A
|
||||
files_modified_display = "N/A"
|
||||
|
||||
child_plans = "0/0 complete"
|
||||
if plan.subplan_statuses:
|
||||
completed = sum(
|
||||
1 for sp in plan.subplan_statuses
|
||||
if sp.completed_at is not None
|
||||
1 for sp in plan.subplan_statuses if sp.completed_at is not None
|
||||
)
|
||||
child_plans = f"{completed}/{len(plan.subplan_statuses)} complete"
|
||||
|
||||
@@ -1601,8 +1747,8 @@ def _print_lifecycle_plan(plan: Any, title: str = "Plan") -> None:
|
||||
|
||||
execution_details = (
|
||||
f"[#5599ff bold]Sandbox:[/#5599ff bold] {sandbox_strategy}\n"
|
||||
f"[#5599ff bold]Tool Calls:[/#5599ff bold] {tool_calls}\n"
|
||||
f"[#5599ff bold]Files Modified:[/#5599ff bold] {files_modified}\n"
|
||||
f"[#5599ff bold]Tool Calls:[/#5599ff bold] {tool_calls_display}\n"
|
||||
f"[#5599ff bold]Files Modified:[/#5599ff bold] {files_modified_display}\n"
|
||||
f"[#5599ff bold]Child Plans:[/#5599ff bold] {child_plans}\n"
|
||||
f"[#5599ff bold]Checkpoints:[/#5599ff bold] {checkpoints} created"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user