attune-ai 2.0.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.
- attune/__init__.py +358 -0
- attune/adaptive/__init__.py +13 -0
- attune/adaptive/task_complexity.py +127 -0
- attune/agent_monitoring.py +414 -0
- attune/cache/__init__.py +117 -0
- attune/cache/base.py +166 -0
- attune/cache/dependency_manager.py +256 -0
- attune/cache/hash_only.py +251 -0
- attune/cache/hybrid.py +457 -0
- attune/cache/storage.py +285 -0
- attune/cache_monitor.py +356 -0
- attune/cache_stats.py +298 -0
- attune/cli/__init__.py +152 -0
- attune/cli/__main__.py +12 -0
- attune/cli/commands/__init__.py +1 -0
- attune/cli/commands/batch.py +264 -0
- attune/cli/commands/cache.py +248 -0
- attune/cli/commands/help.py +331 -0
- attune/cli/commands/info.py +140 -0
- attune/cli/commands/inspect.py +436 -0
- attune/cli/commands/inspection.py +57 -0
- attune/cli/commands/memory.py +48 -0
- attune/cli/commands/metrics.py +92 -0
- attune/cli/commands/orchestrate.py +184 -0
- attune/cli/commands/patterns.py +207 -0
- attune/cli/commands/profiling.py +202 -0
- attune/cli/commands/provider.py +98 -0
- attune/cli/commands/routing.py +285 -0
- attune/cli/commands/setup.py +96 -0
- attune/cli/commands/status.py +235 -0
- attune/cli/commands/sync.py +166 -0
- attune/cli/commands/tier.py +121 -0
- attune/cli/commands/utilities.py +114 -0
- attune/cli/commands/workflow.py +579 -0
- attune/cli/core.py +32 -0
- attune/cli/parsers/__init__.py +68 -0
- attune/cli/parsers/batch.py +118 -0
- attune/cli/parsers/cache.py +65 -0
- attune/cli/parsers/help.py +41 -0
- attune/cli/parsers/info.py +26 -0
- attune/cli/parsers/inspect.py +66 -0
- attune/cli/parsers/metrics.py +42 -0
- attune/cli/parsers/orchestrate.py +61 -0
- attune/cli/parsers/patterns.py +54 -0
- attune/cli/parsers/provider.py +40 -0
- attune/cli/parsers/routing.py +110 -0
- attune/cli/parsers/setup.py +42 -0
- attune/cli/parsers/status.py +47 -0
- attune/cli/parsers/sync.py +31 -0
- attune/cli/parsers/tier.py +33 -0
- attune/cli/parsers/workflow.py +77 -0
- attune/cli/utils/__init__.py +1 -0
- attune/cli/utils/data.py +242 -0
- attune/cli/utils/helpers.py +68 -0
- attune/cli_legacy.py +3957 -0
- attune/cli_minimal.py +1159 -0
- attune/cli_router.py +437 -0
- attune/cli_unified.py +814 -0
- attune/config/__init__.py +66 -0
- attune/config/xml_config.py +286 -0
- attune/config.py +545 -0
- attune/coordination.py +870 -0
- attune/core.py +1511 -0
- attune/core_modules/__init__.py +15 -0
- attune/cost_tracker.py +626 -0
- attune/dashboard/__init__.py +41 -0
- attune/dashboard/app.py +512 -0
- attune/dashboard/simple_server.py +435 -0
- attune/dashboard/standalone_server.py +547 -0
- attune/discovery.py +306 -0
- attune/emergence.py +306 -0
- attune/exceptions.py +123 -0
- attune/feedback_loops.py +373 -0
- attune/hot_reload/README.md +473 -0
- attune/hot_reload/__init__.py +62 -0
- attune/hot_reload/config.py +83 -0
- attune/hot_reload/integration.py +229 -0
- attune/hot_reload/reloader.py +298 -0
- attune/hot_reload/watcher.py +183 -0
- attune/hot_reload/websocket.py +177 -0
- attune/levels.py +577 -0
- attune/leverage_points.py +441 -0
- attune/logging_config.py +261 -0
- attune/mcp/__init__.py +10 -0
- attune/mcp/server.py +506 -0
- attune/memory/__init__.py +237 -0
- attune/memory/claude_memory.py +469 -0
- attune/memory/config.py +224 -0
- attune/memory/control_panel.py +1290 -0
- attune/memory/control_panel_support.py +145 -0
- attune/memory/cross_session.py +845 -0
- attune/memory/edges.py +179 -0
- attune/memory/encryption.py +159 -0
- attune/memory/file_session.py +770 -0
- attune/memory/graph.py +570 -0
- attune/memory/long_term.py +913 -0
- attune/memory/long_term_types.py +99 -0
- attune/memory/mixins/__init__.py +25 -0
- attune/memory/mixins/backend_init_mixin.py +249 -0
- attune/memory/mixins/capabilities_mixin.py +208 -0
- attune/memory/mixins/handoff_mixin.py +208 -0
- attune/memory/mixins/lifecycle_mixin.py +49 -0
- attune/memory/mixins/long_term_mixin.py +352 -0
- attune/memory/mixins/promotion_mixin.py +109 -0
- attune/memory/mixins/short_term_mixin.py +182 -0
- attune/memory/nodes.py +179 -0
- attune/memory/redis_bootstrap.py +540 -0
- attune/memory/security/__init__.py +31 -0
- attune/memory/security/audit_logger.py +932 -0
- attune/memory/security/pii_scrubber.py +640 -0
- attune/memory/security/secrets_detector.py +678 -0
- attune/memory/short_term.py +2192 -0
- attune/memory/simple_storage.py +302 -0
- attune/memory/storage/__init__.py +15 -0
- attune/memory/storage_backend.py +167 -0
- attune/memory/summary_index.py +583 -0
- attune/memory/types.py +446 -0
- attune/memory/unified.py +182 -0
- attune/meta_workflows/__init__.py +74 -0
- attune/meta_workflows/agent_creator.py +248 -0
- attune/meta_workflows/builtin_templates.py +567 -0
- attune/meta_workflows/cli_commands/__init__.py +56 -0
- attune/meta_workflows/cli_commands/agent_commands.py +321 -0
- attune/meta_workflows/cli_commands/analytics_commands.py +442 -0
- attune/meta_workflows/cli_commands/config_commands.py +232 -0
- attune/meta_workflows/cli_commands/memory_commands.py +182 -0
- attune/meta_workflows/cli_commands/template_commands.py +354 -0
- attune/meta_workflows/cli_commands/workflow_commands.py +382 -0
- attune/meta_workflows/cli_meta_workflows.py +59 -0
- attune/meta_workflows/form_engine.py +292 -0
- attune/meta_workflows/intent_detector.py +409 -0
- attune/meta_workflows/models.py +569 -0
- attune/meta_workflows/pattern_learner.py +738 -0
- attune/meta_workflows/plan_generator.py +384 -0
- attune/meta_workflows/session_context.py +397 -0
- attune/meta_workflows/template_registry.py +229 -0
- attune/meta_workflows/workflow.py +984 -0
- attune/metrics/__init__.py +12 -0
- attune/metrics/collector.py +31 -0
- attune/metrics/prompt_metrics.py +194 -0
- attune/models/__init__.py +172 -0
- attune/models/__main__.py +13 -0
- attune/models/adaptive_routing.py +437 -0
- attune/models/auth_cli.py +444 -0
- attune/models/auth_strategy.py +450 -0
- attune/models/cli.py +655 -0
- attune/models/empathy_executor.py +354 -0
- attune/models/executor.py +257 -0
- attune/models/fallback.py +762 -0
- attune/models/provider_config.py +282 -0
- attune/models/registry.py +472 -0
- attune/models/tasks.py +359 -0
- attune/models/telemetry/__init__.py +71 -0
- attune/models/telemetry/analytics.py +594 -0
- attune/models/telemetry/backend.py +196 -0
- attune/models/telemetry/data_models.py +431 -0
- attune/models/telemetry/storage.py +489 -0
- attune/models/token_estimator.py +420 -0
- attune/models/validation.py +280 -0
- attune/monitoring/__init__.py +52 -0
- attune/monitoring/alerts.py +946 -0
- attune/monitoring/alerts_cli.py +448 -0
- attune/monitoring/multi_backend.py +271 -0
- attune/monitoring/otel_backend.py +362 -0
- attune/optimization/__init__.py +19 -0
- attune/optimization/context_optimizer.py +272 -0
- attune/orchestration/__init__.py +67 -0
- attune/orchestration/agent_templates.py +707 -0
- attune/orchestration/config_store.py +499 -0
- attune/orchestration/execution_strategies.py +2111 -0
- attune/orchestration/meta_orchestrator.py +1168 -0
- attune/orchestration/pattern_learner.py +696 -0
- attune/orchestration/real_tools.py +931 -0
- attune/pattern_cache.py +187 -0
- attune/pattern_library.py +542 -0
- attune/patterns/debugging/all_patterns.json +81 -0
- attune/patterns/debugging/workflow_20260107_1770825e.json +77 -0
- attune/patterns/refactoring_memory.json +89 -0
- attune/persistence.py +564 -0
- attune/platform_utils.py +265 -0
- attune/plugins/__init__.py +28 -0
- attune/plugins/base.py +361 -0
- attune/plugins/registry.py +268 -0
- attune/project_index/__init__.py +32 -0
- attune/project_index/cli.py +335 -0
- attune/project_index/index.py +667 -0
- attune/project_index/models.py +504 -0
- attune/project_index/reports.py +474 -0
- attune/project_index/scanner.py +777 -0
- attune/project_index/scanner_parallel.py +291 -0
- attune/prompts/__init__.py +61 -0
- attune/prompts/config.py +77 -0
- attune/prompts/context.py +177 -0
- attune/prompts/parser.py +285 -0
- attune/prompts/registry.py +313 -0
- attune/prompts/templates.py +208 -0
- attune/redis_config.py +302 -0
- attune/redis_memory.py +799 -0
- attune/resilience/__init__.py +56 -0
- attune/resilience/circuit_breaker.py +256 -0
- attune/resilience/fallback.py +179 -0
- attune/resilience/health.py +300 -0
- attune/resilience/retry.py +209 -0
- attune/resilience/timeout.py +135 -0
- attune/routing/__init__.py +43 -0
- attune/routing/chain_executor.py +433 -0
- attune/routing/classifier.py +217 -0
- attune/routing/smart_router.py +234 -0
- attune/routing/workflow_registry.py +343 -0
- attune/scaffolding/README.md +589 -0
- attune/scaffolding/__init__.py +35 -0
- attune/scaffolding/__main__.py +14 -0
- attune/scaffolding/cli.py +240 -0
- attune/scaffolding/templates/base_wizard.py.jinja2 +121 -0
- attune/scaffolding/templates/coach_wizard.py.jinja2 +321 -0
- attune/scaffolding/templates/domain_wizard.py.jinja2 +408 -0
- attune/scaffolding/templates/linear_flow_wizard.py.jinja2 +203 -0
- attune/socratic/__init__.py +256 -0
- attune/socratic/ab_testing.py +958 -0
- attune/socratic/blueprint.py +533 -0
- attune/socratic/cli.py +703 -0
- attune/socratic/collaboration.py +1114 -0
- attune/socratic/domain_templates.py +924 -0
- attune/socratic/embeddings.py +738 -0
- attune/socratic/engine.py +794 -0
- attune/socratic/explainer.py +682 -0
- attune/socratic/feedback.py +772 -0
- attune/socratic/forms.py +629 -0
- attune/socratic/generator.py +732 -0
- attune/socratic/llm_analyzer.py +637 -0
- attune/socratic/mcp_server.py +702 -0
- attune/socratic/session.py +312 -0
- attune/socratic/storage.py +667 -0
- attune/socratic/success.py +730 -0
- attune/socratic/visual_editor.py +860 -0
- attune/socratic/web_ui.py +958 -0
- attune/telemetry/__init__.py +39 -0
- attune/telemetry/agent_coordination.py +475 -0
- attune/telemetry/agent_tracking.py +367 -0
- attune/telemetry/approval_gates.py +545 -0
- attune/telemetry/cli.py +1231 -0
- attune/telemetry/commands/__init__.py +14 -0
- attune/telemetry/commands/dashboard_commands.py +696 -0
- attune/telemetry/event_streaming.py +409 -0
- attune/telemetry/feedback_loop.py +567 -0
- attune/telemetry/usage_tracker.py +591 -0
- attune/templates.py +754 -0
- attune/test_generator/__init__.py +38 -0
- attune/test_generator/__main__.py +14 -0
- attune/test_generator/cli.py +234 -0
- attune/test_generator/generator.py +355 -0
- attune/test_generator/risk_analyzer.py +216 -0
- attune/test_generator/templates/unit_test.py.jinja2 +272 -0
- attune/tier_recommender.py +384 -0
- attune/tools.py +183 -0
- attune/trust/__init__.py +28 -0
- attune/trust/circuit_breaker.py +579 -0
- attune/trust_building.py +527 -0
- attune/validation/__init__.py +19 -0
- attune/validation/xml_validator.py +281 -0
- attune/vscode_bridge.py +173 -0
- attune/workflow_commands.py +780 -0
- attune/workflow_patterns/__init__.py +33 -0
- attune/workflow_patterns/behavior.py +249 -0
- attune/workflow_patterns/core.py +76 -0
- attune/workflow_patterns/output.py +99 -0
- attune/workflow_patterns/registry.py +255 -0
- attune/workflow_patterns/structural.py +288 -0
- attune/workflows/__init__.py +539 -0
- attune/workflows/autonomous_test_gen.py +1268 -0
- attune/workflows/base.py +2667 -0
- attune/workflows/batch_processing.py +342 -0
- attune/workflows/bug_predict.py +1084 -0
- attune/workflows/builder.py +273 -0
- attune/workflows/caching.py +253 -0
- attune/workflows/code_review.py +1048 -0
- attune/workflows/code_review_adapters.py +312 -0
- attune/workflows/code_review_pipeline.py +722 -0
- attune/workflows/config.py +645 -0
- attune/workflows/dependency_check.py +644 -0
- attune/workflows/document_gen/__init__.py +25 -0
- attune/workflows/document_gen/config.py +30 -0
- attune/workflows/document_gen/report_formatter.py +162 -0
- attune/workflows/document_gen/workflow.py +1426 -0
- attune/workflows/document_manager.py +216 -0
- attune/workflows/document_manager_README.md +134 -0
- attune/workflows/documentation_orchestrator.py +1205 -0
- attune/workflows/history.py +510 -0
- attune/workflows/keyboard_shortcuts/__init__.py +39 -0
- attune/workflows/keyboard_shortcuts/generators.py +391 -0
- attune/workflows/keyboard_shortcuts/parsers.py +416 -0
- attune/workflows/keyboard_shortcuts/prompts.py +295 -0
- attune/workflows/keyboard_shortcuts/schema.py +193 -0
- attune/workflows/keyboard_shortcuts/workflow.py +509 -0
- attune/workflows/llm_base.py +363 -0
- attune/workflows/manage_docs.py +87 -0
- attune/workflows/manage_docs_README.md +134 -0
- attune/workflows/manage_documentation.py +821 -0
- attune/workflows/new_sample_workflow1.py +149 -0
- attune/workflows/new_sample_workflow1_README.md +150 -0
- attune/workflows/orchestrated_health_check.py +849 -0
- attune/workflows/orchestrated_release_prep.py +600 -0
- attune/workflows/output.py +413 -0
- attune/workflows/perf_audit.py +863 -0
- attune/workflows/pr_review.py +762 -0
- attune/workflows/progress.py +785 -0
- attune/workflows/progress_server.py +322 -0
- attune/workflows/progressive/README 2.md +454 -0
- attune/workflows/progressive/README.md +454 -0
- attune/workflows/progressive/__init__.py +82 -0
- attune/workflows/progressive/cli.py +219 -0
- attune/workflows/progressive/core.py +488 -0
- attune/workflows/progressive/orchestrator.py +723 -0
- attune/workflows/progressive/reports.py +520 -0
- attune/workflows/progressive/telemetry.py +274 -0
- attune/workflows/progressive/test_gen.py +495 -0
- attune/workflows/progressive/workflow.py +589 -0
- attune/workflows/refactor_plan.py +694 -0
- attune/workflows/release_prep.py +895 -0
- attune/workflows/release_prep_crew.py +969 -0
- attune/workflows/research_synthesis.py +404 -0
- attune/workflows/routing.py +168 -0
- attune/workflows/secure_release.py +593 -0
- attune/workflows/security_adapters.py +297 -0
- attune/workflows/security_audit.py +1329 -0
- attune/workflows/security_audit_phase3.py +355 -0
- attune/workflows/seo_optimization.py +633 -0
- attune/workflows/step_config.py +234 -0
- attune/workflows/telemetry_mixin.py +269 -0
- attune/workflows/test5.py +125 -0
- attune/workflows/test5_README.md +158 -0
- attune/workflows/test_coverage_boost_crew.py +849 -0
- attune/workflows/test_gen/__init__.py +52 -0
- attune/workflows/test_gen/ast_analyzer.py +249 -0
- attune/workflows/test_gen/config.py +88 -0
- attune/workflows/test_gen/data_models.py +38 -0
- attune/workflows/test_gen/report_formatter.py +289 -0
- attune/workflows/test_gen/test_templates.py +381 -0
- attune/workflows/test_gen/workflow.py +655 -0
- attune/workflows/test_gen.py +54 -0
- attune/workflows/test_gen_behavioral.py +477 -0
- attune/workflows/test_gen_parallel.py +341 -0
- attune/workflows/test_lifecycle.py +526 -0
- attune/workflows/test_maintenance.py +627 -0
- attune/workflows/test_maintenance_cli.py +590 -0
- attune/workflows/test_maintenance_crew.py +840 -0
- attune/workflows/test_runner.py +622 -0
- attune/workflows/tier_tracking.py +531 -0
- attune/workflows/xml_enhanced_crew.py +285 -0
- attune_ai-2.0.0.dist-info/METADATA +1026 -0
- attune_ai-2.0.0.dist-info/RECORD +457 -0
- attune_ai-2.0.0.dist-info/WHEEL +5 -0
- attune_ai-2.0.0.dist-info/entry_points.txt +26 -0
- attune_ai-2.0.0.dist-info/licenses/LICENSE +201 -0
- attune_ai-2.0.0.dist-info/licenses/LICENSE_CHANGE_ANNOUNCEMENT.md +101 -0
- attune_ai-2.0.0.dist-info/top_level.txt +5 -0
- attune_healthcare/__init__.py +13 -0
- attune_healthcare/monitors/__init__.py +9 -0
- attune_healthcare/monitors/clinical_protocol_monitor.py +315 -0
- attune_healthcare/monitors/monitoring/__init__.py +44 -0
- attune_healthcare/monitors/monitoring/protocol_checker.py +300 -0
- attune_healthcare/monitors/monitoring/protocol_loader.py +214 -0
- attune_healthcare/monitors/monitoring/sensor_parsers.py +306 -0
- attune_healthcare/monitors/monitoring/trajectory_analyzer.py +389 -0
- attune_llm/README.md +553 -0
- attune_llm/__init__.py +28 -0
- attune_llm/agent_factory/__init__.py +53 -0
- attune_llm/agent_factory/adapters/__init__.py +85 -0
- attune_llm/agent_factory/adapters/autogen_adapter.py +312 -0
- attune_llm/agent_factory/adapters/crewai_adapter.py +483 -0
- attune_llm/agent_factory/adapters/haystack_adapter.py +298 -0
- attune_llm/agent_factory/adapters/langchain_adapter.py +362 -0
- attune_llm/agent_factory/adapters/langgraph_adapter.py +333 -0
- attune_llm/agent_factory/adapters/native.py +228 -0
- attune_llm/agent_factory/adapters/wizard_adapter.py +423 -0
- attune_llm/agent_factory/base.py +305 -0
- attune_llm/agent_factory/crews/__init__.py +67 -0
- attune_llm/agent_factory/crews/code_review.py +1113 -0
- attune_llm/agent_factory/crews/health_check.py +1262 -0
- attune_llm/agent_factory/crews/refactoring.py +1128 -0
- attune_llm/agent_factory/crews/security_audit.py +1018 -0
- attune_llm/agent_factory/decorators.py +287 -0
- attune_llm/agent_factory/factory.py +558 -0
- attune_llm/agent_factory/framework.py +193 -0
- attune_llm/agent_factory/memory_integration.py +328 -0
- attune_llm/agent_factory/resilient.py +320 -0
- attune_llm/agents_md/__init__.py +22 -0
- attune_llm/agents_md/loader.py +218 -0
- attune_llm/agents_md/parser.py +271 -0
- attune_llm/agents_md/registry.py +307 -0
- attune_llm/claude_memory.py +466 -0
- attune_llm/cli/__init__.py +8 -0
- attune_llm/cli/sync_claude.py +487 -0
- attune_llm/code_health.py +1313 -0
- attune_llm/commands/__init__.py +51 -0
- attune_llm/commands/context.py +375 -0
- attune_llm/commands/loader.py +301 -0
- attune_llm/commands/models.py +231 -0
- attune_llm/commands/parser.py +371 -0
- attune_llm/commands/registry.py +429 -0
- attune_llm/config/__init__.py +29 -0
- attune_llm/config/unified.py +291 -0
- attune_llm/context/__init__.py +22 -0
- attune_llm/context/compaction.py +455 -0
- attune_llm/context/manager.py +434 -0
- attune_llm/contextual_patterns.py +361 -0
- attune_llm/core.py +907 -0
- attune_llm/git_pattern_extractor.py +435 -0
- attune_llm/hooks/__init__.py +24 -0
- attune_llm/hooks/config.py +306 -0
- attune_llm/hooks/executor.py +289 -0
- attune_llm/hooks/registry.py +302 -0
- attune_llm/hooks/scripts/__init__.py +39 -0
- attune_llm/hooks/scripts/evaluate_session.py +201 -0
- attune_llm/hooks/scripts/first_time_init.py +285 -0
- attune_llm/hooks/scripts/pre_compact.py +207 -0
- attune_llm/hooks/scripts/session_end.py +183 -0
- attune_llm/hooks/scripts/session_start.py +163 -0
- attune_llm/hooks/scripts/suggest_compact.py +225 -0
- attune_llm/learning/__init__.py +30 -0
- attune_llm/learning/evaluator.py +438 -0
- attune_llm/learning/extractor.py +514 -0
- attune_llm/learning/storage.py +560 -0
- attune_llm/levels.py +227 -0
- attune_llm/pattern_confidence.py +414 -0
- attune_llm/pattern_resolver.py +272 -0
- attune_llm/pattern_summary.py +350 -0
- attune_llm/providers.py +967 -0
- attune_llm/routing/__init__.py +32 -0
- attune_llm/routing/model_router.py +362 -0
- attune_llm/security/IMPLEMENTATION_SUMMARY.md +413 -0
- attune_llm/security/PHASE2_COMPLETE.md +384 -0
- attune_llm/security/PHASE2_SECRETS_DETECTOR_COMPLETE.md +271 -0
- attune_llm/security/QUICK_REFERENCE.md +316 -0
- attune_llm/security/README.md +262 -0
- attune_llm/security/__init__.py +62 -0
- attune_llm/security/audit_logger.py +929 -0
- attune_llm/security/audit_logger_example.py +152 -0
- attune_llm/security/pii_scrubber.py +640 -0
- attune_llm/security/secrets_detector.py +678 -0
- attune_llm/security/secrets_detector_example.py +304 -0
- attune_llm/security/secure_memdocs.py +1192 -0
- attune_llm/security/secure_memdocs_example.py +278 -0
- attune_llm/session_status.py +745 -0
- attune_llm/state.py +246 -0
- attune_llm/utils/__init__.py +5 -0
- attune_llm/utils/tokens.py +349 -0
- attune_software/SOFTWARE_PLUGIN_README.md +57 -0
- attune_software/__init__.py +13 -0
- attune_software/cli/__init__.py +120 -0
- attune_software/cli/inspect.py +362 -0
- attune_software/cli.py +574 -0
- attune_software/plugin.py +188 -0
- workflow_scaffolding/__init__.py +11 -0
- workflow_scaffolding/__main__.py +12 -0
- workflow_scaffolding/cli.py +206 -0
- workflow_scaffolding/generator.py +265 -0
|
@@ -0,0 +1,644 @@
|
|
|
1
|
+
"""Dependency Check Workflow
|
|
2
|
+
|
|
3
|
+
Audits dependencies for vulnerabilities, updates, and licensing issues.
|
|
4
|
+
Parses lockfiles and checks against known vulnerability patterns.
|
|
5
|
+
|
|
6
|
+
Stages:
|
|
7
|
+
1. inventory (CHEAP) - Parse requirements.txt, package.json, etc.
|
|
8
|
+
2. assess (CAPABLE) - Check for known vulnerabilities and updates
|
|
9
|
+
3. report (CAPABLE) - Generate risk assessment and recommendations
|
|
10
|
+
|
|
11
|
+
Copyright 2025 Smart-AI-Memory
|
|
12
|
+
Licensed under Fair Source License 0.9
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import re
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
from .base import BaseWorkflow, ModelTier
|
|
21
|
+
from .step_config import WorkflowStepConfig
|
|
22
|
+
|
|
23
|
+
# Define step configurations for executor-based execution
|
|
24
|
+
DEPENDENCY_CHECK_STEPS = {
|
|
25
|
+
"report": WorkflowStepConfig(
|
|
26
|
+
name="report",
|
|
27
|
+
task_type="analyze", # Capable tier task
|
|
28
|
+
tier_hint="capable",
|
|
29
|
+
description="Generate dependency security report with remediation steps",
|
|
30
|
+
max_tokens=3000,
|
|
31
|
+
),
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# Known vulnerable package patterns (simulated CVE database)
|
|
35
|
+
KNOWN_VULNERABILITIES = {
|
|
36
|
+
"requests": {"affected_versions": ["<2.25.0"], "severity": "medium", "cve": "CVE-2021-XXXX"},
|
|
37
|
+
"urllib3": {"affected_versions": ["<1.26.5"], "severity": "high", "cve": "CVE-2021-XXXX"},
|
|
38
|
+
"pyyaml": {"affected_versions": ["<5.4"], "severity": "critical", "cve": "CVE-2020-XXXX"},
|
|
39
|
+
"django": {"affected_versions": ["<3.2.4"], "severity": "high", "cve": "CVE-2021-XXXX"},
|
|
40
|
+
"flask": {"affected_versions": ["<2.0.0"], "severity": "medium", "cve": "CVE-2021-XXXX"},
|
|
41
|
+
"lodash": {"affected_versions": ["<4.17.21"], "severity": "high", "cve": "CVE-2021-XXXX"},
|
|
42
|
+
"axios": {"affected_versions": ["<0.21.1"], "severity": "medium", "cve": "CVE-2021-XXXX"},
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class DependencyCheckWorkflow(BaseWorkflow):
|
|
47
|
+
"""Audit dependencies for security and updates.
|
|
48
|
+
|
|
49
|
+
Scans dependency files to identify vulnerable, outdated,
|
|
50
|
+
or potentially problematic packages.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
name = "dependency-check"
|
|
54
|
+
description = "Audit dependencies for vulnerabilities and updates"
|
|
55
|
+
stages = ["inventory", "assess", "report"]
|
|
56
|
+
tier_map = {
|
|
57
|
+
"inventory": ModelTier.CHEAP,
|
|
58
|
+
"assess": ModelTier.CAPABLE,
|
|
59
|
+
"report": ModelTier.CAPABLE,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
def __init__(self, **kwargs: Any):
|
|
63
|
+
"""Initialize dependency check workflow.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
**kwargs: Additional arguments passed to BaseWorkflow
|
|
67
|
+
|
|
68
|
+
"""
|
|
69
|
+
super().__init__(**kwargs)
|
|
70
|
+
|
|
71
|
+
async def run_stage(
|
|
72
|
+
self,
|
|
73
|
+
stage_name: str,
|
|
74
|
+
tier: ModelTier,
|
|
75
|
+
input_data: Any,
|
|
76
|
+
) -> tuple[Any, int, int]:
|
|
77
|
+
"""Route to specific stage implementation."""
|
|
78
|
+
if stage_name == "inventory":
|
|
79
|
+
return await self._inventory(input_data, tier)
|
|
80
|
+
if stage_name == "assess":
|
|
81
|
+
return await self._assess(input_data, tier)
|
|
82
|
+
if stage_name == "report":
|
|
83
|
+
return await self._report(input_data, tier)
|
|
84
|
+
raise ValueError(f"Unknown stage: {stage_name}")
|
|
85
|
+
|
|
86
|
+
async def _inventory(self, input_data: dict, tier: ModelTier) -> tuple[dict, int, int]:
|
|
87
|
+
"""Parse dependency files to build inventory.
|
|
88
|
+
|
|
89
|
+
Supports requirements.txt, pyproject.toml, package.json,
|
|
90
|
+
and their lockfiles.
|
|
91
|
+
"""
|
|
92
|
+
target_path = input_data.get("path", ".")
|
|
93
|
+
target = Path(target_path)
|
|
94
|
+
|
|
95
|
+
dependencies: dict[str, list[dict]] = {
|
|
96
|
+
"python": [],
|
|
97
|
+
"node": [],
|
|
98
|
+
}
|
|
99
|
+
files_found: list[str] = []
|
|
100
|
+
|
|
101
|
+
# Parse Python dependencies
|
|
102
|
+
req_files = ["requirements.txt", "requirements-dev.txt", "requirements-test.txt"]
|
|
103
|
+
for req_file in req_files:
|
|
104
|
+
req_path = target / req_file
|
|
105
|
+
if req_path.exists():
|
|
106
|
+
files_found.append(str(req_path))
|
|
107
|
+
deps = self._parse_requirements(req_path)
|
|
108
|
+
dependencies["python"].extend(deps)
|
|
109
|
+
|
|
110
|
+
# Parse pyproject.toml
|
|
111
|
+
pyproject_path = target / "pyproject.toml"
|
|
112
|
+
if pyproject_path.exists():
|
|
113
|
+
files_found.append(str(pyproject_path))
|
|
114
|
+
deps = self._parse_pyproject(pyproject_path)
|
|
115
|
+
dependencies["python"].extend(deps)
|
|
116
|
+
|
|
117
|
+
# Parse package.json
|
|
118
|
+
package_json = target / "package.json"
|
|
119
|
+
if package_json.exists():
|
|
120
|
+
files_found.append(str(package_json))
|
|
121
|
+
deps = self._parse_package_json(package_json)
|
|
122
|
+
dependencies["node"].extend(deps)
|
|
123
|
+
|
|
124
|
+
# Deduplicate
|
|
125
|
+
for ecosystem in dependencies:
|
|
126
|
+
seen = set()
|
|
127
|
+
unique = []
|
|
128
|
+
for dep in dependencies[ecosystem]:
|
|
129
|
+
name = dep["name"].lower()
|
|
130
|
+
if name not in seen:
|
|
131
|
+
seen.add(name)
|
|
132
|
+
unique.append(dep)
|
|
133
|
+
dependencies[ecosystem] = unique
|
|
134
|
+
|
|
135
|
+
total_count = sum(len(deps) for deps in dependencies.values())
|
|
136
|
+
|
|
137
|
+
input_tokens = len(str(input_data)) // 4
|
|
138
|
+
output_tokens = len(str(dependencies)) // 4
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
{
|
|
142
|
+
"dependencies": dependencies,
|
|
143
|
+
"files_found": files_found,
|
|
144
|
+
"total_dependencies": total_count,
|
|
145
|
+
"python_count": len(dependencies["python"]),
|
|
146
|
+
"node_count": len(dependencies["node"]),
|
|
147
|
+
**input_data,
|
|
148
|
+
},
|
|
149
|
+
input_tokens,
|
|
150
|
+
output_tokens,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
def _parse_requirements(self, path: Path) -> list[dict]:
|
|
154
|
+
"""Parse requirements.txt format."""
|
|
155
|
+
deps = []
|
|
156
|
+
try:
|
|
157
|
+
content = path.read_text()
|
|
158
|
+
for line in content.splitlines():
|
|
159
|
+
line = line.strip()
|
|
160
|
+
if not line or line.startswith("#") or line.startswith("-"):
|
|
161
|
+
continue
|
|
162
|
+
|
|
163
|
+
# Parse version specifiers
|
|
164
|
+
match = re.match(r"^([a-zA-Z0-9_-]+)\s*([<>=!~]+\s*[\d.]+)?", line)
|
|
165
|
+
if match:
|
|
166
|
+
name = match.group(1)
|
|
167
|
+
version = match.group(2).strip() if match.group(2) else "any"
|
|
168
|
+
deps.append(
|
|
169
|
+
{
|
|
170
|
+
"name": name,
|
|
171
|
+
"version": version,
|
|
172
|
+
"source": str(path),
|
|
173
|
+
"ecosystem": "python",
|
|
174
|
+
},
|
|
175
|
+
)
|
|
176
|
+
except OSError:
|
|
177
|
+
pass
|
|
178
|
+
return deps
|
|
179
|
+
|
|
180
|
+
def _parse_pyproject(self, path: Path) -> list[dict]:
|
|
181
|
+
"""Parse pyproject.toml for dependencies."""
|
|
182
|
+
deps = []
|
|
183
|
+
try:
|
|
184
|
+
content = path.read_text()
|
|
185
|
+
# Simple TOML parsing for dependencies
|
|
186
|
+
in_deps = False
|
|
187
|
+
for line in content.splitlines():
|
|
188
|
+
if "dependencies" in line and "=" in line:
|
|
189
|
+
in_deps = True
|
|
190
|
+
continue
|
|
191
|
+
if in_deps:
|
|
192
|
+
if line.strip().startswith("]"):
|
|
193
|
+
in_deps = False
|
|
194
|
+
continue
|
|
195
|
+
match = re.search(r'"([a-zA-Z0-9_-]+)([<>=!~]+[\d.]+)?"', line)
|
|
196
|
+
if match:
|
|
197
|
+
name = match.group(1)
|
|
198
|
+
version = match.group(2) if match.group(2) else "any"
|
|
199
|
+
deps.append(
|
|
200
|
+
{
|
|
201
|
+
"name": name,
|
|
202
|
+
"version": version,
|
|
203
|
+
"source": str(path),
|
|
204
|
+
"ecosystem": "python",
|
|
205
|
+
},
|
|
206
|
+
)
|
|
207
|
+
except OSError:
|
|
208
|
+
pass
|
|
209
|
+
return deps
|
|
210
|
+
|
|
211
|
+
def _parse_package_json(self, path: Path) -> list[dict]:
|
|
212
|
+
"""Parse package.json for dependencies."""
|
|
213
|
+
deps = []
|
|
214
|
+
try:
|
|
215
|
+
with open(path) as f:
|
|
216
|
+
data = json.load(f)
|
|
217
|
+
|
|
218
|
+
for dep_type in ["dependencies", "devDependencies"]:
|
|
219
|
+
for name, version in data.get(dep_type, {}).items():
|
|
220
|
+
deps.append(
|
|
221
|
+
{
|
|
222
|
+
"name": name,
|
|
223
|
+
"version": version,
|
|
224
|
+
"source": str(path),
|
|
225
|
+
"ecosystem": "node",
|
|
226
|
+
"dev": dep_type == "devDependencies",
|
|
227
|
+
},
|
|
228
|
+
)
|
|
229
|
+
except (OSError, json.JSONDecodeError):
|
|
230
|
+
pass
|
|
231
|
+
return deps
|
|
232
|
+
|
|
233
|
+
async def _assess(self, input_data: dict, tier: ModelTier) -> tuple[dict, int, int]:
|
|
234
|
+
"""Check dependencies for vulnerabilities.
|
|
235
|
+
|
|
236
|
+
Compares against known vulnerability database and
|
|
237
|
+
identifies outdated packages.
|
|
238
|
+
"""
|
|
239
|
+
dependencies = input_data.get("dependencies", {})
|
|
240
|
+
|
|
241
|
+
vulnerabilities: list[dict] = []
|
|
242
|
+
outdated: list[dict] = []
|
|
243
|
+
|
|
244
|
+
for ecosystem, deps in dependencies.items():
|
|
245
|
+
for dep in deps:
|
|
246
|
+
name = dep["name"].lower()
|
|
247
|
+
|
|
248
|
+
# Check known vulnerabilities
|
|
249
|
+
if name in KNOWN_VULNERABILITIES:
|
|
250
|
+
vuln_info = KNOWN_VULNERABILITIES[name]
|
|
251
|
+
vulnerabilities.append(
|
|
252
|
+
{
|
|
253
|
+
"package": dep["name"],
|
|
254
|
+
"current_version": dep["version"],
|
|
255
|
+
"affected_versions": vuln_info["affected_versions"],
|
|
256
|
+
"severity": vuln_info["severity"],
|
|
257
|
+
"cve": vuln_info["cve"],
|
|
258
|
+
"ecosystem": ecosystem,
|
|
259
|
+
},
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
# Check for outdated (simulate version check)
|
|
263
|
+
if dep["version"].startswith("<") or dep["version"].startswith("^0."):
|
|
264
|
+
outdated.append(
|
|
265
|
+
{
|
|
266
|
+
"package": dep["name"],
|
|
267
|
+
"current_version": dep["version"],
|
|
268
|
+
"status": "potentially_outdated",
|
|
269
|
+
"ecosystem": ecosystem,
|
|
270
|
+
},
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
# Categorize by severity
|
|
274
|
+
critical = [v for v in vulnerabilities if v["severity"] == "critical"]
|
|
275
|
+
high = [v for v in vulnerabilities if v["severity"] == "high"]
|
|
276
|
+
medium = [v for v in vulnerabilities if v["severity"] == "medium"]
|
|
277
|
+
|
|
278
|
+
assessment = {
|
|
279
|
+
"vulnerabilities": vulnerabilities,
|
|
280
|
+
"outdated": outdated,
|
|
281
|
+
"vulnerability_count": len(vulnerabilities),
|
|
282
|
+
"critical_count": len(critical),
|
|
283
|
+
"high_count": len(high),
|
|
284
|
+
"medium_count": len(medium),
|
|
285
|
+
"outdated_count": len(outdated),
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
input_tokens = len(str(input_data)) // 4
|
|
289
|
+
output_tokens = len(str(assessment)) // 4
|
|
290
|
+
|
|
291
|
+
return (
|
|
292
|
+
{
|
|
293
|
+
"assessment": assessment,
|
|
294
|
+
**input_data,
|
|
295
|
+
},
|
|
296
|
+
input_tokens,
|
|
297
|
+
output_tokens,
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
async def _report(self, input_data: dict, tier: ModelTier) -> tuple[dict, int, int]:
|
|
301
|
+
"""Generate risk assessment and recommendations using LLM.
|
|
302
|
+
|
|
303
|
+
Creates actionable report with remediation steps.
|
|
304
|
+
|
|
305
|
+
Supports XML-enhanced prompts when enabled in workflow config.
|
|
306
|
+
"""
|
|
307
|
+
assessment = input_data.get("assessment", {})
|
|
308
|
+
vulnerabilities = assessment.get("vulnerabilities", [])
|
|
309
|
+
outdated = assessment.get("outdated", [])
|
|
310
|
+
target = input_data.get("path", "")
|
|
311
|
+
|
|
312
|
+
# Calculate risk score
|
|
313
|
+
risk_score = (
|
|
314
|
+
assessment.get("critical_count", 0) * 25
|
|
315
|
+
+ assessment.get("high_count", 0) * 10
|
|
316
|
+
+ assessment.get("medium_count", 0) * 3
|
|
317
|
+
+ assessment.get("outdated_count", 0) * 1
|
|
318
|
+
)
|
|
319
|
+
risk_score = min(100, risk_score)
|
|
320
|
+
|
|
321
|
+
risk_level = (
|
|
322
|
+
"critical"
|
|
323
|
+
if risk_score >= 75
|
|
324
|
+
else "high" if risk_score >= 50 else "medium" if risk_score >= 25 else "low"
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
# Build vulnerability summary for LLM
|
|
328
|
+
vuln_summary = []
|
|
329
|
+
for v in vulnerabilities[:15]:
|
|
330
|
+
vuln_summary.append(
|
|
331
|
+
f"- {v.get('package')}@{v.get('current_version')}: "
|
|
332
|
+
f"{v.get('cve')} ({v.get('severity')})",
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
# Build input payload for prompt
|
|
336
|
+
input_payload = f"""Target: {target or "codebase"}
|
|
337
|
+
|
|
338
|
+
Total Dependencies: {input_data.get("total_dependencies", 0)}
|
|
339
|
+
Risk Score: {risk_score}/100
|
|
340
|
+
Risk Level: {risk_level}
|
|
341
|
+
|
|
342
|
+
Vulnerabilities ({len(vulnerabilities)}):
|
|
343
|
+
{chr(10).join(vuln_summary) if vuln_summary else "None found"}
|
|
344
|
+
|
|
345
|
+
Outdated Packages: {len(outdated)}
|
|
346
|
+
|
|
347
|
+
Severity Summary:
|
|
348
|
+
- Critical: {assessment.get("critical_count", 0)}
|
|
349
|
+
- High: {assessment.get("high_count", 0)}
|
|
350
|
+
- Medium: {assessment.get("medium_count", 0)}"""
|
|
351
|
+
|
|
352
|
+
# Check if XML prompts are enabled
|
|
353
|
+
if self._is_xml_enabled():
|
|
354
|
+
# Use XML-enhanced prompt
|
|
355
|
+
user_message = self._render_xml_prompt(
|
|
356
|
+
role="security engineer specializing in dependency management",
|
|
357
|
+
goal="Generate a comprehensive dependency security report with remediation steps",
|
|
358
|
+
instructions=[
|
|
359
|
+
"Analyze the vulnerability findings and their severity",
|
|
360
|
+
"Prioritize remediation actions by risk level",
|
|
361
|
+
"Provide specific upgrade recommendations",
|
|
362
|
+
"Identify potential breaking changes from upgrades",
|
|
363
|
+
"Suggest a remediation timeline",
|
|
364
|
+
],
|
|
365
|
+
constraints=[
|
|
366
|
+
"Focus on actionable recommendations",
|
|
367
|
+
"Prioritize critical and high severity issues",
|
|
368
|
+
"Include version upgrade targets where possible",
|
|
369
|
+
],
|
|
370
|
+
input_type="dependency_vulnerabilities",
|
|
371
|
+
input_payload=input_payload,
|
|
372
|
+
extra={
|
|
373
|
+
"risk_score": risk_score,
|
|
374
|
+
"risk_level": risk_level,
|
|
375
|
+
},
|
|
376
|
+
)
|
|
377
|
+
system = None # XML prompt includes all context
|
|
378
|
+
else:
|
|
379
|
+
# Use legacy plain text prompts
|
|
380
|
+
system = """You are a security engineer specializing in dependency management.
|
|
381
|
+
Analyze the vulnerability findings and generate a comprehensive remediation report.
|
|
382
|
+
|
|
383
|
+
Focus on:
|
|
384
|
+
1. Prioritizing by severity
|
|
385
|
+
2. Specific upgrade recommendations
|
|
386
|
+
3. Potential breaking changes
|
|
387
|
+
4. Remediation timeline"""
|
|
388
|
+
|
|
389
|
+
user_message = f"""Generate a dependency security report:
|
|
390
|
+
|
|
391
|
+
{input_payload}
|
|
392
|
+
|
|
393
|
+
Provide actionable remediation recommendations."""
|
|
394
|
+
|
|
395
|
+
# Try executor-based execution first (Phase 3 pattern)
|
|
396
|
+
if self._executor is not None or self._api_key:
|
|
397
|
+
try:
|
|
398
|
+
step = DEPENDENCY_CHECK_STEPS["report"]
|
|
399
|
+
response, input_tokens, output_tokens, cost = await self.run_step_with_executor(
|
|
400
|
+
step=step,
|
|
401
|
+
prompt=user_message,
|
|
402
|
+
system=system,
|
|
403
|
+
)
|
|
404
|
+
except Exception:
|
|
405
|
+
# Fall back to legacy _call_llm if executor fails
|
|
406
|
+
response, input_tokens, output_tokens = await self._call_llm(
|
|
407
|
+
tier,
|
|
408
|
+
system or "",
|
|
409
|
+
user_message,
|
|
410
|
+
max_tokens=3000,
|
|
411
|
+
)
|
|
412
|
+
else:
|
|
413
|
+
# Legacy path for backward compatibility
|
|
414
|
+
response, input_tokens, output_tokens = await self._call_llm(
|
|
415
|
+
tier,
|
|
416
|
+
system or "",
|
|
417
|
+
user_message,
|
|
418
|
+
max_tokens=3000,
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
# Parse XML response if enforcement is enabled
|
|
422
|
+
parsed_data = self._parse_xml_response(response)
|
|
423
|
+
|
|
424
|
+
# Generate basic recommendations for backwards compatibility
|
|
425
|
+
recommendations: list[dict] = []
|
|
426
|
+
|
|
427
|
+
for vuln in vulnerabilities:
|
|
428
|
+
recommendations.append(
|
|
429
|
+
{
|
|
430
|
+
"priority": 1 if vuln["severity"] == "critical" else 2,
|
|
431
|
+
"action": "upgrade",
|
|
432
|
+
"package": vuln["package"],
|
|
433
|
+
"reason": f"Fix {vuln['cve']} ({vuln['severity']} severity)",
|
|
434
|
+
"suggestion": f"Upgrade {vuln['package']} to latest version",
|
|
435
|
+
},
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
for dep in outdated[:10]: # Top 10 outdated
|
|
439
|
+
recommendations.append(
|
|
440
|
+
{
|
|
441
|
+
"priority": 3,
|
|
442
|
+
"action": "review",
|
|
443
|
+
"package": dep["package"],
|
|
444
|
+
"reason": "Potentially outdated version",
|
|
445
|
+
"suggestion": f"Review and update {dep['package']}",
|
|
446
|
+
},
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
# Sort by priority
|
|
450
|
+
recommendations.sort(key=lambda x: x["priority"])
|
|
451
|
+
|
|
452
|
+
result = {
|
|
453
|
+
"risk_score": risk_score,
|
|
454
|
+
"risk_level": risk_level,
|
|
455
|
+
"total_dependencies": input_data.get("total_dependencies", 0),
|
|
456
|
+
"vulnerability_count": len(vulnerabilities),
|
|
457
|
+
"outdated_count": len(outdated),
|
|
458
|
+
"recommendations": recommendations[:20],
|
|
459
|
+
"security_report": response,
|
|
460
|
+
"summary": {
|
|
461
|
+
"critical": assessment.get("critical_count", 0),
|
|
462
|
+
"high": assessment.get("high_count", 0),
|
|
463
|
+
"medium": assessment.get("medium_count", 0),
|
|
464
|
+
},
|
|
465
|
+
"model_tier_used": tier.value,
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
# Merge parsed XML data if available
|
|
469
|
+
if parsed_data.get("xml_parsed"):
|
|
470
|
+
result.update(
|
|
471
|
+
{
|
|
472
|
+
"xml_parsed": True,
|
|
473
|
+
"report_summary": parsed_data.get("summary"),
|
|
474
|
+
"findings": parsed_data.get("findings", []),
|
|
475
|
+
"checklist": parsed_data.get("checklist", []),
|
|
476
|
+
},
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
# Add formatted report for human readability
|
|
480
|
+
result["formatted_report"] = format_dependency_check_report(result, input_data)
|
|
481
|
+
|
|
482
|
+
return (result, input_tokens, output_tokens)
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def format_dependency_check_report(result: dict, input_data: dict) -> str:
|
|
486
|
+
"""Format dependency check output as a human-readable report.
|
|
487
|
+
|
|
488
|
+
Args:
|
|
489
|
+
result: The report stage result
|
|
490
|
+
input_data: Input data from previous stages
|
|
491
|
+
|
|
492
|
+
Returns:
|
|
493
|
+
Formatted report string
|
|
494
|
+
|
|
495
|
+
"""
|
|
496
|
+
lines = []
|
|
497
|
+
|
|
498
|
+
# Header with risk level
|
|
499
|
+
risk_score = result.get("risk_score", 0)
|
|
500
|
+
|
|
501
|
+
if risk_score >= 75:
|
|
502
|
+
risk_icon = "🔴"
|
|
503
|
+
risk_text = "CRITICAL"
|
|
504
|
+
elif risk_score >= 50:
|
|
505
|
+
risk_icon = "🟠"
|
|
506
|
+
risk_text = "HIGH RISK"
|
|
507
|
+
elif risk_score >= 25:
|
|
508
|
+
risk_icon = "🟡"
|
|
509
|
+
risk_text = "MEDIUM RISK"
|
|
510
|
+
else:
|
|
511
|
+
risk_icon = "🟢"
|
|
512
|
+
risk_text = "LOW RISK"
|
|
513
|
+
|
|
514
|
+
lines.append("=" * 60)
|
|
515
|
+
lines.append("DEPENDENCY SECURITY REPORT")
|
|
516
|
+
lines.append("=" * 60)
|
|
517
|
+
lines.append("")
|
|
518
|
+
lines.append(f"Risk Level: {risk_icon} {risk_text}")
|
|
519
|
+
lines.append(f"Risk Score: {risk_score}/100")
|
|
520
|
+
lines.append("")
|
|
521
|
+
|
|
522
|
+
# Inventory summary
|
|
523
|
+
total_deps = result.get("total_dependencies", 0)
|
|
524
|
+
python_count = input_data.get("python_count", 0)
|
|
525
|
+
node_count = input_data.get("node_count", 0)
|
|
526
|
+
files_found = input_data.get("files_found", [])
|
|
527
|
+
|
|
528
|
+
lines.append("-" * 60)
|
|
529
|
+
lines.append("DEPENDENCY INVENTORY")
|
|
530
|
+
lines.append("-" * 60)
|
|
531
|
+
lines.append(f"Total Dependencies: {total_deps}")
|
|
532
|
+
if python_count:
|
|
533
|
+
lines.append(f" Python: {python_count}")
|
|
534
|
+
if node_count:
|
|
535
|
+
lines.append(f" Node.js: {node_count}")
|
|
536
|
+
if files_found:
|
|
537
|
+
lines.append(f"Files Scanned: {len(files_found)}")
|
|
538
|
+
for f in files_found[:5]:
|
|
539
|
+
lines.append(f" • {f}")
|
|
540
|
+
lines.append("")
|
|
541
|
+
|
|
542
|
+
# Vulnerability summary
|
|
543
|
+
summary = result.get("summary", {})
|
|
544
|
+
vuln_count = result.get("vulnerability_count", 0)
|
|
545
|
+
outdated_count = result.get("outdated_count", 0)
|
|
546
|
+
|
|
547
|
+
lines.append("-" * 60)
|
|
548
|
+
lines.append("SECURITY FINDINGS")
|
|
549
|
+
lines.append("-" * 60)
|
|
550
|
+
lines.append(f"Vulnerabilities: {vuln_count}")
|
|
551
|
+
lines.append(f" 🔴 Critical: {summary.get('critical', 0)}")
|
|
552
|
+
lines.append(f" 🟠High: {summary.get('high', 0)}")
|
|
553
|
+
lines.append(f" 🟡 Medium: {summary.get('medium', 0)}")
|
|
554
|
+
lines.append(f"Outdated Packages: {outdated_count}")
|
|
555
|
+
lines.append("")
|
|
556
|
+
|
|
557
|
+
# Vulnerabilities detail
|
|
558
|
+
assessment = input_data.get("assessment", {})
|
|
559
|
+
vulnerabilities = assessment.get("vulnerabilities", [])
|
|
560
|
+
if vulnerabilities:
|
|
561
|
+
lines.append("-" * 60)
|
|
562
|
+
lines.append("VULNERABLE PACKAGES")
|
|
563
|
+
lines.append("-" * 60)
|
|
564
|
+
for vuln in vulnerabilities[:10]:
|
|
565
|
+
severity = vuln.get("severity", "unknown").upper()
|
|
566
|
+
pkg = vuln.get("package", "unknown")
|
|
567
|
+
version = vuln.get("current_version", "?")
|
|
568
|
+
cve = vuln.get("cve", "N/A")
|
|
569
|
+
sev_icon = {"CRITICAL": "🔴", "HIGH": "🟠", "MEDIUM": "🟡"}.get(severity, "⚪")
|
|
570
|
+
lines.append(f" {sev_icon} {pkg}@{version}")
|
|
571
|
+
lines.append(f" CVE: {cve} | Severity: {severity}")
|
|
572
|
+
if len(vulnerabilities) > 10:
|
|
573
|
+
lines.append(f" ... and {len(vulnerabilities) - 10} more")
|
|
574
|
+
lines.append("")
|
|
575
|
+
|
|
576
|
+
# Recommendations
|
|
577
|
+
recommendations = result.get("recommendations", [])
|
|
578
|
+
if recommendations:
|
|
579
|
+
lines.append("-" * 60)
|
|
580
|
+
lines.append("REMEDIATION ACTIONS")
|
|
581
|
+
lines.append("-" * 60)
|
|
582
|
+
priority_labels = {1: "🔴 URGENT", 2: "🟠HIGH", 3: "🟡 REVIEW"}
|
|
583
|
+
for rec in recommendations[:10]:
|
|
584
|
+
priority = rec.get("priority", 3)
|
|
585
|
+
pkg = rec.get("package", "unknown")
|
|
586
|
+
suggestion = rec.get("suggestion", "")
|
|
587
|
+
label = priority_labels.get(priority, "⚪ LOW")
|
|
588
|
+
lines.append(f" {label}: {pkg}")
|
|
589
|
+
lines.append(f" {suggestion}")
|
|
590
|
+
lines.append("")
|
|
591
|
+
|
|
592
|
+
# Security report from LLM (if available)
|
|
593
|
+
security_report = result.get("security_report", "")
|
|
594
|
+
if security_report and not security_report.startswith("[Simulated"):
|
|
595
|
+
lines.append("-" * 60)
|
|
596
|
+
lines.append("DETAILED ANALYSIS")
|
|
597
|
+
lines.append("-" * 60)
|
|
598
|
+
# Truncate if very long
|
|
599
|
+
if len(security_report) > 1500:
|
|
600
|
+
lines.append(security_report[:1500] + "...")
|
|
601
|
+
else:
|
|
602
|
+
lines.append(security_report)
|
|
603
|
+
lines.append("")
|
|
604
|
+
|
|
605
|
+
# Footer
|
|
606
|
+
lines.append("=" * 60)
|
|
607
|
+
model_tier = result.get("model_tier_used", "unknown")
|
|
608
|
+
lines.append(f"Scanned {total_deps} dependencies using {model_tier} tier model")
|
|
609
|
+
lines.append("=" * 60)
|
|
610
|
+
|
|
611
|
+
return "\n".join(lines)
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
def main():
|
|
615
|
+
"""CLI entry point for dependency check workflow."""
|
|
616
|
+
import asyncio
|
|
617
|
+
|
|
618
|
+
async def run():
|
|
619
|
+
workflow = DependencyCheckWorkflow()
|
|
620
|
+
result = await workflow.execute(path=".")
|
|
621
|
+
|
|
622
|
+
print("\nDependency Check Results")
|
|
623
|
+
print("=" * 50)
|
|
624
|
+
print(f"Provider: {result.provider}")
|
|
625
|
+
print(f"Success: {result.success}")
|
|
626
|
+
|
|
627
|
+
report = result.final_output.get("report", {})
|
|
628
|
+
print(f"Risk Level: {report.get('risk_level', 'N/A')}")
|
|
629
|
+
print(f"Risk Score: {report.get('risk_score', 0)}/100")
|
|
630
|
+
print(f"Total Dependencies: {report.get('total_dependencies', 0)}")
|
|
631
|
+
print(f"Vulnerabilities: {report.get('vulnerability_count', 0)}")
|
|
632
|
+
print(f"Outdated: {report.get('outdated_count', 0)}")
|
|
633
|
+
|
|
634
|
+
print("\nCost Report:")
|
|
635
|
+
print(f" Total Cost: ${result.cost_report.total_cost:.4f}")
|
|
636
|
+
savings = result.cost_report.savings
|
|
637
|
+
pct = result.cost_report.savings_percent
|
|
638
|
+
print(f" Savings: ${savings:.4f} ({pct:.1f}%)")
|
|
639
|
+
|
|
640
|
+
asyncio.run(run())
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
if __name__ == "__main__":
|
|
644
|
+
main()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Document Generation Workflow Package.
|
|
2
|
+
|
|
3
|
+
Cost-optimized documentation generation pipeline.
|
|
4
|
+
|
|
5
|
+
Copyright 2025 Smart-AI-Memory
|
|
6
|
+
Licensed under Fair Source License 0.9
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# Core workflow
|
|
10
|
+
# Configuration
|
|
11
|
+
from .config import DOC_GEN_STEPS, TOKEN_COSTS
|
|
12
|
+
|
|
13
|
+
# Report formatter
|
|
14
|
+
from .report_formatter import format_doc_gen_report
|
|
15
|
+
from .workflow import DocumentGenerationWorkflow
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
# Workflow
|
|
19
|
+
"DocumentGenerationWorkflow",
|
|
20
|
+
# Configuration
|
|
21
|
+
"DOC_GEN_STEPS",
|
|
22
|
+
"TOKEN_COSTS",
|
|
23
|
+
# Report formatter
|
|
24
|
+
"format_doc_gen_report",
|
|
25
|
+
]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Document Generation Configuration.
|
|
2
|
+
|
|
3
|
+
Token costs and step configurations for documentation generation workflow.
|
|
4
|
+
|
|
5
|
+
Copyright 2025 Smart-AI-Memory
|
|
6
|
+
Licensed under Fair Source License 0.9
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from ..base import ModelTier
|
|
10
|
+
from ..step_config import WorkflowStepConfig
|
|
11
|
+
|
|
12
|
+
# Approximate cost per 1K tokens (USD) - used for cost estimation
|
|
13
|
+
# These are estimates and should be updated as pricing changes
|
|
14
|
+
TOKEN_COSTS = {
|
|
15
|
+
ModelTier.CHEAP: {"input": 0.00025, "output": 0.00125}, # Haiku
|
|
16
|
+
ModelTier.CAPABLE: {"input": 0.003, "output": 0.015}, # Sonnet
|
|
17
|
+
ModelTier.PREMIUM: {"input": 0.015, "output": 0.075}, # Opus
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
# Define step configurations for executor-based execution
|
|
21
|
+
# Note: max_tokens for polish is dynamically set based on input size
|
|
22
|
+
DOC_GEN_STEPS = {
|
|
23
|
+
"polish": WorkflowStepConfig(
|
|
24
|
+
name="polish",
|
|
25
|
+
task_type="final_review", # Premium tier task
|
|
26
|
+
tier_hint="premium",
|
|
27
|
+
description="Polish and improve documentation for consistency and quality",
|
|
28
|
+
max_tokens=20000, # Increased to handle large chunked documents
|
|
29
|
+
),
|
|
30
|
+
}
|