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
attune/socratic/cli.py
ADDED
|
@@ -0,0 +1,703 @@
|
|
|
1
|
+
"""CLI for Socratic Workflow Builder
|
|
2
|
+
|
|
3
|
+
Provides command-line interface for:
|
|
4
|
+
- Starting interactive Socratic sessions
|
|
5
|
+
- Listing and resuming sessions
|
|
6
|
+
- Generating workflows from sessions
|
|
7
|
+
- Managing blueprints
|
|
8
|
+
|
|
9
|
+
Commands:
|
|
10
|
+
empathy socratic start [--goal "..."]
|
|
11
|
+
empathy socratic resume <session_id>
|
|
12
|
+
empathy socratic list [--state completed|in_progress]
|
|
13
|
+
empathy socratic generate <session_id>
|
|
14
|
+
empathy socratic blueprints [--domain security]
|
|
15
|
+
empathy socratic run <blueprint_id> [--input file.json]
|
|
16
|
+
|
|
17
|
+
Copyright 2026 Smart-AI-Memory
|
|
18
|
+
Licensed under Fair Source License 0.9
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import argparse
|
|
24
|
+
import json
|
|
25
|
+
import sys
|
|
26
|
+
from typing import Any
|
|
27
|
+
|
|
28
|
+
from .engine import SocraticWorkflowBuilder
|
|
29
|
+
from .forms import FieldType, Form, FormField
|
|
30
|
+
from .session import SessionState
|
|
31
|
+
from .storage import get_default_storage
|
|
32
|
+
|
|
33
|
+
# =============================================================================
|
|
34
|
+
# CONSOLE FORMATTING
|
|
35
|
+
# =============================================================================
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Console:
|
|
39
|
+
"""Simple console output formatting."""
|
|
40
|
+
|
|
41
|
+
COLORS = {
|
|
42
|
+
"reset": "\033[0m",
|
|
43
|
+
"bold": "\033[1m",
|
|
44
|
+
"dim": "\033[2m",
|
|
45
|
+
"red": "\033[91m",
|
|
46
|
+
"green": "\033[92m",
|
|
47
|
+
"yellow": "\033[93m",
|
|
48
|
+
"blue": "\033[94m",
|
|
49
|
+
"magenta": "\033[95m",
|
|
50
|
+
"cyan": "\033[96m",
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
def __init__(self, use_color: bool = True):
|
|
54
|
+
self.use_color = use_color and sys.stdout.isatty()
|
|
55
|
+
|
|
56
|
+
def _c(self, color: str, text: str) -> str:
|
|
57
|
+
"""Apply color to text."""
|
|
58
|
+
if not self.use_color:
|
|
59
|
+
return text
|
|
60
|
+
return f"{self.COLORS.get(color, '')}{text}{self.COLORS['reset']}"
|
|
61
|
+
|
|
62
|
+
def header(self, text: str) -> None:
|
|
63
|
+
"""Print a header."""
|
|
64
|
+
print()
|
|
65
|
+
print(self._c("bold", "=" * 60))
|
|
66
|
+
print(self._c("bold", f" {text}"))
|
|
67
|
+
print(self._c("bold", "=" * 60))
|
|
68
|
+
print()
|
|
69
|
+
|
|
70
|
+
def subheader(self, text: str) -> None:
|
|
71
|
+
"""Print a subheader."""
|
|
72
|
+
print()
|
|
73
|
+
print(self._c("cyan", f"── {text} ──"))
|
|
74
|
+
print()
|
|
75
|
+
|
|
76
|
+
def success(self, text: str) -> None:
|
|
77
|
+
"""Print success message."""
|
|
78
|
+
print(self._c("green", f"✓ {text}"))
|
|
79
|
+
|
|
80
|
+
def error(self, text: str) -> None:
|
|
81
|
+
"""Print error message."""
|
|
82
|
+
print(self._c("red", f"✗ {text}"))
|
|
83
|
+
|
|
84
|
+
def warning(self, text: str) -> None:
|
|
85
|
+
"""Print warning message."""
|
|
86
|
+
print(self._c("yellow", f"⚠ {text}"))
|
|
87
|
+
|
|
88
|
+
def info(self, text: str) -> None:
|
|
89
|
+
"""Print info message."""
|
|
90
|
+
print(self._c("blue", f"ℹ {text}"))
|
|
91
|
+
|
|
92
|
+
def dim(self, text: str) -> None:
|
|
93
|
+
"""Print dimmed text."""
|
|
94
|
+
print(self._c("dim", text))
|
|
95
|
+
|
|
96
|
+
def progress(self, value: float, width: int = 30) -> str:
|
|
97
|
+
"""Generate progress bar."""
|
|
98
|
+
filled = int(value * width)
|
|
99
|
+
bar = "▓" * filled + "░" * (width - filled)
|
|
100
|
+
return f"[{bar}] {value:.0%}"
|
|
101
|
+
|
|
102
|
+
def table(self, headers: list[str], rows: list[list[str]]) -> None:
|
|
103
|
+
"""Print a simple table."""
|
|
104
|
+
# Calculate column widths
|
|
105
|
+
widths = [len(h) for h in headers]
|
|
106
|
+
for row in rows:
|
|
107
|
+
for i, cell in enumerate(row):
|
|
108
|
+
if i < len(widths):
|
|
109
|
+
widths[i] = max(widths[i], len(str(cell)))
|
|
110
|
+
|
|
111
|
+
# Print header
|
|
112
|
+
header_line = " | ".join(self._c("bold", h.ljust(widths[i])) for i, h in enumerate(headers))
|
|
113
|
+
print(header_line)
|
|
114
|
+
print("-" * (sum(widths) + len(widths) * 3 - 1))
|
|
115
|
+
|
|
116
|
+
# Print rows
|
|
117
|
+
for row in rows:
|
|
118
|
+
row_line = " | ".join(str(cell).ljust(widths[i]) for i, cell in enumerate(row))
|
|
119
|
+
print(row_line)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
console = Console()
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
# =============================================================================
|
|
126
|
+
# INTERACTIVE FORM RENDERER
|
|
127
|
+
# =============================================================================
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def render_form_interactive(form: Form, console: Console) -> dict[str, Any]:
|
|
131
|
+
"""Render a form and collect user input.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
form: Form to render
|
|
135
|
+
console: Console for output
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Dictionary of answers
|
|
139
|
+
"""
|
|
140
|
+
console.subheader(form.title)
|
|
141
|
+
|
|
142
|
+
if form.description:
|
|
143
|
+
print(form.description)
|
|
144
|
+
print()
|
|
145
|
+
|
|
146
|
+
print(console.progress(form.progress))
|
|
147
|
+
print()
|
|
148
|
+
|
|
149
|
+
answers: dict[str, Any] = {}
|
|
150
|
+
|
|
151
|
+
for field in form.fields:
|
|
152
|
+
# Check visibility
|
|
153
|
+
if not field.should_show(answers):
|
|
154
|
+
continue
|
|
155
|
+
|
|
156
|
+
# Render field
|
|
157
|
+
required = " *" if field.validation.required else ""
|
|
158
|
+
print(f"{console._c('bold', field.label)}{required}")
|
|
159
|
+
|
|
160
|
+
if field.help_text:
|
|
161
|
+
console.dim(f" {field.help_text}")
|
|
162
|
+
|
|
163
|
+
# Handle by field type
|
|
164
|
+
if field.field_type == FieldType.SINGLE_SELECT:
|
|
165
|
+
answers[field.id] = _input_single_select(field, console)
|
|
166
|
+
|
|
167
|
+
elif field.field_type == FieldType.MULTI_SELECT:
|
|
168
|
+
answers[field.id] = _input_multi_select(field, console)
|
|
169
|
+
|
|
170
|
+
elif field.field_type == FieldType.BOOLEAN:
|
|
171
|
+
answers[field.id] = _input_boolean(field, console)
|
|
172
|
+
|
|
173
|
+
elif field.field_type == FieldType.TEXT_AREA:
|
|
174
|
+
answers[field.id] = _input_text_area(field, console)
|
|
175
|
+
|
|
176
|
+
else: # TEXT, NUMBER, etc.
|
|
177
|
+
answers[field.id] = _input_text(field, console)
|
|
178
|
+
|
|
179
|
+
print()
|
|
180
|
+
|
|
181
|
+
return answers
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _input_single_select(field: FormField, console: Console) -> str | None:
|
|
185
|
+
"""Input for single select field."""
|
|
186
|
+
for i, opt in enumerate(field.options, 1):
|
|
187
|
+
rec = console._c("green", " (Recommended)") if opt.recommended else ""
|
|
188
|
+
print(f" {i}. {opt.label}{rec}")
|
|
189
|
+
if opt.description:
|
|
190
|
+
console.dim(f" {opt.description}")
|
|
191
|
+
|
|
192
|
+
while True:
|
|
193
|
+
response = input("\n Enter number: ").strip()
|
|
194
|
+
|
|
195
|
+
if not response:
|
|
196
|
+
if not field.validation.required:
|
|
197
|
+
return None
|
|
198
|
+
console.error("This field is required")
|
|
199
|
+
continue
|
|
200
|
+
|
|
201
|
+
try:
|
|
202
|
+
idx = int(response) - 1
|
|
203
|
+
if 0 <= idx < len(field.options):
|
|
204
|
+
return field.options[idx].value
|
|
205
|
+
console.error(f"Enter a number between 1 and {len(field.options)}")
|
|
206
|
+
except ValueError:
|
|
207
|
+
console.error("Enter a valid number")
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _input_multi_select(field: FormField, console: Console) -> list[str]:
|
|
211
|
+
"""Input for multi select field."""
|
|
212
|
+
for i, opt in enumerate(field.options, 1):
|
|
213
|
+
rec = console._c("green", " (Recommended)") if opt.recommended else ""
|
|
214
|
+
print(f" {i}. {opt.label}{rec}")
|
|
215
|
+
if opt.description:
|
|
216
|
+
console.dim(f" {opt.description}")
|
|
217
|
+
|
|
218
|
+
while True:
|
|
219
|
+
response = input("\n Enter numbers (comma-separated): ").strip()
|
|
220
|
+
|
|
221
|
+
if not response:
|
|
222
|
+
if not field.validation.required:
|
|
223
|
+
return []
|
|
224
|
+
console.error("Select at least one option")
|
|
225
|
+
continue
|
|
226
|
+
|
|
227
|
+
try:
|
|
228
|
+
indices = [int(x.strip()) - 1 for x in response.split(",")]
|
|
229
|
+
selected = []
|
|
230
|
+
for idx in indices:
|
|
231
|
+
if 0 <= idx < len(field.options):
|
|
232
|
+
selected.append(field.options[idx].value)
|
|
233
|
+
|
|
234
|
+
if selected:
|
|
235
|
+
return selected
|
|
236
|
+
console.error("No valid options selected")
|
|
237
|
+
except ValueError:
|
|
238
|
+
console.error("Enter valid numbers separated by commas")
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _input_boolean(field: FormField, console: Console) -> bool:
|
|
242
|
+
"""Input for boolean field."""
|
|
243
|
+
while True:
|
|
244
|
+
response = input(" (y/n): ").strip().lower()
|
|
245
|
+
|
|
246
|
+
if response in ("y", "yes", "true", "1"):
|
|
247
|
+
return True
|
|
248
|
+
elif response in ("n", "no", "false", "0"):
|
|
249
|
+
return False
|
|
250
|
+
elif not response and not field.validation.required:
|
|
251
|
+
return False
|
|
252
|
+
else:
|
|
253
|
+
console.error("Enter 'y' or 'n'")
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _input_text(field: FormField, console: Console) -> str:
|
|
257
|
+
"""Input for text field."""
|
|
258
|
+
prompt = f" {field.placeholder or 'Enter value'}: " if field.placeholder else " > "
|
|
259
|
+
|
|
260
|
+
while True:
|
|
261
|
+
response = input(prompt).strip()
|
|
262
|
+
|
|
263
|
+
if not response:
|
|
264
|
+
if not field.validation.required:
|
|
265
|
+
return ""
|
|
266
|
+
console.error("This field is required")
|
|
267
|
+
continue
|
|
268
|
+
|
|
269
|
+
# Validate
|
|
270
|
+
is_valid, error = field.validate(response)
|
|
271
|
+
if is_valid:
|
|
272
|
+
return response
|
|
273
|
+
console.error(error)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _input_text_area(field: FormField, console: Console) -> str:
|
|
277
|
+
"""Input for text area field."""
|
|
278
|
+
print(" (Enter text, then press Enter twice to finish)")
|
|
279
|
+
|
|
280
|
+
lines = []
|
|
281
|
+
empty_count = 0
|
|
282
|
+
|
|
283
|
+
while True:
|
|
284
|
+
line = input(" > " if not lines else " ")
|
|
285
|
+
|
|
286
|
+
if not line:
|
|
287
|
+
empty_count += 1
|
|
288
|
+
if empty_count >= 2:
|
|
289
|
+
break
|
|
290
|
+
lines.append("")
|
|
291
|
+
else:
|
|
292
|
+
empty_count = 0
|
|
293
|
+
lines.append(line)
|
|
294
|
+
|
|
295
|
+
response = "\n".join(lines).strip()
|
|
296
|
+
|
|
297
|
+
if not response and field.validation.required:
|
|
298
|
+
console.error("This field is required")
|
|
299
|
+
return _input_text_area(field, console)
|
|
300
|
+
|
|
301
|
+
return response
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
# =============================================================================
|
|
305
|
+
# CLI COMMANDS
|
|
306
|
+
# =============================================================================
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def cmd_start(args: argparse.Namespace) -> int:
|
|
310
|
+
"""Start a new Socratic session."""
|
|
311
|
+
storage = get_default_storage()
|
|
312
|
+
builder = SocraticWorkflowBuilder()
|
|
313
|
+
|
|
314
|
+
console.header("SOCRATIC WORKFLOW BUILDER")
|
|
315
|
+
|
|
316
|
+
# Start session
|
|
317
|
+
session = builder.start_session()
|
|
318
|
+
console.info(f"Session ID: {session.session_id[:8]}...")
|
|
319
|
+
|
|
320
|
+
# Get initial goal
|
|
321
|
+
if args.goal:
|
|
322
|
+
goal = args.goal
|
|
323
|
+
console.info(f"Goal: {goal}")
|
|
324
|
+
else:
|
|
325
|
+
initial_form = builder.get_initial_form()
|
|
326
|
+
answers = render_form_interactive(initial_form, console)
|
|
327
|
+
goal = answers.get("goal", "")
|
|
328
|
+
|
|
329
|
+
if not goal:
|
|
330
|
+
console.error("No goal provided")
|
|
331
|
+
return 1
|
|
332
|
+
|
|
333
|
+
# Set goal and analyze
|
|
334
|
+
session = builder.set_goal(session, goal)
|
|
335
|
+
storage.save_session(session)
|
|
336
|
+
|
|
337
|
+
# Show analysis
|
|
338
|
+
if session.goal_analysis:
|
|
339
|
+
console.subheader("Goal Analysis")
|
|
340
|
+
print(f" Domain: {session.goal_analysis.domain}")
|
|
341
|
+
print(f" Confidence: {session.goal_analysis.confidence:.0%}")
|
|
342
|
+
|
|
343
|
+
if session.goal_analysis.ambiguities:
|
|
344
|
+
print()
|
|
345
|
+
console.warning("Ambiguities detected:")
|
|
346
|
+
for amb in session.goal_analysis.ambiguities:
|
|
347
|
+
print(f" • {amb}")
|
|
348
|
+
|
|
349
|
+
# Interactive questioning loop
|
|
350
|
+
while not builder.is_ready_to_generate(session):
|
|
351
|
+
form = builder.get_next_questions(session)
|
|
352
|
+
if not form:
|
|
353
|
+
break
|
|
354
|
+
|
|
355
|
+
answers = render_form_interactive(form, console)
|
|
356
|
+
session = builder.submit_answers(session, answers)
|
|
357
|
+
storage.save_session(session)
|
|
358
|
+
|
|
359
|
+
# Show progress
|
|
360
|
+
summary = builder.get_session_summary(session)
|
|
361
|
+
console.info(f"Requirements completeness: {summary['requirements_completeness']:.0%}")
|
|
362
|
+
|
|
363
|
+
# Generate workflow
|
|
364
|
+
if builder.is_ready_to_generate(session):
|
|
365
|
+
console.subheader("Generating Workflow")
|
|
366
|
+
|
|
367
|
+
workflow = builder.generate_workflow(session)
|
|
368
|
+
storage.save_session(session)
|
|
369
|
+
|
|
370
|
+
if session.blueprint:
|
|
371
|
+
storage.save_blueprint(session.blueprint)
|
|
372
|
+
|
|
373
|
+
console.success("Workflow generated!")
|
|
374
|
+
print()
|
|
375
|
+
print(workflow.describe())
|
|
376
|
+
|
|
377
|
+
if session.blueprint:
|
|
378
|
+
console.info(f"Blueprint ID: {session.blueprint.id[:8]}...")
|
|
379
|
+
|
|
380
|
+
return 0
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def cmd_resume(args: argparse.Namespace) -> int:
|
|
384
|
+
"""Resume an existing session."""
|
|
385
|
+
storage = get_default_storage()
|
|
386
|
+
builder = SocraticWorkflowBuilder()
|
|
387
|
+
|
|
388
|
+
# Find session
|
|
389
|
+
session = storage.load_session(args.session_id)
|
|
390
|
+
|
|
391
|
+
# Try partial match
|
|
392
|
+
if not session:
|
|
393
|
+
sessions = storage.list_sessions()
|
|
394
|
+
matches = [s for s in sessions if s["session_id"].startswith(args.session_id)]
|
|
395
|
+
if len(matches) == 1:
|
|
396
|
+
session = storage.load_session(matches[0]["session_id"])
|
|
397
|
+
elif len(matches) > 1:
|
|
398
|
+
console.error(f"Multiple sessions match '{args.session_id}':")
|
|
399
|
+
for m in matches:
|
|
400
|
+
print(f" - {m['session_id'][:8]}... ({m['state']})")
|
|
401
|
+
return 1
|
|
402
|
+
|
|
403
|
+
if not session:
|
|
404
|
+
console.error(f"Session not found: {args.session_id}")
|
|
405
|
+
return 1
|
|
406
|
+
|
|
407
|
+
console.header(f"RESUMING SESSION {session.session_id[:8]}...")
|
|
408
|
+
console.info(f"Goal: {session.goal[:80]}...")
|
|
409
|
+
console.info(f"State: {session.state.value}")
|
|
410
|
+
|
|
411
|
+
# Rebuild builder state
|
|
412
|
+
if session.goal:
|
|
413
|
+
builder._sessions[session.session_id] = session
|
|
414
|
+
|
|
415
|
+
# Continue questioning or generate
|
|
416
|
+
if session.state == SessionState.COMPLETED:
|
|
417
|
+
console.success("Session already completed")
|
|
418
|
+
return 0
|
|
419
|
+
|
|
420
|
+
while not builder.is_ready_to_generate(session):
|
|
421
|
+
form = builder.get_next_questions(session)
|
|
422
|
+
if not form:
|
|
423
|
+
break
|
|
424
|
+
|
|
425
|
+
answers = render_form_interactive(form, console)
|
|
426
|
+
session = builder.submit_answers(session, answers)
|
|
427
|
+
storage.save_session(session)
|
|
428
|
+
|
|
429
|
+
if builder.is_ready_to_generate(session):
|
|
430
|
+
console.subheader("Generating Workflow")
|
|
431
|
+
|
|
432
|
+
workflow = builder.generate_workflow(session)
|
|
433
|
+
storage.save_session(session)
|
|
434
|
+
|
|
435
|
+
if session.blueprint:
|
|
436
|
+
storage.save_blueprint(session.blueprint)
|
|
437
|
+
|
|
438
|
+
console.success("Workflow generated!")
|
|
439
|
+
print()
|
|
440
|
+
print(workflow.describe())
|
|
441
|
+
|
|
442
|
+
return 0
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def cmd_list(args: argparse.Namespace) -> int:
|
|
446
|
+
"""List sessions."""
|
|
447
|
+
storage = get_default_storage()
|
|
448
|
+
|
|
449
|
+
state = None
|
|
450
|
+
if args.state:
|
|
451
|
+
try:
|
|
452
|
+
state = SessionState(args.state)
|
|
453
|
+
except ValueError:
|
|
454
|
+
console.error(f"Invalid state: {args.state}")
|
|
455
|
+
return 1
|
|
456
|
+
|
|
457
|
+
sessions = storage.list_sessions(state=state, limit=args.limit)
|
|
458
|
+
|
|
459
|
+
if not sessions:
|
|
460
|
+
console.info("No sessions found")
|
|
461
|
+
return 0
|
|
462
|
+
|
|
463
|
+
console.header("SOCRATIC SESSIONS")
|
|
464
|
+
|
|
465
|
+
headers = ["ID", "State", "Goal", "Updated"]
|
|
466
|
+
rows = []
|
|
467
|
+
for s in sessions:
|
|
468
|
+
rows.append(
|
|
469
|
+
[
|
|
470
|
+
s["session_id"][:8],
|
|
471
|
+
s["state"],
|
|
472
|
+
(
|
|
473
|
+
(s.get("goal") or "")[:40] + "..."
|
|
474
|
+
if len(s.get("goal") or "") > 40
|
|
475
|
+
else s.get("goal") or ""
|
|
476
|
+
),
|
|
477
|
+
s.get("updated_at", "")[:16],
|
|
478
|
+
]
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
console.table(headers, rows)
|
|
482
|
+
return 0
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def cmd_blueprints(args: argparse.Namespace) -> int:
|
|
486
|
+
"""List blueprints."""
|
|
487
|
+
storage = get_default_storage()
|
|
488
|
+
|
|
489
|
+
blueprints = storage.list_blueprints(domain=args.domain, limit=args.limit)
|
|
490
|
+
|
|
491
|
+
if not blueprints:
|
|
492
|
+
console.info("No blueprints found")
|
|
493
|
+
return 0
|
|
494
|
+
|
|
495
|
+
console.header("WORKFLOW BLUEPRINTS")
|
|
496
|
+
|
|
497
|
+
headers = ["ID", "Name", "Domain", "Agents", "Generated"]
|
|
498
|
+
rows = []
|
|
499
|
+
for b in blueprints:
|
|
500
|
+
rows.append(
|
|
501
|
+
[
|
|
502
|
+
b["id"][:8] if b.get("id") else "?",
|
|
503
|
+
b.get("name", "")[:30],
|
|
504
|
+
b.get("domain", ""),
|
|
505
|
+
str(b.get("agents_count", 0)),
|
|
506
|
+
(b.get("generated_at") or "")[:16],
|
|
507
|
+
]
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
console.table(headers, rows)
|
|
511
|
+
return 0
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
def cmd_show(args: argparse.Namespace) -> int:
|
|
515
|
+
"""Show details of a session or blueprint."""
|
|
516
|
+
storage = get_default_storage()
|
|
517
|
+
|
|
518
|
+
# Try as session first
|
|
519
|
+
session = storage.load_session(args.id)
|
|
520
|
+
if session:
|
|
521
|
+
console.header(f"SESSION: {session.session_id[:8]}...")
|
|
522
|
+
|
|
523
|
+
print(f"State: {session.state.value}")
|
|
524
|
+
print(f"Goal: {session.goal}")
|
|
525
|
+
print(f"Created: {session.created_at}")
|
|
526
|
+
print(f"Updated: {session.updated_at}")
|
|
527
|
+
|
|
528
|
+
if session.goal_analysis:
|
|
529
|
+
console.subheader("Analysis")
|
|
530
|
+
print(f"Domain: {session.goal_analysis.domain}")
|
|
531
|
+
print(f"Confidence: {session.goal_analysis.confidence:.0%}")
|
|
532
|
+
print(f"Intent: {session.goal_analysis.intent}")
|
|
533
|
+
|
|
534
|
+
if session.requirements.must_have:
|
|
535
|
+
console.subheader("Requirements")
|
|
536
|
+
for req in session.requirements.must_have:
|
|
537
|
+
print(f" • {req}")
|
|
538
|
+
|
|
539
|
+
return 0
|
|
540
|
+
|
|
541
|
+
# Try as blueprint
|
|
542
|
+
blueprint = storage.load_blueprint(args.id)
|
|
543
|
+
if blueprint:
|
|
544
|
+
console.header(f"BLUEPRINT: {blueprint.name}")
|
|
545
|
+
|
|
546
|
+
print(f"ID: {blueprint.id}")
|
|
547
|
+
print(f"Domain: {blueprint.domain}")
|
|
548
|
+
print(f"Languages: {', '.join(blueprint.supported_languages)}")
|
|
549
|
+
print(f"Quality Focus: {', '.join(blueprint.quality_focus)}")
|
|
550
|
+
|
|
551
|
+
console.subheader("Agents")
|
|
552
|
+
for agent in blueprint.agents:
|
|
553
|
+
print(f" • {agent.spec.name} ({agent.spec.role.value})")
|
|
554
|
+
print(f" Goal: {agent.spec.goal[:60]}...")
|
|
555
|
+
|
|
556
|
+
console.subheader("Stages")
|
|
557
|
+
for stage in blueprint.stages:
|
|
558
|
+
parallel = "(parallel)" if stage.parallel else "(sequential)"
|
|
559
|
+
print(f" • {stage.name} {parallel}")
|
|
560
|
+
print(f" Agents: {', '.join(stage.agent_ids)}")
|
|
561
|
+
|
|
562
|
+
return 0
|
|
563
|
+
|
|
564
|
+
console.error(f"Not found: {args.id}")
|
|
565
|
+
return 1
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
def cmd_delete(args: argparse.Namespace) -> int:
|
|
569
|
+
"""Delete a session."""
|
|
570
|
+
storage = get_default_storage()
|
|
571
|
+
|
|
572
|
+
if not args.force:
|
|
573
|
+
response = input(f"Delete session {args.session_id}? (y/N): ").strip().lower()
|
|
574
|
+
if response != "y":
|
|
575
|
+
console.info("Cancelled")
|
|
576
|
+
return 0
|
|
577
|
+
|
|
578
|
+
if storage.delete_session(args.session_id):
|
|
579
|
+
console.success(f"Deleted session {args.session_id}")
|
|
580
|
+
return 0
|
|
581
|
+
else:
|
|
582
|
+
console.error(f"Session not found: {args.session_id}")
|
|
583
|
+
return 1
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
def cmd_export(args: argparse.Namespace) -> int:
|
|
587
|
+
"""Export a blueprint to JSON."""
|
|
588
|
+
storage = get_default_storage()
|
|
589
|
+
|
|
590
|
+
blueprint = storage.load_blueprint(args.blueprint_id)
|
|
591
|
+
if not blueprint:
|
|
592
|
+
console.error(f"Blueprint not found: {args.blueprint_id}")
|
|
593
|
+
return 1
|
|
594
|
+
|
|
595
|
+
data = blueprint.to_dict()
|
|
596
|
+
|
|
597
|
+
if args.output:
|
|
598
|
+
with open(args.output, "w") as f:
|
|
599
|
+
json.dump(data, f, indent=2, default=str)
|
|
600
|
+
console.success(f"Exported to {args.output}")
|
|
601
|
+
else:
|
|
602
|
+
print(json.dumps(data, indent=2, default=str))
|
|
603
|
+
|
|
604
|
+
return 0
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
# =============================================================================
|
|
608
|
+
# MAIN ENTRY POINT
|
|
609
|
+
# =============================================================================
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
def create_parser() -> argparse.ArgumentParser:
|
|
613
|
+
"""Create the argument parser."""
|
|
614
|
+
parser = argparse.ArgumentParser(
|
|
615
|
+
prog="empathy socratic",
|
|
616
|
+
description="Socratic Workflow Builder - Generate agent workflows through guided questioning",
|
|
617
|
+
)
|
|
618
|
+
|
|
619
|
+
subparsers = parser.add_subparsers(dest="command", help="Commands")
|
|
620
|
+
|
|
621
|
+
# start
|
|
622
|
+
start_parser = subparsers.add_parser("start", help="Start a new Socratic session")
|
|
623
|
+
start_parser.add_argument("--goal", "-g", help="Initial goal (skip first question)")
|
|
624
|
+
start_parser.add_argument("--non-interactive", action="store_true", help="Non-interactive mode")
|
|
625
|
+
|
|
626
|
+
# resume
|
|
627
|
+
resume_parser = subparsers.add_parser("resume", help="Resume an existing session")
|
|
628
|
+
resume_parser.add_argument("session_id", help="Session ID (can be partial)")
|
|
629
|
+
|
|
630
|
+
# list
|
|
631
|
+
list_parser = subparsers.add_parser("list", help="List sessions")
|
|
632
|
+
list_parser.add_argument(
|
|
633
|
+
"--state",
|
|
634
|
+
"-s",
|
|
635
|
+
choices=[
|
|
636
|
+
"awaiting_goal",
|
|
637
|
+
"awaiting_answers",
|
|
638
|
+
"ready_to_generate",
|
|
639
|
+
"completed",
|
|
640
|
+
"cancelled",
|
|
641
|
+
],
|
|
642
|
+
)
|
|
643
|
+
list_parser.add_argument("--limit", "-n", type=int, default=20)
|
|
644
|
+
|
|
645
|
+
# blueprints
|
|
646
|
+
bp_parser = subparsers.add_parser("blueprints", help="List workflow blueprints")
|
|
647
|
+
bp_parser.add_argument("--domain", "-d", help="Filter by domain")
|
|
648
|
+
bp_parser.add_argument("--limit", "-n", type=int, default=20)
|
|
649
|
+
|
|
650
|
+
# show
|
|
651
|
+
show_parser = subparsers.add_parser("show", help="Show session or blueprint details")
|
|
652
|
+
show_parser.add_argument("id", help="Session or blueprint ID")
|
|
653
|
+
|
|
654
|
+
# delete
|
|
655
|
+
delete_parser = subparsers.add_parser("delete", help="Delete a session")
|
|
656
|
+
delete_parser.add_argument("session_id", help="Session ID")
|
|
657
|
+
delete_parser.add_argument("--force", "-f", action="store_true", help="Skip confirmation")
|
|
658
|
+
|
|
659
|
+
# export
|
|
660
|
+
export_parser = subparsers.add_parser("export", help="Export blueprint to JSON")
|
|
661
|
+
export_parser.add_argument("blueprint_id", help="Blueprint ID")
|
|
662
|
+
export_parser.add_argument("--output", "-o", help="Output file (default: stdout)")
|
|
663
|
+
|
|
664
|
+
return parser
|
|
665
|
+
|
|
666
|
+
|
|
667
|
+
def main(argv: list[str] | None = None) -> int:
|
|
668
|
+
"""Main entry point."""
|
|
669
|
+
parser = create_parser()
|
|
670
|
+
args = parser.parse_args(argv)
|
|
671
|
+
|
|
672
|
+
if not args.command:
|
|
673
|
+
parser.print_help()
|
|
674
|
+
return 0
|
|
675
|
+
|
|
676
|
+
commands = {
|
|
677
|
+
"start": cmd_start,
|
|
678
|
+
"resume": cmd_resume,
|
|
679
|
+
"list": cmd_list,
|
|
680
|
+
"blueprints": cmd_blueprints,
|
|
681
|
+
"show": cmd_show,
|
|
682
|
+
"delete": cmd_delete,
|
|
683
|
+
"export": cmd_export,
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
cmd_func = commands.get(args.command)
|
|
687
|
+
if cmd_func:
|
|
688
|
+
try:
|
|
689
|
+
return cmd_func(args)
|
|
690
|
+
except KeyboardInterrupt:
|
|
691
|
+
print()
|
|
692
|
+
console.info("Interrupted")
|
|
693
|
+
return 130
|
|
694
|
+
except Exception as e:
|
|
695
|
+
console.error(f"Error: {e}")
|
|
696
|
+
return 1
|
|
697
|
+
|
|
698
|
+
parser.print_help()
|
|
699
|
+
return 0
|
|
700
|
+
|
|
701
|
+
|
|
702
|
+
if __name__ == "__main__":
|
|
703
|
+
sys.exit(main())
|