tapps-agents 3.5.41__py3-none-any.whl → 3.6.0__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.
- tapps_agents/__init__.py +2 -2
- tapps_agents/agents/__init__.py +22 -22
- tapps_agents/agents/analyst/__init__.py +5 -5
- tapps_agents/agents/architect/__init__.py +5 -5
- tapps_agents/agents/architect/agent.py +1033 -1033
- tapps_agents/agents/architect/pattern_detector.py +75 -75
- tapps_agents/agents/cleanup/__init__.py +7 -7
- tapps_agents/agents/cleanup/agent.py +445 -445
- tapps_agents/agents/debugger/__init__.py +7 -7
- tapps_agents/agents/debugger/agent.py +310 -310
- tapps_agents/agents/debugger/error_analyzer.py +437 -437
- tapps_agents/agents/designer/__init__.py +5 -5
- tapps_agents/agents/designer/agent.py +786 -786
- tapps_agents/agents/designer/visual_designer.py +638 -638
- tapps_agents/agents/documenter/__init__.py +7 -7
- tapps_agents/agents/documenter/agent.py +531 -531
- tapps_agents/agents/documenter/doc_generator.py +472 -472
- tapps_agents/agents/documenter/doc_validator.py +393 -393
- tapps_agents/agents/documenter/framework_doc_updater.py +493 -493
- tapps_agents/agents/enhancer/__init__.py +7 -7
- tapps_agents/agents/evaluator/__init__.py +7 -7
- tapps_agents/agents/evaluator/agent.py +443 -443
- tapps_agents/agents/evaluator/priority_evaluator.py +641 -641
- tapps_agents/agents/evaluator/quality_analyzer.py +147 -147
- tapps_agents/agents/evaluator/report_generator.py +344 -344
- tapps_agents/agents/evaluator/usage_analyzer.py +192 -192
- tapps_agents/agents/evaluator/workflow_analyzer.py +189 -189
- tapps_agents/agents/implementer/__init__.py +7 -7
- tapps_agents/agents/implementer/agent.py +798 -798
- tapps_agents/agents/implementer/auto_fix.py +1119 -1119
- tapps_agents/agents/implementer/code_generator.py +73 -73
- tapps_agents/agents/improver/__init__.py +1 -1
- tapps_agents/agents/improver/agent.py +753 -753
- tapps_agents/agents/ops/__init__.py +1 -1
- tapps_agents/agents/ops/agent.py +619 -619
- tapps_agents/agents/ops/dependency_analyzer.py +600 -600
- tapps_agents/agents/orchestrator/__init__.py +5 -5
- tapps_agents/agents/orchestrator/agent.py +522 -522
- tapps_agents/agents/planner/__init__.py +7 -7
- tapps_agents/agents/planner/agent.py +1127 -1127
- tapps_agents/agents/reviewer/__init__.py +24 -24
- tapps_agents/agents/reviewer/agent.py +3513 -3513
- tapps_agents/agents/reviewer/aggregator.py +213 -213
- tapps_agents/agents/reviewer/batch_review.py +448 -448
- tapps_agents/agents/reviewer/cache.py +443 -443
- tapps_agents/agents/reviewer/context7_enhancer.py +630 -630
- tapps_agents/agents/reviewer/context_detector.py +203 -203
- tapps_agents/agents/reviewer/docker_compose_validator.py +158 -158
- tapps_agents/agents/reviewer/dockerfile_validator.py +176 -176
- tapps_agents/agents/reviewer/error_handling.py +126 -126
- tapps_agents/agents/reviewer/feedback_generator.py +490 -490
- tapps_agents/agents/reviewer/influxdb_validator.py +316 -316
- tapps_agents/agents/reviewer/issue_tracking.py +169 -169
- tapps_agents/agents/reviewer/library_detector.py +295 -295
- tapps_agents/agents/reviewer/library_patterns.py +268 -268
- tapps_agents/agents/reviewer/maintainability_scorer.py +593 -593
- tapps_agents/agents/reviewer/metric_strategies.py +276 -276
- tapps_agents/agents/reviewer/mqtt_validator.py +160 -160
- tapps_agents/agents/reviewer/output_enhancer.py +105 -105
- tapps_agents/agents/reviewer/pattern_detector.py +241 -241
- tapps_agents/agents/reviewer/performance_scorer.py +357 -357
- tapps_agents/agents/reviewer/phased_review.py +516 -516
- tapps_agents/agents/reviewer/progressive_review.py +435 -435
- tapps_agents/agents/reviewer/react_scorer.py +331 -331
- tapps_agents/agents/reviewer/score_constants.py +228 -228
- tapps_agents/agents/reviewer/score_validator.py +507 -507
- tapps_agents/agents/reviewer/scorer_registry.py +373 -373
- tapps_agents/agents/reviewer/scoring.py +1566 -1566
- tapps_agents/agents/reviewer/service_discovery.py +534 -534
- tapps_agents/agents/reviewer/tools/__init__.py +41 -41
- tapps_agents/agents/reviewer/tools/parallel_executor.py +581 -581
- tapps_agents/agents/reviewer/tools/ruff_grouping.py +250 -250
- tapps_agents/agents/reviewer/tools/scoped_mypy.py +284 -284
- tapps_agents/agents/reviewer/typescript_scorer.py +1142 -1142
- tapps_agents/agents/reviewer/validation.py +208 -208
- tapps_agents/agents/reviewer/websocket_validator.py +132 -132
- tapps_agents/agents/tester/__init__.py +7 -7
- tapps_agents/agents/tester/accessibility_auditor.py +309 -309
- tapps_agents/agents/tester/agent.py +1080 -1080
- tapps_agents/agents/tester/batch_generator.py +54 -54
- tapps_agents/agents/tester/context_learner.py +51 -51
- tapps_agents/agents/tester/coverage_analyzer.py +386 -386
- tapps_agents/agents/tester/coverage_test_generator.py +290 -290
- tapps_agents/agents/tester/debug_enhancer.py +238 -238
- tapps_agents/agents/tester/device_emulator.py +241 -241
- tapps_agents/agents/tester/integration_generator.py +62 -62
- tapps_agents/agents/tester/network_recorder.py +300 -300
- tapps_agents/agents/tester/performance_monitor.py +320 -320
- tapps_agents/agents/tester/test_fixer.py +316 -316
- tapps_agents/agents/tester/test_generator.py +632 -632
- tapps_agents/agents/tester/trace_manager.py +234 -234
- tapps_agents/agents/tester/visual_regression.py +291 -291
- tapps_agents/analysis/pattern_detector.py +36 -36
- tapps_agents/beads/hydration.py +213 -213
- tapps_agents/beads/parse.py +32 -32
- tapps_agents/beads/specs.py +206 -206
- tapps_agents/cli/__init__.py +9 -9
- tapps_agents/cli/__main__.py +8 -8
- tapps_agents/cli/base.py +478 -478
- tapps_agents/cli/command_classifier.py +72 -72
- tapps_agents/cli/commands/__init__.py +2 -2
- tapps_agents/cli/commands/analyst.py +173 -173
- tapps_agents/cli/commands/architect.py +109 -109
- tapps_agents/cli/commands/cleanup_agent.py +92 -92
- tapps_agents/cli/commands/common.py +126 -126
- tapps_agents/cli/commands/debugger.py +90 -90
- tapps_agents/cli/commands/designer.py +112 -112
- tapps_agents/cli/commands/documenter.py +136 -136
- tapps_agents/cli/commands/enhancer.py +110 -110
- tapps_agents/cli/commands/evaluator.py +255 -255
- tapps_agents/cli/commands/health.py +665 -665
- tapps_agents/cli/commands/implementer.py +301 -301
- tapps_agents/cli/commands/improver.py +91 -91
- tapps_agents/cli/commands/knowledge.py +111 -111
- tapps_agents/cli/commands/learning.py +172 -172
- tapps_agents/cli/commands/observability.py +283 -283
- tapps_agents/cli/commands/ops.py +135 -135
- tapps_agents/cli/commands/orchestrator.py +116 -116
- tapps_agents/cli/commands/planner.py +237 -237
- tapps_agents/cli/commands/reviewer.py +1872 -1872
- tapps_agents/cli/commands/status.py +285 -285
- tapps_agents/cli/commands/task.py +227 -227
- tapps_agents/cli/commands/tester.py +191 -191
- tapps_agents/cli/commands/top_level.py +3586 -3586
- tapps_agents/cli/feedback.py +936 -936
- tapps_agents/cli/formatters.py +608 -608
- tapps_agents/cli/help/__init__.py +7 -7
- tapps_agents/cli/help/static_help.py +425 -425
- tapps_agents/cli/network_detection.py +110 -110
- tapps_agents/cli/output_compactor.py +274 -274
- tapps_agents/cli/parsers/__init__.py +2 -2
- tapps_agents/cli/parsers/analyst.py +186 -186
- tapps_agents/cli/parsers/architect.py +167 -167
- tapps_agents/cli/parsers/cleanup_agent.py +228 -228
- tapps_agents/cli/parsers/debugger.py +116 -116
- tapps_agents/cli/parsers/designer.py +182 -182
- tapps_agents/cli/parsers/documenter.py +134 -134
- tapps_agents/cli/parsers/enhancer.py +113 -113
- tapps_agents/cli/parsers/evaluator.py +213 -213
- tapps_agents/cli/parsers/implementer.py +168 -168
- tapps_agents/cli/parsers/improver.py +132 -132
- tapps_agents/cli/parsers/ops.py +159 -159
- tapps_agents/cli/parsers/orchestrator.py +98 -98
- tapps_agents/cli/parsers/planner.py +145 -145
- tapps_agents/cli/parsers/reviewer.py +462 -462
- tapps_agents/cli/parsers/tester.py +124 -124
- tapps_agents/cli/progress_heartbeat.py +254 -254
- tapps_agents/cli/streaming_progress.py +336 -336
- tapps_agents/cli/utils/__init__.py +6 -6
- tapps_agents/cli/utils/agent_lifecycle.py +48 -48
- tapps_agents/cli/utils/error_formatter.py +82 -82
- tapps_agents/cli/utils/error_recovery.py +188 -188
- tapps_agents/cli/utils/output_handler.py +59 -59
- tapps_agents/cli/utils/prompt_enhancer.py +319 -319
- tapps_agents/cli/validators/__init__.py +9 -9
- tapps_agents/cli/validators/command_validator.py +81 -81
- tapps_agents/context7/__init__.py +112 -112
- tapps_agents/context7/agent_integration.py +869 -869
- tapps_agents/context7/analytics.py +382 -382
- tapps_agents/context7/analytics_dashboard.py +299 -299
- tapps_agents/context7/async_cache.py +681 -681
- tapps_agents/context7/backup_client.py +958 -958
- tapps_agents/context7/cache_locking.py +194 -194
- tapps_agents/context7/cache_metadata.py +214 -214
- tapps_agents/context7/cache_prewarm.py +488 -488
- tapps_agents/context7/cache_structure.py +168 -168
- tapps_agents/context7/cache_warming.py +604 -604
- tapps_agents/context7/circuit_breaker.py +376 -376
- tapps_agents/context7/cleanup.py +461 -461
- tapps_agents/context7/commands.py +858 -858
- tapps_agents/context7/credential_validation.py +276 -276
- tapps_agents/context7/cross_reference_resolver.py +168 -168
- tapps_agents/context7/cross_references.py +424 -424
- tapps_agents/context7/doc_manager.py +225 -225
- tapps_agents/context7/fuzzy_matcher.py +369 -369
- tapps_agents/context7/kb_cache.py +404 -404
- tapps_agents/context7/language_detector.py +219 -219
- tapps_agents/context7/library_detector.py +725 -725
- tapps_agents/context7/lookup.py +738 -738
- tapps_agents/context7/metadata.py +258 -258
- tapps_agents/context7/refresh_queue.py +300 -300
- tapps_agents/context7/security.py +373 -373
- tapps_agents/context7/staleness_policies.py +278 -278
- tapps_agents/context7/tiles_integration.py +47 -47
- tapps_agents/continuous_bug_fix/__init__.py +20 -20
- tapps_agents/continuous_bug_fix/bug_finder.py +306 -306
- tapps_agents/continuous_bug_fix/bug_fix_coordinator.py +177 -177
- tapps_agents/continuous_bug_fix/commit_manager.py +178 -178
- tapps_agents/continuous_bug_fix/continuous_bug_fixer.py +322 -322
- tapps_agents/continuous_bug_fix/proactive_bug_finder.py +285 -285
- tapps_agents/core/__init__.py +298 -298
- tapps_agents/core/adaptive_cache_config.py +432 -432
- tapps_agents/core/agent_base.py +647 -647
- tapps_agents/core/agent_cache.py +466 -466
- tapps_agents/core/agent_learning.py +1865 -1865
- tapps_agents/core/analytics_dashboard.py +563 -563
- tapps_agents/core/analytics_enhancements.py +597 -597
- tapps_agents/core/anonymization.py +274 -274
- tapps_agents/core/artifact_context_builder.py +293 -0
- tapps_agents/core/ast_parser.py +228 -228
- tapps_agents/core/async_file_ops.py +402 -402
- tapps_agents/core/best_practice_consultant.py +299 -299
- tapps_agents/core/brownfield_analyzer.py +299 -299
- tapps_agents/core/brownfield_review.py +541 -541
- tapps_agents/core/browser_controller.py +513 -513
- tapps_agents/core/capability_registry.py +418 -418
- tapps_agents/core/change_impact_analyzer.py +190 -190
- tapps_agents/core/checkpoint_manager.py +377 -377
- tapps_agents/core/code_generator.py +329 -329
- tapps_agents/core/code_validator.py +276 -276
- tapps_agents/core/command_registry.py +327 -327
- tapps_agents/core/config.py +33 -0
- tapps_agents/core/context_gathering/__init__.py +2 -2
- tapps_agents/core/context_gathering/repository_explorer.py +28 -28
- tapps_agents/core/context_intelligence/__init__.py +2 -2
- tapps_agents/core/context_intelligence/relevance_scorer.py +24 -24
- tapps_agents/core/context_intelligence/token_budget_manager.py +27 -27
- tapps_agents/core/context_manager.py +240 -240
- tapps_agents/core/cursor_feedback_monitor.py +146 -146
- tapps_agents/core/cursor_verification.py +290 -290
- tapps_agents/core/customization_loader.py +280 -280
- tapps_agents/core/customization_schema.py +260 -260
- tapps_agents/core/customization_template.py +238 -238
- tapps_agents/core/debug_logger.py +124 -124
- tapps_agents/core/design_validator.py +298 -298
- tapps_agents/core/diagram_generator.py +226 -226
- tapps_agents/core/docker_utils.py +232 -232
- tapps_agents/core/document_generator.py +617 -617
- tapps_agents/core/domain_detector.py +30 -30
- tapps_agents/core/error_envelope.py +454 -454
- tapps_agents/core/error_handler.py +270 -270
- tapps_agents/core/estimation_tracker.py +189 -189
- tapps_agents/core/eval_prompt_engine.py +116 -116
- tapps_agents/core/evaluation_base.py +119 -119
- tapps_agents/core/evaluation_models.py +320 -320
- tapps_agents/core/evaluation_orchestrator.py +225 -225
- tapps_agents/core/evaluators/__init__.py +7 -7
- tapps_agents/core/evaluators/architectural_evaluator.py +205 -205
- tapps_agents/core/evaluators/behavioral_evaluator.py +160 -160
- tapps_agents/core/evaluators/performance_profile_evaluator.py +160 -160
- tapps_agents/core/evaluators/security_posture_evaluator.py +148 -148
- tapps_agents/core/evaluators/spec_compliance_evaluator.py +181 -181
- tapps_agents/core/exceptions.py +107 -107
- tapps_agents/core/expert_config_generator.py +293 -293
- tapps_agents/core/export_schema.py +202 -202
- tapps_agents/core/external_feedback_models.py +102 -102
- tapps_agents/core/external_feedback_storage.py +213 -213
- tapps_agents/core/fallback_strategy.py +314 -314
- tapps_agents/core/feedback_analyzer.py +162 -162
- tapps_agents/core/feedback_collector.py +178 -178
- tapps_agents/core/git_operations.py +445 -445
- tapps_agents/core/hardware_profiler.py +151 -151
- tapps_agents/core/instructions.py +324 -324
- tapps_agents/core/io_guardrails.py +69 -69
- tapps_agents/core/issue_manifest.py +249 -249
- tapps_agents/core/issue_schema.py +139 -139
- tapps_agents/core/json_utils.py +128 -128
- tapps_agents/core/knowledge_graph.py +446 -446
- tapps_agents/core/language_detector.py +296 -296
- tapps_agents/core/learning_confidence.py +242 -242
- tapps_agents/core/learning_dashboard.py +246 -246
- tapps_agents/core/learning_decision.py +384 -384
- tapps_agents/core/learning_explainability.py +578 -578
- tapps_agents/core/learning_export.py +287 -287
- tapps_agents/core/learning_integration.py +228 -228
- tapps_agents/core/llm_behavior.py +232 -232
- tapps_agents/core/long_duration_support.py +786 -786
- tapps_agents/core/mcp_setup.py +106 -106
- tapps_agents/core/memory_integration.py +396 -396
- tapps_agents/core/meta_learning.py +666 -666
- tapps_agents/core/module_path_sanitizer.py +199 -199
- tapps_agents/core/multi_agent_orchestrator.py +382 -382
- tapps_agents/core/network_errors.py +125 -125
- tapps_agents/core/nfr_validator.py +336 -336
- tapps_agents/core/offline_mode.py +158 -158
- tapps_agents/core/output_contracts.py +300 -300
- tapps_agents/core/output_formatter.py +300 -300
- tapps_agents/core/path_normalizer.py +174 -174
- tapps_agents/core/path_validator.py +322 -322
- tapps_agents/core/pattern_library.py +250 -250
- tapps_agents/core/performance_benchmark.py +301 -301
- tapps_agents/core/performance_monitor.py +184 -184
- tapps_agents/core/playwright_mcp_controller.py +771 -771
- tapps_agents/core/policy_loader.py +135 -135
- tapps_agents/core/progress.py +166 -166
- tapps_agents/core/project_profile.py +354 -354
- tapps_agents/core/project_type_detector.py +454 -454
- tapps_agents/core/prompt_base.py +223 -223
- tapps_agents/core/prompt_learning/__init__.py +2 -2
- tapps_agents/core/prompt_learning/learning_loop.py +24 -24
- tapps_agents/core/prompt_learning/project_prompt_store.py +25 -25
- tapps_agents/core/prompt_learning/skills_prompt_analyzer.py +35 -35
- tapps_agents/core/prompt_optimization/__init__.py +6 -6
- tapps_agents/core/prompt_optimization/ab_tester.py +114 -114
- tapps_agents/core/prompt_optimization/correlation_analyzer.py +160 -160
- tapps_agents/core/prompt_optimization/progressive_refiner.py +129 -129
- tapps_agents/core/prompt_optimization/prompt_library.py +37 -37
- tapps_agents/core/requirements_evaluator.py +431 -431
- tapps_agents/core/resource_aware_executor.py +449 -449
- tapps_agents/core/resource_monitor.py +343 -343
- tapps_agents/core/resume_handler.py +298 -298
- tapps_agents/core/retry_handler.py +197 -197
- tapps_agents/core/review_checklists.py +479 -479
- tapps_agents/core/role_loader.py +201 -201
- tapps_agents/core/role_template_loader.py +201 -201
- tapps_agents/core/runtime_mode.py +60 -60
- tapps_agents/core/security_scanner.py +342 -342
- tapps_agents/core/skill_agent_registry.py +194 -194
- tapps_agents/core/skill_integration.py +208 -208
- tapps_agents/core/skill_loader.py +492 -492
- tapps_agents/core/skill_template.py +341 -341
- tapps_agents/core/skill_validator.py +478 -478
- tapps_agents/core/stack_analyzer.py +35 -35
- tapps_agents/core/startup.py +174 -174
- tapps_agents/core/storage_manager.py +397 -397
- tapps_agents/core/storage_models.py +166 -166
- tapps_agents/core/story_evaluator.py +410 -410
- tapps_agents/core/subprocess_utils.py +170 -170
- tapps_agents/core/task_duration.py +296 -296
- tapps_agents/core/task_memory.py +582 -582
- tapps_agents/core/task_state.py +226 -226
- tapps_agents/core/tech_stack_priorities.py +208 -208
- tapps_agents/core/temp_directory.py +194 -194
- tapps_agents/core/template_merger.py +600 -600
- tapps_agents/core/template_selector.py +280 -280
- tapps_agents/core/test_generator.py +286 -286
- tapps_agents/core/tiered_context.py +253 -253
- tapps_agents/core/token_monitor.py +345 -345
- tapps_agents/core/traceability.py +254 -254
- tapps_agents/core/trajectory_tracker.py +50 -50
- tapps_agents/core/unicode_safe.py +143 -143
- tapps_agents/core/unified_cache_config.py +170 -170
- tapps_agents/core/unified_state.py +324 -324
- tapps_agents/core/validate_cursor_setup.py +237 -237
- tapps_agents/core/validation_registry.py +136 -136
- tapps_agents/core/validators/__init__.py +4 -4
- tapps_agents/core/validators/python_validator.py +87 -87
- tapps_agents/core/verification_agent.py +90 -90
- tapps_agents/core/visual_feedback.py +644 -644
- tapps_agents/core/workflow_validator.py +197 -197
- tapps_agents/core/worktree.py +367 -367
- tapps_agents/docker/__init__.py +10 -10
- tapps_agents/docker/analyzer.py +186 -186
- tapps_agents/docker/debugger.py +229 -229
- tapps_agents/docker/error_patterns.py +216 -216
- tapps_agents/epic/__init__.py +22 -22
- tapps_agents/epic/beads_sync.py +115 -115
- tapps_agents/epic/markdown_sync.py +105 -105
- tapps_agents/epic/models.py +96 -96
- tapps_agents/experts/__init__.py +163 -163
- tapps_agents/experts/agent_integration.py +243 -243
- tapps_agents/experts/auto_generator.py +331 -331
- tapps_agents/experts/base_expert.py +536 -536
- tapps_agents/experts/builtin_registry.py +261 -261
- tapps_agents/experts/business_metrics.py +565 -565
- tapps_agents/experts/cache.py +266 -266
- tapps_agents/experts/confidence_breakdown.py +306 -306
- tapps_agents/experts/confidence_calculator.py +336 -336
- tapps_agents/experts/confidence_metrics.py +236 -236
- tapps_agents/experts/domain_config.py +311 -311
- tapps_agents/experts/domain_detector.py +550 -550
- tapps_agents/experts/domain_utils.py +84 -84
- tapps_agents/experts/expert_config.py +113 -113
- tapps_agents/experts/expert_engine.py +465 -465
- tapps_agents/experts/expert_registry.py +744 -744
- tapps_agents/experts/expert_synthesizer.py +70 -70
- tapps_agents/experts/governance.py +197 -197
- tapps_agents/experts/history_logger.py +312 -312
- tapps_agents/experts/knowledge/README.md +180 -180
- tapps_agents/experts/knowledge/accessibility/accessible-forms.md +331 -331
- tapps_agents/experts/knowledge/accessibility/aria-patterns.md +344 -344
- tapps_agents/experts/knowledge/accessibility/color-contrast.md +285 -285
- tapps_agents/experts/knowledge/accessibility/keyboard-navigation.md +332 -332
- tapps_agents/experts/knowledge/accessibility/screen-readers.md +282 -282
- tapps_agents/experts/knowledge/accessibility/semantic-html.md +355 -355
- tapps_agents/experts/knowledge/accessibility/testing-accessibility.md +369 -369
- tapps_agents/experts/knowledge/accessibility/wcag-2.1.md +296 -296
- tapps_agents/experts/knowledge/accessibility/wcag-2.2.md +211 -211
- tapps_agents/experts/knowledge/agent-learning/best-practices.md +715 -715
- tapps_agents/experts/knowledge/agent-learning/pattern-extraction.md +282 -282
- tapps_agents/experts/knowledge/agent-learning/prompt-optimization.md +320 -320
- tapps_agents/experts/knowledge/ai-frameworks/model-optimization.md +90 -90
- tapps_agents/experts/knowledge/ai-frameworks/openvino-patterns.md +260 -260
- tapps_agents/experts/knowledge/api-design-integration/api-gateway-patterns.md +309 -309
- tapps_agents/experts/knowledge/api-design-integration/api-security-patterns.md +521 -521
- tapps_agents/experts/knowledge/api-design-integration/api-versioning.md +421 -421
- tapps_agents/experts/knowledge/api-design-integration/async-protocol-patterns.md +61 -61
- tapps_agents/experts/knowledge/api-design-integration/contract-testing.md +221 -221
- tapps_agents/experts/knowledge/api-design-integration/external-api-integration.md +489 -489
- tapps_agents/experts/knowledge/api-design-integration/fastapi-patterns.md +360 -360
- tapps_agents/experts/knowledge/api-design-integration/fastapi-testing.md +262 -262
- tapps_agents/experts/knowledge/api-design-integration/graphql-patterns.md +582 -582
- tapps_agents/experts/knowledge/api-design-integration/grpc-best-practices.md +499 -499
- tapps_agents/experts/knowledge/api-design-integration/mqtt-patterns.md +455 -455
- tapps_agents/experts/knowledge/api-design-integration/rate-limiting.md +507 -507
- tapps_agents/experts/knowledge/api-design-integration/restful-api-design.md +618 -618
- tapps_agents/experts/knowledge/api-design-integration/websocket-patterns.md +480 -480
- tapps_agents/experts/knowledge/cloud-infrastructure/cloud-native-patterns.md +175 -175
- tapps_agents/experts/knowledge/cloud-infrastructure/container-health-checks.md +261 -261
- tapps_agents/experts/knowledge/cloud-infrastructure/containerization.md +222 -222
- tapps_agents/experts/knowledge/cloud-infrastructure/cost-optimization.md +122 -122
- tapps_agents/experts/knowledge/cloud-infrastructure/disaster-recovery.md +153 -153
- tapps_agents/experts/knowledge/cloud-infrastructure/dockerfile-patterns.md +285 -285
- tapps_agents/experts/knowledge/cloud-infrastructure/infrastructure-as-code.md +187 -187
- tapps_agents/experts/knowledge/cloud-infrastructure/kubernetes-patterns.md +253 -253
- tapps_agents/experts/knowledge/cloud-infrastructure/multi-cloud-strategies.md +155 -155
- tapps_agents/experts/knowledge/cloud-infrastructure/serverless-architecture.md +200 -200
- tapps_agents/experts/knowledge/code-quality-analysis/README.md +16 -16
- tapps_agents/experts/knowledge/code-quality-analysis/code-metrics.md +137 -137
- tapps_agents/experts/knowledge/code-quality-analysis/complexity-analysis.md +181 -181
- tapps_agents/experts/knowledge/code-quality-analysis/technical-debt-patterns.md +191 -191
- tapps_agents/experts/knowledge/data-privacy-compliance/anonymization.md +313 -313
- tapps_agents/experts/knowledge/data-privacy-compliance/ccpa.md +255 -255
- tapps_agents/experts/knowledge/data-privacy-compliance/consent-management.md +282 -282
- tapps_agents/experts/knowledge/data-privacy-compliance/data-minimization.md +275 -275
- tapps_agents/experts/knowledge/data-privacy-compliance/data-retention.md +297 -297
- tapps_agents/experts/knowledge/data-privacy-compliance/data-subject-rights.md +383 -383
- tapps_agents/experts/knowledge/data-privacy-compliance/encryption-privacy.md +285 -285
- tapps_agents/experts/knowledge/data-privacy-compliance/gdpr.md +344 -344
- tapps_agents/experts/knowledge/data-privacy-compliance/hipaa.md +385 -385
- tapps_agents/experts/knowledge/data-privacy-compliance/privacy-by-design.md +280 -280
- tapps_agents/experts/knowledge/database-data-management/acid-vs-cap.md +164 -164
- tapps_agents/experts/knowledge/database-data-management/backup-and-recovery.md +182 -182
- tapps_agents/experts/knowledge/database-data-management/data-modeling.md +172 -172
- tapps_agents/experts/knowledge/database-data-management/database-design.md +187 -187
- tapps_agents/experts/knowledge/database-data-management/flux-query-optimization.md +342 -342
- tapps_agents/experts/knowledge/database-data-management/influxdb-connection-patterns.md +432 -432
- tapps_agents/experts/knowledge/database-data-management/influxdb-patterns.md +442 -442
- tapps_agents/experts/knowledge/database-data-management/migration-strategies.md +216 -216
- tapps_agents/experts/knowledge/database-data-management/nosql-patterns.md +259 -259
- tapps_agents/experts/knowledge/database-data-management/scalability-patterns.md +184 -184
- tapps_agents/experts/knowledge/database-data-management/sql-optimization.md +175 -175
- tapps_agents/experts/knowledge/database-data-management/time-series-modeling.md +444 -444
- tapps_agents/experts/knowledge/development-workflow/README.md +16 -16
- tapps_agents/experts/knowledge/development-workflow/automation-best-practices.md +216 -216
- tapps_agents/experts/knowledge/development-workflow/build-strategies.md +198 -198
- tapps_agents/experts/knowledge/development-workflow/deployment-patterns.md +205 -205
- tapps_agents/experts/knowledge/development-workflow/git-workflows.md +205 -205
- tapps_agents/experts/knowledge/documentation-knowledge-management/README.md +16 -16
- tapps_agents/experts/knowledge/documentation-knowledge-management/api-documentation-patterns.md +231 -231
- tapps_agents/experts/knowledge/documentation-knowledge-management/documentation-standards.md +191 -191
- tapps_agents/experts/knowledge/documentation-knowledge-management/knowledge-management.md +171 -171
- tapps_agents/experts/knowledge/documentation-knowledge-management/technical-writing-guide.md +192 -192
- tapps_agents/experts/knowledge/observability-monitoring/alerting-patterns.md +461 -461
- tapps_agents/experts/knowledge/observability-monitoring/apm-tools.md +459 -459
- tapps_agents/experts/knowledge/observability-monitoring/distributed-tracing.md +367 -367
- tapps_agents/experts/knowledge/observability-monitoring/logging-strategies.md +478 -478
- tapps_agents/experts/knowledge/observability-monitoring/metrics-and-monitoring.md +510 -510
- tapps_agents/experts/knowledge/observability-monitoring/observability-best-practices.md +492 -492
- tapps_agents/experts/knowledge/observability-monitoring/open-telemetry.md +573 -573
- tapps_agents/experts/knowledge/observability-monitoring/slo-sli-sla.md +419 -419
- tapps_agents/experts/knowledge/performance/anti-patterns.md +284 -284
- tapps_agents/experts/knowledge/performance/api-performance.md +256 -256
- tapps_agents/experts/knowledge/performance/caching.md +327 -327
- tapps_agents/experts/knowledge/performance/database-performance.md +252 -252
- tapps_agents/experts/knowledge/performance/optimization-patterns.md +327 -327
- tapps_agents/experts/knowledge/performance/profiling.md +297 -297
- tapps_agents/experts/knowledge/performance/resource-management.md +293 -293
- tapps_agents/experts/knowledge/performance/scalability.md +306 -306
- tapps_agents/experts/knowledge/security/owasp-top10.md +209 -209
- tapps_agents/experts/knowledge/security/secure-coding-practices.md +207 -207
- tapps_agents/experts/knowledge/security/threat-modeling.md +220 -220
- tapps_agents/experts/knowledge/security/vulnerability-patterns.md +342 -342
- tapps_agents/experts/knowledge/software-architecture/docker-compose-patterns.md +314 -314
- tapps_agents/experts/knowledge/software-architecture/microservices-patterns.md +379 -379
- tapps_agents/experts/knowledge/software-architecture/service-communication.md +316 -316
- tapps_agents/experts/knowledge/testing/best-practices.md +310 -310
- tapps_agents/experts/knowledge/testing/coverage-analysis.md +293 -293
- tapps_agents/experts/knowledge/testing/mocking.md +256 -256
- tapps_agents/experts/knowledge/testing/test-automation.md +276 -276
- tapps_agents/experts/knowledge/testing/test-data.md +271 -271
- tapps_agents/experts/knowledge/testing/test-design-patterns.md +280 -280
- tapps_agents/experts/knowledge/testing/test-maintenance.md +236 -236
- tapps_agents/experts/knowledge/testing/test-strategies.md +311 -311
- tapps_agents/experts/knowledge/user-experience/information-architecture.md +325 -325
- tapps_agents/experts/knowledge/user-experience/interaction-design.md +363 -363
- tapps_agents/experts/knowledge/user-experience/prototyping.md +293 -293
- tapps_agents/experts/knowledge/user-experience/usability-heuristics.md +337 -337
- tapps_agents/experts/knowledge/user-experience/usability-testing.md +311 -311
- tapps_agents/experts/knowledge/user-experience/user-journeys.md +296 -296
- tapps_agents/experts/knowledge/user-experience/user-research.md +373 -373
- tapps_agents/experts/knowledge/user-experience/ux-principles.md +340 -340
- tapps_agents/experts/knowledge_freshness.py +321 -321
- tapps_agents/experts/knowledge_ingestion.py +438 -438
- tapps_agents/experts/knowledge_need_detector.py +93 -93
- tapps_agents/experts/knowledge_validator.py +382 -382
- tapps_agents/experts/observability.py +440 -440
- tapps_agents/experts/passive_notifier.py +238 -238
- tapps_agents/experts/proactive_orchestrator.py +32 -32
- tapps_agents/experts/rag_chunker.py +205 -205
- tapps_agents/experts/rag_embedder.py +152 -152
- tapps_agents/experts/rag_evaluation.py +299 -299
- tapps_agents/experts/rag_index.py +303 -303
- tapps_agents/experts/rag_metrics.py +293 -293
- tapps_agents/experts/rag_safety.py +263 -263
- tapps_agents/experts/report_generator.py +296 -296
- tapps_agents/experts/setup_wizard.py +441 -441
- tapps_agents/experts/simple_rag.py +431 -431
- tapps_agents/experts/vector_rag.py +354 -354
- tapps_agents/experts/weight_distributor.py +304 -304
- tapps_agents/health/__init__.py +24 -24
- tapps_agents/health/base.py +75 -75
- tapps_agents/health/checks/__init__.py +22 -22
- tapps_agents/health/checks/automation.py +127 -127
- tapps_agents/health/checks/context7_cache.py +210 -210
- tapps_agents/health/checks/environment.py +116 -116
- tapps_agents/health/checks/execution.py +170 -170
- tapps_agents/health/checks/knowledge_base.py +187 -187
- tapps_agents/health/checks/outcomes.py +324 -324
- tapps_agents/health/collector.py +280 -280
- tapps_agents/health/dashboard.py +137 -137
- tapps_agents/health/metrics.py +151 -151
- tapps_agents/health/orchestrator.py +271 -271
- tapps_agents/health/registry.py +166 -166
- tapps_agents/hooks/__init__.py +33 -33
- tapps_agents/hooks/config.py +140 -140
- tapps_agents/hooks/events.py +135 -135
- tapps_agents/hooks/executor.py +128 -128
- tapps_agents/hooks/manager.py +143 -143
- tapps_agents/integration/__init__.py +8 -8
- tapps_agents/integration/service_integrator.py +121 -121
- tapps_agents/integrations/__init__.py +10 -10
- tapps_agents/integrations/clawdbot.py +525 -525
- tapps_agents/integrations/memory_bridge.py +356 -356
- tapps_agents/mcp/__init__.py +18 -18
- tapps_agents/mcp/gateway.py +112 -112
- tapps_agents/mcp/servers/__init__.py +13 -13
- tapps_agents/mcp/servers/analysis.py +204 -204
- tapps_agents/mcp/servers/context7.py +198 -198
- tapps_agents/mcp/servers/filesystem.py +218 -218
- tapps_agents/mcp/servers/git.py +201 -201
- tapps_agents/mcp/tool_registry.py +115 -115
- tapps_agents/quality/__init__.py +54 -54
- tapps_agents/quality/coverage_analyzer.py +379 -379
- tapps_agents/quality/enforcement.py +82 -82
- tapps_agents/quality/gates/__init__.py +37 -37
- tapps_agents/quality/gates/approval_gate.py +255 -255
- tapps_agents/quality/gates/base.py +84 -84
- tapps_agents/quality/gates/exceptions.py +43 -43
- tapps_agents/quality/gates/policy_gate.py +195 -195
- tapps_agents/quality/gates/registry.py +239 -239
- tapps_agents/quality/gates/security_gate.py +156 -156
- tapps_agents/quality/quality_gates.py +369 -369
- tapps_agents/quality/secret_scanner.py +335 -335
- tapps_agents/session/__init__.py +19 -19
- tapps_agents/session/manager.py +256 -256
- tapps_agents/simple_mode/__init__.py +66 -66
- tapps_agents/simple_mode/agent_contracts.py +357 -357
- tapps_agents/simple_mode/beads_hooks.py +151 -151
- tapps_agents/simple_mode/code_snippet_handler.py +382 -382
- tapps_agents/simple_mode/documentation_manager.py +395 -395
- tapps_agents/simple_mode/documentation_reader.py +187 -187
- tapps_agents/simple_mode/file_inference.py +292 -292
- tapps_agents/simple_mode/framework_change_detector.py +268 -268
- tapps_agents/simple_mode/intent_parser.py +510 -510
- tapps_agents/simple_mode/learning_progression.py +358 -358
- tapps_agents/simple_mode/nl_handler.py +700 -700
- tapps_agents/simple_mode/onboarding.py +253 -253
- tapps_agents/simple_mode/orchestrators/__init__.py +38 -38
- tapps_agents/simple_mode/orchestrators/base.py +185 -185
- tapps_agents/simple_mode/orchestrators/breakdown_orchestrator.py +49 -49
- tapps_agents/simple_mode/orchestrators/brownfield_orchestrator.py +135 -135
- tapps_agents/simple_mode/orchestrators/build_orchestrator.py +2700 -2667
- tapps_agents/simple_mode/orchestrators/deliverable_checklist.py +349 -349
- tapps_agents/simple_mode/orchestrators/enhance_orchestrator.py +53 -53
- tapps_agents/simple_mode/orchestrators/epic_orchestrator.py +122 -122
- tapps_agents/simple_mode/orchestrators/explore_orchestrator.py +184 -184
- tapps_agents/simple_mode/orchestrators/fix_orchestrator.py +723 -723
- tapps_agents/simple_mode/orchestrators/plan_analysis_orchestrator.py +206 -206
- tapps_agents/simple_mode/orchestrators/pr_orchestrator.py +237 -237
- tapps_agents/simple_mode/orchestrators/refactor_orchestrator.py +222 -222
- tapps_agents/simple_mode/orchestrators/requirements_tracer.py +262 -262
- tapps_agents/simple_mode/orchestrators/resume_orchestrator.py +210 -210
- tapps_agents/simple_mode/orchestrators/review_orchestrator.py +161 -161
- tapps_agents/simple_mode/orchestrators/test_orchestrator.py +82 -82
- tapps_agents/simple_mode/output_aggregator.py +340 -340
- tapps_agents/simple_mode/result_formatters.py +598 -598
- tapps_agents/simple_mode/step_dependencies.py +382 -382
- tapps_agents/simple_mode/step_results.py +276 -276
- tapps_agents/simple_mode/streaming.py +388 -388
- tapps_agents/simple_mode/variations.py +129 -129
- tapps_agents/simple_mode/visual_feedback.py +238 -238
- tapps_agents/simple_mode/zero_config.py +274 -274
- tapps_agents/suggestions/__init__.py +8 -8
- tapps_agents/suggestions/inline_suggester.py +52 -52
- tapps_agents/templates/__init__.py +8 -8
- tapps_agents/templates/microservice_generator.py +274 -274
- tapps_agents/utils/env_validator.py +291 -291
- tapps_agents/workflow/__init__.py +171 -171
- tapps_agents/workflow/acceptance_verifier.py +132 -132
- tapps_agents/workflow/agent_handlers/__init__.py +41 -41
- tapps_agents/workflow/agent_handlers/analyst_handler.py +75 -75
- tapps_agents/workflow/agent_handlers/architect_handler.py +107 -107
- tapps_agents/workflow/agent_handlers/base.py +84 -84
- tapps_agents/workflow/agent_handlers/debugger_handler.py +100 -100
- tapps_agents/workflow/agent_handlers/designer_handler.py +110 -110
- tapps_agents/workflow/agent_handlers/documenter_handler.py +94 -94
- tapps_agents/workflow/agent_handlers/implementer_handler.py +235 -235
- tapps_agents/workflow/agent_handlers/ops_handler.py +62 -62
- tapps_agents/workflow/agent_handlers/orchestrator_handler.py +43 -43
- tapps_agents/workflow/agent_handlers/planner_handler.py +98 -98
- tapps_agents/workflow/agent_handlers/registry.py +119 -119
- tapps_agents/workflow/agent_handlers/reviewer_handler.py +119 -119
- tapps_agents/workflow/agent_handlers/tester_handler.py +69 -69
- tapps_agents/workflow/analytics_accessor.py +337 -337
- tapps_agents/workflow/analytics_alerts.py +416 -416
- tapps_agents/workflow/analytics_dashboard_cursor.py +281 -281
- tapps_agents/workflow/analytics_dual_write.py +103 -103
- tapps_agents/workflow/analytics_integration.py +119 -119
- tapps_agents/workflow/analytics_query_parser.py +278 -278
- tapps_agents/workflow/analytics_visualizer.py +259 -259
- tapps_agents/workflow/artifact_helper.py +204 -204
- tapps_agents/workflow/audit_logger.py +263 -263
- tapps_agents/workflow/auto_execution_config.py +340 -340
- tapps_agents/workflow/auto_progression.py +586 -586
- tapps_agents/workflow/branch_cleanup.py +349 -349
- tapps_agents/workflow/checkpoint.py +256 -256
- tapps_agents/workflow/checkpoint_manager.py +178 -178
- tapps_agents/workflow/code_artifact.py +179 -179
- tapps_agents/workflow/common_enums.py +96 -96
- tapps_agents/workflow/confirmation_handler.py +130 -130
- tapps_agents/workflow/context_analyzer.py +222 -222
- tapps_agents/workflow/context_artifact.py +230 -230
- tapps_agents/workflow/cursor_chat.py +94 -94
- tapps_agents/workflow/cursor_executor.py +2337 -2337
- tapps_agents/workflow/cursor_skill_helper.py +516 -516
- tapps_agents/workflow/dependency_resolver.py +244 -244
- tapps_agents/workflow/design_artifact.py +156 -156
- tapps_agents/workflow/detector.py +751 -751
- tapps_agents/workflow/direct_execution_fallback.py +301 -301
- tapps_agents/workflow/docs_artifact.py +168 -168
- tapps_agents/workflow/enforcer.py +389 -389
- tapps_agents/workflow/enhancement_artifact.py +142 -142
- tapps_agents/workflow/error_recovery.py +806 -806
- tapps_agents/workflow/event_bus.py +183 -183
- tapps_agents/workflow/event_log.py +612 -612
- tapps_agents/workflow/events.py +63 -63
- tapps_agents/workflow/exceptions.py +43 -43
- tapps_agents/workflow/execution_graph.py +498 -498
- tapps_agents/workflow/execution_plan.py +126 -126
- tapps_agents/workflow/file_utils.py +186 -186
- tapps_agents/workflow/gate_evaluator.py +182 -182
- tapps_agents/workflow/gate_integration.py +200 -200
- tapps_agents/workflow/graph_visualizer.py +130 -130
- tapps_agents/workflow/health_checker.py +206 -206
- tapps_agents/workflow/logging_helper.py +243 -243
- tapps_agents/workflow/manifest.py +582 -582
- tapps_agents/workflow/marker_writer.py +250 -250
- tapps_agents/workflow/message_formatter.py +188 -188
- tapps_agents/workflow/messaging.py +325 -325
- tapps_agents/workflow/metadata_models.py +91 -91
- tapps_agents/workflow/metrics_integration.py +226 -226
- tapps_agents/workflow/migration_utils.py +116 -116
- tapps_agents/workflow/models.py +148 -148
- tapps_agents/workflow/nlp_config.py +198 -198
- tapps_agents/workflow/nlp_error_handler.py +207 -207
- tapps_agents/workflow/nlp_executor.py +163 -163
- tapps_agents/workflow/nlp_parser.py +528 -528
- tapps_agents/workflow/observability_dashboard.py +451 -451
- tapps_agents/workflow/observer.py +170 -170
- tapps_agents/workflow/ops_artifact.py +257 -257
- tapps_agents/workflow/output_passing.py +214 -214
- tapps_agents/workflow/parallel_executor.py +463 -463
- tapps_agents/workflow/planning_artifact.py +179 -179
- tapps_agents/workflow/preset_loader.py +285 -285
- tapps_agents/workflow/preset_recommender.py +270 -270
- tapps_agents/workflow/progress_logger.py +145 -145
- tapps_agents/workflow/progress_manager.py +303 -303
- tapps_agents/workflow/progress_monitor.py +186 -186
- tapps_agents/workflow/progress_updates.py +423 -423
- tapps_agents/workflow/quality_artifact.py +158 -158
- tapps_agents/workflow/quality_loopback.py +101 -101
- tapps_agents/workflow/recommender.py +387 -387
- tapps_agents/workflow/remediation_loop.py +166 -166
- tapps_agents/workflow/result_aggregator.py +300 -300
- tapps_agents/workflow/review_artifact.py +185 -185
- tapps_agents/workflow/schema_validator.py +522 -522
- tapps_agents/workflow/session_handoff.py +178 -178
- tapps_agents/workflow/skill_invoker.py +648 -648
- tapps_agents/workflow/state_manager.py +756 -756
- tapps_agents/workflow/state_persistence_config.py +331 -331
- tapps_agents/workflow/status_monitor.py +449 -449
- tapps_agents/workflow/step_checkpoint.py +314 -314
- tapps_agents/workflow/step_details.py +201 -201
- tapps_agents/workflow/story_models.py +147 -147
- tapps_agents/workflow/streaming.py +416 -416
- tapps_agents/workflow/suggestion_engine.py +552 -552
- tapps_agents/workflow/testing_artifact.py +186 -186
- tapps_agents/workflow/timeline.py +158 -158
- tapps_agents/workflow/token_integration.py +209 -209
- tapps_agents/workflow/validation.py +217 -217
- tapps_agents/workflow/visual_feedback.py +391 -391
- tapps_agents/workflow/workflow_chain.py +95 -95
- tapps_agents/workflow/workflow_summary.py +219 -219
- tapps_agents/workflow/worktree_manager.py +724 -724
- {tapps_agents-3.5.41.dist-info → tapps_agents-3.6.0.dist-info}/METADATA +672 -672
- tapps_agents-3.6.0.dist-info/RECORD +758 -0
- {tapps_agents-3.5.41.dist-info → tapps_agents-3.6.0.dist-info}/licenses/LICENSE +22 -22
- tapps_agents/health/checks/outcomes.backup_20260204_064058.py +0 -324
- tapps_agents/health/checks/outcomes.backup_20260204_064256.py +0 -324
- tapps_agents/health/checks/outcomes.backup_20260204_064600.py +0 -324
- tapps_agents-3.5.41.dist-info/RECORD +0 -760
- {tapps_agents-3.5.41.dist-info → tapps_agents-3.6.0.dist-info}/WHEEL +0 -0
- {tapps_agents-3.5.41.dist-info → tapps_agents-3.6.0.dist-info}/entry_points.txt +0 -0
- {tapps_agents-3.5.41.dist-info → tapps_agents-3.6.0.dist-info}/top_level.txt +0 -0
|
@@ -1,582 +1,582 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Task Manifest Generator
|
|
3
|
-
|
|
4
|
-
Generates human-readable task checklists from workflow YAML and execution state.
|
|
5
|
-
Epic 7: Task Manifest Generation System
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from datetime import datetime
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
from typing import Any
|
|
11
|
-
|
|
12
|
-
from .execution_plan import generate_execution_plan
|
|
13
|
-
from .models import Artifact, StepExecution, Workflow, WorkflowState, WorkflowStep
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class TaskManifestGenerator:
|
|
17
|
-
"""Generates markdown task manifests from workflow state and execution plan."""
|
|
18
|
-
|
|
19
|
-
def __init__(self, workflow: Workflow, state: WorkflowState):
|
|
20
|
-
"""
|
|
21
|
-
Initialize manifest generator.
|
|
22
|
-
|
|
23
|
-
Args:
|
|
24
|
-
workflow: Workflow definition
|
|
25
|
-
state: Current workflow state
|
|
26
|
-
"""
|
|
27
|
-
self.workflow = workflow
|
|
28
|
-
self.state = state
|
|
29
|
-
self.execution_plan = generate_execution_plan(workflow)
|
|
30
|
-
|
|
31
|
-
def generate(self) -> str:
|
|
32
|
-
"""
|
|
33
|
-
Generate markdown task manifest.
|
|
34
|
-
|
|
35
|
-
Returns:
|
|
36
|
-
Markdown string with task checklist
|
|
37
|
-
"""
|
|
38
|
-
# Edge case: empty workflow
|
|
39
|
-
if not self.workflow.steps:
|
|
40
|
-
return f"# Workflow: {self.workflow.name}\n\n**Status**: No steps defined.\n"
|
|
41
|
-
|
|
42
|
-
lines: list[str] = []
|
|
43
|
-
|
|
44
|
-
# Workflow metadata section
|
|
45
|
-
lines.extend(self._generate_metadata_section())
|
|
46
|
-
|
|
47
|
-
# Completed steps section
|
|
48
|
-
lines.extend(self._generate_completed_steps_section())
|
|
49
|
-
|
|
50
|
-
# Skipped steps section
|
|
51
|
-
lines.extend(self._generate_skipped_steps_section())
|
|
52
|
-
|
|
53
|
-
# Current step section
|
|
54
|
-
lines.extend(self._generate_current_step_section())
|
|
55
|
-
|
|
56
|
-
# Upcoming steps section
|
|
57
|
-
lines.extend(self._generate_upcoming_steps_section())
|
|
58
|
-
|
|
59
|
-
# Artifacts section
|
|
60
|
-
lines.extend(self._generate_artifacts_section())
|
|
61
|
-
|
|
62
|
-
return "\n".join(lines)
|
|
63
|
-
|
|
64
|
-
def _generate_metadata_section(self) -> list[str]:
|
|
65
|
-
"""Generate workflow metadata section."""
|
|
66
|
-
lines = [
|
|
67
|
-
f"# Workflow: {self.workflow.name}",
|
|
68
|
-
"",
|
|
69
|
-
f"**ID**: `{self.workflow.id}`",
|
|
70
|
-
f"**Version**: {self.workflow.version}",
|
|
71
|
-
f"**Type**: {self.workflow.type.value}",
|
|
72
|
-
"",
|
|
73
|
-
]
|
|
74
|
-
|
|
75
|
-
# Status and progress
|
|
76
|
-
completed_count = len(self.state.completed_steps)
|
|
77
|
-
skipped_count = len(self.state.skipped_steps)
|
|
78
|
-
total_steps = len(self.workflow.steps)
|
|
79
|
-
progress_pct = int((completed_count / total_steps) * 100) if total_steps > 0 else 0
|
|
80
|
-
|
|
81
|
-
# Calculate current step number
|
|
82
|
-
if self.state.status == "completed":
|
|
83
|
-
current_step_num = total_steps
|
|
84
|
-
elif self.state.current_step:
|
|
85
|
-
# Find index of current step
|
|
86
|
-
current_idx = next(
|
|
87
|
-
(i for i, s in enumerate(self.workflow.steps) if s.id == self.state.current_step),
|
|
88
|
-
completed_count
|
|
89
|
-
)
|
|
90
|
-
current_step_num = current_idx + 1
|
|
91
|
-
else:
|
|
92
|
-
current_step_num = completed_count + 1
|
|
93
|
-
|
|
94
|
-
status_emoji = self._get_status_emoji(self.state.status)
|
|
95
|
-
lines.extend([
|
|
96
|
-
f"## Status: {status_emoji} {self.state.status.title()} (Step {current_step_num} of {total_steps})",
|
|
97
|
-
"",
|
|
98
|
-
f"**Progress**: {completed_count}/{total_steps} steps completed ({progress_pct}%)",
|
|
99
|
-
])
|
|
100
|
-
|
|
101
|
-
if skipped_count > 0:
|
|
102
|
-
lines.append(f"**Skipped**: {skipped_count} steps")
|
|
103
|
-
|
|
104
|
-
if self.state.started_at:
|
|
105
|
-
lines.append(f"**Started**: {self.state.started_at.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
106
|
-
|
|
107
|
-
if self.state.status == "completed":
|
|
108
|
-
# Find completion time from last step execution
|
|
109
|
-
last_execution = self._get_last_step_execution()
|
|
110
|
-
if last_execution and last_execution.completed_at:
|
|
111
|
-
lines.append(f"**Completed**: {last_execution.completed_at.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
112
|
-
|
|
113
|
-
if self.state.error:
|
|
114
|
-
lines.append(f"**Error**: {self.state.error}")
|
|
115
|
-
|
|
116
|
-
lines.append("")
|
|
117
|
-
return lines
|
|
118
|
-
|
|
119
|
-
def _generate_completed_steps_section(self) -> list[str]:
|
|
120
|
-
"""Generate completed steps section."""
|
|
121
|
-
if not self.state.completed_steps:
|
|
122
|
-
return []
|
|
123
|
-
|
|
124
|
-
lines = ["### Completed Steps", ""]
|
|
125
|
-
|
|
126
|
-
for step_id in self.state.completed_steps:
|
|
127
|
-
step = self._get_step_by_id(step_id)
|
|
128
|
-
if not step:
|
|
129
|
-
continue
|
|
130
|
-
|
|
131
|
-
execution = self._get_step_execution(step_id)
|
|
132
|
-
completion_time = ""
|
|
133
|
-
if execution and execution.completed_at:
|
|
134
|
-
completion_time = f" - Completed {execution.completed_at.strftime('%Y-%m-%d %H:%M:%S')}"
|
|
135
|
-
|
|
136
|
-
duration = ""
|
|
137
|
-
if execution and execution.duration_seconds:
|
|
138
|
-
duration = f" ({execution.duration_seconds:.1f}s)"
|
|
139
|
-
|
|
140
|
-
lines.append(
|
|
141
|
-
f"- [x] **{step.id}** ({step.agent}) - {step.action}{completion_time}{duration}"
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
lines.append("")
|
|
145
|
-
return lines
|
|
146
|
-
|
|
147
|
-
def _generate_skipped_steps_section(self) -> list[str]:
|
|
148
|
-
"""Generate skipped steps section."""
|
|
149
|
-
if not self.state.skipped_steps:
|
|
150
|
-
return []
|
|
151
|
-
|
|
152
|
-
lines = ["### Skipped Steps", ""]
|
|
153
|
-
|
|
154
|
-
for step_id in self.state.skipped_steps:
|
|
155
|
-
step = self._get_step_by_id(step_id)
|
|
156
|
-
if not step:
|
|
157
|
-
continue
|
|
158
|
-
|
|
159
|
-
lines.append(
|
|
160
|
-
f"- [⏭️] **{step.id}** ({step.agent}) - {step.action} - **SKIPPED**"
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
lines.append("")
|
|
164
|
-
return lines
|
|
165
|
-
|
|
166
|
-
def _generate_current_step_section(self) -> list[str]:
|
|
167
|
-
"""Generate current step section."""
|
|
168
|
-
if not self.state.current_step:
|
|
169
|
-
return []
|
|
170
|
-
|
|
171
|
-
step = self._get_step_by_id(self.state.current_step)
|
|
172
|
-
if not step:
|
|
173
|
-
return []
|
|
174
|
-
|
|
175
|
-
lines = [
|
|
176
|
-
"### Current Step",
|
|
177
|
-
"",
|
|
178
|
-
f"- [ ] **{step.id}** ({step.agent}) - {step.action} - **IN PROGRESS**",
|
|
179
|
-
"",
|
|
180
|
-
]
|
|
181
|
-
|
|
182
|
-
# Step details
|
|
183
|
-
if step.requires:
|
|
184
|
-
requires_status = self._format_artifact_status(step.requires)
|
|
185
|
-
lines.append(f" - **Requires**: {requires_status}")
|
|
186
|
-
|
|
187
|
-
if step.creates:
|
|
188
|
-
creates_status = self._format_expected_artifacts(step.creates)
|
|
189
|
-
lines.append(f" - **Creates**: {creates_status}")
|
|
190
|
-
|
|
191
|
-
if step.consults:
|
|
192
|
-
lines.append(f" - **Consults**: {', '.join(step.consults)}")
|
|
193
|
-
|
|
194
|
-
if step.gate:
|
|
195
|
-
lines.append(f" - **Gate**: Quality gate configured")
|
|
196
|
-
|
|
197
|
-
# Worktree info (if available in metadata)
|
|
198
|
-
if step.metadata and step.metadata.get("worktree"):
|
|
199
|
-
lines.append(f" - **Worktree**: `{step.metadata['worktree']}`")
|
|
200
|
-
|
|
201
|
-
# Command (if available in metadata)
|
|
202
|
-
if step.metadata and step.metadata.get("command"):
|
|
203
|
-
lines.append(f" - **Command**: `{step.metadata['command']}`")
|
|
204
|
-
|
|
205
|
-
lines.append("")
|
|
206
|
-
return lines
|
|
207
|
-
|
|
208
|
-
def _generate_upcoming_steps_section(self) -> list[str]:
|
|
209
|
-
"""Generate upcoming steps section."""
|
|
210
|
-
# Find steps that are not completed, not skipped, and not current
|
|
211
|
-
upcoming_steps = []
|
|
212
|
-
for step in self.workflow.steps:
|
|
213
|
-
if (
|
|
214
|
-
step.id not in self.state.completed_steps
|
|
215
|
-
and step.id not in self.state.skipped_steps
|
|
216
|
-
and step.id != self.state.current_step
|
|
217
|
-
):
|
|
218
|
-
upcoming_steps.append(step)
|
|
219
|
-
|
|
220
|
-
if not upcoming_steps:
|
|
221
|
-
return []
|
|
222
|
-
|
|
223
|
-
lines = ["### Upcoming Steps", ""]
|
|
224
|
-
|
|
225
|
-
for step in upcoming_steps:
|
|
226
|
-
# Check if step is blocked (missing required artifacts)
|
|
227
|
-
is_blocked = self._is_step_blocked(step)
|
|
228
|
-
status_indicator = "⏸️ Blocked" if is_blocked else "⏳ Waiting"
|
|
229
|
-
|
|
230
|
-
blocking_info = ""
|
|
231
|
-
if is_blocked:
|
|
232
|
-
missing = self._get_missing_artifacts(step)
|
|
233
|
-
if missing:
|
|
234
|
-
blocking_info = f" - Waiting for {', '.join(missing)}"
|
|
235
|
-
|
|
236
|
-
lines.append(
|
|
237
|
-
f"- [ ] **{step.id}** ({step.agent}) - {step.action} - {status_indicator}{blocking_info}"
|
|
238
|
-
)
|
|
239
|
-
|
|
240
|
-
lines.append("")
|
|
241
|
-
return lines
|
|
242
|
-
|
|
243
|
-
def _generate_artifacts_section(self) -> list[str]:
|
|
244
|
-
"""Generate artifacts section."""
|
|
245
|
-
lines = ["### Artifacts", ""]
|
|
246
|
-
|
|
247
|
-
# Collect all expected artifacts from workflow steps
|
|
248
|
-
expected_artifacts: set[str] = set()
|
|
249
|
-
for step in self.workflow.steps:
|
|
250
|
-
expected_artifacts.update(step.creates)
|
|
251
|
-
|
|
252
|
-
# Show created artifacts
|
|
253
|
-
created_artifacts = [
|
|
254
|
-
art_name
|
|
255
|
-
for art_name, artifact in self.state.artifacts.items()
|
|
256
|
-
if artifact.status == "complete"
|
|
257
|
-
]
|
|
258
|
-
if created_artifacts:
|
|
259
|
-
for art_name in sorted(created_artifacts):
|
|
260
|
-
artifact = self.state.artifacts[art_name]
|
|
261
|
-
created_by = f" (created by {artifact.created_by})" if artifact.created_by else ""
|
|
262
|
-
lines.append(f"- ✅ `{art_name}`{created_by}")
|
|
263
|
-
|
|
264
|
-
# Show expected but not yet created artifacts (not blocking any steps)
|
|
265
|
-
pending_artifacts = expected_artifacts - set(self.state.artifacts.keys())
|
|
266
|
-
blocking_artifacts = self._get_all_missing_artifacts()
|
|
267
|
-
non_blocking_pending = pending_artifacts - blocking_artifacts
|
|
268
|
-
|
|
269
|
-
if non_blocking_pending:
|
|
270
|
-
for art_name in sorted(non_blocking_pending):
|
|
271
|
-
# Find which step creates this artifact
|
|
272
|
-
creating_step = self._find_step_creating_artifact(art_name)
|
|
273
|
-
step_info = f" (expected from {creating_step.id})" if creating_step else ""
|
|
274
|
-
lines.append(f"- ⏳ `{art_name}`{step_info}")
|
|
275
|
-
|
|
276
|
-
# Show missing artifacts (blocking steps)
|
|
277
|
-
if blocking_artifacts:
|
|
278
|
-
for art_name in sorted(blocking_artifacts):
|
|
279
|
-
lines.append(f"- ❌ `{art_name}` (missing, blocking steps)")
|
|
280
|
-
|
|
281
|
-
lines.append("")
|
|
282
|
-
return lines
|
|
283
|
-
|
|
284
|
-
def _get_status_emoji(self, status: str) -> str:
|
|
285
|
-
"""Get emoji for workflow status."""
|
|
286
|
-
status_map = {
|
|
287
|
-
"running": "⏳",
|
|
288
|
-
"completed": "✅",
|
|
289
|
-
"failed": "❌",
|
|
290
|
-
"paused": "⏸️",
|
|
291
|
-
}
|
|
292
|
-
return status_map.get(status.lower(), "⏳")
|
|
293
|
-
|
|
294
|
-
def _get_step_by_id(self, step_id: str) -> WorkflowStep | None:
|
|
295
|
-
"""Get workflow step by ID."""
|
|
296
|
-
return next((s for s in self.workflow.steps if s.id == step_id), None)
|
|
297
|
-
|
|
298
|
-
def _get_step_execution(self, step_id: str) -> StepExecution | None:
|
|
299
|
-
"""Get step execution record."""
|
|
300
|
-
return next(
|
|
301
|
-
(se for se in self.state.step_executions if se.step_id == step_id),
|
|
302
|
-
None,
|
|
303
|
-
)
|
|
304
|
-
|
|
305
|
-
def _get_last_step_execution(self) -> StepExecution | None:
|
|
306
|
-
"""Get the last step execution."""
|
|
307
|
-
if not self.state.step_executions:
|
|
308
|
-
return None
|
|
309
|
-
# Get execution with latest completion time, or latest start time if not completed
|
|
310
|
-
return max(
|
|
311
|
-
self.state.step_executions,
|
|
312
|
-
key=lambda se: (
|
|
313
|
-
se.completed_at if se.completed_at else datetime.min,
|
|
314
|
-
se.started_at if se.started_at else datetime.min,
|
|
315
|
-
),
|
|
316
|
-
)
|
|
317
|
-
|
|
318
|
-
def _format_artifact_status(self, artifact_names: list[str]) -> str:
|
|
319
|
-
"""Format artifact status with emojis."""
|
|
320
|
-
parts = []
|
|
321
|
-
for art_name in artifact_names:
|
|
322
|
-
if art_name in self.state.artifacts:
|
|
323
|
-
artifact = self.state.artifacts[art_name]
|
|
324
|
-
if artifact.status == "complete":
|
|
325
|
-
parts.append(f"`{art_name}` ✅")
|
|
326
|
-
else:
|
|
327
|
-
parts.append(f"`{art_name}` ⏳")
|
|
328
|
-
else:
|
|
329
|
-
parts.append(f"`{art_name}` ❌")
|
|
330
|
-
return ", ".join(parts)
|
|
331
|
-
|
|
332
|
-
def _format_expected_artifacts(self, artifact_names: list[str]) -> str:
|
|
333
|
-
"""Format expected artifacts."""
|
|
334
|
-
parts = [f"`{name}` ⏳" for name in artifact_names]
|
|
335
|
-
return ", ".join(parts)
|
|
336
|
-
|
|
337
|
-
def _is_step_blocked(self, step: WorkflowStep) -> bool:
|
|
338
|
-
"""Check if step is blocked by missing artifacts."""
|
|
339
|
-
for required_artifact in step.requires:
|
|
340
|
-
if required_artifact not in self.state.artifacts:
|
|
341
|
-
return True
|
|
342
|
-
artifact = self.state.artifacts[required_artifact]
|
|
343
|
-
if artifact.status != "complete":
|
|
344
|
-
return True
|
|
345
|
-
return False
|
|
346
|
-
|
|
347
|
-
def _get_missing_artifacts(self, step: WorkflowStep) -> list[str]:
|
|
348
|
-
"""Get list of missing artifacts for a step."""
|
|
349
|
-
missing = []
|
|
350
|
-
for required_artifact in step.requires:
|
|
351
|
-
if required_artifact not in self.state.artifacts:
|
|
352
|
-
missing.append(required_artifact)
|
|
353
|
-
else:
|
|
354
|
-
artifact = self.state.artifacts[required_artifact]
|
|
355
|
-
if artifact.status != "complete":
|
|
356
|
-
missing.append(required_artifact)
|
|
357
|
-
return missing
|
|
358
|
-
|
|
359
|
-
def _get_all_missing_artifacts(self) -> set[str]:
|
|
360
|
-
"""Get all missing artifacts that are blocking steps."""
|
|
361
|
-
missing = set()
|
|
362
|
-
for step in self.workflow.steps:
|
|
363
|
-
if step.id not in self.state.completed_steps:
|
|
364
|
-
missing.update(self._get_missing_artifacts(step))
|
|
365
|
-
return missing
|
|
366
|
-
|
|
367
|
-
def _find_step_creating_artifact(self, artifact_name: str) -> WorkflowStep | None:
|
|
368
|
-
"""Find step that creates an artifact."""
|
|
369
|
-
return next(
|
|
370
|
-
(s for s in self.workflow.steps if artifact_name in s.creates),
|
|
371
|
-
None,
|
|
372
|
-
)
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
def generate_manifest(
|
|
376
|
-
workflow: Workflow, state: WorkflowState
|
|
377
|
-
) -> str:
|
|
378
|
-
"""
|
|
379
|
-
Generate task manifest from workflow and state.
|
|
380
|
-
|
|
381
|
-
Args:
|
|
382
|
-
workflow: Workflow definition
|
|
383
|
-
state: Current workflow state
|
|
384
|
-
|
|
385
|
-
Returns:
|
|
386
|
-
Markdown task manifest string
|
|
387
|
-
"""
|
|
388
|
-
generator = TaskManifestGenerator(workflow, state)
|
|
389
|
-
return generator.generate()
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
def save_manifest(
|
|
393
|
-
manifest: str, state_dir: Path, workflow_id: str
|
|
394
|
-
) -> Path:
|
|
395
|
-
"""
|
|
396
|
-
Save manifest to workflow state directory.
|
|
397
|
-
|
|
398
|
-
Args:
|
|
399
|
-
manifest: Manifest markdown content
|
|
400
|
-
state_dir: Directory where workflow state is stored
|
|
401
|
-
workflow_id: Workflow ID
|
|
402
|
-
|
|
403
|
-
Returns:
|
|
404
|
-
Path to saved manifest file
|
|
405
|
-
"""
|
|
406
|
-
state_dir.mkdir(parents=True, exist_ok=True)
|
|
407
|
-
manifest_path = state_dir / f"{workflow_id}" / "task-manifest.md"
|
|
408
|
-
|
|
409
|
-
manifest_path.parent.mkdir(parents=True, exist_ok=True)
|
|
410
|
-
manifest_path.write_text(manifest, encoding="utf-8")
|
|
411
|
-
|
|
412
|
-
return manifest_path
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
def sync_manifest_to_project_root(
|
|
416
|
-
manifest: str, project_root: Path, filename: str = "workflow-tasks.md"
|
|
417
|
-
) -> Path:
|
|
418
|
-
"""
|
|
419
|
-
Sync manifest to project root for visibility.
|
|
420
|
-
|
|
421
|
-
Args:
|
|
422
|
-
manifest: Manifest markdown content
|
|
423
|
-
project_root: Project root directory
|
|
424
|
-
filename: Filename for manifest in project root
|
|
425
|
-
|
|
426
|
-
Returns:
|
|
427
|
-
Path to synced manifest file
|
|
428
|
-
"""
|
|
429
|
-
manifest_path = project_root / filename
|
|
430
|
-
manifest_path.write_text(manifest, encoding="utf-8")
|
|
431
|
-
return manifest_path
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
class TaskManifestParser:
|
|
435
|
-
"""Parser for reading and querying task manifests."""
|
|
436
|
-
|
|
437
|
-
def __init__(self, manifest_path: Path):
|
|
438
|
-
"""
|
|
439
|
-
Initialize manifest parser.
|
|
440
|
-
|
|
441
|
-
Args:
|
|
442
|
-
manifest_path: Path to manifest markdown file
|
|
443
|
-
"""
|
|
444
|
-
self.manifest_path = manifest_path
|
|
445
|
-
self.content = manifest_path.read_text(encoding="utf-8") if manifest_path.exists() else ""
|
|
446
|
-
|
|
447
|
-
def get_current_step(self) -> str | None:
|
|
448
|
-
"""
|
|
449
|
-
Get current step ID from manifest.
|
|
450
|
-
|
|
451
|
-
Returns:
|
|
452
|
-
Current step ID or None if not found
|
|
453
|
-
"""
|
|
454
|
-
import re
|
|
455
|
-
# More robust regex that handles whitespace variations
|
|
456
|
-
match = re.search(
|
|
457
|
-
r"###\s+Current\s+Step\s+-?\s*\[?\s*\]?\s*\*\*([^\*]+)\*\*",
|
|
458
|
-
self.content,
|
|
459
|
-
re.MULTILINE | re.IGNORECASE,
|
|
460
|
-
)
|
|
461
|
-
return match.group(1).strip() if match else None
|
|
462
|
-
|
|
463
|
-
def get_blocked_steps(self) -> list[str]:
|
|
464
|
-
"""
|
|
465
|
-
Get list of blocked step IDs.
|
|
466
|
-
|
|
467
|
-
Returns:
|
|
468
|
-
List of blocked step IDs
|
|
469
|
-
"""
|
|
470
|
-
import re
|
|
471
|
-
blocked = []
|
|
472
|
-
# More robust regex that handles whitespace and emoji variations
|
|
473
|
-
matches = re.finditer(
|
|
474
|
-
r"-?\s*\[?\s*\]?\s*\*\*([^\*]+)\*\*.*?⏸️\s*Blocked",
|
|
475
|
-
self.content,
|
|
476
|
-
re.MULTILINE,
|
|
477
|
-
)
|
|
478
|
-
for match in matches:
|
|
479
|
-
step_id = match.group(1).strip()
|
|
480
|
-
if step_id:
|
|
481
|
-
blocked.append(step_id)
|
|
482
|
-
return blocked
|
|
483
|
-
|
|
484
|
-
def get_completed_steps(self) -> list[str]:
|
|
485
|
-
"""
|
|
486
|
-
Get list of completed step IDs.
|
|
487
|
-
|
|
488
|
-
Returns:
|
|
489
|
-
List of completed step IDs
|
|
490
|
-
"""
|
|
491
|
-
import re
|
|
492
|
-
completed = []
|
|
493
|
-
# More robust regex that handles whitespace variations
|
|
494
|
-
matches = re.finditer(
|
|
495
|
-
r"-?\s*\[x\]\s*\*\*([^\*]+)\*\*",
|
|
496
|
-
self.content,
|
|
497
|
-
re.MULTILINE | re.IGNORECASE,
|
|
498
|
-
)
|
|
499
|
-
for match in matches:
|
|
500
|
-
step_id = match.group(1).strip()
|
|
501
|
-
if step_id:
|
|
502
|
-
completed.append(step_id)
|
|
503
|
-
return completed
|
|
504
|
-
|
|
505
|
-
def get_missing_artifacts(self) -> list[str]:
|
|
506
|
-
"""
|
|
507
|
-
Get list of missing artifacts.
|
|
508
|
-
|
|
509
|
-
Returns:
|
|
510
|
-
List of missing artifact names
|
|
511
|
-
"""
|
|
512
|
-
import re
|
|
513
|
-
missing = []
|
|
514
|
-
# More robust regex that handles whitespace variations
|
|
515
|
-
matches = re.finditer(
|
|
516
|
-
r"-?\s*❌\s*`([^`]+)`\s*\(missing",
|
|
517
|
-
self.content,
|
|
518
|
-
re.MULTILINE,
|
|
519
|
-
)
|
|
520
|
-
for match in matches:
|
|
521
|
-
artifact_name = match.group(1).strip()
|
|
522
|
-
if artifact_name:
|
|
523
|
-
missing.append(artifact_name)
|
|
524
|
-
return missing
|
|
525
|
-
|
|
526
|
-
def get_workflow_status(self) -> dict[str, Any]:
|
|
527
|
-
"""
|
|
528
|
-
Get workflow status information.
|
|
529
|
-
|
|
530
|
-
Returns:
|
|
531
|
-
Dictionary with status information
|
|
532
|
-
"""
|
|
533
|
-
import re
|
|
534
|
-
|
|
535
|
-
status_match = re.search(
|
|
536
|
-
r"## Status: .*? (\w+) \(Step (\d+) of (\d+)\)",
|
|
537
|
-
self.content,
|
|
538
|
-
)
|
|
539
|
-
|
|
540
|
-
progress_match = re.search(
|
|
541
|
-
r"\*\*Progress\*\*: (\d+)/(\d+) steps completed \((\d+)%\)",
|
|
542
|
-
self.content,
|
|
543
|
-
)
|
|
544
|
-
|
|
545
|
-
return {
|
|
546
|
-
"status": status_match.group(1).lower() if status_match else "unknown",
|
|
547
|
-
"current_step_number": int(status_match.group(2)) if status_match else 0,
|
|
548
|
-
"total_steps": int(status_match.group(3)) if status_match else 0,
|
|
549
|
-
"completed_count": int(progress_match.group(1)) if progress_match else 0,
|
|
550
|
-
"total_count": int(progress_match.group(2)) if progress_match else 0,
|
|
551
|
-
"progress_percent": int(progress_match.group(3)) if progress_match else 0,
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
def validate_against_state(self, state: WorkflowState) -> tuple[bool, list[str]]:
|
|
555
|
-
"""
|
|
556
|
-
Validate manifest against workflow state.
|
|
557
|
-
|
|
558
|
-
Args:
|
|
559
|
-
state: Workflow state to validate against
|
|
560
|
-
|
|
561
|
-
Returns:
|
|
562
|
-
Tuple of (is_valid, list of validation errors)
|
|
563
|
-
"""
|
|
564
|
-
errors = []
|
|
565
|
-
|
|
566
|
-
# Check completed steps match
|
|
567
|
-
manifest_completed = set(self.get_completed_steps())
|
|
568
|
-
state_completed = set(state.completed_steps)
|
|
569
|
-
if manifest_completed != state_completed:
|
|
570
|
-
errors.append(
|
|
571
|
-
f"Completed steps mismatch: manifest={manifest_completed}, state={state_completed}"
|
|
572
|
-
)
|
|
573
|
-
|
|
574
|
-
# Check current step matches
|
|
575
|
-
manifest_current = self.get_current_step()
|
|
576
|
-
if manifest_current != state.current_step:
|
|
577
|
-
errors.append(
|
|
578
|
-
f"Current step mismatch: manifest={manifest_current}, state={state.current_step}"
|
|
579
|
-
)
|
|
580
|
-
|
|
581
|
-
return len(errors) == 0, errors
|
|
582
|
-
|
|
1
|
+
"""
|
|
2
|
+
Task Manifest Generator
|
|
3
|
+
|
|
4
|
+
Generates human-readable task checklists from workflow YAML and execution state.
|
|
5
|
+
Epic 7: Task Manifest Generation System
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from .execution_plan import generate_execution_plan
|
|
13
|
+
from .models import Artifact, StepExecution, Workflow, WorkflowState, WorkflowStep
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TaskManifestGenerator:
|
|
17
|
+
"""Generates markdown task manifests from workflow state and execution plan."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, workflow: Workflow, state: WorkflowState):
|
|
20
|
+
"""
|
|
21
|
+
Initialize manifest generator.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
workflow: Workflow definition
|
|
25
|
+
state: Current workflow state
|
|
26
|
+
"""
|
|
27
|
+
self.workflow = workflow
|
|
28
|
+
self.state = state
|
|
29
|
+
self.execution_plan = generate_execution_plan(workflow)
|
|
30
|
+
|
|
31
|
+
def generate(self) -> str:
|
|
32
|
+
"""
|
|
33
|
+
Generate markdown task manifest.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Markdown string with task checklist
|
|
37
|
+
"""
|
|
38
|
+
# Edge case: empty workflow
|
|
39
|
+
if not self.workflow.steps:
|
|
40
|
+
return f"# Workflow: {self.workflow.name}\n\n**Status**: No steps defined.\n"
|
|
41
|
+
|
|
42
|
+
lines: list[str] = []
|
|
43
|
+
|
|
44
|
+
# Workflow metadata section
|
|
45
|
+
lines.extend(self._generate_metadata_section())
|
|
46
|
+
|
|
47
|
+
# Completed steps section
|
|
48
|
+
lines.extend(self._generate_completed_steps_section())
|
|
49
|
+
|
|
50
|
+
# Skipped steps section
|
|
51
|
+
lines.extend(self._generate_skipped_steps_section())
|
|
52
|
+
|
|
53
|
+
# Current step section
|
|
54
|
+
lines.extend(self._generate_current_step_section())
|
|
55
|
+
|
|
56
|
+
# Upcoming steps section
|
|
57
|
+
lines.extend(self._generate_upcoming_steps_section())
|
|
58
|
+
|
|
59
|
+
# Artifacts section
|
|
60
|
+
lines.extend(self._generate_artifacts_section())
|
|
61
|
+
|
|
62
|
+
return "\n".join(lines)
|
|
63
|
+
|
|
64
|
+
def _generate_metadata_section(self) -> list[str]:
|
|
65
|
+
"""Generate workflow metadata section."""
|
|
66
|
+
lines = [
|
|
67
|
+
f"# Workflow: {self.workflow.name}",
|
|
68
|
+
"",
|
|
69
|
+
f"**ID**: `{self.workflow.id}`",
|
|
70
|
+
f"**Version**: {self.workflow.version}",
|
|
71
|
+
f"**Type**: {self.workflow.type.value}",
|
|
72
|
+
"",
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
# Status and progress
|
|
76
|
+
completed_count = len(self.state.completed_steps)
|
|
77
|
+
skipped_count = len(self.state.skipped_steps)
|
|
78
|
+
total_steps = len(self.workflow.steps)
|
|
79
|
+
progress_pct = int((completed_count / total_steps) * 100) if total_steps > 0 else 0
|
|
80
|
+
|
|
81
|
+
# Calculate current step number
|
|
82
|
+
if self.state.status == "completed":
|
|
83
|
+
current_step_num = total_steps
|
|
84
|
+
elif self.state.current_step:
|
|
85
|
+
# Find index of current step
|
|
86
|
+
current_idx = next(
|
|
87
|
+
(i for i, s in enumerate(self.workflow.steps) if s.id == self.state.current_step),
|
|
88
|
+
completed_count
|
|
89
|
+
)
|
|
90
|
+
current_step_num = current_idx + 1
|
|
91
|
+
else:
|
|
92
|
+
current_step_num = completed_count + 1
|
|
93
|
+
|
|
94
|
+
status_emoji = self._get_status_emoji(self.state.status)
|
|
95
|
+
lines.extend([
|
|
96
|
+
f"## Status: {status_emoji} {self.state.status.title()} (Step {current_step_num} of {total_steps})",
|
|
97
|
+
"",
|
|
98
|
+
f"**Progress**: {completed_count}/{total_steps} steps completed ({progress_pct}%)",
|
|
99
|
+
])
|
|
100
|
+
|
|
101
|
+
if skipped_count > 0:
|
|
102
|
+
lines.append(f"**Skipped**: {skipped_count} steps")
|
|
103
|
+
|
|
104
|
+
if self.state.started_at:
|
|
105
|
+
lines.append(f"**Started**: {self.state.started_at.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
106
|
+
|
|
107
|
+
if self.state.status == "completed":
|
|
108
|
+
# Find completion time from last step execution
|
|
109
|
+
last_execution = self._get_last_step_execution()
|
|
110
|
+
if last_execution and last_execution.completed_at:
|
|
111
|
+
lines.append(f"**Completed**: {last_execution.completed_at.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
112
|
+
|
|
113
|
+
if self.state.error:
|
|
114
|
+
lines.append(f"**Error**: {self.state.error}")
|
|
115
|
+
|
|
116
|
+
lines.append("")
|
|
117
|
+
return lines
|
|
118
|
+
|
|
119
|
+
def _generate_completed_steps_section(self) -> list[str]:
|
|
120
|
+
"""Generate completed steps section."""
|
|
121
|
+
if not self.state.completed_steps:
|
|
122
|
+
return []
|
|
123
|
+
|
|
124
|
+
lines = ["### Completed Steps", ""]
|
|
125
|
+
|
|
126
|
+
for step_id in self.state.completed_steps:
|
|
127
|
+
step = self._get_step_by_id(step_id)
|
|
128
|
+
if not step:
|
|
129
|
+
continue
|
|
130
|
+
|
|
131
|
+
execution = self._get_step_execution(step_id)
|
|
132
|
+
completion_time = ""
|
|
133
|
+
if execution and execution.completed_at:
|
|
134
|
+
completion_time = f" - Completed {execution.completed_at.strftime('%Y-%m-%d %H:%M:%S')}"
|
|
135
|
+
|
|
136
|
+
duration = ""
|
|
137
|
+
if execution and execution.duration_seconds:
|
|
138
|
+
duration = f" ({execution.duration_seconds:.1f}s)"
|
|
139
|
+
|
|
140
|
+
lines.append(
|
|
141
|
+
f"- [x] **{step.id}** ({step.agent}) - {step.action}{completion_time}{duration}"
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
lines.append("")
|
|
145
|
+
return lines
|
|
146
|
+
|
|
147
|
+
def _generate_skipped_steps_section(self) -> list[str]:
|
|
148
|
+
"""Generate skipped steps section."""
|
|
149
|
+
if not self.state.skipped_steps:
|
|
150
|
+
return []
|
|
151
|
+
|
|
152
|
+
lines = ["### Skipped Steps", ""]
|
|
153
|
+
|
|
154
|
+
for step_id in self.state.skipped_steps:
|
|
155
|
+
step = self._get_step_by_id(step_id)
|
|
156
|
+
if not step:
|
|
157
|
+
continue
|
|
158
|
+
|
|
159
|
+
lines.append(
|
|
160
|
+
f"- [⏭️] **{step.id}** ({step.agent}) - {step.action} - **SKIPPED**"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
lines.append("")
|
|
164
|
+
return lines
|
|
165
|
+
|
|
166
|
+
def _generate_current_step_section(self) -> list[str]:
|
|
167
|
+
"""Generate current step section."""
|
|
168
|
+
if not self.state.current_step:
|
|
169
|
+
return []
|
|
170
|
+
|
|
171
|
+
step = self._get_step_by_id(self.state.current_step)
|
|
172
|
+
if not step:
|
|
173
|
+
return []
|
|
174
|
+
|
|
175
|
+
lines = [
|
|
176
|
+
"### Current Step",
|
|
177
|
+
"",
|
|
178
|
+
f"- [ ] **{step.id}** ({step.agent}) - {step.action} - **IN PROGRESS**",
|
|
179
|
+
"",
|
|
180
|
+
]
|
|
181
|
+
|
|
182
|
+
# Step details
|
|
183
|
+
if step.requires:
|
|
184
|
+
requires_status = self._format_artifact_status(step.requires)
|
|
185
|
+
lines.append(f" - **Requires**: {requires_status}")
|
|
186
|
+
|
|
187
|
+
if step.creates:
|
|
188
|
+
creates_status = self._format_expected_artifacts(step.creates)
|
|
189
|
+
lines.append(f" - **Creates**: {creates_status}")
|
|
190
|
+
|
|
191
|
+
if step.consults:
|
|
192
|
+
lines.append(f" - **Consults**: {', '.join(step.consults)}")
|
|
193
|
+
|
|
194
|
+
if step.gate:
|
|
195
|
+
lines.append(f" - **Gate**: Quality gate configured")
|
|
196
|
+
|
|
197
|
+
# Worktree info (if available in metadata)
|
|
198
|
+
if step.metadata and step.metadata.get("worktree"):
|
|
199
|
+
lines.append(f" - **Worktree**: `{step.metadata['worktree']}`")
|
|
200
|
+
|
|
201
|
+
# Command (if available in metadata)
|
|
202
|
+
if step.metadata and step.metadata.get("command"):
|
|
203
|
+
lines.append(f" - **Command**: `{step.metadata['command']}`")
|
|
204
|
+
|
|
205
|
+
lines.append("")
|
|
206
|
+
return lines
|
|
207
|
+
|
|
208
|
+
def _generate_upcoming_steps_section(self) -> list[str]:
|
|
209
|
+
"""Generate upcoming steps section."""
|
|
210
|
+
# Find steps that are not completed, not skipped, and not current
|
|
211
|
+
upcoming_steps = []
|
|
212
|
+
for step in self.workflow.steps:
|
|
213
|
+
if (
|
|
214
|
+
step.id not in self.state.completed_steps
|
|
215
|
+
and step.id not in self.state.skipped_steps
|
|
216
|
+
and step.id != self.state.current_step
|
|
217
|
+
):
|
|
218
|
+
upcoming_steps.append(step)
|
|
219
|
+
|
|
220
|
+
if not upcoming_steps:
|
|
221
|
+
return []
|
|
222
|
+
|
|
223
|
+
lines = ["### Upcoming Steps", ""]
|
|
224
|
+
|
|
225
|
+
for step in upcoming_steps:
|
|
226
|
+
# Check if step is blocked (missing required artifacts)
|
|
227
|
+
is_blocked = self._is_step_blocked(step)
|
|
228
|
+
status_indicator = "⏸️ Blocked" if is_blocked else "⏳ Waiting"
|
|
229
|
+
|
|
230
|
+
blocking_info = ""
|
|
231
|
+
if is_blocked:
|
|
232
|
+
missing = self._get_missing_artifacts(step)
|
|
233
|
+
if missing:
|
|
234
|
+
blocking_info = f" - Waiting for {', '.join(missing)}"
|
|
235
|
+
|
|
236
|
+
lines.append(
|
|
237
|
+
f"- [ ] **{step.id}** ({step.agent}) - {step.action} - {status_indicator}{blocking_info}"
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
lines.append("")
|
|
241
|
+
return lines
|
|
242
|
+
|
|
243
|
+
def _generate_artifacts_section(self) -> list[str]:
|
|
244
|
+
"""Generate artifacts section."""
|
|
245
|
+
lines = ["### Artifacts", ""]
|
|
246
|
+
|
|
247
|
+
# Collect all expected artifacts from workflow steps
|
|
248
|
+
expected_artifacts: set[str] = set()
|
|
249
|
+
for step in self.workflow.steps:
|
|
250
|
+
expected_artifacts.update(step.creates)
|
|
251
|
+
|
|
252
|
+
# Show created artifacts
|
|
253
|
+
created_artifacts = [
|
|
254
|
+
art_name
|
|
255
|
+
for art_name, artifact in self.state.artifacts.items()
|
|
256
|
+
if artifact.status == "complete"
|
|
257
|
+
]
|
|
258
|
+
if created_artifacts:
|
|
259
|
+
for art_name in sorted(created_artifacts):
|
|
260
|
+
artifact = self.state.artifacts[art_name]
|
|
261
|
+
created_by = f" (created by {artifact.created_by})" if artifact.created_by else ""
|
|
262
|
+
lines.append(f"- ✅ `{art_name}`{created_by}")
|
|
263
|
+
|
|
264
|
+
# Show expected but not yet created artifacts (not blocking any steps)
|
|
265
|
+
pending_artifacts = expected_artifacts - set(self.state.artifacts.keys())
|
|
266
|
+
blocking_artifacts = self._get_all_missing_artifacts()
|
|
267
|
+
non_blocking_pending = pending_artifacts - blocking_artifacts
|
|
268
|
+
|
|
269
|
+
if non_blocking_pending:
|
|
270
|
+
for art_name in sorted(non_blocking_pending):
|
|
271
|
+
# Find which step creates this artifact
|
|
272
|
+
creating_step = self._find_step_creating_artifact(art_name)
|
|
273
|
+
step_info = f" (expected from {creating_step.id})" if creating_step else ""
|
|
274
|
+
lines.append(f"- ⏳ `{art_name}`{step_info}")
|
|
275
|
+
|
|
276
|
+
# Show missing artifacts (blocking steps)
|
|
277
|
+
if blocking_artifacts:
|
|
278
|
+
for art_name in sorted(blocking_artifacts):
|
|
279
|
+
lines.append(f"- ❌ `{art_name}` (missing, blocking steps)")
|
|
280
|
+
|
|
281
|
+
lines.append("")
|
|
282
|
+
return lines
|
|
283
|
+
|
|
284
|
+
def _get_status_emoji(self, status: str) -> str:
|
|
285
|
+
"""Get emoji for workflow status."""
|
|
286
|
+
status_map = {
|
|
287
|
+
"running": "⏳",
|
|
288
|
+
"completed": "✅",
|
|
289
|
+
"failed": "❌",
|
|
290
|
+
"paused": "⏸️",
|
|
291
|
+
}
|
|
292
|
+
return status_map.get(status.lower(), "⏳")
|
|
293
|
+
|
|
294
|
+
def _get_step_by_id(self, step_id: str) -> WorkflowStep | None:
|
|
295
|
+
"""Get workflow step by ID."""
|
|
296
|
+
return next((s for s in self.workflow.steps if s.id == step_id), None)
|
|
297
|
+
|
|
298
|
+
def _get_step_execution(self, step_id: str) -> StepExecution | None:
|
|
299
|
+
"""Get step execution record."""
|
|
300
|
+
return next(
|
|
301
|
+
(se for se in self.state.step_executions if se.step_id == step_id),
|
|
302
|
+
None,
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
def _get_last_step_execution(self) -> StepExecution | None:
|
|
306
|
+
"""Get the last step execution."""
|
|
307
|
+
if not self.state.step_executions:
|
|
308
|
+
return None
|
|
309
|
+
# Get execution with latest completion time, or latest start time if not completed
|
|
310
|
+
return max(
|
|
311
|
+
self.state.step_executions,
|
|
312
|
+
key=lambda se: (
|
|
313
|
+
se.completed_at if se.completed_at else datetime.min,
|
|
314
|
+
se.started_at if se.started_at else datetime.min,
|
|
315
|
+
),
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
def _format_artifact_status(self, artifact_names: list[str]) -> str:
|
|
319
|
+
"""Format artifact status with emojis."""
|
|
320
|
+
parts = []
|
|
321
|
+
for art_name in artifact_names:
|
|
322
|
+
if art_name in self.state.artifacts:
|
|
323
|
+
artifact = self.state.artifacts[art_name]
|
|
324
|
+
if artifact.status == "complete":
|
|
325
|
+
parts.append(f"`{art_name}` ✅")
|
|
326
|
+
else:
|
|
327
|
+
parts.append(f"`{art_name}` ⏳")
|
|
328
|
+
else:
|
|
329
|
+
parts.append(f"`{art_name}` ❌")
|
|
330
|
+
return ", ".join(parts)
|
|
331
|
+
|
|
332
|
+
def _format_expected_artifacts(self, artifact_names: list[str]) -> str:
|
|
333
|
+
"""Format expected artifacts."""
|
|
334
|
+
parts = [f"`{name}` ⏳" for name in artifact_names]
|
|
335
|
+
return ", ".join(parts)
|
|
336
|
+
|
|
337
|
+
def _is_step_blocked(self, step: WorkflowStep) -> bool:
|
|
338
|
+
"""Check if step is blocked by missing artifacts."""
|
|
339
|
+
for required_artifact in step.requires:
|
|
340
|
+
if required_artifact not in self.state.artifacts:
|
|
341
|
+
return True
|
|
342
|
+
artifact = self.state.artifacts[required_artifact]
|
|
343
|
+
if artifact.status != "complete":
|
|
344
|
+
return True
|
|
345
|
+
return False
|
|
346
|
+
|
|
347
|
+
def _get_missing_artifacts(self, step: WorkflowStep) -> list[str]:
|
|
348
|
+
"""Get list of missing artifacts for a step."""
|
|
349
|
+
missing = []
|
|
350
|
+
for required_artifact in step.requires:
|
|
351
|
+
if required_artifact not in self.state.artifacts:
|
|
352
|
+
missing.append(required_artifact)
|
|
353
|
+
else:
|
|
354
|
+
artifact = self.state.artifacts[required_artifact]
|
|
355
|
+
if artifact.status != "complete":
|
|
356
|
+
missing.append(required_artifact)
|
|
357
|
+
return missing
|
|
358
|
+
|
|
359
|
+
def _get_all_missing_artifacts(self) -> set[str]:
|
|
360
|
+
"""Get all missing artifacts that are blocking steps."""
|
|
361
|
+
missing = set()
|
|
362
|
+
for step in self.workflow.steps:
|
|
363
|
+
if step.id not in self.state.completed_steps:
|
|
364
|
+
missing.update(self._get_missing_artifacts(step))
|
|
365
|
+
return missing
|
|
366
|
+
|
|
367
|
+
def _find_step_creating_artifact(self, artifact_name: str) -> WorkflowStep | None:
|
|
368
|
+
"""Find step that creates an artifact."""
|
|
369
|
+
return next(
|
|
370
|
+
(s for s in self.workflow.steps if artifact_name in s.creates),
|
|
371
|
+
None,
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def generate_manifest(
|
|
376
|
+
workflow: Workflow, state: WorkflowState
|
|
377
|
+
) -> str:
|
|
378
|
+
"""
|
|
379
|
+
Generate task manifest from workflow and state.
|
|
380
|
+
|
|
381
|
+
Args:
|
|
382
|
+
workflow: Workflow definition
|
|
383
|
+
state: Current workflow state
|
|
384
|
+
|
|
385
|
+
Returns:
|
|
386
|
+
Markdown task manifest string
|
|
387
|
+
"""
|
|
388
|
+
generator = TaskManifestGenerator(workflow, state)
|
|
389
|
+
return generator.generate()
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def save_manifest(
|
|
393
|
+
manifest: str, state_dir: Path, workflow_id: str
|
|
394
|
+
) -> Path:
|
|
395
|
+
"""
|
|
396
|
+
Save manifest to workflow state directory.
|
|
397
|
+
|
|
398
|
+
Args:
|
|
399
|
+
manifest: Manifest markdown content
|
|
400
|
+
state_dir: Directory where workflow state is stored
|
|
401
|
+
workflow_id: Workflow ID
|
|
402
|
+
|
|
403
|
+
Returns:
|
|
404
|
+
Path to saved manifest file
|
|
405
|
+
"""
|
|
406
|
+
state_dir.mkdir(parents=True, exist_ok=True)
|
|
407
|
+
manifest_path = state_dir / f"{workflow_id}" / "task-manifest.md"
|
|
408
|
+
|
|
409
|
+
manifest_path.parent.mkdir(parents=True, exist_ok=True)
|
|
410
|
+
manifest_path.write_text(manifest, encoding="utf-8")
|
|
411
|
+
|
|
412
|
+
return manifest_path
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def sync_manifest_to_project_root(
|
|
416
|
+
manifest: str, project_root: Path, filename: str = "workflow-tasks.md"
|
|
417
|
+
) -> Path:
|
|
418
|
+
"""
|
|
419
|
+
Sync manifest to project root for visibility.
|
|
420
|
+
|
|
421
|
+
Args:
|
|
422
|
+
manifest: Manifest markdown content
|
|
423
|
+
project_root: Project root directory
|
|
424
|
+
filename: Filename for manifest in project root
|
|
425
|
+
|
|
426
|
+
Returns:
|
|
427
|
+
Path to synced manifest file
|
|
428
|
+
"""
|
|
429
|
+
manifest_path = project_root / filename
|
|
430
|
+
manifest_path.write_text(manifest, encoding="utf-8")
|
|
431
|
+
return manifest_path
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
class TaskManifestParser:
|
|
435
|
+
"""Parser for reading and querying task manifests."""
|
|
436
|
+
|
|
437
|
+
def __init__(self, manifest_path: Path):
|
|
438
|
+
"""
|
|
439
|
+
Initialize manifest parser.
|
|
440
|
+
|
|
441
|
+
Args:
|
|
442
|
+
manifest_path: Path to manifest markdown file
|
|
443
|
+
"""
|
|
444
|
+
self.manifest_path = manifest_path
|
|
445
|
+
self.content = manifest_path.read_text(encoding="utf-8") if manifest_path.exists() else ""
|
|
446
|
+
|
|
447
|
+
def get_current_step(self) -> str | None:
|
|
448
|
+
"""
|
|
449
|
+
Get current step ID from manifest.
|
|
450
|
+
|
|
451
|
+
Returns:
|
|
452
|
+
Current step ID or None if not found
|
|
453
|
+
"""
|
|
454
|
+
import re
|
|
455
|
+
# More robust regex that handles whitespace variations
|
|
456
|
+
match = re.search(
|
|
457
|
+
r"###\s+Current\s+Step\s+-?\s*\[?\s*\]?\s*\*\*([^\*]+)\*\*",
|
|
458
|
+
self.content,
|
|
459
|
+
re.MULTILINE | re.IGNORECASE,
|
|
460
|
+
)
|
|
461
|
+
return match.group(1).strip() if match else None
|
|
462
|
+
|
|
463
|
+
def get_blocked_steps(self) -> list[str]:
|
|
464
|
+
"""
|
|
465
|
+
Get list of blocked step IDs.
|
|
466
|
+
|
|
467
|
+
Returns:
|
|
468
|
+
List of blocked step IDs
|
|
469
|
+
"""
|
|
470
|
+
import re
|
|
471
|
+
blocked = []
|
|
472
|
+
# More robust regex that handles whitespace and emoji variations
|
|
473
|
+
matches = re.finditer(
|
|
474
|
+
r"-?\s*\[?\s*\]?\s*\*\*([^\*]+)\*\*.*?⏸️\s*Blocked",
|
|
475
|
+
self.content,
|
|
476
|
+
re.MULTILINE,
|
|
477
|
+
)
|
|
478
|
+
for match in matches:
|
|
479
|
+
step_id = match.group(1).strip()
|
|
480
|
+
if step_id:
|
|
481
|
+
blocked.append(step_id)
|
|
482
|
+
return blocked
|
|
483
|
+
|
|
484
|
+
def get_completed_steps(self) -> list[str]:
|
|
485
|
+
"""
|
|
486
|
+
Get list of completed step IDs.
|
|
487
|
+
|
|
488
|
+
Returns:
|
|
489
|
+
List of completed step IDs
|
|
490
|
+
"""
|
|
491
|
+
import re
|
|
492
|
+
completed = []
|
|
493
|
+
# More robust regex that handles whitespace variations
|
|
494
|
+
matches = re.finditer(
|
|
495
|
+
r"-?\s*\[x\]\s*\*\*([^\*]+)\*\*",
|
|
496
|
+
self.content,
|
|
497
|
+
re.MULTILINE | re.IGNORECASE,
|
|
498
|
+
)
|
|
499
|
+
for match in matches:
|
|
500
|
+
step_id = match.group(1).strip()
|
|
501
|
+
if step_id:
|
|
502
|
+
completed.append(step_id)
|
|
503
|
+
return completed
|
|
504
|
+
|
|
505
|
+
def get_missing_artifacts(self) -> list[str]:
|
|
506
|
+
"""
|
|
507
|
+
Get list of missing artifacts.
|
|
508
|
+
|
|
509
|
+
Returns:
|
|
510
|
+
List of missing artifact names
|
|
511
|
+
"""
|
|
512
|
+
import re
|
|
513
|
+
missing = []
|
|
514
|
+
# More robust regex that handles whitespace variations
|
|
515
|
+
matches = re.finditer(
|
|
516
|
+
r"-?\s*❌\s*`([^`]+)`\s*\(missing",
|
|
517
|
+
self.content,
|
|
518
|
+
re.MULTILINE,
|
|
519
|
+
)
|
|
520
|
+
for match in matches:
|
|
521
|
+
artifact_name = match.group(1).strip()
|
|
522
|
+
if artifact_name:
|
|
523
|
+
missing.append(artifact_name)
|
|
524
|
+
return missing
|
|
525
|
+
|
|
526
|
+
def get_workflow_status(self) -> dict[str, Any]:
|
|
527
|
+
"""
|
|
528
|
+
Get workflow status information.
|
|
529
|
+
|
|
530
|
+
Returns:
|
|
531
|
+
Dictionary with status information
|
|
532
|
+
"""
|
|
533
|
+
import re
|
|
534
|
+
|
|
535
|
+
status_match = re.search(
|
|
536
|
+
r"## Status: .*? (\w+) \(Step (\d+) of (\d+)\)",
|
|
537
|
+
self.content,
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
progress_match = re.search(
|
|
541
|
+
r"\*\*Progress\*\*: (\d+)/(\d+) steps completed \((\d+)%\)",
|
|
542
|
+
self.content,
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
return {
|
|
546
|
+
"status": status_match.group(1).lower() if status_match else "unknown",
|
|
547
|
+
"current_step_number": int(status_match.group(2)) if status_match else 0,
|
|
548
|
+
"total_steps": int(status_match.group(3)) if status_match else 0,
|
|
549
|
+
"completed_count": int(progress_match.group(1)) if progress_match else 0,
|
|
550
|
+
"total_count": int(progress_match.group(2)) if progress_match else 0,
|
|
551
|
+
"progress_percent": int(progress_match.group(3)) if progress_match else 0,
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
def validate_against_state(self, state: WorkflowState) -> tuple[bool, list[str]]:
|
|
555
|
+
"""
|
|
556
|
+
Validate manifest against workflow state.
|
|
557
|
+
|
|
558
|
+
Args:
|
|
559
|
+
state: Workflow state to validate against
|
|
560
|
+
|
|
561
|
+
Returns:
|
|
562
|
+
Tuple of (is_valid, list of validation errors)
|
|
563
|
+
"""
|
|
564
|
+
errors = []
|
|
565
|
+
|
|
566
|
+
# Check completed steps match
|
|
567
|
+
manifest_completed = set(self.get_completed_steps())
|
|
568
|
+
state_completed = set(state.completed_steps)
|
|
569
|
+
if manifest_completed != state_completed:
|
|
570
|
+
errors.append(
|
|
571
|
+
f"Completed steps mismatch: manifest={manifest_completed}, state={state_completed}"
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
# Check current step matches
|
|
575
|
+
manifest_current = self.get_current_step()
|
|
576
|
+
if manifest_current != state.current_step:
|
|
577
|
+
errors.append(
|
|
578
|
+
f"Current step mismatch: manifest={manifest_current}, state={state.current_step}"
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
return len(errors) == 0, errors
|
|
582
|
+
|