empathy-framework 2.4.0__py3-none-any.whl → 3.8.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- coach_wizards/__init__.py +13 -12
- coach_wizards/accessibility_wizard.py +12 -12
- coach_wizards/api_wizard.py +12 -12
- coach_wizards/base_wizard.py +26 -20
- coach_wizards/cicd_wizard.py +15 -13
- coach_wizards/code_reviewer_README.md +60 -0
- coach_wizards/code_reviewer_wizard.py +180 -0
- coach_wizards/compliance_wizard.py +12 -12
- coach_wizards/database_wizard.py +12 -12
- coach_wizards/debugging_wizard.py +12 -12
- coach_wizards/documentation_wizard.py +12 -12
- coach_wizards/generate_wizards.py +1 -2
- coach_wizards/localization_wizard.py +101 -19
- coach_wizards/migration_wizard.py +12 -12
- coach_wizards/monitoring_wizard.py +12 -12
- coach_wizards/observability_wizard.py +12 -12
- coach_wizards/performance_wizard.py +12 -12
- coach_wizards/prompt_engineering_wizard.py +661 -0
- coach_wizards/refactoring_wizard.py +12 -12
- coach_wizards/scaling_wizard.py +12 -12
- coach_wizards/security_wizard.py +12 -12
- coach_wizards/testing_wizard.py +12 -12
- empathy_framework-3.8.2.dist-info/METADATA +1176 -0
- empathy_framework-3.8.2.dist-info/RECORD +333 -0
- empathy_framework-3.8.2.dist-info/entry_points.txt +22 -0
- {empathy_framework-2.4.0.dist-info → empathy_framework-3.8.2.dist-info}/top_level.txt +5 -1
- empathy_healthcare_plugin/__init__.py +1 -2
- empathy_healthcare_plugin/monitors/__init__.py +9 -0
- empathy_healthcare_plugin/monitors/clinical_protocol_monitor.py +315 -0
- empathy_healthcare_plugin/monitors/monitoring/__init__.py +44 -0
- empathy_healthcare_plugin/monitors/monitoring/protocol_checker.py +300 -0
- empathy_healthcare_plugin/monitors/monitoring/protocol_loader.py +214 -0
- empathy_healthcare_plugin/monitors/monitoring/sensor_parsers.py +306 -0
- empathy_healthcare_plugin/monitors/monitoring/trajectory_analyzer.py +389 -0
- empathy_llm_toolkit/__init__.py +7 -7
- empathy_llm_toolkit/agent_factory/__init__.py +53 -0
- empathy_llm_toolkit/agent_factory/adapters/__init__.py +85 -0
- empathy_llm_toolkit/agent_factory/adapters/autogen_adapter.py +312 -0
- empathy_llm_toolkit/agent_factory/adapters/crewai_adapter.py +454 -0
- empathy_llm_toolkit/agent_factory/adapters/haystack_adapter.py +298 -0
- empathy_llm_toolkit/agent_factory/adapters/langchain_adapter.py +362 -0
- empathy_llm_toolkit/agent_factory/adapters/langgraph_adapter.py +333 -0
- empathy_llm_toolkit/agent_factory/adapters/native.py +228 -0
- empathy_llm_toolkit/agent_factory/adapters/wizard_adapter.py +426 -0
- empathy_llm_toolkit/agent_factory/base.py +305 -0
- empathy_llm_toolkit/agent_factory/crews/__init__.py +67 -0
- empathy_llm_toolkit/agent_factory/crews/code_review.py +1113 -0
- empathy_llm_toolkit/agent_factory/crews/health_check.py +1246 -0
- empathy_llm_toolkit/agent_factory/crews/refactoring.py +1128 -0
- empathy_llm_toolkit/agent_factory/crews/security_audit.py +1018 -0
- empathy_llm_toolkit/agent_factory/decorators.py +286 -0
- empathy_llm_toolkit/agent_factory/factory.py +558 -0
- empathy_llm_toolkit/agent_factory/framework.py +192 -0
- empathy_llm_toolkit/agent_factory/memory_integration.py +324 -0
- empathy_llm_toolkit/agent_factory/resilient.py +320 -0
- empathy_llm_toolkit/claude_memory.py +14 -15
- empathy_llm_toolkit/cli/__init__.py +8 -0
- empathy_llm_toolkit/cli/sync_claude.py +487 -0
- empathy_llm_toolkit/code_health.py +186 -28
- empathy_llm_toolkit/config/__init__.py +29 -0
- empathy_llm_toolkit/config/unified.py +295 -0
- empathy_llm_toolkit/contextual_patterns.py +11 -12
- empathy_llm_toolkit/core.py +168 -53
- empathy_llm_toolkit/git_pattern_extractor.py +17 -13
- empathy_llm_toolkit/levels.py +6 -13
- empathy_llm_toolkit/pattern_confidence.py +14 -18
- empathy_llm_toolkit/pattern_resolver.py +10 -12
- empathy_llm_toolkit/pattern_summary.py +16 -14
- empathy_llm_toolkit/providers.py +194 -28
- empathy_llm_toolkit/routing/__init__.py +32 -0
- empathy_llm_toolkit/routing/model_router.py +362 -0
- empathy_llm_toolkit/security/IMPLEMENTATION_SUMMARY.md +413 -0
- empathy_llm_toolkit/security/PHASE2_COMPLETE.md +384 -0
- empathy_llm_toolkit/security/PHASE2_SECRETS_DETECTOR_COMPLETE.md +271 -0
- empathy_llm_toolkit/security/QUICK_REFERENCE.md +316 -0
- empathy_llm_toolkit/security/README.md +262 -0
- empathy_llm_toolkit/security/__init__.py +62 -0
- empathy_llm_toolkit/security/audit_logger.py +929 -0
- empathy_llm_toolkit/security/audit_logger_example.py +152 -0
- empathy_llm_toolkit/security/pii_scrubber.py +640 -0
- empathy_llm_toolkit/security/secrets_detector.py +678 -0
- empathy_llm_toolkit/security/secrets_detector_example.py +304 -0
- empathy_llm_toolkit/security/secure_memdocs.py +1192 -0
- empathy_llm_toolkit/security/secure_memdocs_example.py +278 -0
- empathy_llm_toolkit/session_status.py +20 -22
- empathy_llm_toolkit/state.py +28 -21
- empathy_llm_toolkit/wizards/__init__.py +38 -0
- empathy_llm_toolkit/wizards/base_wizard.py +364 -0
- empathy_llm_toolkit/wizards/customer_support_wizard.py +190 -0
- empathy_llm_toolkit/wizards/healthcare_wizard.py +362 -0
- empathy_llm_toolkit/wizards/patient_assessment_README.md +64 -0
- empathy_llm_toolkit/wizards/patient_assessment_wizard.py +193 -0
- empathy_llm_toolkit/wizards/technology_wizard.py +194 -0
- empathy_os/__init__.py +125 -84
- empathy_os/adaptive/__init__.py +13 -0
- empathy_os/adaptive/task_complexity.py +127 -0
- empathy_os/{monitoring.py → agent_monitoring.py} +28 -28
- empathy_os/cache/__init__.py +117 -0
- empathy_os/cache/base.py +166 -0
- empathy_os/cache/dependency_manager.py +253 -0
- empathy_os/cache/hash_only.py +248 -0
- empathy_os/cache/hybrid.py +390 -0
- empathy_os/cache/storage.py +282 -0
- empathy_os/cli.py +1516 -70
- empathy_os/cli_unified.py +597 -0
- empathy_os/config/__init__.py +63 -0
- empathy_os/config/xml_config.py +239 -0
- empathy_os/config.py +95 -37
- empathy_os/coordination.py +72 -68
- empathy_os/core.py +94 -107
- empathy_os/cost_tracker.py +74 -55
- empathy_os/dashboard/__init__.py +15 -0
- empathy_os/dashboard/server.py +743 -0
- empathy_os/discovery.py +17 -14
- empathy_os/emergence.py +21 -22
- empathy_os/exceptions.py +18 -30
- empathy_os/feedback_loops.py +30 -33
- empathy_os/levels.py +32 -35
- empathy_os/leverage_points.py +31 -32
- empathy_os/logging_config.py +19 -16
- empathy_os/memory/__init__.py +195 -0
- empathy_os/memory/claude_memory.py +466 -0
- empathy_os/memory/config.py +224 -0
- empathy_os/memory/control_panel.py +1298 -0
- empathy_os/memory/edges.py +179 -0
- empathy_os/memory/graph.py +567 -0
- empathy_os/memory/long_term.py +1194 -0
- empathy_os/memory/nodes.py +179 -0
- empathy_os/memory/redis_bootstrap.py +540 -0
- empathy_os/memory/security/__init__.py +31 -0
- empathy_os/memory/security/audit_logger.py +930 -0
- empathy_os/memory/security/pii_scrubber.py +640 -0
- empathy_os/memory/security/secrets_detector.py +678 -0
- empathy_os/memory/short_term.py +2119 -0
- empathy_os/memory/storage/__init__.py +15 -0
- empathy_os/memory/summary_index.py +583 -0
- empathy_os/memory/unified.py +619 -0
- empathy_os/metrics/__init__.py +12 -0
- empathy_os/metrics/prompt_metrics.py +190 -0
- empathy_os/models/__init__.py +136 -0
- empathy_os/models/__main__.py +13 -0
- empathy_os/models/cli.py +655 -0
- empathy_os/models/empathy_executor.py +354 -0
- empathy_os/models/executor.py +252 -0
- empathy_os/models/fallback.py +671 -0
- empathy_os/models/provider_config.py +563 -0
- empathy_os/models/registry.py +382 -0
- empathy_os/models/tasks.py +302 -0
- empathy_os/models/telemetry.py +548 -0
- empathy_os/models/token_estimator.py +378 -0
- empathy_os/models/validation.py +274 -0
- empathy_os/monitoring/__init__.py +52 -0
- empathy_os/monitoring/alerts.py +23 -0
- empathy_os/monitoring/alerts_cli.py +268 -0
- empathy_os/monitoring/multi_backend.py +271 -0
- empathy_os/monitoring/otel_backend.py +363 -0
- empathy_os/optimization/__init__.py +19 -0
- empathy_os/optimization/context_optimizer.py +272 -0
- empathy_os/pattern_library.py +30 -29
- empathy_os/persistence.py +35 -37
- empathy_os/platform_utils.py +261 -0
- empathy_os/plugins/__init__.py +28 -0
- empathy_os/plugins/base.py +361 -0
- empathy_os/plugins/registry.py +268 -0
- empathy_os/project_index/__init__.py +30 -0
- empathy_os/project_index/cli.py +335 -0
- empathy_os/project_index/crew_integration.py +430 -0
- empathy_os/project_index/index.py +425 -0
- empathy_os/project_index/models.py +501 -0
- empathy_os/project_index/reports.py +473 -0
- empathy_os/project_index/scanner.py +538 -0
- empathy_os/prompts/__init__.py +61 -0
- empathy_os/prompts/config.py +77 -0
- empathy_os/prompts/context.py +177 -0
- empathy_os/prompts/parser.py +285 -0
- empathy_os/prompts/registry.py +313 -0
- empathy_os/prompts/templates.py +208 -0
- empathy_os/redis_config.py +144 -58
- empathy_os/redis_memory.py +79 -77
- empathy_os/resilience/__init__.py +56 -0
- empathy_os/resilience/circuit_breaker.py +256 -0
- empathy_os/resilience/fallback.py +179 -0
- empathy_os/resilience/health.py +300 -0
- empathy_os/resilience/retry.py +209 -0
- empathy_os/resilience/timeout.py +135 -0
- empathy_os/routing/__init__.py +43 -0
- empathy_os/routing/chain_executor.py +433 -0
- empathy_os/routing/classifier.py +217 -0
- empathy_os/routing/smart_router.py +234 -0
- empathy_os/routing/wizard_registry.py +307 -0
- empathy_os/templates.py +19 -14
- empathy_os/trust/__init__.py +28 -0
- empathy_os/trust/circuit_breaker.py +579 -0
- empathy_os/trust_building.py +67 -58
- empathy_os/validation/__init__.py +19 -0
- empathy_os/validation/xml_validator.py +281 -0
- empathy_os/wizard_factory_cli.py +170 -0
- empathy_os/{workflows.py → workflow_commands.py} +131 -37
- empathy_os/workflows/__init__.py +360 -0
- empathy_os/workflows/base.py +1660 -0
- empathy_os/workflows/bug_predict.py +962 -0
- empathy_os/workflows/code_review.py +960 -0
- empathy_os/workflows/code_review_adapters.py +310 -0
- empathy_os/workflows/code_review_pipeline.py +720 -0
- empathy_os/workflows/config.py +600 -0
- empathy_os/workflows/dependency_check.py +648 -0
- empathy_os/workflows/document_gen.py +1069 -0
- empathy_os/workflows/documentation_orchestrator.py +1205 -0
- empathy_os/workflows/health_check.py +679 -0
- empathy_os/workflows/keyboard_shortcuts/__init__.py +39 -0
- empathy_os/workflows/keyboard_shortcuts/generators.py +386 -0
- empathy_os/workflows/keyboard_shortcuts/parsers.py +414 -0
- empathy_os/workflows/keyboard_shortcuts/prompts.py +295 -0
- empathy_os/workflows/keyboard_shortcuts/schema.py +193 -0
- empathy_os/workflows/keyboard_shortcuts/workflow.py +505 -0
- empathy_os/workflows/manage_documentation.py +804 -0
- empathy_os/workflows/new_sample_workflow1.py +146 -0
- empathy_os/workflows/new_sample_workflow1_README.md +150 -0
- empathy_os/workflows/perf_audit.py +687 -0
- empathy_os/workflows/pr_review.py +748 -0
- empathy_os/workflows/progress.py +445 -0
- empathy_os/workflows/progress_server.py +322 -0
- empathy_os/workflows/refactor_plan.py +693 -0
- empathy_os/workflows/release_prep.py +808 -0
- empathy_os/workflows/research_synthesis.py +404 -0
- empathy_os/workflows/secure_release.py +585 -0
- empathy_os/workflows/security_adapters.py +297 -0
- empathy_os/workflows/security_audit.py +1046 -0
- empathy_os/workflows/step_config.py +234 -0
- empathy_os/workflows/test5.py +125 -0
- empathy_os/workflows/test5_README.md +158 -0
- empathy_os/workflows/test_gen.py +1855 -0
- empathy_os/workflows/test_lifecycle.py +526 -0
- empathy_os/workflows/test_maintenance.py +626 -0
- empathy_os/workflows/test_maintenance_cli.py +590 -0
- empathy_os/workflows/test_maintenance_crew.py +821 -0
- empathy_os/workflows/xml_enhanced_crew.py +285 -0
- empathy_software_plugin/__init__.py +1 -2
- empathy_software_plugin/cli/__init__.py +120 -0
- empathy_software_plugin/cli/inspect.py +362 -0
- empathy_software_plugin/cli.py +49 -27
- empathy_software_plugin/plugin.py +4 -8
- empathy_software_plugin/wizards/__init__.py +42 -0
- empathy_software_plugin/wizards/advanced_debugging_wizard.py +392 -0
- empathy_software_plugin/wizards/agent_orchestration_wizard.py +511 -0
- empathy_software_plugin/wizards/ai_collaboration_wizard.py +503 -0
- empathy_software_plugin/wizards/ai_context_wizard.py +441 -0
- empathy_software_plugin/wizards/ai_documentation_wizard.py +503 -0
- empathy_software_plugin/wizards/base_wizard.py +288 -0
- empathy_software_plugin/wizards/book_chapter_wizard.py +519 -0
- empathy_software_plugin/wizards/code_review_wizard.py +606 -0
- empathy_software_plugin/wizards/debugging/__init__.py +50 -0
- empathy_software_plugin/wizards/debugging/bug_risk_analyzer.py +414 -0
- empathy_software_plugin/wizards/debugging/config_loaders.py +442 -0
- empathy_software_plugin/wizards/debugging/fix_applier.py +469 -0
- empathy_software_plugin/wizards/debugging/language_patterns.py +383 -0
- empathy_software_plugin/wizards/debugging/linter_parsers.py +470 -0
- empathy_software_plugin/wizards/debugging/verification.py +369 -0
- empathy_software_plugin/wizards/enhanced_testing_wizard.py +537 -0
- empathy_software_plugin/wizards/memory_enhanced_debugging_wizard.py +816 -0
- empathy_software_plugin/wizards/multi_model_wizard.py +501 -0
- empathy_software_plugin/wizards/pattern_extraction_wizard.py +422 -0
- empathy_software_plugin/wizards/pattern_retriever_wizard.py +400 -0
- empathy_software_plugin/wizards/performance/__init__.py +9 -0
- empathy_software_plugin/wizards/performance/bottleneck_detector.py +221 -0
- empathy_software_plugin/wizards/performance/profiler_parsers.py +278 -0
- empathy_software_plugin/wizards/performance/trajectory_analyzer.py +429 -0
- empathy_software_plugin/wizards/performance_profiling_wizard.py +305 -0
- empathy_software_plugin/wizards/prompt_engineering_wizard.py +425 -0
- empathy_software_plugin/wizards/rag_pattern_wizard.py +461 -0
- empathy_software_plugin/wizards/security/__init__.py +32 -0
- empathy_software_plugin/wizards/security/exploit_analyzer.py +290 -0
- empathy_software_plugin/wizards/security/owasp_patterns.py +241 -0
- empathy_software_plugin/wizards/security/vulnerability_scanner.py +604 -0
- empathy_software_plugin/wizards/security_analysis_wizard.py +322 -0
- empathy_software_plugin/wizards/security_learning_wizard.py +740 -0
- empathy_software_plugin/wizards/tech_debt_wizard.py +726 -0
- empathy_software_plugin/wizards/testing/__init__.py +27 -0
- empathy_software_plugin/wizards/testing/coverage_analyzer.py +459 -0
- empathy_software_plugin/wizards/testing/quality_analyzer.py +531 -0
- empathy_software_plugin/wizards/testing/test_suggester.py +533 -0
- empathy_software_plugin/wizards/testing_wizard.py +274 -0
- hot_reload/README.md +473 -0
- hot_reload/__init__.py +62 -0
- hot_reload/config.py +84 -0
- hot_reload/integration.py +228 -0
- hot_reload/reloader.py +298 -0
- hot_reload/watcher.py +179 -0
- hot_reload/websocket.py +176 -0
- scaffolding/README.md +589 -0
- scaffolding/__init__.py +35 -0
- scaffolding/__main__.py +14 -0
- scaffolding/cli.py +240 -0
- test_generator/__init__.py +38 -0
- test_generator/__main__.py +14 -0
- test_generator/cli.py +226 -0
- test_generator/generator.py +325 -0
- test_generator/risk_analyzer.py +216 -0
- workflow_patterns/__init__.py +33 -0
- workflow_patterns/behavior.py +249 -0
- workflow_patterns/core.py +76 -0
- workflow_patterns/output.py +99 -0
- workflow_patterns/registry.py +255 -0
- workflow_patterns/structural.py +288 -0
- workflow_scaffolding/__init__.py +11 -0
- workflow_scaffolding/__main__.py +12 -0
- workflow_scaffolding/cli.py +206 -0
- workflow_scaffolding/generator.py +265 -0
- agents/code_inspection/patterns/inspection/recurring_B112.json +0 -18
- agents/code_inspection/patterns/inspection/recurring_F541.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_FORMAT.json +0 -25
- agents/code_inspection/patterns/inspection/recurring_bug_20250822_def456.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_20250915_abc123.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_20251212_3c5b9951.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_20251212_97c0f72f.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_20251212_a0871d53.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_20251212_a9b6ec41.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_bug_null_001.json +0 -16
- agents/code_inspection/patterns/inspection/recurring_builtin.json +0 -16
- agents/compliance_anticipation_agent.py +0 -1427
- agents/epic_integration_wizard.py +0 -541
- agents/trust_building_behaviors.py +0 -891
- empathy_framework-2.4.0.dist-info/METADATA +0 -485
- empathy_framework-2.4.0.dist-info/RECORD +0 -102
- empathy_framework-2.4.0.dist-info/entry_points.txt +0 -6
- empathy_llm_toolkit/htmlcov/status.json +0 -1
- empathy_llm_toolkit/security/htmlcov/status.json +0 -1
- {empathy_framework-2.4.0.dist-info → empathy_framework-3.8.2.dist-info}/WHEEL +0 -0
- {empathy_framework-2.4.0.dist-info → empathy_framework-3.8.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
"""CLI for Test Maintenance Workflow
|
|
2
|
+
|
|
3
|
+
Commands for managing the test lifecycle:
|
|
4
|
+
- analyze: Generate maintenance plan
|
|
5
|
+
- execute: Execute plan items
|
|
6
|
+
- auto: Auto-execute eligible items
|
|
7
|
+
- report: Generate test health report
|
|
8
|
+
- queue: Manage task queue
|
|
9
|
+
- crew: Run the test maintenance crew
|
|
10
|
+
|
|
11
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
12
|
+
Licensed under Fair Source 0.9
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import argparse
|
|
16
|
+
import asyncio
|
|
17
|
+
import json
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
from ..project_index import ProjectIndex
|
|
22
|
+
from .test_lifecycle import TestLifecycleManager
|
|
23
|
+
from .test_maintenance import TestMaintenanceWorkflow
|
|
24
|
+
from .test_maintenance_crew import CrewConfig, TestMaintenanceCrew
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def main() -> int:
|
|
28
|
+
"""Main CLI entry point."""
|
|
29
|
+
parser = argparse.ArgumentParser(
|
|
30
|
+
description="Test Maintenance - Automatic Test Lifecycle Management",
|
|
31
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
32
|
+
epilog="""
|
|
33
|
+
Examples:
|
|
34
|
+
# Analyze test coverage and generate plan
|
|
35
|
+
python -m empathy_os.workflows.test_maintenance_cli analyze
|
|
36
|
+
|
|
37
|
+
# Auto-execute eligible test generation
|
|
38
|
+
python -m empathy_os.workflows.test_maintenance_cli auto
|
|
39
|
+
|
|
40
|
+
# Run the full maintenance crew
|
|
41
|
+
python -m empathy_os.workflows.test_maintenance_cli crew --mode full
|
|
42
|
+
|
|
43
|
+
# View task queue
|
|
44
|
+
python -m empathy_os.workflows.test_maintenance_cli queue list
|
|
45
|
+
|
|
46
|
+
# Process git hook
|
|
47
|
+
python -m empathy_os.workflows.test_maintenance_cli hook post-commit
|
|
48
|
+
""",
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
parser.add_argument(
|
|
52
|
+
"--project",
|
|
53
|
+
"-p",
|
|
54
|
+
default=".",
|
|
55
|
+
help="Project root directory (default: current directory)",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
parser.add_argument(
|
|
59
|
+
"--json",
|
|
60
|
+
"-j",
|
|
61
|
+
action="store_true",
|
|
62
|
+
help="Output in JSON format",
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
subparsers = parser.add_subparsers(dest="command", help="Command to run")
|
|
66
|
+
|
|
67
|
+
# analyze command
|
|
68
|
+
analyze_parser = subparsers.add_parser("analyze", help="Analyze and generate plan")
|
|
69
|
+
analyze_parser.add_argument(
|
|
70
|
+
"--max-items",
|
|
71
|
+
type=int,
|
|
72
|
+
default=20,
|
|
73
|
+
help="Maximum items in plan",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# execute command
|
|
77
|
+
execute_parser = subparsers.add_parser("execute", help="Execute plan items")
|
|
78
|
+
execute_parser.add_argument(
|
|
79
|
+
"--action",
|
|
80
|
+
choices=["create", "update", "review", "delete"],
|
|
81
|
+
help="Only execute items of this action type",
|
|
82
|
+
)
|
|
83
|
+
execute_parser.add_argument(
|
|
84
|
+
"--priority",
|
|
85
|
+
choices=["critical", "high", "medium", "low"],
|
|
86
|
+
help="Only execute items of this priority or higher",
|
|
87
|
+
)
|
|
88
|
+
execute_parser.add_argument(
|
|
89
|
+
"--dry-run",
|
|
90
|
+
action="store_true",
|
|
91
|
+
help="Don't actually execute, just show what would be done",
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# auto command
|
|
95
|
+
auto_parser = subparsers.add_parser("auto", help="Auto-execute eligible items")
|
|
96
|
+
auto_parser.add_argument(
|
|
97
|
+
"--max-items",
|
|
98
|
+
type=int,
|
|
99
|
+
default=10,
|
|
100
|
+
help="Maximum items to process",
|
|
101
|
+
)
|
|
102
|
+
auto_parser.add_argument(
|
|
103
|
+
"--dry-run",
|
|
104
|
+
action="store_true",
|
|
105
|
+
help="Don't actually execute",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# report command
|
|
109
|
+
report_parser = subparsers.add_parser("report", help="Generate test health report")
|
|
110
|
+
report_parser.add_argument(
|
|
111
|
+
"--type",
|
|
112
|
+
choices=["health", "gaps", "staleness", "all"],
|
|
113
|
+
default="all",
|
|
114
|
+
help="Type of report",
|
|
115
|
+
)
|
|
116
|
+
report_parser.add_argument(
|
|
117
|
+
"--markdown",
|
|
118
|
+
"-m",
|
|
119
|
+
action="store_true",
|
|
120
|
+
help="Output in markdown format",
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# queue command
|
|
124
|
+
queue_parser = subparsers.add_parser("queue", help="Manage task queue")
|
|
125
|
+
queue_subparsers = queue_parser.add_subparsers(dest="queue_action")
|
|
126
|
+
|
|
127
|
+
queue_subparsers.add_parser("list", help="List queued tasks")
|
|
128
|
+
queue_subparsers.add_parser("status", help="Show queue status")
|
|
129
|
+
queue_subparsers.add_parser("clear", help="Clear the queue")
|
|
130
|
+
|
|
131
|
+
queue_process = queue_subparsers.add_parser("process", help="Process queue")
|
|
132
|
+
queue_process.add_argument("--max", type=int, default=10, help="Max tasks to process")
|
|
133
|
+
|
|
134
|
+
# crew command
|
|
135
|
+
crew_parser = subparsers.add_parser("crew", help="Run test maintenance crew")
|
|
136
|
+
crew_parser.add_argument(
|
|
137
|
+
"--mode",
|
|
138
|
+
choices=["full", "analyze", "generate", "validate", "validate-only", "report"],
|
|
139
|
+
default="analyze",
|
|
140
|
+
help="Crew operation mode",
|
|
141
|
+
)
|
|
142
|
+
crew_parser.add_argument(
|
|
143
|
+
"--files",
|
|
144
|
+
nargs="*",
|
|
145
|
+
help="Test files for validate-only mode",
|
|
146
|
+
)
|
|
147
|
+
crew_parser.add_argument(
|
|
148
|
+
"--validation-optional",
|
|
149
|
+
action="store_true",
|
|
150
|
+
default=True,
|
|
151
|
+
help="Continue if validation fails (default: True)",
|
|
152
|
+
)
|
|
153
|
+
crew_parser.add_argument(
|
|
154
|
+
"--validation-timeout",
|
|
155
|
+
type=int,
|
|
156
|
+
default=120,
|
|
157
|
+
help="Timeout per test file in seconds (default: 120)",
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# hook command
|
|
161
|
+
hook_parser = subparsers.add_parser("hook", help="Process git hooks")
|
|
162
|
+
hook_parser.add_argument(
|
|
163
|
+
"hook_type",
|
|
164
|
+
choices=["pre-commit", "post-commit"],
|
|
165
|
+
help="Type of hook",
|
|
166
|
+
)
|
|
167
|
+
hook_parser.add_argument(
|
|
168
|
+
"--files",
|
|
169
|
+
nargs="*",
|
|
170
|
+
help="Files involved in the hook",
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# status command
|
|
174
|
+
subparsers.add_parser("status", help="Show test maintenance status")
|
|
175
|
+
|
|
176
|
+
args = parser.parse_args()
|
|
177
|
+
|
|
178
|
+
if not args.command:
|
|
179
|
+
parser.print_help()
|
|
180
|
+
return 1
|
|
181
|
+
|
|
182
|
+
# Run appropriate command
|
|
183
|
+
if args.command == "analyze":
|
|
184
|
+
return asyncio.run(cmd_analyze(args))
|
|
185
|
+
if args.command == "execute":
|
|
186
|
+
return asyncio.run(cmd_execute(args))
|
|
187
|
+
if args.command == "auto":
|
|
188
|
+
return asyncio.run(cmd_auto(args))
|
|
189
|
+
if args.command == "report":
|
|
190
|
+
return asyncio.run(cmd_report(args))
|
|
191
|
+
if args.command == "queue":
|
|
192
|
+
return asyncio.run(cmd_queue(args))
|
|
193
|
+
if args.command == "crew":
|
|
194
|
+
return asyncio.run(cmd_crew(args))
|
|
195
|
+
if args.command == "hook":
|
|
196
|
+
return asyncio.run(cmd_hook(args))
|
|
197
|
+
if args.command == "status":
|
|
198
|
+
return asyncio.run(cmd_status(args))
|
|
199
|
+
|
|
200
|
+
return 0
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
async def cmd_analyze(args: argparse.Namespace) -> int:
|
|
204
|
+
"""Run analysis and generate plan."""
|
|
205
|
+
project_root = Path(args.project).resolve()
|
|
206
|
+
index = ProjectIndex(str(project_root))
|
|
207
|
+
if not index.load():
|
|
208
|
+
index.refresh()
|
|
209
|
+
|
|
210
|
+
workflow = TestMaintenanceWorkflow(str(project_root), index)
|
|
211
|
+
result = await workflow.run(
|
|
212
|
+
{
|
|
213
|
+
"mode": "analyze",
|
|
214
|
+
"max_items": args.max_items,
|
|
215
|
+
},
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
if args.json:
|
|
219
|
+
print(json.dumps(result, indent=2))
|
|
220
|
+
else:
|
|
221
|
+
_print_plan(result)
|
|
222
|
+
|
|
223
|
+
return 0
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
async def cmd_execute(args: argparse.Namespace) -> int:
|
|
227
|
+
"""Execute plan items."""
|
|
228
|
+
project_root = Path(args.project).resolve()
|
|
229
|
+
index = ProjectIndex(str(project_root))
|
|
230
|
+
if not index.load():
|
|
231
|
+
index.refresh()
|
|
232
|
+
|
|
233
|
+
workflow = TestMaintenanceWorkflow(str(project_root), index)
|
|
234
|
+
result = await workflow.run(
|
|
235
|
+
{
|
|
236
|
+
"mode": "execute",
|
|
237
|
+
"dry_run": args.dry_run,
|
|
238
|
+
},
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
if args.json:
|
|
242
|
+
print(json.dumps(result, indent=2))
|
|
243
|
+
else:
|
|
244
|
+
_print_execution_result(result)
|
|
245
|
+
|
|
246
|
+
return 0
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
async def cmd_auto(args: argparse.Namespace) -> int:
|
|
250
|
+
"""Auto-execute eligible items."""
|
|
251
|
+
project_root = Path(args.project).resolve()
|
|
252
|
+
index = ProjectIndex(str(project_root))
|
|
253
|
+
if not index.load():
|
|
254
|
+
index.refresh()
|
|
255
|
+
|
|
256
|
+
workflow = TestMaintenanceWorkflow(str(project_root), index)
|
|
257
|
+
result = await workflow.run(
|
|
258
|
+
{
|
|
259
|
+
"mode": "auto",
|
|
260
|
+
"max_items": args.max_items,
|
|
261
|
+
"dry_run": args.dry_run,
|
|
262
|
+
},
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
if args.json:
|
|
266
|
+
print(json.dumps(result, indent=2))
|
|
267
|
+
else:
|
|
268
|
+
if args.dry_run:
|
|
269
|
+
print("DRY RUN - No changes made")
|
|
270
|
+
_print_execution_result(result)
|
|
271
|
+
|
|
272
|
+
return 0
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
async def cmd_report(args: argparse.Namespace) -> int:
|
|
276
|
+
"""Generate test health report."""
|
|
277
|
+
project_root = Path(args.project).resolve()
|
|
278
|
+
index = ProjectIndex(str(project_root))
|
|
279
|
+
if not index.load():
|
|
280
|
+
index.refresh()
|
|
281
|
+
|
|
282
|
+
workflow = TestMaintenanceWorkflow(str(project_root), index)
|
|
283
|
+
result = await workflow.run({"mode": "report"})
|
|
284
|
+
|
|
285
|
+
if args.json:
|
|
286
|
+
print(json.dumps(result.get("report", {}), indent=2))
|
|
287
|
+
else:
|
|
288
|
+
_print_report(result.get("report", {}), args.type, args.markdown)
|
|
289
|
+
|
|
290
|
+
return 0
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
async def cmd_queue(args: argparse.Namespace) -> int:
|
|
294
|
+
"""Manage task queue."""
|
|
295
|
+
project_root = Path(args.project).resolve()
|
|
296
|
+
index = ProjectIndex(str(project_root))
|
|
297
|
+
if not index.load():
|
|
298
|
+
index.refresh()
|
|
299
|
+
|
|
300
|
+
manager = TestLifecycleManager(str(project_root), index)
|
|
301
|
+
|
|
302
|
+
if args.queue_action == "list":
|
|
303
|
+
queue = manager.get_queue()
|
|
304
|
+
if args.json:
|
|
305
|
+
print(json.dumps(queue, indent=2))
|
|
306
|
+
else:
|
|
307
|
+
_print_queue(queue)
|
|
308
|
+
|
|
309
|
+
elif args.queue_action == "status":
|
|
310
|
+
status = manager.get_status()
|
|
311
|
+
if args.json:
|
|
312
|
+
print(json.dumps(status, indent=2))
|
|
313
|
+
else:
|
|
314
|
+
_print_queue_status(status)
|
|
315
|
+
|
|
316
|
+
elif args.queue_action == "clear":
|
|
317
|
+
count = manager.clear_queue()
|
|
318
|
+
print(f"Cleared {count} tasks from queue")
|
|
319
|
+
|
|
320
|
+
elif args.queue_action == "process":
|
|
321
|
+
result = await manager.process_queue(max_tasks=args.max)
|
|
322
|
+
if args.json:
|
|
323
|
+
print(json.dumps(result, indent=2))
|
|
324
|
+
else:
|
|
325
|
+
print(f"Processed {result['processed']} tasks")
|
|
326
|
+
print(f" Succeeded: {result['succeeded']}")
|
|
327
|
+
print(f" Failed: {result['failed']}")
|
|
328
|
+
|
|
329
|
+
return 0
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
async def cmd_crew(args: argparse.Namespace) -> int:
|
|
333
|
+
"""Run test maintenance crew."""
|
|
334
|
+
project_root = Path(args.project).resolve()
|
|
335
|
+
index = ProjectIndex(str(project_root))
|
|
336
|
+
if not index.load():
|
|
337
|
+
index.refresh()
|
|
338
|
+
|
|
339
|
+
# Configure with CLI options
|
|
340
|
+
config = CrewConfig(
|
|
341
|
+
validation_optional=getattr(args, "validation_optional", True),
|
|
342
|
+
validation_timeout_seconds=getattr(args, "validation_timeout", 120),
|
|
343
|
+
)
|
|
344
|
+
crew = TestMaintenanceCrew(str(project_root), index, config)
|
|
345
|
+
|
|
346
|
+
print(f"Starting Test Maintenance Crew in {args.mode} mode...")
|
|
347
|
+
print("=" * 60)
|
|
348
|
+
|
|
349
|
+
# Handle validate-only mode
|
|
350
|
+
test_files = getattr(args, "files", None)
|
|
351
|
+
if args.mode == "validate-only" and not test_files:
|
|
352
|
+
print("ERROR: validate-only mode requires --files argument")
|
|
353
|
+
return 1
|
|
354
|
+
|
|
355
|
+
result = await crew.run(args.mode, test_files=test_files)
|
|
356
|
+
|
|
357
|
+
if args.json:
|
|
358
|
+
print(json.dumps(result, indent=2))
|
|
359
|
+
else:
|
|
360
|
+
_print_crew_result(result)
|
|
361
|
+
|
|
362
|
+
return 0 if result.get("success") else 1
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
async def cmd_hook(args: argparse.Namespace) -> int:
|
|
366
|
+
"""Process git hooks."""
|
|
367
|
+
project_root = Path(args.project).resolve()
|
|
368
|
+
index = ProjectIndex(str(project_root))
|
|
369
|
+
if not index.load():
|
|
370
|
+
index.refresh()
|
|
371
|
+
|
|
372
|
+
manager = TestLifecycleManager(str(project_root), index)
|
|
373
|
+
|
|
374
|
+
files = args.files or []
|
|
375
|
+
|
|
376
|
+
if args.hook_type == "pre-commit":
|
|
377
|
+
result = await manager.process_git_pre_commit(files)
|
|
378
|
+
|
|
379
|
+
if args.json:
|
|
380
|
+
print(json.dumps(result, indent=2))
|
|
381
|
+
elif result.get("blocking"):
|
|
382
|
+
print("COMMIT BLOCKED")
|
|
383
|
+
print("=" * 40)
|
|
384
|
+
for item in result["blocking"]:
|
|
385
|
+
print(f" {item['file']}: {item['reason']}")
|
|
386
|
+
return 1
|
|
387
|
+
elif result.get("warnings"):
|
|
388
|
+
print("COMMIT ALLOWED (with warnings)")
|
|
389
|
+
print("=" * 40)
|
|
390
|
+
for item in result["warnings"]:
|
|
391
|
+
print(f" WARNING: {item['file']}: {item['reason']}")
|
|
392
|
+
|
|
393
|
+
elif args.hook_type == "post-commit":
|
|
394
|
+
result = await manager.process_git_post_commit(files)
|
|
395
|
+
|
|
396
|
+
if args.json:
|
|
397
|
+
print(json.dumps(result, indent=2))
|
|
398
|
+
else:
|
|
399
|
+
print(f"Processed {result['changed_files']} changed files")
|
|
400
|
+
print(f"Queued {result['tasks_queued']} test tasks")
|
|
401
|
+
|
|
402
|
+
return 0
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
async def cmd_status(args: argparse.Namespace) -> int:
|
|
406
|
+
"""Show test maintenance status."""
|
|
407
|
+
project_root = Path(args.project).resolve()
|
|
408
|
+
index = ProjectIndex(str(project_root))
|
|
409
|
+
if not index.load():
|
|
410
|
+
index.refresh()
|
|
411
|
+
|
|
412
|
+
workflow = TestMaintenanceWorkflow(str(project_root), index)
|
|
413
|
+
manager = TestLifecycleManager(str(project_root), index)
|
|
414
|
+
|
|
415
|
+
health = workflow.get_test_health_summary()
|
|
416
|
+
queue_status = manager.get_status()
|
|
417
|
+
|
|
418
|
+
if args.json:
|
|
419
|
+
print(
|
|
420
|
+
json.dumps(
|
|
421
|
+
{
|
|
422
|
+
"health": health,
|
|
423
|
+
"queue": queue_status,
|
|
424
|
+
},
|
|
425
|
+
indent=2,
|
|
426
|
+
),
|
|
427
|
+
)
|
|
428
|
+
else:
|
|
429
|
+
print("TEST MAINTENANCE STATUS")
|
|
430
|
+
print("=" * 60)
|
|
431
|
+
print()
|
|
432
|
+
print("TEST HEALTH")
|
|
433
|
+
print(f" Files requiring tests: {health['files_requiring_tests']}")
|
|
434
|
+
print(f" Files WITH tests: {health['files_with_tests']}")
|
|
435
|
+
print(f" Files WITHOUT tests: {health['files_without_tests']}")
|
|
436
|
+
print(f" Average coverage: {health['coverage_avg']:.1f}%")
|
|
437
|
+
print(f" Stale tests: {health['stale_count']}")
|
|
438
|
+
print()
|
|
439
|
+
print("TASK QUEUE")
|
|
440
|
+
print(f" Pending tasks: {queue_status['pending']}")
|
|
441
|
+
print(f" Running tasks: {queue_status['running']}")
|
|
442
|
+
print(f" Auto-execute: {queue_status['auto_execute']}")
|
|
443
|
+
print()
|
|
444
|
+
|
|
445
|
+
return 0
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
# ===== Output Formatting =====
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def _print_plan(result: dict) -> None:
|
|
452
|
+
"""Print maintenance plan."""
|
|
453
|
+
plan = result.get("plan", {})
|
|
454
|
+
summary = plan.get("summary", {})
|
|
455
|
+
items = plan.get("items", [])
|
|
456
|
+
options = plan.get("options", [])
|
|
457
|
+
|
|
458
|
+
print("TEST MAINTENANCE PLAN")
|
|
459
|
+
print("=" * 60)
|
|
460
|
+
print()
|
|
461
|
+
|
|
462
|
+
print("SUMMARY")
|
|
463
|
+
print(f" Total items: {summary.get('total_items', 0)}")
|
|
464
|
+
print(f" Auto-executable: {summary.get('auto_executable', 0)}")
|
|
465
|
+
print(f" Manual required: {summary.get('manual_required', 0)}")
|
|
466
|
+
print()
|
|
467
|
+
|
|
468
|
+
print("BY ACTION")
|
|
469
|
+
for action, count in summary.get("by_action", {}).items():
|
|
470
|
+
if count > 0:
|
|
471
|
+
print(f" {action}: {count}")
|
|
472
|
+
print()
|
|
473
|
+
|
|
474
|
+
print("BY PRIORITY")
|
|
475
|
+
for priority, count in summary.get("by_priority", {}).items():
|
|
476
|
+
if count > 0:
|
|
477
|
+
print(f" {priority}: {count}")
|
|
478
|
+
print()
|
|
479
|
+
|
|
480
|
+
if items:
|
|
481
|
+
print("PLAN ITEMS")
|
|
482
|
+
print("-" * 60)
|
|
483
|
+
for i, item in enumerate(items[:10], 1):
|
|
484
|
+
auto = "[AUTO]" if item.get("auto_executable") else "[MANUAL]"
|
|
485
|
+
print(f"{i}. {auto} [{item['priority']}] {item['file_path']}")
|
|
486
|
+
print(f" Action: {item['action']} - {item['reason']}")
|
|
487
|
+
print(f" Effort: {item.get('estimated_effort', 'unknown')}")
|
|
488
|
+
print()
|
|
489
|
+
|
|
490
|
+
if options:
|
|
491
|
+
print("EXECUTION OPTIONS")
|
|
492
|
+
print("-" * 60)
|
|
493
|
+
for opt in options:
|
|
494
|
+
print(f" {opt['name']}")
|
|
495
|
+
print(f" {opt['description']}")
|
|
496
|
+
print(f" Command: {opt.get('command', 'N/A')}")
|
|
497
|
+
print()
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
def _print_execution_result(result: dict) -> None:
|
|
501
|
+
"""Print execution result."""
|
|
502
|
+
execution = result.get("execution", {})
|
|
503
|
+
|
|
504
|
+
print("EXECUTION RESULT")
|
|
505
|
+
print("=" * 60)
|
|
506
|
+
print(f" Total: {execution.get('total', 0)}")
|
|
507
|
+
print(f" Succeeded: {execution.get('succeeded', 0)}")
|
|
508
|
+
print(f" Failed: {execution.get('failed', 0)}")
|
|
509
|
+
print(f" Skipped: {execution.get('skipped', 0)}")
|
|
510
|
+
print()
|
|
511
|
+
|
|
512
|
+
details = execution.get("details", [])
|
|
513
|
+
if details:
|
|
514
|
+
print("DETAILS")
|
|
515
|
+
for item in details[:10]:
|
|
516
|
+
status = "OK" if item.get("success") else "FAILED"
|
|
517
|
+
print(f" [{status}] {item['file']} - {item['action']}")
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def _print_report(report: dict, report_type: str, markdown: bool) -> None:
|
|
521
|
+
"""Print test health report."""
|
|
522
|
+
if report_type in ["all", "health"]:
|
|
523
|
+
health = report.get("health", {})
|
|
524
|
+
print("TEST HEALTH REPORT")
|
|
525
|
+
print("=" * 60)
|
|
526
|
+
print(
|
|
527
|
+
f" Health Score: {health.get('health_score', 0):.1f}/100 ({health.get('health_grade', 'N/A')})",
|
|
528
|
+
)
|
|
529
|
+
print()
|
|
530
|
+
|
|
531
|
+
for concern in health.get("concerns", []):
|
|
532
|
+
print(f" CONCERN: {concern}")
|
|
533
|
+
print()
|
|
534
|
+
|
|
535
|
+
for action in health.get("action_items", []):
|
|
536
|
+
print(f" [{action['priority'].upper()}] {action['action']}")
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
def _print_queue(queue: list) -> None:
|
|
540
|
+
"""Print task queue."""
|
|
541
|
+
if not queue:
|
|
542
|
+
print("Queue is empty")
|
|
543
|
+
return
|
|
544
|
+
|
|
545
|
+
print("TASK QUEUE")
|
|
546
|
+
print("=" * 60)
|
|
547
|
+
for task in queue:
|
|
548
|
+
print(f" [{task['priority']}] {task['file_path']}")
|
|
549
|
+
print(f" Action: {task['action']}, Status: {task['status']}")
|
|
550
|
+
print()
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
def _print_queue_status(status: dict) -> None:
|
|
554
|
+
"""Print queue status."""
|
|
555
|
+
print("QUEUE STATUS")
|
|
556
|
+
print("=" * 60)
|
|
557
|
+
print(f" Queue size: {status['queue_size']}")
|
|
558
|
+
print(f" Pending: {status['pending']}")
|
|
559
|
+
print(f" Running: {status['running']}")
|
|
560
|
+
print(f" Auto-execute: {status['auto_execute']}")
|
|
561
|
+
print()
|
|
562
|
+
print("By Priority:")
|
|
563
|
+
for priority, count in status.get("by_priority", {}).items():
|
|
564
|
+
if count > 0:
|
|
565
|
+
print(f" {priority}: {count}")
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
def _print_crew_result(result: dict) -> None:
|
|
569
|
+
"""Print crew execution result."""
|
|
570
|
+
summary = result.get("summary", {})
|
|
571
|
+
|
|
572
|
+
print()
|
|
573
|
+
print("CREW EXECUTION COMPLETE")
|
|
574
|
+
print("=" * 60)
|
|
575
|
+
print(f" Mode: {result.get('mode')}")
|
|
576
|
+
print(f" Success: {result.get('success')}")
|
|
577
|
+
print(f" Agents executed: {summary.get('agents_executed', 0)}")
|
|
578
|
+
print(f" Agents succeeded: {summary.get('agents_succeeded', 0)}")
|
|
579
|
+
print(f" Total duration: {summary.get('total_duration_ms', 0)}ms")
|
|
580
|
+
print()
|
|
581
|
+
|
|
582
|
+
if summary.get("agent_results"):
|
|
583
|
+
print("AGENT RESULTS")
|
|
584
|
+
for agent in summary["agent_results"]:
|
|
585
|
+
status = "OK" if agent["success"] else "FAILED"
|
|
586
|
+
print(f" [{status}] {agent['agent']}: {agent['task']} ({agent['duration_ms']}ms)")
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
if __name__ == "__main__":
|
|
590
|
+
sys.exit(main())
|