tapps-agents 3.5.40__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 -219
- 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 -2196
- 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 -111
- 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.40.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.40.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.40.dist-info/RECORD +0 -760
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/WHEEL +0 -0
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/entry_points.txt +0 -0
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/top_level.txt +0 -0
|
@@ -1,478 +1,478 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Custom Skill Validation System.
|
|
3
|
-
|
|
4
|
-
Validates custom Skills for format and capability correctness.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
import re
|
|
10
|
-
from dataclasses import dataclass
|
|
11
|
-
from enum import Enum
|
|
12
|
-
from pathlib import Path
|
|
13
|
-
from typing import Any
|
|
14
|
-
|
|
15
|
-
import yaml
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class ValidationSeverity(Enum):
|
|
19
|
-
"""Validation error severity levels."""
|
|
20
|
-
|
|
21
|
-
ERROR = "error"
|
|
22
|
-
WARNING = "warning"
|
|
23
|
-
INFO = "info"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
@dataclass
|
|
27
|
-
class ValidationError:
|
|
28
|
-
"""Represents a validation error or warning."""
|
|
29
|
-
|
|
30
|
-
severity: ValidationSeverity
|
|
31
|
-
field: str | None
|
|
32
|
-
message: str
|
|
33
|
-
suggestion: str | None = None
|
|
34
|
-
line_number: int | None = None
|
|
35
|
-
|
|
36
|
-
def __str__(self) -> str:
|
|
37
|
-
"""Format error message."""
|
|
38
|
-
parts = [f"[{self.severity.value.upper()}]"]
|
|
39
|
-
if self.field:
|
|
40
|
-
parts.append(f"Field '{self.field}':")
|
|
41
|
-
parts.append(self.message)
|
|
42
|
-
if self.suggestion:
|
|
43
|
-
parts.append(f"({self.suggestion})")
|
|
44
|
-
if self.line_number:
|
|
45
|
-
parts.append(f"[Line {self.line_number}]")
|
|
46
|
-
return " ".join(parts)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
@dataclass
|
|
50
|
-
class ValidationResult:
|
|
51
|
-
"""Result of Skill validation."""
|
|
52
|
-
|
|
53
|
-
is_valid: bool
|
|
54
|
-
errors: list[ValidationError]
|
|
55
|
-
skill_path: Path
|
|
56
|
-
skill_name: str | None = None
|
|
57
|
-
|
|
58
|
-
def has_errors(self) -> bool:
|
|
59
|
-
"""Check if result has any errors."""
|
|
60
|
-
return any(e.severity == ValidationSeverity.ERROR for e in self.errors)
|
|
61
|
-
|
|
62
|
-
def has_warnings(self) -> bool:
|
|
63
|
-
"""Check if result has any warnings."""
|
|
64
|
-
return any(e.severity == ValidationSeverity.WARNING for e in self.errors)
|
|
65
|
-
|
|
66
|
-
def get_error_summary(self) -> str:
|
|
67
|
-
"""Get summary of validation errors."""
|
|
68
|
-
if self.is_valid and not self.has_warnings():
|
|
69
|
-
return f"✅ Skill '{self.skill_name or self.skill_path.name}' is valid."
|
|
70
|
-
|
|
71
|
-
summary_parts = [f"Skill '{self.skill_name or self.skill_path.name}':"]
|
|
72
|
-
error_count = sum(1 for e in self.errors if e.severity == ValidationSeverity.ERROR)
|
|
73
|
-
warning_count = sum(1 for e in self.errors if e.severity == ValidationSeverity.WARNING)
|
|
74
|
-
|
|
75
|
-
if error_count > 0:
|
|
76
|
-
summary_parts.append(f"{error_count} error(s)")
|
|
77
|
-
if warning_count > 0:
|
|
78
|
-
summary_parts.append(f"{warning_count} warning(s)")
|
|
79
|
-
|
|
80
|
-
return " ".join(summary_parts)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
class SkillValidator:
|
|
84
|
-
"""Validates custom Skills for format and capability correctness."""
|
|
85
|
-
|
|
86
|
-
# Required YAML frontmatter fields
|
|
87
|
-
REQUIRED_FIELDS = ["name", "description", "allowed-tools", "model_profile"]
|
|
88
|
-
|
|
89
|
-
# Optional YAML frontmatter fields
|
|
90
|
-
OPTIONAL_FIELDS = ["version", "capabilities"]
|
|
91
|
-
|
|
92
|
-
# Optional capability tags for guardrails and docs (plan 1.3)
|
|
93
|
-
VALID_CAPABILITIES = frozenset({
|
|
94
|
-
"read-only", "writes-files", "calls-llm", "uses-bash", "orchestrator",
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
# Valid tool names (from Claude Code Skills format)
|
|
98
|
-
VALID_TOOLS = {
|
|
99
|
-
"Read",
|
|
100
|
-
"Write",
|
|
101
|
-
"Edit",
|
|
102
|
-
"Grep",
|
|
103
|
-
"Glob",
|
|
104
|
-
"Bash",
|
|
105
|
-
"ListDirectory",
|
|
106
|
-
"SearchCodebase",
|
|
107
|
-
"ReadTextFile",
|
|
108
|
-
"WriteTextFile",
|
|
109
|
-
"EditTextFile",
|
|
110
|
-
"DeleteFile",
|
|
111
|
-
"MoveFile",
|
|
112
|
-
"CopyFile",
|
|
113
|
-
"CreateDirectory",
|
|
114
|
-
"DeleteDirectory",
|
|
115
|
-
"RunCommand",
|
|
116
|
-
"SearchFiles",
|
|
117
|
-
"FindInFiles",
|
|
118
|
-
"GetFileInfo",
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
# Required markdown sections
|
|
122
|
-
REQUIRED_SECTIONS = ["Identity", "Instructions"]
|
|
123
|
-
|
|
124
|
-
# Optional markdown sections
|
|
125
|
-
OPTIONAL_SECTIONS = [
|
|
126
|
-
"Commands",
|
|
127
|
-
"Capabilities",
|
|
128
|
-
"Constraints",
|
|
129
|
-
"Configuration",
|
|
130
|
-
"Integration",
|
|
131
|
-
"Example Workflow",
|
|
132
|
-
"Best Practices",
|
|
133
|
-
"Usage Examples",
|
|
134
|
-
]
|
|
135
|
-
|
|
136
|
-
def __init__(self, project_root: Path | None = None):
|
|
137
|
-
"""
|
|
138
|
-
Initialize validator.
|
|
139
|
-
|
|
140
|
-
Args:
|
|
141
|
-
project_root: Project root directory (for resolving paths)
|
|
142
|
-
"""
|
|
143
|
-
if project_root is None:
|
|
144
|
-
project_root = Path.cwd()
|
|
145
|
-
self.project_root = project_root
|
|
146
|
-
|
|
147
|
-
def validate_skill(self, skill_path: Path) -> ValidationResult:
|
|
148
|
-
"""
|
|
149
|
-
Validate a Skill file.
|
|
150
|
-
|
|
151
|
-
Args:
|
|
152
|
-
skill_path: Path to Skill directory or SKILL.md file
|
|
153
|
-
|
|
154
|
-
Returns:
|
|
155
|
-
Validation result
|
|
156
|
-
"""
|
|
157
|
-
errors: list[ValidationError] = []
|
|
158
|
-
|
|
159
|
-
# Resolve Skill file path
|
|
160
|
-
if skill_path.is_dir():
|
|
161
|
-
skill_file = skill_path / "SKILL.md"
|
|
162
|
-
else:
|
|
163
|
-
skill_file = skill_path
|
|
164
|
-
|
|
165
|
-
if not skill_file.exists():
|
|
166
|
-
errors.append(
|
|
167
|
-
ValidationError(
|
|
168
|
-
severity=ValidationSeverity.ERROR,
|
|
169
|
-
field=None,
|
|
170
|
-
message=f"Skill file not found: {skill_file}",
|
|
171
|
-
suggestion="Ensure SKILL.md exists in the Skill directory",
|
|
172
|
-
)
|
|
173
|
-
)
|
|
174
|
-
return ValidationResult(
|
|
175
|
-
is_valid=False,
|
|
176
|
-
errors=errors,
|
|
177
|
-
skill_path=skill_path,
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
# Read and parse Skill file
|
|
181
|
-
try:
|
|
182
|
-
content = skill_file.read_text(encoding="utf-8")
|
|
183
|
-
except UnicodeDecodeError:
|
|
184
|
-
errors.append(
|
|
185
|
-
ValidationError(
|
|
186
|
-
severity=ValidationSeverity.ERROR,
|
|
187
|
-
field=None,
|
|
188
|
-
message="Skill file is not valid UTF-8",
|
|
189
|
-
suggestion="Ensure file is saved with UTF-8 encoding",
|
|
190
|
-
)
|
|
191
|
-
)
|
|
192
|
-
return ValidationResult(
|
|
193
|
-
is_valid=False,
|
|
194
|
-
errors=errors,
|
|
195
|
-
skill_path=skill_path,
|
|
196
|
-
)
|
|
197
|
-
except Exception as e:
|
|
198
|
-
errors.append(
|
|
199
|
-
ValidationError(
|
|
200
|
-
severity=ValidationSeverity.ERROR,
|
|
201
|
-
field=None,
|
|
202
|
-
message=f"Failed to read Skill file: {e}",
|
|
203
|
-
)
|
|
204
|
-
)
|
|
205
|
-
return ValidationResult(
|
|
206
|
-
is_valid=False,
|
|
207
|
-
errors=errors,
|
|
208
|
-
skill_path=skill_path,
|
|
209
|
-
)
|
|
210
|
-
|
|
211
|
-
# Validate format
|
|
212
|
-
format_errors = self._validate_format(content, skill_file)
|
|
213
|
-
errors.extend(format_errors)
|
|
214
|
-
|
|
215
|
-
# Extract metadata for capability validation
|
|
216
|
-
metadata = self._extract_metadata(content)
|
|
217
|
-
skill_name = metadata.get("name") if metadata else None
|
|
218
|
-
|
|
219
|
-
# Validate capabilities (only if format is valid)
|
|
220
|
-
if not any(e.severity == ValidationSeverity.ERROR for e in format_errors):
|
|
221
|
-
capability_errors = self._validate_capabilities(metadata, content)
|
|
222
|
-
errors.extend(capability_errors)
|
|
223
|
-
|
|
224
|
-
# Determine if valid (no errors, warnings are OK)
|
|
225
|
-
is_valid = not any(e.severity == ValidationSeverity.ERROR for e in errors)
|
|
226
|
-
|
|
227
|
-
return ValidationResult(
|
|
228
|
-
is_valid=is_valid,
|
|
229
|
-
errors=errors,
|
|
230
|
-
skill_path=skill_path,
|
|
231
|
-
skill_name=skill_name,
|
|
232
|
-
)
|
|
233
|
-
|
|
234
|
-
def _validate_format(self, content: str, skill_file: Path) -> list[ValidationError]:
|
|
235
|
-
"""Validate Skill file format."""
|
|
236
|
-
errors: list[ValidationError] = []
|
|
237
|
-
|
|
238
|
-
# Check for YAML frontmatter
|
|
239
|
-
frontmatter_match = re.match(r"^---\s*\n(.*?)\n---\s*\n", content, re.DOTALL)
|
|
240
|
-
if not frontmatter_match:
|
|
241
|
-
errors.append(
|
|
242
|
-
ValidationError(
|
|
243
|
-
severity=ValidationSeverity.ERROR,
|
|
244
|
-
field=None,
|
|
245
|
-
message="Missing YAML frontmatter",
|
|
246
|
-
suggestion="Add YAML frontmatter between --- markers at the start of the file",
|
|
247
|
-
line_number=1,
|
|
248
|
-
)
|
|
249
|
-
)
|
|
250
|
-
return errors # Can't continue without frontmatter
|
|
251
|
-
|
|
252
|
-
frontmatter_text = frontmatter_match.group(1)
|
|
253
|
-
try:
|
|
254
|
-
metadata = yaml.safe_load(frontmatter_text)
|
|
255
|
-
except yaml.YAMLError as e:
|
|
256
|
-
errors.append(
|
|
257
|
-
ValidationError(
|
|
258
|
-
severity=ValidationSeverity.ERROR,
|
|
259
|
-
field=None,
|
|
260
|
-
message=f"Invalid YAML frontmatter: {e}",
|
|
261
|
-
suggestion="Check YAML syntax in frontmatter",
|
|
262
|
-
line_number=2,
|
|
263
|
-
)
|
|
264
|
-
)
|
|
265
|
-
return errors # Can't continue with invalid YAML
|
|
266
|
-
|
|
267
|
-
if not isinstance(metadata, dict):
|
|
268
|
-
errors.append(
|
|
269
|
-
ValidationError(
|
|
270
|
-
severity=ValidationSeverity.ERROR,
|
|
271
|
-
field=None,
|
|
272
|
-
message="YAML frontmatter must be a dictionary/object",
|
|
273
|
-
suggestion="Ensure frontmatter is a valid YAML object",
|
|
274
|
-
line_number=2,
|
|
275
|
-
)
|
|
276
|
-
)
|
|
277
|
-
return errors
|
|
278
|
-
|
|
279
|
-
# Validate required fields
|
|
280
|
-
for field in self.REQUIRED_FIELDS:
|
|
281
|
-
if field not in metadata:
|
|
282
|
-
errors.append(
|
|
283
|
-
ValidationError(
|
|
284
|
-
severity=ValidationSeverity.ERROR,
|
|
285
|
-
field=field,
|
|
286
|
-
message=f"Missing required field: '{field}'",
|
|
287
|
-
suggestion=f"Add '{field}' to YAML frontmatter",
|
|
288
|
-
)
|
|
289
|
-
)
|
|
290
|
-
|
|
291
|
-
# Validate field types
|
|
292
|
-
if "name" in metadata and not isinstance(metadata["name"], str):
|
|
293
|
-
errors.append(
|
|
294
|
-
ValidationError(
|
|
295
|
-
severity=ValidationSeverity.ERROR,
|
|
296
|
-
field="name",
|
|
297
|
-
message="Field 'name' must be a string",
|
|
298
|
-
suggestion="Set 'name' to a string value",
|
|
299
|
-
)
|
|
300
|
-
)
|
|
301
|
-
|
|
302
|
-
if "description" in metadata and not isinstance(metadata["description"], str):
|
|
303
|
-
errors.append(
|
|
304
|
-
ValidationError(
|
|
305
|
-
severity=ValidationSeverity.ERROR,
|
|
306
|
-
field="description",
|
|
307
|
-
message="Field 'description' must be a string",
|
|
308
|
-
suggestion="Set 'description' to a string value",
|
|
309
|
-
)
|
|
310
|
-
)
|
|
311
|
-
|
|
312
|
-
if "allowed-tools" in metadata:
|
|
313
|
-
allowed_tools = metadata["allowed-tools"]
|
|
314
|
-
if not isinstance(allowed_tools, (str, list)):
|
|
315
|
-
errors.append(
|
|
316
|
-
ValidationError(
|
|
317
|
-
severity=ValidationSeverity.ERROR,
|
|
318
|
-
field="allowed-tools",
|
|
319
|
-
message="Field 'allowed-tools' must be a string or list",
|
|
320
|
-
suggestion="Set 'allowed-tools' to a comma-separated string or list",
|
|
321
|
-
)
|
|
322
|
-
)
|
|
323
|
-
|
|
324
|
-
if "model_profile" in metadata and not isinstance(metadata["model_profile"], str):
|
|
325
|
-
errors.append(
|
|
326
|
-
ValidationError(
|
|
327
|
-
severity=ValidationSeverity.ERROR,
|
|
328
|
-
field="model_profile",
|
|
329
|
-
message="Field 'model_profile' must be a string",
|
|
330
|
-
suggestion="Set 'model_profile' to a string value",
|
|
331
|
-
)
|
|
332
|
-
)
|
|
333
|
-
|
|
334
|
-
# Validate markdown structure
|
|
335
|
-
markdown_content = content[frontmatter_match.end() :]
|
|
336
|
-
section_errors = self._validate_markdown_sections(markdown_content)
|
|
337
|
-
errors.extend(section_errors)
|
|
338
|
-
|
|
339
|
-
return errors
|
|
340
|
-
|
|
341
|
-
def _validate_markdown_sections(self, content: str) -> list[ValidationError]:
|
|
342
|
-
"""Validate required markdown sections."""
|
|
343
|
-
errors: list[ValidationError] = []
|
|
344
|
-
|
|
345
|
-
# Check for required sections
|
|
346
|
-
for section in self.REQUIRED_SECTIONS:
|
|
347
|
-
# Look for section header (## Section Name or # Section Name)
|
|
348
|
-
pattern = rf"^#+\s+{re.escape(section)}\s*$"
|
|
349
|
-
if not re.search(pattern, content, re.MULTILINE | re.IGNORECASE):
|
|
350
|
-
errors.append(
|
|
351
|
-
ValidationError(
|
|
352
|
-
severity=ValidationSeverity.ERROR,
|
|
353
|
-
field=None,
|
|
354
|
-
message=f"Missing required section: '{section}'",
|
|
355
|
-
suggestion=f"Add a '## {section}' section to the Skill file",
|
|
356
|
-
)
|
|
357
|
-
)
|
|
358
|
-
|
|
359
|
-
return errors
|
|
360
|
-
|
|
361
|
-
def _extract_metadata(self, content: str) -> dict[str, Any] | None:
|
|
362
|
-
"""Extract metadata from Skill file."""
|
|
363
|
-
frontmatter_match = re.match(r"^---\s*\n(.*?)\n---\s*\n", content, re.DOTALL)
|
|
364
|
-
if not frontmatter_match:
|
|
365
|
-
return None
|
|
366
|
-
|
|
367
|
-
frontmatter_text = frontmatter_match.group(1)
|
|
368
|
-
try:
|
|
369
|
-
return yaml.safe_load(frontmatter_text)
|
|
370
|
-
except yaml.YAMLError:
|
|
371
|
-
return None
|
|
372
|
-
|
|
373
|
-
def _validate_capabilities(self, metadata: dict[str, Any] | None, content: str) -> list[ValidationError]:
|
|
374
|
-
"""Validate Skill capabilities."""
|
|
375
|
-
errors: list[ValidationError] = []
|
|
376
|
-
|
|
377
|
-
if not metadata:
|
|
378
|
-
return errors
|
|
379
|
-
|
|
380
|
-
# Validate tool names
|
|
381
|
-
allowed_tools = metadata.get("allowed-tools")
|
|
382
|
-
if allowed_tools:
|
|
383
|
-
# Parse tools (can be string or list)
|
|
384
|
-
if isinstance(allowed_tools, str):
|
|
385
|
-
tools = [tool.strip() for tool in allowed_tools.split(",")]
|
|
386
|
-
elif isinstance(allowed_tools, list):
|
|
387
|
-
tools = [str(tool).strip() for tool in allowed_tools]
|
|
388
|
-
else:
|
|
389
|
-
return errors # Already validated in format check
|
|
390
|
-
|
|
391
|
-
# Check each tool
|
|
392
|
-
for tool in tools:
|
|
393
|
-
if tool and tool not in self.VALID_TOOLS:
|
|
394
|
-
errors.append(
|
|
395
|
-
ValidationError(
|
|
396
|
-
severity=ValidationSeverity.WARNING,
|
|
397
|
-
field="allowed-tools",
|
|
398
|
-
message=f"Unknown tool name: '{tool}'",
|
|
399
|
-
suggestion=f"Use one of: {', '.join(sorted(self.VALID_TOOLS))}",
|
|
400
|
-
)
|
|
401
|
-
)
|
|
402
|
-
|
|
403
|
-
# Validate model_profile (basic check - just ensure it's not empty)
|
|
404
|
-
model_profile = metadata.get("model_profile")
|
|
405
|
-
if model_profile and not isinstance(model_profile, str):
|
|
406
|
-
errors.append(
|
|
407
|
-
ValidationError(
|
|
408
|
-
severity=ValidationSeverity.ERROR,
|
|
409
|
-
field="model_profile",
|
|
410
|
-
message="Field 'model_profile' must be a non-empty string",
|
|
411
|
-
suggestion="Set 'model_profile' to a valid profile name",
|
|
412
|
-
)
|
|
413
|
-
)
|
|
414
|
-
elif model_profile and len(model_profile.strip()) == 0:
|
|
415
|
-
errors.append(
|
|
416
|
-
ValidationError(
|
|
417
|
-
severity=ValidationSeverity.ERROR,
|
|
418
|
-
field="model_profile",
|
|
419
|
-
message="Field 'model_profile' cannot be empty",
|
|
420
|
-
suggestion="Set 'model_profile' to a valid profile name",
|
|
421
|
-
)
|
|
422
|
-
)
|
|
423
|
-
|
|
424
|
-
# Validate capabilities (optional; subset of known set)
|
|
425
|
-
caps = metadata.get("capabilities")
|
|
426
|
-
if isinstance(caps, list):
|
|
427
|
-
for c in caps:
|
|
428
|
-
if isinstance(c, str) and c.strip() and c not in self.VALID_CAPABILITIES:
|
|
429
|
-
errors.append(
|
|
430
|
-
ValidationError(
|
|
431
|
-
severity=ValidationSeverity.WARNING,
|
|
432
|
-
field="capabilities",
|
|
433
|
-
message=f"Unknown capability: '{c}'",
|
|
434
|
-
suggestion=f"Use one of: {', '.join(sorted(self.VALID_CAPABILITIES))}",
|
|
435
|
-
)
|
|
436
|
-
)
|
|
437
|
-
|
|
438
|
-
# Validate name format (should be lowercase with hyphens)
|
|
439
|
-
name = metadata.get("name")
|
|
440
|
-
if name and isinstance(name, str):
|
|
441
|
-
if not re.match(r"^[a-z0-9-]+$", name):
|
|
442
|
-
errors.append(
|
|
443
|
-
ValidationError(
|
|
444
|
-
severity=ValidationSeverity.WARNING,
|
|
445
|
-
field="name",
|
|
446
|
-
message=f"Skill name '{name}' should use lowercase letters, numbers, and hyphens",
|
|
447
|
-
suggestion="Use format like 'my-custom-skill' (lowercase, hyphens)",
|
|
448
|
-
)
|
|
449
|
-
)
|
|
450
|
-
|
|
451
|
-
return errors
|
|
452
|
-
|
|
453
|
-
def validate_all_skills(self, skills_dir: Path | None = None) -> list[ValidationResult]:
|
|
454
|
-
"""
|
|
455
|
-
Validate all custom Skills in a directory.
|
|
456
|
-
|
|
457
|
-
Args:
|
|
458
|
-
skills_dir: Directory containing Skills (defaults to .claude/skills)
|
|
459
|
-
|
|
460
|
-
Returns:
|
|
461
|
-
List of validation results
|
|
462
|
-
"""
|
|
463
|
-
if skills_dir is None:
|
|
464
|
-
skills_dir = self.project_root / ".claude" / "skills"
|
|
465
|
-
|
|
466
|
-
if not skills_dir.exists():
|
|
467
|
-
return []
|
|
468
|
-
|
|
469
|
-
results = []
|
|
470
|
-
for skill_dir in skills_dir.iterdir():
|
|
471
|
-
if skill_dir.is_dir():
|
|
472
|
-
skill_file = skill_dir / "SKILL.md"
|
|
473
|
-
if skill_file.exists():
|
|
474
|
-
result = self.validate_skill(skill_dir)
|
|
475
|
-
results.append(result)
|
|
476
|
-
|
|
477
|
-
return results
|
|
478
|
-
|
|
1
|
+
"""
|
|
2
|
+
Custom Skill Validation System.
|
|
3
|
+
|
|
4
|
+
Validates custom Skills for format and capability correctness.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from enum import Enum
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
import yaml
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ValidationSeverity(Enum):
|
|
19
|
+
"""Validation error severity levels."""
|
|
20
|
+
|
|
21
|
+
ERROR = "error"
|
|
22
|
+
WARNING = "warning"
|
|
23
|
+
INFO = "info"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class ValidationError:
|
|
28
|
+
"""Represents a validation error or warning."""
|
|
29
|
+
|
|
30
|
+
severity: ValidationSeverity
|
|
31
|
+
field: str | None
|
|
32
|
+
message: str
|
|
33
|
+
suggestion: str | None = None
|
|
34
|
+
line_number: int | None = None
|
|
35
|
+
|
|
36
|
+
def __str__(self) -> str:
|
|
37
|
+
"""Format error message."""
|
|
38
|
+
parts = [f"[{self.severity.value.upper()}]"]
|
|
39
|
+
if self.field:
|
|
40
|
+
parts.append(f"Field '{self.field}':")
|
|
41
|
+
parts.append(self.message)
|
|
42
|
+
if self.suggestion:
|
|
43
|
+
parts.append(f"({self.suggestion})")
|
|
44
|
+
if self.line_number:
|
|
45
|
+
parts.append(f"[Line {self.line_number}]")
|
|
46
|
+
return " ".join(parts)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class ValidationResult:
|
|
51
|
+
"""Result of Skill validation."""
|
|
52
|
+
|
|
53
|
+
is_valid: bool
|
|
54
|
+
errors: list[ValidationError]
|
|
55
|
+
skill_path: Path
|
|
56
|
+
skill_name: str | None = None
|
|
57
|
+
|
|
58
|
+
def has_errors(self) -> bool:
|
|
59
|
+
"""Check if result has any errors."""
|
|
60
|
+
return any(e.severity == ValidationSeverity.ERROR for e in self.errors)
|
|
61
|
+
|
|
62
|
+
def has_warnings(self) -> bool:
|
|
63
|
+
"""Check if result has any warnings."""
|
|
64
|
+
return any(e.severity == ValidationSeverity.WARNING for e in self.errors)
|
|
65
|
+
|
|
66
|
+
def get_error_summary(self) -> str:
|
|
67
|
+
"""Get summary of validation errors."""
|
|
68
|
+
if self.is_valid and not self.has_warnings():
|
|
69
|
+
return f"✅ Skill '{self.skill_name or self.skill_path.name}' is valid."
|
|
70
|
+
|
|
71
|
+
summary_parts = [f"Skill '{self.skill_name or self.skill_path.name}':"]
|
|
72
|
+
error_count = sum(1 for e in self.errors if e.severity == ValidationSeverity.ERROR)
|
|
73
|
+
warning_count = sum(1 for e in self.errors if e.severity == ValidationSeverity.WARNING)
|
|
74
|
+
|
|
75
|
+
if error_count > 0:
|
|
76
|
+
summary_parts.append(f"{error_count} error(s)")
|
|
77
|
+
if warning_count > 0:
|
|
78
|
+
summary_parts.append(f"{warning_count} warning(s)")
|
|
79
|
+
|
|
80
|
+
return " ".join(summary_parts)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class SkillValidator:
|
|
84
|
+
"""Validates custom Skills for format and capability correctness."""
|
|
85
|
+
|
|
86
|
+
# Required YAML frontmatter fields
|
|
87
|
+
REQUIRED_FIELDS = ["name", "description", "allowed-tools", "model_profile"]
|
|
88
|
+
|
|
89
|
+
# Optional YAML frontmatter fields
|
|
90
|
+
OPTIONAL_FIELDS = ["version", "capabilities"]
|
|
91
|
+
|
|
92
|
+
# Optional capability tags for guardrails and docs (plan 1.3)
|
|
93
|
+
VALID_CAPABILITIES = frozenset({
|
|
94
|
+
"read-only", "writes-files", "calls-llm", "uses-bash", "orchestrator",
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
# Valid tool names (from Claude Code Skills format)
|
|
98
|
+
VALID_TOOLS = {
|
|
99
|
+
"Read",
|
|
100
|
+
"Write",
|
|
101
|
+
"Edit",
|
|
102
|
+
"Grep",
|
|
103
|
+
"Glob",
|
|
104
|
+
"Bash",
|
|
105
|
+
"ListDirectory",
|
|
106
|
+
"SearchCodebase",
|
|
107
|
+
"ReadTextFile",
|
|
108
|
+
"WriteTextFile",
|
|
109
|
+
"EditTextFile",
|
|
110
|
+
"DeleteFile",
|
|
111
|
+
"MoveFile",
|
|
112
|
+
"CopyFile",
|
|
113
|
+
"CreateDirectory",
|
|
114
|
+
"DeleteDirectory",
|
|
115
|
+
"RunCommand",
|
|
116
|
+
"SearchFiles",
|
|
117
|
+
"FindInFiles",
|
|
118
|
+
"GetFileInfo",
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# Required markdown sections
|
|
122
|
+
REQUIRED_SECTIONS = ["Identity", "Instructions"]
|
|
123
|
+
|
|
124
|
+
# Optional markdown sections
|
|
125
|
+
OPTIONAL_SECTIONS = [
|
|
126
|
+
"Commands",
|
|
127
|
+
"Capabilities",
|
|
128
|
+
"Constraints",
|
|
129
|
+
"Configuration",
|
|
130
|
+
"Integration",
|
|
131
|
+
"Example Workflow",
|
|
132
|
+
"Best Practices",
|
|
133
|
+
"Usage Examples",
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
def __init__(self, project_root: Path | None = None):
|
|
137
|
+
"""
|
|
138
|
+
Initialize validator.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
project_root: Project root directory (for resolving paths)
|
|
142
|
+
"""
|
|
143
|
+
if project_root is None:
|
|
144
|
+
project_root = Path.cwd()
|
|
145
|
+
self.project_root = project_root
|
|
146
|
+
|
|
147
|
+
def validate_skill(self, skill_path: Path) -> ValidationResult:
|
|
148
|
+
"""
|
|
149
|
+
Validate a Skill file.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
skill_path: Path to Skill directory or SKILL.md file
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
Validation result
|
|
156
|
+
"""
|
|
157
|
+
errors: list[ValidationError] = []
|
|
158
|
+
|
|
159
|
+
# Resolve Skill file path
|
|
160
|
+
if skill_path.is_dir():
|
|
161
|
+
skill_file = skill_path / "SKILL.md"
|
|
162
|
+
else:
|
|
163
|
+
skill_file = skill_path
|
|
164
|
+
|
|
165
|
+
if not skill_file.exists():
|
|
166
|
+
errors.append(
|
|
167
|
+
ValidationError(
|
|
168
|
+
severity=ValidationSeverity.ERROR,
|
|
169
|
+
field=None,
|
|
170
|
+
message=f"Skill file not found: {skill_file}",
|
|
171
|
+
suggestion="Ensure SKILL.md exists in the Skill directory",
|
|
172
|
+
)
|
|
173
|
+
)
|
|
174
|
+
return ValidationResult(
|
|
175
|
+
is_valid=False,
|
|
176
|
+
errors=errors,
|
|
177
|
+
skill_path=skill_path,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# Read and parse Skill file
|
|
181
|
+
try:
|
|
182
|
+
content = skill_file.read_text(encoding="utf-8")
|
|
183
|
+
except UnicodeDecodeError:
|
|
184
|
+
errors.append(
|
|
185
|
+
ValidationError(
|
|
186
|
+
severity=ValidationSeverity.ERROR,
|
|
187
|
+
field=None,
|
|
188
|
+
message="Skill file is not valid UTF-8",
|
|
189
|
+
suggestion="Ensure file is saved with UTF-8 encoding",
|
|
190
|
+
)
|
|
191
|
+
)
|
|
192
|
+
return ValidationResult(
|
|
193
|
+
is_valid=False,
|
|
194
|
+
errors=errors,
|
|
195
|
+
skill_path=skill_path,
|
|
196
|
+
)
|
|
197
|
+
except Exception as e:
|
|
198
|
+
errors.append(
|
|
199
|
+
ValidationError(
|
|
200
|
+
severity=ValidationSeverity.ERROR,
|
|
201
|
+
field=None,
|
|
202
|
+
message=f"Failed to read Skill file: {e}",
|
|
203
|
+
)
|
|
204
|
+
)
|
|
205
|
+
return ValidationResult(
|
|
206
|
+
is_valid=False,
|
|
207
|
+
errors=errors,
|
|
208
|
+
skill_path=skill_path,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
# Validate format
|
|
212
|
+
format_errors = self._validate_format(content, skill_file)
|
|
213
|
+
errors.extend(format_errors)
|
|
214
|
+
|
|
215
|
+
# Extract metadata for capability validation
|
|
216
|
+
metadata = self._extract_metadata(content)
|
|
217
|
+
skill_name = metadata.get("name") if metadata else None
|
|
218
|
+
|
|
219
|
+
# Validate capabilities (only if format is valid)
|
|
220
|
+
if not any(e.severity == ValidationSeverity.ERROR for e in format_errors):
|
|
221
|
+
capability_errors = self._validate_capabilities(metadata, content)
|
|
222
|
+
errors.extend(capability_errors)
|
|
223
|
+
|
|
224
|
+
# Determine if valid (no errors, warnings are OK)
|
|
225
|
+
is_valid = not any(e.severity == ValidationSeverity.ERROR for e in errors)
|
|
226
|
+
|
|
227
|
+
return ValidationResult(
|
|
228
|
+
is_valid=is_valid,
|
|
229
|
+
errors=errors,
|
|
230
|
+
skill_path=skill_path,
|
|
231
|
+
skill_name=skill_name,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
def _validate_format(self, content: str, skill_file: Path) -> list[ValidationError]:
|
|
235
|
+
"""Validate Skill file format."""
|
|
236
|
+
errors: list[ValidationError] = []
|
|
237
|
+
|
|
238
|
+
# Check for YAML frontmatter
|
|
239
|
+
frontmatter_match = re.match(r"^---\s*\n(.*?)\n---\s*\n", content, re.DOTALL)
|
|
240
|
+
if not frontmatter_match:
|
|
241
|
+
errors.append(
|
|
242
|
+
ValidationError(
|
|
243
|
+
severity=ValidationSeverity.ERROR,
|
|
244
|
+
field=None,
|
|
245
|
+
message="Missing YAML frontmatter",
|
|
246
|
+
suggestion="Add YAML frontmatter between --- markers at the start of the file",
|
|
247
|
+
line_number=1,
|
|
248
|
+
)
|
|
249
|
+
)
|
|
250
|
+
return errors # Can't continue without frontmatter
|
|
251
|
+
|
|
252
|
+
frontmatter_text = frontmatter_match.group(1)
|
|
253
|
+
try:
|
|
254
|
+
metadata = yaml.safe_load(frontmatter_text)
|
|
255
|
+
except yaml.YAMLError as e:
|
|
256
|
+
errors.append(
|
|
257
|
+
ValidationError(
|
|
258
|
+
severity=ValidationSeverity.ERROR,
|
|
259
|
+
field=None,
|
|
260
|
+
message=f"Invalid YAML frontmatter: {e}",
|
|
261
|
+
suggestion="Check YAML syntax in frontmatter",
|
|
262
|
+
line_number=2,
|
|
263
|
+
)
|
|
264
|
+
)
|
|
265
|
+
return errors # Can't continue with invalid YAML
|
|
266
|
+
|
|
267
|
+
if not isinstance(metadata, dict):
|
|
268
|
+
errors.append(
|
|
269
|
+
ValidationError(
|
|
270
|
+
severity=ValidationSeverity.ERROR,
|
|
271
|
+
field=None,
|
|
272
|
+
message="YAML frontmatter must be a dictionary/object",
|
|
273
|
+
suggestion="Ensure frontmatter is a valid YAML object",
|
|
274
|
+
line_number=2,
|
|
275
|
+
)
|
|
276
|
+
)
|
|
277
|
+
return errors
|
|
278
|
+
|
|
279
|
+
# Validate required fields
|
|
280
|
+
for field in self.REQUIRED_FIELDS:
|
|
281
|
+
if field not in metadata:
|
|
282
|
+
errors.append(
|
|
283
|
+
ValidationError(
|
|
284
|
+
severity=ValidationSeverity.ERROR,
|
|
285
|
+
field=field,
|
|
286
|
+
message=f"Missing required field: '{field}'",
|
|
287
|
+
suggestion=f"Add '{field}' to YAML frontmatter",
|
|
288
|
+
)
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
# Validate field types
|
|
292
|
+
if "name" in metadata and not isinstance(metadata["name"], str):
|
|
293
|
+
errors.append(
|
|
294
|
+
ValidationError(
|
|
295
|
+
severity=ValidationSeverity.ERROR,
|
|
296
|
+
field="name",
|
|
297
|
+
message="Field 'name' must be a string",
|
|
298
|
+
suggestion="Set 'name' to a string value",
|
|
299
|
+
)
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
if "description" in metadata and not isinstance(metadata["description"], str):
|
|
303
|
+
errors.append(
|
|
304
|
+
ValidationError(
|
|
305
|
+
severity=ValidationSeverity.ERROR,
|
|
306
|
+
field="description",
|
|
307
|
+
message="Field 'description' must be a string",
|
|
308
|
+
suggestion="Set 'description' to a string value",
|
|
309
|
+
)
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
if "allowed-tools" in metadata:
|
|
313
|
+
allowed_tools = metadata["allowed-tools"]
|
|
314
|
+
if not isinstance(allowed_tools, (str, list)):
|
|
315
|
+
errors.append(
|
|
316
|
+
ValidationError(
|
|
317
|
+
severity=ValidationSeverity.ERROR,
|
|
318
|
+
field="allowed-tools",
|
|
319
|
+
message="Field 'allowed-tools' must be a string or list",
|
|
320
|
+
suggestion="Set 'allowed-tools' to a comma-separated string or list",
|
|
321
|
+
)
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
if "model_profile" in metadata and not isinstance(metadata["model_profile"], str):
|
|
325
|
+
errors.append(
|
|
326
|
+
ValidationError(
|
|
327
|
+
severity=ValidationSeverity.ERROR,
|
|
328
|
+
field="model_profile",
|
|
329
|
+
message="Field 'model_profile' must be a string",
|
|
330
|
+
suggestion="Set 'model_profile' to a string value",
|
|
331
|
+
)
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
# Validate markdown structure
|
|
335
|
+
markdown_content = content[frontmatter_match.end() :]
|
|
336
|
+
section_errors = self._validate_markdown_sections(markdown_content)
|
|
337
|
+
errors.extend(section_errors)
|
|
338
|
+
|
|
339
|
+
return errors
|
|
340
|
+
|
|
341
|
+
def _validate_markdown_sections(self, content: str) -> list[ValidationError]:
|
|
342
|
+
"""Validate required markdown sections."""
|
|
343
|
+
errors: list[ValidationError] = []
|
|
344
|
+
|
|
345
|
+
# Check for required sections
|
|
346
|
+
for section in self.REQUIRED_SECTIONS:
|
|
347
|
+
# Look for section header (## Section Name or # Section Name)
|
|
348
|
+
pattern = rf"^#+\s+{re.escape(section)}\s*$"
|
|
349
|
+
if not re.search(pattern, content, re.MULTILINE | re.IGNORECASE):
|
|
350
|
+
errors.append(
|
|
351
|
+
ValidationError(
|
|
352
|
+
severity=ValidationSeverity.ERROR,
|
|
353
|
+
field=None,
|
|
354
|
+
message=f"Missing required section: '{section}'",
|
|
355
|
+
suggestion=f"Add a '## {section}' section to the Skill file",
|
|
356
|
+
)
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
return errors
|
|
360
|
+
|
|
361
|
+
def _extract_metadata(self, content: str) -> dict[str, Any] | None:
|
|
362
|
+
"""Extract metadata from Skill file."""
|
|
363
|
+
frontmatter_match = re.match(r"^---\s*\n(.*?)\n---\s*\n", content, re.DOTALL)
|
|
364
|
+
if not frontmatter_match:
|
|
365
|
+
return None
|
|
366
|
+
|
|
367
|
+
frontmatter_text = frontmatter_match.group(1)
|
|
368
|
+
try:
|
|
369
|
+
return yaml.safe_load(frontmatter_text)
|
|
370
|
+
except yaml.YAMLError:
|
|
371
|
+
return None
|
|
372
|
+
|
|
373
|
+
def _validate_capabilities(self, metadata: dict[str, Any] | None, content: str) -> list[ValidationError]:
|
|
374
|
+
"""Validate Skill capabilities."""
|
|
375
|
+
errors: list[ValidationError] = []
|
|
376
|
+
|
|
377
|
+
if not metadata:
|
|
378
|
+
return errors
|
|
379
|
+
|
|
380
|
+
# Validate tool names
|
|
381
|
+
allowed_tools = metadata.get("allowed-tools")
|
|
382
|
+
if allowed_tools:
|
|
383
|
+
# Parse tools (can be string or list)
|
|
384
|
+
if isinstance(allowed_tools, str):
|
|
385
|
+
tools = [tool.strip() for tool in allowed_tools.split(",")]
|
|
386
|
+
elif isinstance(allowed_tools, list):
|
|
387
|
+
tools = [str(tool).strip() for tool in allowed_tools]
|
|
388
|
+
else:
|
|
389
|
+
return errors # Already validated in format check
|
|
390
|
+
|
|
391
|
+
# Check each tool
|
|
392
|
+
for tool in tools:
|
|
393
|
+
if tool and tool not in self.VALID_TOOLS:
|
|
394
|
+
errors.append(
|
|
395
|
+
ValidationError(
|
|
396
|
+
severity=ValidationSeverity.WARNING,
|
|
397
|
+
field="allowed-tools",
|
|
398
|
+
message=f"Unknown tool name: '{tool}'",
|
|
399
|
+
suggestion=f"Use one of: {', '.join(sorted(self.VALID_TOOLS))}",
|
|
400
|
+
)
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
# Validate model_profile (basic check - just ensure it's not empty)
|
|
404
|
+
model_profile = metadata.get("model_profile")
|
|
405
|
+
if model_profile and not isinstance(model_profile, str):
|
|
406
|
+
errors.append(
|
|
407
|
+
ValidationError(
|
|
408
|
+
severity=ValidationSeverity.ERROR,
|
|
409
|
+
field="model_profile",
|
|
410
|
+
message="Field 'model_profile' must be a non-empty string",
|
|
411
|
+
suggestion="Set 'model_profile' to a valid profile name",
|
|
412
|
+
)
|
|
413
|
+
)
|
|
414
|
+
elif model_profile and len(model_profile.strip()) == 0:
|
|
415
|
+
errors.append(
|
|
416
|
+
ValidationError(
|
|
417
|
+
severity=ValidationSeverity.ERROR,
|
|
418
|
+
field="model_profile",
|
|
419
|
+
message="Field 'model_profile' cannot be empty",
|
|
420
|
+
suggestion="Set 'model_profile' to a valid profile name",
|
|
421
|
+
)
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
# Validate capabilities (optional; subset of known set)
|
|
425
|
+
caps = metadata.get("capabilities")
|
|
426
|
+
if isinstance(caps, list):
|
|
427
|
+
for c in caps:
|
|
428
|
+
if isinstance(c, str) and c.strip() and c not in self.VALID_CAPABILITIES:
|
|
429
|
+
errors.append(
|
|
430
|
+
ValidationError(
|
|
431
|
+
severity=ValidationSeverity.WARNING,
|
|
432
|
+
field="capabilities",
|
|
433
|
+
message=f"Unknown capability: '{c}'",
|
|
434
|
+
suggestion=f"Use one of: {', '.join(sorted(self.VALID_CAPABILITIES))}",
|
|
435
|
+
)
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
# Validate name format (should be lowercase with hyphens)
|
|
439
|
+
name = metadata.get("name")
|
|
440
|
+
if name and isinstance(name, str):
|
|
441
|
+
if not re.match(r"^[a-z0-9-]+$", name):
|
|
442
|
+
errors.append(
|
|
443
|
+
ValidationError(
|
|
444
|
+
severity=ValidationSeverity.WARNING,
|
|
445
|
+
field="name",
|
|
446
|
+
message=f"Skill name '{name}' should use lowercase letters, numbers, and hyphens",
|
|
447
|
+
suggestion="Use format like 'my-custom-skill' (lowercase, hyphens)",
|
|
448
|
+
)
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
return errors
|
|
452
|
+
|
|
453
|
+
def validate_all_skills(self, skills_dir: Path | None = None) -> list[ValidationResult]:
|
|
454
|
+
"""
|
|
455
|
+
Validate all custom Skills in a directory.
|
|
456
|
+
|
|
457
|
+
Args:
|
|
458
|
+
skills_dir: Directory containing Skills (defaults to .claude/skills)
|
|
459
|
+
|
|
460
|
+
Returns:
|
|
461
|
+
List of validation results
|
|
462
|
+
"""
|
|
463
|
+
if skills_dir is None:
|
|
464
|
+
skills_dir = self.project_root / ".claude" / "skills"
|
|
465
|
+
|
|
466
|
+
if not skills_dir.exists():
|
|
467
|
+
return []
|
|
468
|
+
|
|
469
|
+
results = []
|
|
470
|
+
for skill_dir in skills_dir.iterdir():
|
|
471
|
+
if skill_dir.is_dir():
|
|
472
|
+
skill_file = skill_dir / "SKILL.md"
|
|
473
|
+
if skill_file.exists():
|
|
474
|
+
result = self.validate_skill(skill_dir)
|
|
475
|
+
results.append(result)
|
|
476
|
+
|
|
477
|
+
return results
|
|
478
|
+
|