devsper 2.1.6__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.
- devsper/__init__.py +14 -0
- devsper/agents/a2a/__init__.py +27 -0
- devsper/agents/a2a/client.py +126 -0
- devsper/agents/a2a/discovery.py +24 -0
- devsper/agents/a2a/server.py +128 -0
- devsper/agents/a2a/tool_adapter.py +68 -0
- devsper/agents/a2a/types.py +49 -0
- devsper/agents/agent.py +602 -0
- devsper/agents/critic.py +80 -0
- devsper/agents/message_bus.py +124 -0
- devsper/agents/roles.py +181 -0
- devsper/agents/run_agent.py +78 -0
- devsper/analytics/__init__.py +5 -0
- devsper/analytics/tool_analytics.py +78 -0
- devsper/audit/__init__.py +5 -0
- devsper/audit/logger.py +214 -0
- devsper/bus/__init__.py +29 -0
- devsper/bus/backends/__init__.py +5 -0
- devsper/bus/backends/base.py +38 -0
- devsper/bus/backends/memory.py +55 -0
- devsper/bus/backends/redis.py +146 -0
- devsper/bus/message.py +56 -0
- devsper/bus/schema_version.py +3 -0
- devsper/bus/topics.py +19 -0
- devsper/cache/__init__.py +6 -0
- devsper/cache/embedding_index.py +98 -0
- devsper/cache/hashing.py +24 -0
- devsper/cache/store.py +153 -0
- devsper/cache/task_cache.py +191 -0
- devsper/cli/__init__.py +6 -0
- devsper/cli/commands/reg.py +733 -0
- devsper/cli/github_oauth.py +157 -0
- devsper/cli/init.py +637 -0
- devsper/cli/main.py +2956 -0
- devsper/cli/run_progress.py +103 -0
- devsper/cli/ui/__init__.py +65 -0
- devsper/cli/ui/components.py +94 -0
- devsper/cli/ui/errors.py +104 -0
- devsper/cli/ui/logging.py +120 -0
- devsper/cli/ui/onboarding.py +102 -0
- devsper/cli/ui/progress.py +43 -0
- devsper/cli/ui/run_view.py +308 -0
- devsper/cli/ui/theme.py +40 -0
- devsper/cluster/__init__.py +29 -0
- devsper/cluster/election.py +84 -0
- devsper/cluster/local.py +97 -0
- devsper/cluster/node_info.py +77 -0
- devsper/cluster/registry.py +71 -0
- devsper/cluster/router.py +117 -0
- devsper/cluster/state_backend.py +105 -0
- devsper/compliance/__init__.py +5 -0
- devsper/compliance/pii.py +147 -0
- devsper/config/__init__.py +52 -0
- devsper/config/config_loader.py +121 -0
- devsper/config/defaults.py +77 -0
- devsper/config/resolver.py +342 -0
- devsper/config/schema.py +237 -0
- devsper/credentials/__init__.py +19 -0
- devsper/credentials/cli.py +197 -0
- devsper/credentials/migration.py +124 -0
- devsper/credentials/store.py +142 -0
- devsper/dashboard/__init__.py +9 -0
- devsper/dashboard/dashboard.py +87 -0
- devsper/dev/__init__.py +25 -0
- devsper/dev/builder.py +195 -0
- devsper/dev/debugger.py +95 -0
- devsper/dev/repo_index.py +138 -0
- devsper/dev/sandbox.py +203 -0
- devsper/dev/scaffold.py +122 -0
- devsper/embeddings/__init__.py +5 -0
- devsper/embeddings/service.py +36 -0
- devsper/explainability/__init__.py +14 -0
- devsper/explainability/decision_tree.py +104 -0
- devsper/explainability/rationale.py +38 -0
- devsper/explainability/simulation.py +56 -0
- devsper/hitl/__init__.py +13 -0
- devsper/hitl/approval.py +160 -0
- devsper/hitl/escalation.py +95 -0
- devsper/intelligence/__init__.py +9 -0
- devsper/intelligence/adaptation.py +88 -0
- devsper/intelligence/analysis/__init__.py +19 -0
- devsper/intelligence/analysis/analyzer.py +71 -0
- devsper/intelligence/analysis/cost_estimator.py +66 -0
- devsper/intelligence/analysis/formatter.py +103 -0
- devsper/intelligence/analysis/run_report.py +402 -0
- devsper/intelligence/learning_engine.py +92 -0
- devsper/intelligence/strategies/__init__.py +23 -0
- devsper/intelligence/strategies/base.py +14 -0
- devsper/intelligence/strategies/code_analysis_strategy.py +33 -0
- devsper/intelligence/strategies/data_science_strategy.py +33 -0
- devsper/intelligence/strategies/document_pipeline_strategy.py +33 -0
- devsper/intelligence/strategies/experiment_strategy.py +33 -0
- devsper/intelligence/strategies/research_strategy.py +34 -0
- devsper/intelligence/strategy_selector.py +84 -0
- devsper/intelligence/synthesis.py +132 -0
- devsper/intelligence/task_optimizer.py +92 -0
- devsper/knowledge/__init__.py +5 -0
- devsper/knowledge/extractor.py +204 -0
- devsper/knowledge/knowledge_graph.py +184 -0
- devsper/knowledge/query.py +285 -0
- devsper/memory/__init__.py +35 -0
- devsper/memory/consolidation.py +138 -0
- devsper/memory/embeddings.py +60 -0
- devsper/memory/memory_index.py +97 -0
- devsper/memory/memory_router.py +62 -0
- devsper/memory/memory_store.py +221 -0
- devsper/memory/memory_types.py +54 -0
- devsper/memory/namespaces.py +45 -0
- devsper/memory/scoring.py +77 -0
- devsper/memory/summarizer.py +52 -0
- devsper/nodes/__init__.py +5 -0
- devsper/nodes/controller.py +449 -0
- devsper/nodes/rpc.py +127 -0
- devsper/nodes/single.py +161 -0
- devsper/nodes/worker.py +506 -0
- devsper/orchestration/__init__.py +19 -0
- devsper/orchestration/meta_planner.py +239 -0
- devsper/orchestration/priority_queue.py +61 -0
- devsper/plugins/__init__.py +19 -0
- devsper/plugins/marketplace/__init__.py +0 -0
- devsper/plugins/plugin_loader.py +70 -0
- devsper/plugins/plugin_registry.py +34 -0
- devsper/plugins/registry.py +83 -0
- devsper/protocols/__init__.py +6 -0
- devsper/providers/__init__.py +17 -0
- devsper/providers/anthropic.py +84 -0
- devsper/providers/base.py +75 -0
- devsper/providers/complexity_router.py +94 -0
- devsper/providers/gemini.py +36 -0
- devsper/providers/github.py +180 -0
- devsper/providers/model_router.py +40 -0
- devsper/providers/openai.py +105 -0
- devsper/providers/router/__init__.py +21 -0
- devsper/providers/router/backends/__init__.py +19 -0
- devsper/providers/router/backends/anthropic_backend.py +111 -0
- devsper/providers/router/backends/custom_backend.py +138 -0
- devsper/providers/router/backends/gemini_backend.py +89 -0
- devsper/providers/router/backends/github_backend.py +165 -0
- devsper/providers/router/backends/ollama_backend.py +104 -0
- devsper/providers/router/backends/openai_backend.py +142 -0
- devsper/providers/router/backends/vllm_backend.py +35 -0
- devsper/providers/router/base.py +60 -0
- devsper/providers/router/factory.py +92 -0
- devsper/providers/router/legacy.py +101 -0
- devsper/providers/router/router.py +135 -0
- devsper/reasoning/__init__.py +12 -0
- devsper/reasoning/graph.py +59 -0
- devsper/reasoning/nodes.py +20 -0
- devsper/reasoning/store.py +67 -0
- devsper/runtime/__init__.py +12 -0
- devsper/runtime/health.py +88 -0
- devsper/runtime/replay.py +53 -0
- devsper/runtime/replay_engine.py +142 -0
- devsper/runtime/run_history.py +204 -0
- devsper/runtime/telemetry.py +116 -0
- devsper/runtime/visualize.py +58 -0
- devsper/sandbox/__init__.py +13 -0
- devsper/sandbox/sandbox.py +161 -0
- devsper/swarm/checkpointer.py +65 -0
- devsper/swarm/executor.py +558 -0
- devsper/swarm/map_reduce.py +44 -0
- devsper/swarm/planner.py +197 -0
- devsper/swarm/prefetcher.py +91 -0
- devsper/swarm/scheduler.py +153 -0
- devsper/swarm/speculation.py +47 -0
- devsper/swarm/swarm.py +562 -0
- devsper/tools/__init__.py +33 -0
- devsper/tools/base.py +29 -0
- devsper/tools/code_intelligence/__init__.py +13 -0
- devsper/tools/code_intelligence/api_surface_extractor.py +73 -0
- devsper/tools/code_intelligence/architecture_analyzer.py +65 -0
- devsper/tools/code_intelligence/codebase_indexer.py +71 -0
- devsper/tools/code_intelligence/dependency_graph_builder.py +67 -0
- devsper/tools/code_intelligence/design_pattern_detector.py +62 -0
- devsper/tools/code_intelligence/large_function_detector.py +68 -0
- devsper/tools/code_intelligence/module_responsibility_mapper.py +56 -0
- devsper/tools/code_intelligence/parallel_codebase_analysis.py +44 -0
- devsper/tools/code_intelligence/refactor_candidate_detector.py +81 -0
- devsper/tools/code_intelligence/repository_semantic_index.py +61 -0
- devsper/tools/code_intelligence/test_coverage_estimator.py +62 -0
- devsper/tools/coding/__init__.py +12 -0
- devsper/tools/coding/analyze_code_complexity.py +48 -0
- devsper/tools/coding/dependency_analyzer.py +42 -0
- devsper/tools/coding/extract_functions.py +38 -0
- devsper/tools/coding/format_python.py +50 -0
- devsper/tools/coding/generate_docstrings.py +40 -0
- devsper/tools/coding/generate_unit_tests.py +42 -0
- devsper/tools/coding/lint_python.py +51 -0
- devsper/tools/coding/refactor_function.py +41 -0
- devsper/tools/coding/repo_structure_map.py +54 -0
- devsper/tools/coding/run_python.py +53 -0
- devsper/tools/data/__init__.py +12 -0
- devsper/tools/data/column_type_detection.py +64 -0
- devsper/tools/data/csv_summary.py +52 -0
- devsper/tools/data/dataframe_filter.py +51 -0
- devsper/tools/data/dataframe_groupby.py +47 -0
- devsper/tools/data/dataframe_stats.py +38 -0
- devsper/tools/data/dataset_sampling.py +55 -0
- devsper/tools/data/dataset_schema.py +45 -0
- devsper/tools/data/json_pretty_print.py +37 -0
- devsper/tools/data/json_query.py +46 -0
- devsper/tools/data/missing_value_report.py +47 -0
- devsper/tools/data_science/__init__.py +13 -0
- devsper/tools/data_science/correlation_heatmap.py +72 -0
- devsper/tools/data_science/dataset_bias_detector.py +49 -0
- devsper/tools/data_science/dataset_distribution_report.py +64 -0
- devsper/tools/data_science/dataset_drift_detector.py +64 -0
- devsper/tools/data_science/dataset_outlier_detector.py +65 -0
- devsper/tools/data_science/dataset_profile.py +76 -0
- devsper/tools/data_science/distributed_dataset_processor.py +54 -0
- devsper/tools/data_science/feature_engineering_suggestions.py +69 -0
- devsper/tools/data_science/feature_importance_estimator.py +82 -0
- devsper/tools/data_science/model_input_validator.py +59 -0
- devsper/tools/data_science/time_series_analyzer.py +57 -0
- devsper/tools/documents/__init__.py +11 -0
- devsper/tools/documents/_docproc.py +56 -0
- devsper/tools/documents/document_to_markdown.py +29 -0
- devsper/tools/documents/extract_document_images.py +39 -0
- devsper/tools/documents/extract_document_text.py +29 -0
- devsper/tools/documents/extract_equations.py +36 -0
- devsper/tools/documents/extract_tables.py +47 -0
- devsper/tools/documents/summarize_document.py +42 -0
- devsper/tools/documents/write_latex_document.py +133 -0
- devsper/tools/documents/write_markdown_document.py +89 -0
- devsper/tools/documents/write_word_document.py +149 -0
- devsper/tools/experiments/__init__.py +13 -0
- devsper/tools/experiments/bootstrap_estimator.py +54 -0
- devsper/tools/experiments/experiment_report_generator.py +50 -0
- devsper/tools/experiments/experiment_tracker.py +36 -0
- devsper/tools/experiments/grid_search_runner.py +50 -0
- devsper/tools/experiments/model_benchmark_runner.py +45 -0
- devsper/tools/experiments/monte_carlo_experiment.py +38 -0
- devsper/tools/experiments/parameter_sweep_runner.py +51 -0
- devsper/tools/experiments/result_comparator.py +58 -0
- devsper/tools/experiments/simulation_runner.py +43 -0
- devsper/tools/experiments/statistical_significance_test.py +56 -0
- devsper/tools/experiments/swarm_map_reduce.py +42 -0
- devsper/tools/filesystem/__init__.py +12 -0
- devsper/tools/filesystem/append_file.py +42 -0
- devsper/tools/filesystem/file_hash.py +40 -0
- devsper/tools/filesystem/file_line_count.py +36 -0
- devsper/tools/filesystem/file_metadata.py +38 -0
- devsper/tools/filesystem/file_preview.py +55 -0
- devsper/tools/filesystem/find_large_files.py +50 -0
- devsper/tools/filesystem/list_directory.py +39 -0
- devsper/tools/filesystem/read_file.py +35 -0
- devsper/tools/filesystem/search_files.py +60 -0
- devsper/tools/filesystem/write_file.py +41 -0
- devsper/tools/flagship/__init__.py +15 -0
- devsper/tools/flagship/distributed_document_analysis.py +77 -0
- devsper/tools/flagship/docproc_corpus_pipeline.py +91 -0
- devsper/tools/flagship/repository_semantic_map.py +99 -0
- devsper/tools/flagship/research_graph_builder.py +111 -0
- devsper/tools/flagship/swarm_experiment_runner.py +86 -0
- devsper/tools/knowledge/__init__.py +10 -0
- devsper/tools/knowledge/citation_graph_builder.py +69 -0
- devsper/tools/knowledge/concept_frequency_analyzer.py +74 -0
- devsper/tools/knowledge/corpus_builder.py +66 -0
- devsper/tools/knowledge/cross_document_entity_linker.py +71 -0
- devsper/tools/knowledge/document_corpus_summary.py +68 -0
- devsper/tools/knowledge/document_topic_extractor.py +58 -0
- devsper/tools/knowledge/knowledge_graph_extractor.py +58 -0
- devsper/tools/knowledge/timeline_extractor.py +59 -0
- devsper/tools/math/__init__.py +12 -0
- devsper/tools/math/calculate_expression.py +52 -0
- devsper/tools/math/correlation.py +44 -0
- devsper/tools/math/distribution_summary.py +39 -0
- devsper/tools/math/histogram.py +53 -0
- devsper/tools/math/linear_regression.py +47 -0
- devsper/tools/math/matrix_multiply.py +38 -0
- devsper/tools/math/mean_std.py +35 -0
- devsper/tools/math/monte_carlo_simulation.py +43 -0
- devsper/tools/math/polynomial_fit.py +40 -0
- devsper/tools/math/random_sample.py +36 -0
- devsper/tools/mcp/__init__.py +23 -0
- devsper/tools/mcp/adapter.py +53 -0
- devsper/tools/mcp/client.py +235 -0
- devsper/tools/mcp/discovery.py +53 -0
- devsper/tools/memory/__init__.py +16 -0
- devsper/tools/memory/delete_memory.py +25 -0
- devsper/tools/memory/list_memory.py +34 -0
- devsper/tools/memory/search_memory.py +36 -0
- devsper/tools/memory/store_memory.py +47 -0
- devsper/tools/memory/summarize_memory.py +41 -0
- devsper/tools/memory/tag_memory.py +47 -0
- devsper/tools/pipelines.py +92 -0
- devsper/tools/registry.py +39 -0
- devsper/tools/research/__init__.py +12 -0
- devsper/tools/research/arxiv_download.py +55 -0
- devsper/tools/research/arxiv_search.py +58 -0
- devsper/tools/research/citation_extractor.py +35 -0
- devsper/tools/research/duckduckgo_search.py +42 -0
- devsper/tools/research/paper_metadata_extractor.py +45 -0
- devsper/tools/research/paper_summarizer.py +41 -0
- devsper/tools/research/research_question_generator.py +39 -0
- devsper/tools/research/topic_cluster.py +46 -0
- devsper/tools/research/web_search.py +47 -0
- devsper/tools/research/wikipedia_lookup.py +50 -0
- devsper/tools/research_advanced/__init__.py +14 -0
- devsper/tools/research_advanced/citation_context_extractor.py +60 -0
- devsper/tools/research_advanced/literature_review_generator.py +79 -0
- devsper/tools/research_advanced/methodology_extractor.py +58 -0
- devsper/tools/research_advanced/paper_contribution_extractor.py +50 -0
- devsper/tools/research_advanced/paper_dataset_identifier.py +49 -0
- devsper/tools/research_advanced/paper_method_comparator.py +62 -0
- devsper/tools/research_advanced/paper_similarity_search.py +69 -0
- devsper/tools/research_advanced/paper_trend_analyzer.py +69 -0
- devsper/tools/research_advanced/parallel_document_analyzer.py +56 -0
- devsper/tools/research_advanced/research_gap_finder.py +71 -0
- devsper/tools/research_advanced/research_topic_mapper.py +69 -0
- devsper/tools/research_advanced/swarm_literature_review.py +58 -0
- devsper/tools/scoring/__init__.py +52 -0
- devsper/tools/scoring/report.py +44 -0
- devsper/tools/scoring/scorer.py +39 -0
- devsper/tools/scoring/selector.py +61 -0
- devsper/tools/scoring/store.py +267 -0
- devsper/tools/selector.py +130 -0
- devsper/tools/system/__init__.py +12 -0
- devsper/tools/system/cpu_usage.py +22 -0
- devsper/tools/system/disk_usage.py +35 -0
- devsper/tools/system/environment_variables.py +29 -0
- devsper/tools/system/memory_usage.py +23 -0
- devsper/tools/system/pip_install.py +44 -0
- devsper/tools/system/pip_search.py +29 -0
- devsper/tools/system/process_list.py +34 -0
- devsper/tools/system/python_package_list.py +40 -0
- devsper/tools/system/run_shell_command.py +51 -0
- devsper/tools/system/system_info.py +26 -0
- devsper/tools/tool_runner.py +122 -0
- devsper/tui/__init__.py +5 -0
- devsper/tui/activity_feed_view.py +73 -0
- devsper/tui/adaptive_tasks_view.py +75 -0
- devsper/tui/agent_role_view.py +35 -0
- devsper/tui/app.py +395 -0
- devsper/tui/dashboard_screen.py +290 -0
- devsper/tui/dev_view.py +99 -0
- devsper/tui/inject_screen.py +73 -0
- devsper/tui/knowledge_graph_view.py +46 -0
- devsper/tui/layout.py +43 -0
- devsper/tui/logs_view.py +83 -0
- devsper/tui/memory_view.py +58 -0
- devsper/tui/performance_view.py +33 -0
- devsper/tui/reasoning_graph_view.py +39 -0
- devsper/tui/results_view.py +139 -0
- devsper/tui/swarm_view.py +37 -0
- devsper/tui/task_detail_screen.py +55 -0
- devsper/tui/task_view.py +103 -0
- devsper/types/event.py +97 -0
- devsper/types/exceptions.py +21 -0
- devsper/types/swarm.py +41 -0
- devsper/types/task.py +80 -0
- devsper/upgrade/__init__.py +21 -0
- devsper/upgrade/changelog.py +124 -0
- devsper/upgrade/cli.py +145 -0
- devsper/upgrade/installer.py +103 -0
- devsper/upgrade/notifier.py +52 -0
- devsper/upgrade/version_check.py +121 -0
- devsper/utils/event_logger.py +88 -0
- devsper/utils/http.py +43 -0
- devsper/utils/models.py +54 -0
- devsper/visualization/__init__.py +5 -0
- devsper/visualization/dag_export.py +67 -0
- devsper/workflow/__init__.py +18 -0
- devsper/workflow/conditions.py +157 -0
- devsper/workflow/context.py +108 -0
- devsper/workflow/loader.py +156 -0
- devsper/workflow/resolver.py +109 -0
- devsper/workflow/runner.py +562 -0
- devsper/workflow/schema.py +63 -0
- devsper/workflow/validator.py +128 -0
- devsper-2.1.6.dist-info/METADATA +346 -0
- devsper-2.1.6.dist-info/RECORD +375 -0
- devsper-2.1.6.dist-info/WHEEL +4 -0
- devsper-2.1.6.dist-info/entry_points.txt +3 -0
- devsper-2.1.6.dist-info/licenses/LICENSE +639 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI run progress: read event log and return status line + parallel task info for live feedback.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def read_run_status(
|
|
9
|
+
log_path: str | None,
|
|
10
|
+
worker_count: int = 0,
|
|
11
|
+
) -> tuple[str, list[str]]:
|
|
12
|
+
"""
|
|
13
|
+
Read event log and return (status_message, running_task_ids).
|
|
14
|
+
running_task_ids are task ids that have task_started but not yet task_completed.
|
|
15
|
+
worker_count: if > 0, show "up to N in parallel" in executor messages.
|
|
16
|
+
"""
|
|
17
|
+
if not log_path or not os.path.isfile(log_path):
|
|
18
|
+
return "Starting…", []
|
|
19
|
+
|
|
20
|
+
events = []
|
|
21
|
+
try:
|
|
22
|
+
from devsper.types.event import Event
|
|
23
|
+
|
|
24
|
+
with open(log_path, "r") as f:
|
|
25
|
+
for line in f:
|
|
26
|
+
line = line.strip()
|
|
27
|
+
if not line:
|
|
28
|
+
continue
|
|
29
|
+
try:
|
|
30
|
+
events.append(Event.model_validate_json(line))
|
|
31
|
+
except Exception:
|
|
32
|
+
continue
|
|
33
|
+
except Exception:
|
|
34
|
+
return "Running…", []
|
|
35
|
+
|
|
36
|
+
if not events:
|
|
37
|
+
return "Starting…", []
|
|
38
|
+
|
|
39
|
+
# task_id -> description
|
|
40
|
+
task_descriptions: dict[str, str] = {}
|
|
41
|
+
# track which tasks have started and which have completed
|
|
42
|
+
started: set[str] = set()
|
|
43
|
+
completed: set[str] = set()
|
|
44
|
+
|
|
45
|
+
for e in events:
|
|
46
|
+
payload = e.payload or {}
|
|
47
|
+
tid = (payload.get("task_id") or "").strip()
|
|
48
|
+
ev = getattr(e.type, "value", str(e.type))
|
|
49
|
+
|
|
50
|
+
if ev == "task_created" and tid:
|
|
51
|
+
task_descriptions[tid] = (payload.get("description") or "").strip()
|
|
52
|
+
elif ev in ("task_started", "agent_started") and tid:
|
|
53
|
+
started.add(tid)
|
|
54
|
+
elif ev in ("task_completed", "task_failed", "agent_finished") and tid:
|
|
55
|
+
completed.add(tid)
|
|
56
|
+
|
|
57
|
+
running_ids = sorted(started - completed)
|
|
58
|
+
total_tasks = len(task_descriptions)
|
|
59
|
+
completed_count = len(completed)
|
|
60
|
+
|
|
61
|
+
def _truncate(s: str, max_len: int = 52) -> str:
|
|
62
|
+
s = (s or "").strip()
|
|
63
|
+
if len(s) <= max_len:
|
|
64
|
+
return s
|
|
65
|
+
return s[: max_len - 1].rstrip() + "…"
|
|
66
|
+
|
|
67
|
+
last = events[-1]
|
|
68
|
+
ev_type = getattr(last.type, "value", str(last.type))
|
|
69
|
+
payload = last.payload or {}
|
|
70
|
+
task_id = (payload.get("task_id") or "").strip()
|
|
71
|
+
desc = task_descriptions.get(task_id)
|
|
72
|
+
|
|
73
|
+
if ev_type == "swarm_started":
|
|
74
|
+
return "Planning your request…", []
|
|
75
|
+
if ev_type == "planner_started":
|
|
76
|
+
return "Planning your request…", []
|
|
77
|
+
if ev_type == "task_created":
|
|
78
|
+
return f"Planned {total_tasks} task(s). Starting…", []
|
|
79
|
+
if ev_type == "planner_finished":
|
|
80
|
+
n = payload.get("subtask_count", total_tasks) or total_tasks
|
|
81
|
+
tail = f", up to {worker_count} in parallel" if worker_count > 1 else ""
|
|
82
|
+
return f"Executing {n} step(s){tail}…", []
|
|
83
|
+
if ev_type == "executor_started":
|
|
84
|
+
if total_tasks:
|
|
85
|
+
tail = f" (up to {worker_count} in parallel)" if worker_count > 1 else ""
|
|
86
|
+
return f"Executing step 1 of {total_tasks}…{tail}", []
|
|
87
|
+
return "Executing…", []
|
|
88
|
+
if ev_type in ("agent_started", "task_started"):
|
|
89
|
+
step_label = f"Step {completed_count + 1} of {total_tasks}: " if total_tasks else ""
|
|
90
|
+
part = (step_label + _truncate(desc)) if desc else (step_label or "Working on task…")
|
|
91
|
+
if len(running_ids) > 1:
|
|
92
|
+
part += f" [parallel: {len(running_ids)} tasks]"
|
|
93
|
+
return part.rstrip(": ") or "Working…", running_ids
|
|
94
|
+
if ev_type in ("task_completed", "agent_finished"):
|
|
95
|
+
if total_tasks and completed_count < total_tasks:
|
|
96
|
+
return f"Finished step {completed_count} of {total_tasks}. Next…", running_ids
|
|
97
|
+
return f"Finished step {completed_count} of {total_tasks}. Assembling…", running_ids
|
|
98
|
+
if ev_type == "executor_finished":
|
|
99
|
+
return "Assembling result…", []
|
|
100
|
+
if ev_type == "swarm_finished":
|
|
101
|
+
return "Done.", []
|
|
102
|
+
|
|
103
|
+
return "Running…", running_ids
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI UI: theme, components, logging, progress, errors.
|
|
3
|
+
All CLI code imports from devsper.cli.ui, never from rich directly.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from devsper.cli.ui.theme import THEME, console, err_console, reconfigure_console
|
|
7
|
+
from devsper.cli.ui.components import (
|
|
8
|
+
CostDisplay,
|
|
9
|
+
ErrorPanel,
|
|
10
|
+
devsperHeader,
|
|
11
|
+
RoleTag,
|
|
12
|
+
SectionHeader,
|
|
13
|
+
StatusBadge,
|
|
14
|
+
TaskRow,
|
|
15
|
+
)
|
|
16
|
+
from devsper.cli.ui.errors import (
|
|
17
|
+
ConfigNotFoundError,
|
|
18
|
+
devsperError,
|
|
19
|
+
ModelNotFoundError,
|
|
20
|
+
NoWorkersError,
|
|
21
|
+
ProviderConnectionError,
|
|
22
|
+
RedisConnectionError,
|
|
23
|
+
print_error,
|
|
24
|
+
print_unexpected_error,
|
|
25
|
+
)
|
|
26
|
+
from devsper.cli.ui.logging import get_logger, set_log_level, get_log_level, devsperLogger
|
|
27
|
+
from devsper.cli.ui.progress import devsperProgress, progress_spinner_style
|
|
28
|
+
from devsper.cli.ui.run_view import RunViewState, run_live_view, print_run_summary
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
from devsper.cli.ui.onboarding import run_init_wizard
|
|
32
|
+
except ImportError:
|
|
33
|
+
run_init_wizard = None # type: ignore[misc, assignment]
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
"CostDisplay",
|
|
37
|
+
"ConfigNotFoundError",
|
|
38
|
+
"ErrorPanel",
|
|
39
|
+
"devsperError",
|
|
40
|
+
"devsperHeader",
|
|
41
|
+
"devsperLogger",
|
|
42
|
+
"devsperProgress",
|
|
43
|
+
"ModelNotFoundError",
|
|
44
|
+
"NoWorkersError",
|
|
45
|
+
"ProviderConnectionError",
|
|
46
|
+
"RedisConnectionError",
|
|
47
|
+
"RoleTag",
|
|
48
|
+
"SectionHeader",
|
|
49
|
+
"StatusBadge",
|
|
50
|
+
"TaskRow",
|
|
51
|
+
"THEME",
|
|
52
|
+
"console",
|
|
53
|
+
"err_console",
|
|
54
|
+
"get_logger",
|
|
55
|
+
"get_log_level",
|
|
56
|
+
"print_error",
|
|
57
|
+
"print_unexpected_error",
|
|
58
|
+
"print_run_summary",
|
|
59
|
+
"progress_spinner_style",
|
|
60
|
+
"reconfigure_console",
|
|
61
|
+
"run_live_view",
|
|
62
|
+
"RunViewState",
|
|
63
|
+
"run_init_wizard",
|
|
64
|
+
"set_log_level",
|
|
65
|
+
]
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Reusable Rich renderables for CLI. All CLI code imports from here, not rich directly.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from rich.panel import Panel
|
|
6
|
+
from rich.rule import Rule
|
|
7
|
+
from rich.table import Table
|
|
8
|
+
from rich.text import Text
|
|
9
|
+
|
|
10
|
+
from devsper.cli.ui.theme import console
|
|
11
|
+
|
|
12
|
+
# Status badge styles
|
|
13
|
+
BADGE_STYLES = {
|
|
14
|
+
"SUCCESS": "hive.success",
|
|
15
|
+
"FAILED": "hive.error",
|
|
16
|
+
"RUNNING": "hive.secondary",
|
|
17
|
+
"SKIPPED": "hive.muted",
|
|
18
|
+
"CACHED": "hive.primary",
|
|
19
|
+
"PENDING": "hive.dim",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def devsperHeader(
|
|
24
|
+
version: str = "",
|
|
25
|
+
model: str = "",
|
|
26
|
+
workers: int = 0,
|
|
27
|
+
) -> Text:
|
|
28
|
+
"""Single-line wordmark + version. Example: ⬡ devsper v2.1.5 · claude-sonnet-4 · 4 workers"""
|
|
29
|
+
parts = [Text("⬡ devsper", style="hive.primary")]
|
|
30
|
+
if version:
|
|
31
|
+
parts.append(Text(f" v{version}", style="hive.muted"))
|
|
32
|
+
if model:
|
|
33
|
+
parts.append(Text(f" · {model}", style="hive.muted"))
|
|
34
|
+
if workers:
|
|
35
|
+
parts.append(Text(f" · {workers} workers", style="hive.muted"))
|
|
36
|
+
out = Text()
|
|
37
|
+
for p in parts:
|
|
38
|
+
out.append_text(p)
|
|
39
|
+
return out
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def StatusBadge(text: str, style: str | None = None) -> Text:
|
|
43
|
+
"""Inline colored badge: [SUCCESS], [FAILED], [RUNNING], etc."""
|
|
44
|
+
key = text.upper().replace(" ", "_")
|
|
45
|
+
s = style or BADGE_STYLES.get(key, "hive.muted")
|
|
46
|
+
return Text(f"[{text}]", style=s)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def TaskRow(
|
|
50
|
+
short_id: str,
|
|
51
|
+
description: str,
|
|
52
|
+
role: str = "",
|
|
53
|
+
duration: str = "—",
|
|
54
|
+
status: str = "pending",
|
|
55
|
+
icon: str | None = None,
|
|
56
|
+
) -> Text:
|
|
57
|
+
"""Single-line task summary. status: pending|running|completed|failed|cached|skipped."""
|
|
58
|
+
if icon is None:
|
|
59
|
+
icon = {"pending": "○", "running": "◐", "completed": "✓", "failed": "✗", "cached": "⚡", "skipped": "⊘"}.get(status, "○")
|
|
60
|
+
style = {"completed": "hive.success", "failed": "hive.error", "cached": "hive.primary", "skipped": "hive.muted", "running": "hive.secondary", "pending": "hive.dim"}.get(status, "hive.dim")
|
|
61
|
+
desc = (description or "")[:45]
|
|
62
|
+
if len((description or "")) > 45:
|
|
63
|
+
desc = desc.rstrip() + "…"
|
|
64
|
+
role_part = f" {role}" if role else ""
|
|
65
|
+
return Text().append(icon + " ", style=style).append(short_id + " ", style="hive.secondary").append(desc, style="dim").append(role_part, style="hive.muted").append(" ", style="").append(duration, style="hive.muted")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def RoleTag(role: str) -> Text:
|
|
69
|
+
"""Colored inline tag per role: research->blue, code->teal, analysis->purple, critic->orange, architect->amber."""
|
|
70
|
+
styles = {"research": "hive.secondary", "code": "hive.tool", "analysis": "hive.agent", "critic": "hive.planner", "architect": "hive.primary"}
|
|
71
|
+
s = styles.get((role or "").lower(), "hive.muted")
|
|
72
|
+
return Text(role or "—", style=s)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def CostDisplay(usd: float | None) -> Text:
|
|
76
|
+
"""$0.0034 in pink, '—' in muted if None."""
|
|
77
|
+
if usd is None:
|
|
78
|
+
return Text("—", style="hive.muted")
|
|
79
|
+
return Text(f"${usd:.4f}", style="hive.cost")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def SectionHeader(title: str) -> Rule:
|
|
83
|
+
"""Amber rule with title."""
|
|
84
|
+
return Rule(title, style="hive.primary", characters="─")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def ErrorPanel(message: str, hint: str | None = None, docs_url: str | None = None) -> Panel:
|
|
88
|
+
"""Red-bordered panel, title 'Error'. No tracebacks to end users."""
|
|
89
|
+
body = message
|
|
90
|
+
if hint:
|
|
91
|
+
body += "\n\n" + f"Hint: {hint}"
|
|
92
|
+
if docs_url:
|
|
93
|
+
body += "\n" + f"Docs: {docs_url}"
|
|
94
|
+
return Panel(body, title="Error", border_style="hive.error")
|
devsper/cli/ui/errors.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Typed errors with actionable hints. No raw tracebacks shown to end users.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from devsper.cli.ui.theme import err_console
|
|
6
|
+
from devsper.cli.ui.components import ErrorPanel
|
|
7
|
+
|
|
8
|
+
DOCS_BASE = "https://docs.devsper.com"
|
|
9
|
+
REPO_ISSUES = "https://github.com/devsper-com/runtime/issues/new"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class devsperError(Exception):
|
|
13
|
+
"""Base CLI error with message, hint, docs, exit code."""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
message: str,
|
|
18
|
+
hint: str | None = None,
|
|
19
|
+
docs_url: str | None = None,
|
|
20
|
+
exit_code: int = 1,
|
|
21
|
+
) -> None:
|
|
22
|
+
super().__init__(message)
|
|
23
|
+
self.message = message
|
|
24
|
+
self.hint = hint
|
|
25
|
+
self.docs_url = docs_url
|
|
26
|
+
self.exit_code = exit_code
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ProviderConnectionError(devsperError):
|
|
30
|
+
def __init__(self, message: str, provider: str = "") -> None:
|
|
31
|
+
super().__init__(
|
|
32
|
+
message,
|
|
33
|
+
hint="Check your API key with: devsper credentials list",
|
|
34
|
+
docs_url=f"{DOCS_BASE}/providers" if provider else None,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ConfigNotFoundError(devsperError):
|
|
39
|
+
def __init__(self, message: str = "No configuration found.") -> None:
|
|
40
|
+
super().__init__(message, hint="Run devsper init to create a configuration")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class RedisConnectionError(devsperError):
|
|
44
|
+
def __init__(self, message: str = "Cannot connect to Redis.") -> None:
|
|
45
|
+
super().__init__(
|
|
46
|
+
message,
|
|
47
|
+
hint="Start Redis with: docker run -p 6379:6379 redis:7-alpine",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class NoWorkersError(devsperError):
|
|
52
|
+
def __init__(self, message: str = "No workers available.") -> None:
|
|
53
|
+
super().__init__(message, hint="Start a worker with: devsper node start --role worker")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class ModelNotFoundError(devsperError):
|
|
57
|
+
def __init__(self, model_name: str) -> None:
|
|
58
|
+
super().__init__(
|
|
59
|
+
f"Model not found: {model_name}",
|
|
60
|
+
hint="Available models: devsper doctor | grep model",
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def print_error(e: devsperError, json_output: bool = False) -> None:
|
|
65
|
+
"""Print error to stderr. If json_output, print {\"error\", \"hint\", \"exit_code\"}."""
|
|
66
|
+
if json_output:
|
|
67
|
+
import json
|
|
68
|
+
err_console.print(json.dumps({
|
|
69
|
+
"error": e.message,
|
|
70
|
+
"hint": e.hint,
|
|
71
|
+
"docs_url": getattr(e, "docs_url", None),
|
|
72
|
+
"exit_code": e.exit_code,
|
|
73
|
+
}))
|
|
74
|
+
return
|
|
75
|
+
panel = ErrorPanel(e.message, hint=e.hint, docs_url=getattr(e, "docs_url", None))
|
|
76
|
+
err_console.print(panel)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def print_unexpected_error(exc: BaseException, json_output: bool = False) -> None:
|
|
80
|
+
"""Condensed traceback (last 3 frames) + bug report hint."""
|
|
81
|
+
if json_output:
|
|
82
|
+
import json
|
|
83
|
+
err_console.print(json.dumps({
|
|
84
|
+
"error": str(exc),
|
|
85
|
+
"hint": "Unexpected error; see logs.",
|
|
86
|
+
"exit_code": 1,
|
|
87
|
+
}))
|
|
88
|
+
return
|
|
89
|
+
import traceback
|
|
90
|
+
tb_lines = traceback.format_exception(type(exc), exc, exc.__traceback__)
|
|
91
|
+
# Last 3 frames only
|
|
92
|
+
condensed = "".join(tb_lines[-3:]) if len(tb_lines) >= 3 else "".join(tb_lines)
|
|
93
|
+
from devsper.cli.ui.theme import THEME
|
|
94
|
+
from rich.panel import Panel
|
|
95
|
+
from rich.console import Console
|
|
96
|
+
c = Console(stderr=True, theme=THEME)
|
|
97
|
+
c.print(Panel(condensed.strip(), title="Traceback", border_style="hive.dim"))
|
|
98
|
+
c.print("\n[dim]This looks like a bug. Please report it:[/]")
|
|
99
|
+
c.print(f" [link={REPO_ISSUES}]{REPO_ISSUES}[/]")
|
|
100
|
+
try:
|
|
101
|
+
import devsper
|
|
102
|
+
c.print(f" devsper {getattr(devsper, '__version__', '?')} Python {__import__('sys').version.split()[0]} {__import__('platform').system()}")
|
|
103
|
+
except Exception:
|
|
104
|
+
pass
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Structured logger matching tracing (Rust) compact format for visual consistency.
|
|
3
|
+
All subsystems use get_logger(target) instead of logging.getLogger().
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
from datetime import datetime, timezone
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
# Log level: 0=TRACE, 1=DEBUG, 2=INFO, 3=WARN, 4=ERROR. Controlled by env/global.
|
|
12
|
+
_CLI_LOG_LEVEL = 2 # INFO default
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def set_log_level(level: str | int) -> None:
|
|
16
|
+
"""level: 'trace'|'debug'|'info'|'warn'|'error' or 0-4."""
|
|
17
|
+
global _CLI_LOG_LEVEL
|
|
18
|
+
if isinstance(level, int):
|
|
19
|
+
_CLI_LOG_LEVEL = level
|
|
20
|
+
return
|
|
21
|
+
_CLI_LOG_LEVEL = {"trace": 0, "debug": 1, "info": 2, "warn": 3, "warning": 3, "error": 4}.get((level or "").lower(), 2)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_log_level() -> int:
|
|
25
|
+
return _CLI_LOG_LEVEL
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Target -> style for the target column
|
|
29
|
+
TARGET_STYLES = {
|
|
30
|
+
"planner": "hive.planner",
|
|
31
|
+
"executor": "hive.secondary",
|
|
32
|
+
"agent": "hive.agent",
|
|
33
|
+
"tool": "hive.tool",
|
|
34
|
+
"memory": "hive.agent",
|
|
35
|
+
"scheduler": "hive.muted",
|
|
36
|
+
"bus": "hive.dim",
|
|
37
|
+
"swarm": "hive.primary",
|
|
38
|
+
"hitl": "hive.warning",
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _level_style(level: str) -> str:
|
|
43
|
+
if level == "WARN":
|
|
44
|
+
return "hive.warning"
|
|
45
|
+
if level == "ERROR":
|
|
46
|
+
return "hive.error"
|
|
47
|
+
if level == "DEBUG":
|
|
48
|
+
return "hive.muted"
|
|
49
|
+
if level == "TRACE":
|
|
50
|
+
return "hive.dim"
|
|
51
|
+
return ""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _format_ts() -> str:
|
|
55
|
+
return datetime.now(timezone.utc).strftime("%H:%M:%S.") + f"{datetime.now().microsecond // 1000:03d}"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class devsperLogger:
|
|
59
|
+
"""Logger that emits tracing-compatible compact lines: timestamp LEVEL target message key=val ..."""
|
|
60
|
+
|
|
61
|
+
def __init__(self, target: str, run_id_short: str = "") -> None:
|
|
62
|
+
self.target = target
|
|
63
|
+
self.run_id_short = (run_id_short or "")[:8]
|
|
64
|
+
|
|
65
|
+
def _emit(self, level: str, message: str, **fields: Any) -> None:
|
|
66
|
+
lvl_num = {"TRACE": 0, "DEBUG": 1, "INFO": 2, "WARN": 3, "ERROR": 4}.get(level, 2)
|
|
67
|
+
if lvl_num < _CLI_LOG_LEVEL:
|
|
68
|
+
return
|
|
69
|
+
ts = _format_ts()
|
|
70
|
+
target_style = TARGET_STYLES.get(self.target, "hive.muted")
|
|
71
|
+
level_style = _level_style(level)
|
|
72
|
+
parts: list[str] = [ts, " ", level, " ", self.target, " ", message]
|
|
73
|
+
if fields:
|
|
74
|
+
pairs = " ".join(f"{k}={v}" for k, v in fields.items())
|
|
75
|
+
parts.append(" ")
|
|
76
|
+
parts.append(pairs)
|
|
77
|
+
if self.run_id_short:
|
|
78
|
+
parts.append(" ")
|
|
79
|
+
parts.append(f"run_id={self.run_id_short}")
|
|
80
|
+
line = "".join(parts)
|
|
81
|
+
# Use Rich markup for colors when printing
|
|
82
|
+
try:
|
|
83
|
+
from devsper.cli.ui.theme import err_console
|
|
84
|
+
if level_style:
|
|
85
|
+
line = f"[{level_style}]{level}[/] [{target_style}]{self.target}[/] {message}"
|
|
86
|
+
if fields:
|
|
87
|
+
line += " " + " ".join(f"[hive.muted]{k}={v}[/]" for k, v in fields.items())
|
|
88
|
+
if self.run_id_short:
|
|
89
|
+
line += f" [hive.muted]run_id={self.run_id_short}[/]"
|
|
90
|
+
line = f"[hive.muted]{ts}[/] " + line
|
|
91
|
+
else:
|
|
92
|
+
line = f"[hive.muted]{ts}[/] {level} [{target_style}]{self.target}[/] {message}"
|
|
93
|
+
if fields:
|
|
94
|
+
line += " " + " ".join(f"[hive.muted]{k}={v}[/]" for k, v in fields.items())
|
|
95
|
+
if self.run_id_short:
|
|
96
|
+
line += f" [hive.muted]run_id={self.run_id_short}[/]"
|
|
97
|
+
err_console.print(line)
|
|
98
|
+
except Exception:
|
|
99
|
+
print(line, file=sys.stderr)
|
|
100
|
+
|
|
101
|
+
def trace(self, message: str, **fields: Any) -> None:
|
|
102
|
+
self._emit("TRACE", message, **fields)
|
|
103
|
+
|
|
104
|
+
def debug(self, message: str, **fields: Any) -> None:
|
|
105
|
+
self._emit("DEBUG", message, **fields)
|
|
106
|
+
|
|
107
|
+
def info(self, message: str, **fields: Any) -> None:
|
|
108
|
+
self._emit("INFO", message, **fields)
|
|
109
|
+
|
|
110
|
+
def warning(self, message: str, **fields: Any) -> None:
|
|
111
|
+
self._emit("WARN", message, **fields)
|
|
112
|
+
|
|
113
|
+
def error(self, message: str, **fields: Any) -> None:
|
|
114
|
+
self._emit("ERROR", message, **fields)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def get_logger(target: str, run_id: str = "") -> devsperLogger:
|
|
118
|
+
"""Return logger bound to target name. run_id shortened to 8 chars in output."""
|
|
119
|
+
short = (run_id or "")[:8]
|
|
120
|
+
return devsperLogger(target, short)
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Init wizard: welcome screen, provider setup, model selection, workers, features, write config.
|
|
3
|
+
Uses theme and components; --no-interactive writes minimal config and prints next steps.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from rich.panel import Panel
|
|
11
|
+
from rich.text import Text
|
|
12
|
+
|
|
13
|
+
from devsper.cli.ui.theme import console
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
DOCS_URL = "https://docs.devsper.com"
|
|
17
|
+
GITHUB_URL = "https://github.com/devsper-com/runtime"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _wordmark_ascii() -> str:
|
|
21
|
+
"""6-line block letters 'devsper' in ASCII. Fallback if pyfiglet missing."""
|
|
22
|
+
try:
|
|
23
|
+
import pyfiglet
|
|
24
|
+
return pyfiglet.figlet_format("devsper", font="standard").rstrip()
|
|
25
|
+
except ImportError:
|
|
26
|
+
return """
|
|
27
|
+
_ _ _ _ __ __ _____ __ __ ___ _ _ ____
|
|
28
|
+
| | | | | | | \\/ | ____| | \\/ | _ \\| \\ | | _ \\
|
|
29
|
+
| |_| | | | | |\\/| | _| | |\\/| | | | | \\| | | | |
|
|
30
|
+
| _ | |_| | | | | |___ | | | | |_| | |\\ | |_| |
|
|
31
|
+
|_| |_|\\___/|_| |_|_____| |_| |_|___/|_| \\_|____/
|
|
32
|
+
""".strip()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class InitWizard:
|
|
36
|
+
"""Interactive init: welcome, providers, models, workers, features, write config."""
|
|
37
|
+
|
|
38
|
+
def __init__(self, cwd: Path | None = None) -> None:
|
|
39
|
+
self.cwd = cwd or Path.cwd()
|
|
40
|
+
|
|
41
|
+
def run(self, no_interactive: bool = False) -> int:
|
|
42
|
+
"""Run full wizard or minimal write when no_interactive."""
|
|
43
|
+
if no_interactive:
|
|
44
|
+
return self._run_minimal()
|
|
45
|
+
return self._run_interactive()
|
|
46
|
+
|
|
47
|
+
def _run_minimal(self) -> int:
|
|
48
|
+
"""Write minimal devsper.toml and print next steps."""
|
|
49
|
+
from devsper.cli.init import _build_init_toml
|
|
50
|
+
toml = _build_init_toml(workers=4, planner="auto", worker="auto")
|
|
51
|
+
path = self.cwd / "devsper.toml"
|
|
52
|
+
path.write_text(toml, encoding="utf-8")
|
|
53
|
+
console.print("[hive.success]✓[/] Wrote [cyan]devsper.toml[/]")
|
|
54
|
+
console.print()
|
|
55
|
+
console.print("Next steps:")
|
|
56
|
+
console.print(" 1) Set API keys: [cyan]devsper credentials set <provider> api_key[/]")
|
|
57
|
+
console.print(" 2) Run: [cyan]devsper run \"your task\"[/]")
|
|
58
|
+
console.print(f" Docs: [link={DOCS_URL}]{DOCS_URL}[/]")
|
|
59
|
+
return 0
|
|
60
|
+
|
|
61
|
+
def _run_interactive(self) -> int:
|
|
62
|
+
"""Step 1: Welcome. Then delegate to existing init flow."""
|
|
63
|
+
# Step 1 — Welcome
|
|
64
|
+
console.clear()
|
|
65
|
+
wordmark = _wordmark_ascii()
|
|
66
|
+
console.print(Text(wordmark, style="hive.primary"))
|
|
67
|
+
console.print()
|
|
68
|
+
console.print("Welcome to devsper — distributed AI swarm runtime", style="hive.muted")
|
|
69
|
+
try:
|
|
70
|
+
import devsper
|
|
71
|
+
ver = getattr(devsper, "__version__", "?")
|
|
72
|
+
console.print(f"v{ver} · [link={DOCS_URL}]{DOCS_URL}[/] · [link={GITHUB_URL}]{GITHUB_URL}[/]", style="hive.muted")
|
|
73
|
+
except Exception:
|
|
74
|
+
console.print(f"[link={DOCS_URL}]{DOCS_URL}[/] · [link={GITHUB_URL}]{GITHUB_URL}[/]", style="hive.muted")
|
|
75
|
+
console.print()
|
|
76
|
+
try:
|
|
77
|
+
from rich.prompt import Prompt
|
|
78
|
+
Prompt.ask("[dim]Press Enter to continue[/]", default="")
|
|
79
|
+
except Exception:
|
|
80
|
+
input("Press Enter to continue...")
|
|
81
|
+
# Delegate to existing init (writes toml and .env)
|
|
82
|
+
from devsper.cli.init import _run_init_interactive, _write_env_file
|
|
83
|
+
toml_content, api_keys = _run_init_interactive(self.cwd)
|
|
84
|
+
path = self.cwd / "devsper.toml"
|
|
85
|
+
path.write_text(toml_content, encoding="utf-8")
|
|
86
|
+
if api_keys:
|
|
87
|
+
_write_env_file(self.cwd, api_keys)
|
|
88
|
+
console.print()
|
|
89
|
+
console.print(Panel(
|
|
90
|
+
"Try it: [cyan]devsper run \"research quantum computing breakthroughs\"[/]\n\n"
|
|
91
|
+
f"Docs: {DOCS_URL}\n"
|
|
92
|
+
"Discord: https://discord.gg/devsper",
|
|
93
|
+
title="You're all set",
|
|
94
|
+
border_style="hive.success",
|
|
95
|
+
))
|
|
96
|
+
return 0
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def run_init_wizard(no_interactive: bool = False, cwd: Path | None = None) -> int:
|
|
100
|
+
"""Entry point for devsper init."""
|
|
101
|
+
wizard = InitWizard(cwd=cwd)
|
|
102
|
+
return wizard.run(no_interactive=no_interactive)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Styled progress bars for long operations. Amber fill, dim track, consistent columns.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from rich.progress import (
|
|
6
|
+
Progress,
|
|
7
|
+
SpinnerColumn,
|
|
8
|
+
TextColumn,
|
|
9
|
+
BarColumn,
|
|
10
|
+
TaskProgressColumn,
|
|
11
|
+
TimeElapsedColumn,
|
|
12
|
+
)
|
|
13
|
+
from rich.text import Text
|
|
14
|
+
|
|
15
|
+
from devsper.cli.ui.theme import console
|
|
16
|
+
|
|
17
|
+
# Spinner names: dots2, line, arrow (Rich built-in)
|
|
18
|
+
SPINNER_THINKING = "dots2"
|
|
19
|
+
SPINNER_NETWORK = "line"
|
|
20
|
+
SPINNER_BUILDING = "arrow"
|
|
21
|
+
SPINNER_SUCCESS = "dots2" # we override with static ✓ when done
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def devsperProgress(
|
|
25
|
+
transient: bool = False,
|
|
26
|
+
console_use: object = None,
|
|
27
|
+
) -> Progress:
|
|
28
|
+
"""Progress with hive styling: amber bar, dim track, spinner, task count, elapsed."""
|
|
29
|
+
c = console_use or console
|
|
30
|
+
return Progress(
|
|
31
|
+
SpinnerColumn(style="hive.primary", finished_style="hive.success"),
|
|
32
|
+
TextColumn("[bold]{task.description}"),
|
|
33
|
+
BarColumn(bar_width=30, complete_style="hive.primary", finished_style="hive.success", pulse_style="hive.dim"),
|
|
34
|
+
TaskProgressColumn(style="hive.muted"),
|
|
35
|
+
TimeElapsedColumn(style="hive.muted"),
|
|
36
|
+
console=c,
|
|
37
|
+
transient=transient,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def progress_spinner_style(operation: str) -> str:
|
|
42
|
+
"""Spinner style by operation: thinking, network, building, success."""
|
|
43
|
+
return "hive.primary"
|