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.
Files changed (375) hide show
  1. devsper/__init__.py +14 -0
  2. devsper/agents/a2a/__init__.py +27 -0
  3. devsper/agents/a2a/client.py +126 -0
  4. devsper/agents/a2a/discovery.py +24 -0
  5. devsper/agents/a2a/server.py +128 -0
  6. devsper/agents/a2a/tool_adapter.py +68 -0
  7. devsper/agents/a2a/types.py +49 -0
  8. devsper/agents/agent.py +602 -0
  9. devsper/agents/critic.py +80 -0
  10. devsper/agents/message_bus.py +124 -0
  11. devsper/agents/roles.py +181 -0
  12. devsper/agents/run_agent.py +78 -0
  13. devsper/analytics/__init__.py +5 -0
  14. devsper/analytics/tool_analytics.py +78 -0
  15. devsper/audit/__init__.py +5 -0
  16. devsper/audit/logger.py +214 -0
  17. devsper/bus/__init__.py +29 -0
  18. devsper/bus/backends/__init__.py +5 -0
  19. devsper/bus/backends/base.py +38 -0
  20. devsper/bus/backends/memory.py +55 -0
  21. devsper/bus/backends/redis.py +146 -0
  22. devsper/bus/message.py +56 -0
  23. devsper/bus/schema_version.py +3 -0
  24. devsper/bus/topics.py +19 -0
  25. devsper/cache/__init__.py +6 -0
  26. devsper/cache/embedding_index.py +98 -0
  27. devsper/cache/hashing.py +24 -0
  28. devsper/cache/store.py +153 -0
  29. devsper/cache/task_cache.py +191 -0
  30. devsper/cli/__init__.py +6 -0
  31. devsper/cli/commands/reg.py +733 -0
  32. devsper/cli/github_oauth.py +157 -0
  33. devsper/cli/init.py +637 -0
  34. devsper/cli/main.py +2956 -0
  35. devsper/cli/run_progress.py +103 -0
  36. devsper/cli/ui/__init__.py +65 -0
  37. devsper/cli/ui/components.py +94 -0
  38. devsper/cli/ui/errors.py +104 -0
  39. devsper/cli/ui/logging.py +120 -0
  40. devsper/cli/ui/onboarding.py +102 -0
  41. devsper/cli/ui/progress.py +43 -0
  42. devsper/cli/ui/run_view.py +308 -0
  43. devsper/cli/ui/theme.py +40 -0
  44. devsper/cluster/__init__.py +29 -0
  45. devsper/cluster/election.py +84 -0
  46. devsper/cluster/local.py +97 -0
  47. devsper/cluster/node_info.py +77 -0
  48. devsper/cluster/registry.py +71 -0
  49. devsper/cluster/router.py +117 -0
  50. devsper/cluster/state_backend.py +105 -0
  51. devsper/compliance/__init__.py +5 -0
  52. devsper/compliance/pii.py +147 -0
  53. devsper/config/__init__.py +52 -0
  54. devsper/config/config_loader.py +121 -0
  55. devsper/config/defaults.py +77 -0
  56. devsper/config/resolver.py +342 -0
  57. devsper/config/schema.py +237 -0
  58. devsper/credentials/__init__.py +19 -0
  59. devsper/credentials/cli.py +197 -0
  60. devsper/credentials/migration.py +124 -0
  61. devsper/credentials/store.py +142 -0
  62. devsper/dashboard/__init__.py +9 -0
  63. devsper/dashboard/dashboard.py +87 -0
  64. devsper/dev/__init__.py +25 -0
  65. devsper/dev/builder.py +195 -0
  66. devsper/dev/debugger.py +95 -0
  67. devsper/dev/repo_index.py +138 -0
  68. devsper/dev/sandbox.py +203 -0
  69. devsper/dev/scaffold.py +122 -0
  70. devsper/embeddings/__init__.py +5 -0
  71. devsper/embeddings/service.py +36 -0
  72. devsper/explainability/__init__.py +14 -0
  73. devsper/explainability/decision_tree.py +104 -0
  74. devsper/explainability/rationale.py +38 -0
  75. devsper/explainability/simulation.py +56 -0
  76. devsper/hitl/__init__.py +13 -0
  77. devsper/hitl/approval.py +160 -0
  78. devsper/hitl/escalation.py +95 -0
  79. devsper/intelligence/__init__.py +9 -0
  80. devsper/intelligence/adaptation.py +88 -0
  81. devsper/intelligence/analysis/__init__.py +19 -0
  82. devsper/intelligence/analysis/analyzer.py +71 -0
  83. devsper/intelligence/analysis/cost_estimator.py +66 -0
  84. devsper/intelligence/analysis/formatter.py +103 -0
  85. devsper/intelligence/analysis/run_report.py +402 -0
  86. devsper/intelligence/learning_engine.py +92 -0
  87. devsper/intelligence/strategies/__init__.py +23 -0
  88. devsper/intelligence/strategies/base.py +14 -0
  89. devsper/intelligence/strategies/code_analysis_strategy.py +33 -0
  90. devsper/intelligence/strategies/data_science_strategy.py +33 -0
  91. devsper/intelligence/strategies/document_pipeline_strategy.py +33 -0
  92. devsper/intelligence/strategies/experiment_strategy.py +33 -0
  93. devsper/intelligence/strategies/research_strategy.py +34 -0
  94. devsper/intelligence/strategy_selector.py +84 -0
  95. devsper/intelligence/synthesis.py +132 -0
  96. devsper/intelligence/task_optimizer.py +92 -0
  97. devsper/knowledge/__init__.py +5 -0
  98. devsper/knowledge/extractor.py +204 -0
  99. devsper/knowledge/knowledge_graph.py +184 -0
  100. devsper/knowledge/query.py +285 -0
  101. devsper/memory/__init__.py +35 -0
  102. devsper/memory/consolidation.py +138 -0
  103. devsper/memory/embeddings.py +60 -0
  104. devsper/memory/memory_index.py +97 -0
  105. devsper/memory/memory_router.py +62 -0
  106. devsper/memory/memory_store.py +221 -0
  107. devsper/memory/memory_types.py +54 -0
  108. devsper/memory/namespaces.py +45 -0
  109. devsper/memory/scoring.py +77 -0
  110. devsper/memory/summarizer.py +52 -0
  111. devsper/nodes/__init__.py +5 -0
  112. devsper/nodes/controller.py +449 -0
  113. devsper/nodes/rpc.py +127 -0
  114. devsper/nodes/single.py +161 -0
  115. devsper/nodes/worker.py +506 -0
  116. devsper/orchestration/__init__.py +19 -0
  117. devsper/orchestration/meta_planner.py +239 -0
  118. devsper/orchestration/priority_queue.py +61 -0
  119. devsper/plugins/__init__.py +19 -0
  120. devsper/plugins/marketplace/__init__.py +0 -0
  121. devsper/plugins/plugin_loader.py +70 -0
  122. devsper/plugins/plugin_registry.py +34 -0
  123. devsper/plugins/registry.py +83 -0
  124. devsper/protocols/__init__.py +6 -0
  125. devsper/providers/__init__.py +17 -0
  126. devsper/providers/anthropic.py +84 -0
  127. devsper/providers/base.py +75 -0
  128. devsper/providers/complexity_router.py +94 -0
  129. devsper/providers/gemini.py +36 -0
  130. devsper/providers/github.py +180 -0
  131. devsper/providers/model_router.py +40 -0
  132. devsper/providers/openai.py +105 -0
  133. devsper/providers/router/__init__.py +21 -0
  134. devsper/providers/router/backends/__init__.py +19 -0
  135. devsper/providers/router/backends/anthropic_backend.py +111 -0
  136. devsper/providers/router/backends/custom_backend.py +138 -0
  137. devsper/providers/router/backends/gemini_backend.py +89 -0
  138. devsper/providers/router/backends/github_backend.py +165 -0
  139. devsper/providers/router/backends/ollama_backend.py +104 -0
  140. devsper/providers/router/backends/openai_backend.py +142 -0
  141. devsper/providers/router/backends/vllm_backend.py +35 -0
  142. devsper/providers/router/base.py +60 -0
  143. devsper/providers/router/factory.py +92 -0
  144. devsper/providers/router/legacy.py +101 -0
  145. devsper/providers/router/router.py +135 -0
  146. devsper/reasoning/__init__.py +12 -0
  147. devsper/reasoning/graph.py +59 -0
  148. devsper/reasoning/nodes.py +20 -0
  149. devsper/reasoning/store.py +67 -0
  150. devsper/runtime/__init__.py +12 -0
  151. devsper/runtime/health.py +88 -0
  152. devsper/runtime/replay.py +53 -0
  153. devsper/runtime/replay_engine.py +142 -0
  154. devsper/runtime/run_history.py +204 -0
  155. devsper/runtime/telemetry.py +116 -0
  156. devsper/runtime/visualize.py +58 -0
  157. devsper/sandbox/__init__.py +13 -0
  158. devsper/sandbox/sandbox.py +161 -0
  159. devsper/swarm/checkpointer.py +65 -0
  160. devsper/swarm/executor.py +558 -0
  161. devsper/swarm/map_reduce.py +44 -0
  162. devsper/swarm/planner.py +197 -0
  163. devsper/swarm/prefetcher.py +91 -0
  164. devsper/swarm/scheduler.py +153 -0
  165. devsper/swarm/speculation.py +47 -0
  166. devsper/swarm/swarm.py +562 -0
  167. devsper/tools/__init__.py +33 -0
  168. devsper/tools/base.py +29 -0
  169. devsper/tools/code_intelligence/__init__.py +13 -0
  170. devsper/tools/code_intelligence/api_surface_extractor.py +73 -0
  171. devsper/tools/code_intelligence/architecture_analyzer.py +65 -0
  172. devsper/tools/code_intelligence/codebase_indexer.py +71 -0
  173. devsper/tools/code_intelligence/dependency_graph_builder.py +67 -0
  174. devsper/tools/code_intelligence/design_pattern_detector.py +62 -0
  175. devsper/tools/code_intelligence/large_function_detector.py +68 -0
  176. devsper/tools/code_intelligence/module_responsibility_mapper.py +56 -0
  177. devsper/tools/code_intelligence/parallel_codebase_analysis.py +44 -0
  178. devsper/tools/code_intelligence/refactor_candidate_detector.py +81 -0
  179. devsper/tools/code_intelligence/repository_semantic_index.py +61 -0
  180. devsper/tools/code_intelligence/test_coverage_estimator.py +62 -0
  181. devsper/tools/coding/__init__.py +12 -0
  182. devsper/tools/coding/analyze_code_complexity.py +48 -0
  183. devsper/tools/coding/dependency_analyzer.py +42 -0
  184. devsper/tools/coding/extract_functions.py +38 -0
  185. devsper/tools/coding/format_python.py +50 -0
  186. devsper/tools/coding/generate_docstrings.py +40 -0
  187. devsper/tools/coding/generate_unit_tests.py +42 -0
  188. devsper/tools/coding/lint_python.py +51 -0
  189. devsper/tools/coding/refactor_function.py +41 -0
  190. devsper/tools/coding/repo_structure_map.py +54 -0
  191. devsper/tools/coding/run_python.py +53 -0
  192. devsper/tools/data/__init__.py +12 -0
  193. devsper/tools/data/column_type_detection.py +64 -0
  194. devsper/tools/data/csv_summary.py +52 -0
  195. devsper/tools/data/dataframe_filter.py +51 -0
  196. devsper/tools/data/dataframe_groupby.py +47 -0
  197. devsper/tools/data/dataframe_stats.py +38 -0
  198. devsper/tools/data/dataset_sampling.py +55 -0
  199. devsper/tools/data/dataset_schema.py +45 -0
  200. devsper/tools/data/json_pretty_print.py +37 -0
  201. devsper/tools/data/json_query.py +46 -0
  202. devsper/tools/data/missing_value_report.py +47 -0
  203. devsper/tools/data_science/__init__.py +13 -0
  204. devsper/tools/data_science/correlation_heatmap.py +72 -0
  205. devsper/tools/data_science/dataset_bias_detector.py +49 -0
  206. devsper/tools/data_science/dataset_distribution_report.py +64 -0
  207. devsper/tools/data_science/dataset_drift_detector.py +64 -0
  208. devsper/tools/data_science/dataset_outlier_detector.py +65 -0
  209. devsper/tools/data_science/dataset_profile.py +76 -0
  210. devsper/tools/data_science/distributed_dataset_processor.py +54 -0
  211. devsper/tools/data_science/feature_engineering_suggestions.py +69 -0
  212. devsper/tools/data_science/feature_importance_estimator.py +82 -0
  213. devsper/tools/data_science/model_input_validator.py +59 -0
  214. devsper/tools/data_science/time_series_analyzer.py +57 -0
  215. devsper/tools/documents/__init__.py +11 -0
  216. devsper/tools/documents/_docproc.py +56 -0
  217. devsper/tools/documents/document_to_markdown.py +29 -0
  218. devsper/tools/documents/extract_document_images.py +39 -0
  219. devsper/tools/documents/extract_document_text.py +29 -0
  220. devsper/tools/documents/extract_equations.py +36 -0
  221. devsper/tools/documents/extract_tables.py +47 -0
  222. devsper/tools/documents/summarize_document.py +42 -0
  223. devsper/tools/documents/write_latex_document.py +133 -0
  224. devsper/tools/documents/write_markdown_document.py +89 -0
  225. devsper/tools/documents/write_word_document.py +149 -0
  226. devsper/tools/experiments/__init__.py +13 -0
  227. devsper/tools/experiments/bootstrap_estimator.py +54 -0
  228. devsper/tools/experiments/experiment_report_generator.py +50 -0
  229. devsper/tools/experiments/experiment_tracker.py +36 -0
  230. devsper/tools/experiments/grid_search_runner.py +50 -0
  231. devsper/tools/experiments/model_benchmark_runner.py +45 -0
  232. devsper/tools/experiments/monte_carlo_experiment.py +38 -0
  233. devsper/tools/experiments/parameter_sweep_runner.py +51 -0
  234. devsper/tools/experiments/result_comparator.py +58 -0
  235. devsper/tools/experiments/simulation_runner.py +43 -0
  236. devsper/tools/experiments/statistical_significance_test.py +56 -0
  237. devsper/tools/experiments/swarm_map_reduce.py +42 -0
  238. devsper/tools/filesystem/__init__.py +12 -0
  239. devsper/tools/filesystem/append_file.py +42 -0
  240. devsper/tools/filesystem/file_hash.py +40 -0
  241. devsper/tools/filesystem/file_line_count.py +36 -0
  242. devsper/tools/filesystem/file_metadata.py +38 -0
  243. devsper/tools/filesystem/file_preview.py +55 -0
  244. devsper/tools/filesystem/find_large_files.py +50 -0
  245. devsper/tools/filesystem/list_directory.py +39 -0
  246. devsper/tools/filesystem/read_file.py +35 -0
  247. devsper/tools/filesystem/search_files.py +60 -0
  248. devsper/tools/filesystem/write_file.py +41 -0
  249. devsper/tools/flagship/__init__.py +15 -0
  250. devsper/tools/flagship/distributed_document_analysis.py +77 -0
  251. devsper/tools/flagship/docproc_corpus_pipeline.py +91 -0
  252. devsper/tools/flagship/repository_semantic_map.py +99 -0
  253. devsper/tools/flagship/research_graph_builder.py +111 -0
  254. devsper/tools/flagship/swarm_experiment_runner.py +86 -0
  255. devsper/tools/knowledge/__init__.py +10 -0
  256. devsper/tools/knowledge/citation_graph_builder.py +69 -0
  257. devsper/tools/knowledge/concept_frequency_analyzer.py +74 -0
  258. devsper/tools/knowledge/corpus_builder.py +66 -0
  259. devsper/tools/knowledge/cross_document_entity_linker.py +71 -0
  260. devsper/tools/knowledge/document_corpus_summary.py +68 -0
  261. devsper/tools/knowledge/document_topic_extractor.py +58 -0
  262. devsper/tools/knowledge/knowledge_graph_extractor.py +58 -0
  263. devsper/tools/knowledge/timeline_extractor.py +59 -0
  264. devsper/tools/math/__init__.py +12 -0
  265. devsper/tools/math/calculate_expression.py +52 -0
  266. devsper/tools/math/correlation.py +44 -0
  267. devsper/tools/math/distribution_summary.py +39 -0
  268. devsper/tools/math/histogram.py +53 -0
  269. devsper/tools/math/linear_regression.py +47 -0
  270. devsper/tools/math/matrix_multiply.py +38 -0
  271. devsper/tools/math/mean_std.py +35 -0
  272. devsper/tools/math/monte_carlo_simulation.py +43 -0
  273. devsper/tools/math/polynomial_fit.py +40 -0
  274. devsper/tools/math/random_sample.py +36 -0
  275. devsper/tools/mcp/__init__.py +23 -0
  276. devsper/tools/mcp/adapter.py +53 -0
  277. devsper/tools/mcp/client.py +235 -0
  278. devsper/tools/mcp/discovery.py +53 -0
  279. devsper/tools/memory/__init__.py +16 -0
  280. devsper/tools/memory/delete_memory.py +25 -0
  281. devsper/tools/memory/list_memory.py +34 -0
  282. devsper/tools/memory/search_memory.py +36 -0
  283. devsper/tools/memory/store_memory.py +47 -0
  284. devsper/tools/memory/summarize_memory.py +41 -0
  285. devsper/tools/memory/tag_memory.py +47 -0
  286. devsper/tools/pipelines.py +92 -0
  287. devsper/tools/registry.py +39 -0
  288. devsper/tools/research/__init__.py +12 -0
  289. devsper/tools/research/arxiv_download.py +55 -0
  290. devsper/tools/research/arxiv_search.py +58 -0
  291. devsper/tools/research/citation_extractor.py +35 -0
  292. devsper/tools/research/duckduckgo_search.py +42 -0
  293. devsper/tools/research/paper_metadata_extractor.py +45 -0
  294. devsper/tools/research/paper_summarizer.py +41 -0
  295. devsper/tools/research/research_question_generator.py +39 -0
  296. devsper/tools/research/topic_cluster.py +46 -0
  297. devsper/tools/research/web_search.py +47 -0
  298. devsper/tools/research/wikipedia_lookup.py +50 -0
  299. devsper/tools/research_advanced/__init__.py +14 -0
  300. devsper/tools/research_advanced/citation_context_extractor.py +60 -0
  301. devsper/tools/research_advanced/literature_review_generator.py +79 -0
  302. devsper/tools/research_advanced/methodology_extractor.py +58 -0
  303. devsper/tools/research_advanced/paper_contribution_extractor.py +50 -0
  304. devsper/tools/research_advanced/paper_dataset_identifier.py +49 -0
  305. devsper/tools/research_advanced/paper_method_comparator.py +62 -0
  306. devsper/tools/research_advanced/paper_similarity_search.py +69 -0
  307. devsper/tools/research_advanced/paper_trend_analyzer.py +69 -0
  308. devsper/tools/research_advanced/parallel_document_analyzer.py +56 -0
  309. devsper/tools/research_advanced/research_gap_finder.py +71 -0
  310. devsper/tools/research_advanced/research_topic_mapper.py +69 -0
  311. devsper/tools/research_advanced/swarm_literature_review.py +58 -0
  312. devsper/tools/scoring/__init__.py +52 -0
  313. devsper/tools/scoring/report.py +44 -0
  314. devsper/tools/scoring/scorer.py +39 -0
  315. devsper/tools/scoring/selector.py +61 -0
  316. devsper/tools/scoring/store.py +267 -0
  317. devsper/tools/selector.py +130 -0
  318. devsper/tools/system/__init__.py +12 -0
  319. devsper/tools/system/cpu_usage.py +22 -0
  320. devsper/tools/system/disk_usage.py +35 -0
  321. devsper/tools/system/environment_variables.py +29 -0
  322. devsper/tools/system/memory_usage.py +23 -0
  323. devsper/tools/system/pip_install.py +44 -0
  324. devsper/tools/system/pip_search.py +29 -0
  325. devsper/tools/system/process_list.py +34 -0
  326. devsper/tools/system/python_package_list.py +40 -0
  327. devsper/tools/system/run_shell_command.py +51 -0
  328. devsper/tools/system/system_info.py +26 -0
  329. devsper/tools/tool_runner.py +122 -0
  330. devsper/tui/__init__.py +5 -0
  331. devsper/tui/activity_feed_view.py +73 -0
  332. devsper/tui/adaptive_tasks_view.py +75 -0
  333. devsper/tui/agent_role_view.py +35 -0
  334. devsper/tui/app.py +395 -0
  335. devsper/tui/dashboard_screen.py +290 -0
  336. devsper/tui/dev_view.py +99 -0
  337. devsper/tui/inject_screen.py +73 -0
  338. devsper/tui/knowledge_graph_view.py +46 -0
  339. devsper/tui/layout.py +43 -0
  340. devsper/tui/logs_view.py +83 -0
  341. devsper/tui/memory_view.py +58 -0
  342. devsper/tui/performance_view.py +33 -0
  343. devsper/tui/reasoning_graph_view.py +39 -0
  344. devsper/tui/results_view.py +139 -0
  345. devsper/tui/swarm_view.py +37 -0
  346. devsper/tui/task_detail_screen.py +55 -0
  347. devsper/tui/task_view.py +103 -0
  348. devsper/types/event.py +97 -0
  349. devsper/types/exceptions.py +21 -0
  350. devsper/types/swarm.py +41 -0
  351. devsper/types/task.py +80 -0
  352. devsper/upgrade/__init__.py +21 -0
  353. devsper/upgrade/changelog.py +124 -0
  354. devsper/upgrade/cli.py +145 -0
  355. devsper/upgrade/installer.py +103 -0
  356. devsper/upgrade/notifier.py +52 -0
  357. devsper/upgrade/version_check.py +121 -0
  358. devsper/utils/event_logger.py +88 -0
  359. devsper/utils/http.py +43 -0
  360. devsper/utils/models.py +54 -0
  361. devsper/visualization/__init__.py +5 -0
  362. devsper/visualization/dag_export.py +67 -0
  363. devsper/workflow/__init__.py +18 -0
  364. devsper/workflow/conditions.py +157 -0
  365. devsper/workflow/context.py +108 -0
  366. devsper/workflow/loader.py +156 -0
  367. devsper/workflow/resolver.py +109 -0
  368. devsper/workflow/runner.py +562 -0
  369. devsper/workflow/schema.py +63 -0
  370. devsper/workflow/validator.py +128 -0
  371. devsper-2.1.6.dist-info/METADATA +346 -0
  372. devsper-2.1.6.dist-info/RECORD +375 -0
  373. devsper-2.1.6.dist-info/WHEEL +4 -0
  374. devsper-2.1.6.dist-info/entry_points.txt +3 -0
  375. devsper-2.1.6.dist-info/licenses/LICENSE +639 -0
devsper/dev/builder.py ADDED
@@ -0,0 +1,195 @@
1
+ """
2
+ Autonomous Application Builder: build a working repo from an app description.
3
+
4
+ Flow:
5
+ 1) architecture design
6
+ 2) repo scaffold
7
+ 3) module implementation
8
+ 4) test generation
9
+ 5) test execution
10
+ 6) debugging loop
11
+ """
12
+
13
+ from pathlib import Path
14
+
15
+ from devsper.dev.scaffold import ArchitecturePlan, scaffold_repo
16
+ from devsper.dev.sandbox import Sandbox, SandboxLimits
17
+ from devsper.dev.debugger import debug_loop
18
+ from devsper.dev.repo_index import RepoIndex
19
+
20
+
21
+ def _design_architecture(description: str) -> ArchitecturePlan:
22
+ """Produce an architecture plan from app description (heuristic + optional LLM)."""
23
+ desc_lower = (description or "").lower()
24
+ name = (description or "app").strip()[:50].replace("/", "-")
25
+ backend = "fastapi"
26
+ frontend = "none"
27
+ if "flask" in desc_lower:
28
+ backend = "flask"
29
+ if "react" in desc_lower or "frontend" in desc_lower or "ui" in desc_lower:
30
+ frontend = "react"
31
+ if "todo" in desc_lower:
32
+ name = "Todo App"
33
+ return ArchitecturePlan(
34
+ name=name or "App",
35
+ description=description or "Generated application",
36
+ backend=backend,
37
+ frontend=frontend,
38
+ features=[],
39
+ )
40
+
41
+
42
+ def _implement_modules(sandbox: Sandbox, plan: ArchitecturePlan, description: str) -> None:
43
+ """Generate and write main backend module(s)."""
44
+ if "fastapi" in plan.backend.lower() and "todo" in plan.name.lower():
45
+ main_py = '''"""FastAPI Todo API."""
46
+ from fastapi import FastAPI, HTTPException
47
+ from pydantic import BaseModel
48
+ from typing import List
49
+
50
+ app = FastAPI(title="Todo API")
51
+
52
+ class TodoItem(BaseModel):
53
+ id: int
54
+ title: str
55
+ done: bool = False
56
+
57
+ todos: List[TodoItem] = []
58
+ next_id = 1
59
+
60
+ @app.get("/")
61
+ def root():
62
+ return {"message": "Todo API", "docs": "/docs"}
63
+
64
+ @app.get("/todos")
65
+ def list_todos():
66
+ return {"todos": [t.model_dump() for t in todos]}
67
+
68
+ @app.post("/todos")
69
+ def create_todo(title: str):
70
+ global next_id
71
+ item = TodoItem(id=next_id, title=title, done=False)
72
+ next_id += 1
73
+ todos.append(item)
74
+ return item.model_dump()
75
+
76
+ @app.patch("/todos/{item_id}")
77
+ def toggle_todo(item_id: int):
78
+ for t in todos:
79
+ if t.id == item_id:
80
+ t.done = not t.done
81
+ return t.model_dump()
82
+ raise HTTPException(status_code=404, detail="Not found")
83
+
84
+ @app.delete("/todos/{item_id}")
85
+ def delete_todo(item_id: int):
86
+ global todos
87
+ for i, t in enumerate(todos):
88
+ if t.id == item_id:
89
+ todos.pop(i)
90
+ return {"ok": True}
91
+ raise HTTPException(status_code=404, detail="Not found")
92
+ '''
93
+ sandbox.write_file("backend/main.py", main_py)
94
+ elif "fastapi" in plan.backend.lower():
95
+ # Generic FastAPI placeholder already from scaffold; leave or minimal enhance
96
+ pass
97
+
98
+
99
+ def _generate_tests(sandbox: Sandbox, plan: ArchitecturePlan) -> None:
100
+ """Write basic tests."""
101
+ if "todo" in (plan.name or "").lower():
102
+ test_py = '''"""Tests for Todo API."""
103
+ import pytest
104
+ from fastapi.testclient import TestClient
105
+
106
+ # Import app from backend
107
+ import sys
108
+ from pathlib import Path
109
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "backend"))
110
+ from main import app
111
+
112
+ client = TestClient(app)
113
+
114
+ def test_root():
115
+ r = client.get("/")
116
+ assert r.status_code == 200
117
+ assert "message" in r.json()
118
+
119
+ def test_list_todos_empty():
120
+ r = client.get("/todos")
121
+ assert r.status_code == 200
122
+ assert r.json()["todos"] == []
123
+
124
+ def test_create_and_list_todo():
125
+ r = client.post("/todos?title=test task")
126
+ assert r.status_code == 200
127
+ data = r.json()
128
+ assert data["title"] == "test task"
129
+ assert data["done"] is False
130
+ r2 = client.get("/todos")
131
+ assert len(r2.json()["todos"]) >= 1
132
+ '''
133
+ sandbox.write_file("tests/test_app.py", test_py)
134
+
135
+
136
+ def run_build(
137
+ app_description: str,
138
+ output_dir: str | Path,
139
+ *,
140
+ timeout_seconds: int = 300,
141
+ max_debug_iterations: int = 3,
142
+ ) -> dict:
143
+ """
144
+ Build a working repository from an app description.
145
+
146
+ Steps: architecture design, scaffold, implement, generate tests,
147
+ install deps, run tests, debug loop until pass or limit.
148
+
149
+ Returns dict with keys: success, plan, created_paths, test_passed, debug_result, repo_path.
150
+ """
151
+ output_dir = Path(output_dir).resolve()
152
+ output_dir.mkdir(parents=True, exist_ok=True)
153
+
154
+ plan = _design_architecture(app_description)
155
+ created_paths = scaffold_repo(output_dir, plan)
156
+
157
+ limits = SandboxLimits(timeout_seconds=timeout_seconds)
158
+ sandbox = Sandbox(output_dir, limits=limits)
159
+
160
+ _implement_modules(sandbox, plan, app_description)
161
+ _generate_tests(sandbox, plan)
162
+
163
+ install_result = sandbox.install_dependencies(backend=True, frontend=False)
164
+
165
+ def get_fix(stdout: str, stderr: str) -> str:
166
+ try:
167
+ from devsper.utils.models import generate
168
+ from devsper.config import get_config
169
+ cfg = get_config()
170
+ model = getattr(cfg.swarm, "worker_model", "gpt-4o") or "gpt-4o"
171
+ prompt = (
172
+ "The following test run failed. Suggest a single file fix as:\n"
173
+ "FILE: <relative path>\nCONTENT: <full file content>\n\n"
174
+ "Stdout:\n" + (stdout or "")[:2000] + "\n\nStderr:\n" + (stderr or "")[:1000]
175
+ )
176
+ return generate(model, prompt)
177
+ except Exception:
178
+ return ""
179
+
180
+ debug_result = debug_loop(
181
+ sandbox,
182
+ max_iterations=max_debug_iterations,
183
+ test_path="tests",
184
+ get_fix=get_fix,
185
+ )
186
+
187
+ return {
188
+ "success": debug_result.passed,
189
+ "plan": plan,
190
+ "created_paths": created_paths,
191
+ "install_success": install_result.success,
192
+ "test_passed": debug_result.passed,
193
+ "debug_result": debug_result,
194
+ "repo_path": str(output_dir),
195
+ }
@@ -0,0 +1,95 @@
1
+ """
2
+ Test execution loop: run tests, detect errors, spawn fix tasks, patch code.
3
+ Loop until tests pass or max iterations.
4
+ """
5
+
6
+ from dataclasses import dataclass, field
7
+ from typing import Callable
8
+
9
+ from devsper.dev.sandbox import Sandbox, SandboxResult
10
+
11
+
12
+ @dataclass
13
+ class DebugResult:
14
+ """Result of the debug loop."""
15
+
16
+ passed: bool
17
+ iterations: int
18
+ last_stdout: str = ""
19
+ last_stderr: str = ""
20
+ fixes_applied: list[str] = field(default_factory=list)
21
+
22
+
23
+ def debug_loop(
24
+ sandbox: Sandbox,
25
+ max_iterations: int = 5,
26
+ test_path: str = "tests",
27
+ get_fix: Callable[[str, str], str] | None = None,
28
+ ) -> DebugResult:
29
+ """
30
+ Run tests in sandbox; on failure, call get_fix(stdout, stderr) to get patch/code,
31
+ apply it (e.g. via sandbox.write_file), then re-run. Loop until tests pass or
32
+ max_iterations. If get_fix is None, no fixes are applied (just re-run).
33
+ """
34
+ fixes: list[str] = []
35
+ last_stdout = ""
36
+ last_stderr = ""
37
+
38
+ for iteration in range(max_iterations):
39
+ result: SandboxResult = sandbox.run_tests(path=test_path)
40
+ last_stdout = result.stdout
41
+ last_stderr = result.stderr or ""
42
+
43
+ if result.success:
44
+ return DebugResult(
45
+ passed=True,
46
+ iterations=iteration + 1,
47
+ last_stdout=last_stdout,
48
+ last_stderr=last_stderr,
49
+ fixes_applied=fixes,
50
+ )
51
+
52
+ if get_fix and not result.timed_out:
53
+ fix_instruction = get_fix(last_stdout, last_stderr)
54
+ if fix_instruction and fix_instruction.strip():
55
+ fixes.append(fix_instruction[:500])
56
+ # get_fix may return "path: <path>\\ncontent: <content>" or similar
57
+ # A real implementation would parse and call sandbox.write_file;
58
+ # for now we only record that a fix was requested.
59
+ # Caller can pass a get_fix that actually writes files.
60
+ if _apply_fix_instruction(sandbox, fix_instruction):
61
+ continue
62
+ break
63
+
64
+ return DebugResult(
65
+ passed=False,
66
+ iterations=max_iterations,
67
+ last_stdout=last_stdout,
68
+ last_stderr=last_stderr,
69
+ fixes_applied=fixes,
70
+ )
71
+
72
+
73
+ def _apply_fix_instruction(sandbox: Sandbox, instruction: str) -> bool:
74
+ """
75
+ Try to parse a fix instruction (e.g. "FILE: path\\nCONTENT: ...") and apply via sandbox.
76
+ Returns True if something was applied.
77
+ """
78
+ lines = instruction.strip().split("\n")
79
+ path = None
80
+ content_lines: list[str] = []
81
+ in_content = False
82
+ for line in lines:
83
+ if line.upper().startswith("FILE:") or line.upper().startswith("PATH:"):
84
+ path = line.split(":", 1)[1].strip()
85
+ in_content = False
86
+ elif line.upper().startswith("CONTENT:") or line.upper().startswith("CODE:"):
87
+ in_content = True
88
+ content_lines.append(line.split(":", 1)[1] if ":" in line else "")
89
+ elif in_content and path:
90
+ content_lines.append(line)
91
+ if path and content_lines:
92
+ content = "\n".join(content_lines)
93
+ r = sandbox.write_file(path, content)
94
+ return r.success
95
+ return False
@@ -0,0 +1,138 @@
1
+ """
2
+ Codebase index for dev agents: AST parsing, dependency graph, symbol search.
3
+
4
+ Reuses patterns from devsper.tools.code_intelligence; provides a single API
5
+ for agents to understand the generated codebase.
6
+ """
7
+
8
+ import ast
9
+ from pathlib import Path
10
+ from dataclasses import dataclass, field
11
+
12
+
13
+ @dataclass
14
+ class SymbolInfo:
15
+ """A top-level symbol (function or class) in a file."""
16
+
17
+ name: str
18
+ kind: str # "function" or "class"
19
+ file: str
20
+ line: int
21
+
22
+
23
+ @dataclass
24
+ class DependencyEdge:
25
+ """Module A depends on module B (import)."""
26
+
27
+ from_module: str
28
+ to_module: str
29
+
30
+
31
+ @dataclass
32
+ class RepoIndex:
33
+ """
34
+ Index of a repo: files, symbols, dependencies.
35
+ Built via AST parsing; supports dependency graph and symbol search.
36
+ """
37
+
38
+ root: Path
39
+ files: list[str] = field(default_factory=list)
40
+ symbols: list[SymbolInfo] = field(default_factory=list)
41
+ edges: list[DependencyEdge] = field(default_factory=list)
42
+ docstrings: dict[str, str] = field(default_factory=dict)
43
+
44
+ def _rel_path(self, p: Path) -> str:
45
+ return str(p.relative_to(self.root)).replace("\\", "/")
46
+
47
+ def _imports(self, p: Path) -> list[str]:
48
+ out: list[str] = []
49
+ try:
50
+ tree = ast.parse(p.read_text(encoding="utf-8", errors="replace"))
51
+ for node in ast.walk(tree):
52
+ if isinstance(node, ast.Import):
53
+ for alias in node.names:
54
+ out.append(alias.name.split(".")[0])
55
+ elif isinstance(node, ast.ImportFrom):
56
+ if node.module:
57
+ out.append(node.module.split(".")[0])
58
+ except (SyntaxError, OSError):
59
+ pass
60
+ return list(dict.fromkeys(out))
61
+
62
+ def _extract_symbols(self, p: Path) -> list[SymbolInfo]:
63
+ syms: list[SymbolInfo] = []
64
+ try:
65
+ tree = ast.parse(p.read_text(encoding="utf-8", errors="replace"))
66
+ doc = ast.get_docstring(tree)
67
+ if doc:
68
+ self.docstrings[self._rel_path(p)] = doc[:500]
69
+ for node in ast.walk(tree):
70
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
71
+ if node.col_offset == 0:
72
+ kind = "class" if isinstance(node, ast.ClassDef) else "function"
73
+ syms.append(
74
+ SymbolInfo(
75
+ name=node.name,
76
+ kind=kind,
77
+ file=self._rel_path(p),
78
+ line=node.lineno or 0,
79
+ )
80
+ )
81
+ except (SyntaxError, OSError):
82
+ pass
83
+ return syms
84
+
85
+ def build(self, max_files: int = 200) -> None:
86
+ """Build index from repo root: parse AST, collect symbols and dependencies."""
87
+ self.files = []
88
+ self.symbols = []
89
+ self.edges = []
90
+ self.docstrings = {}
91
+ count = 0
92
+ for p in self.root.rglob("*.py"):
93
+ if count >= max_files:
94
+ break
95
+ if not p.is_file() or "__pycache__" in p.parts:
96
+ continue
97
+ count += 1
98
+ rel = self._rel_path(p)
99
+ self.files.append(rel)
100
+ mod = rel.replace(".py", "").replace("/", ".")
101
+ for imp in self._imports(p):
102
+ self.edges.append(DependencyEdge(from_module=mod, to_module=imp))
103
+ self.symbols.extend(self._extract_symbols(p))
104
+
105
+ def dependency_graph(self) -> dict[str, list[str]]:
106
+ """Return adjacency list: module -> list of imported modules."""
107
+ out: dict[str, list[str]] = {}
108
+ for e in self.edges:
109
+ out.setdefault(e.from_module, []).append(e.to_module)
110
+ return out
111
+
112
+ def symbol_search(self, name: str) -> list[SymbolInfo]:
113
+ """Find symbols whose name contains the given string."""
114
+ name_lower = name.lower()
115
+ return [s for s in self.symbols if name_lower in s.name.lower()]
116
+
117
+ def file_search(self, substring: str) -> list[str]:
118
+ """Find files whose path contains the given string."""
119
+ sub = substring.lower()
120
+ return [f for f in self.files if sub in f.lower()]
121
+
122
+ def summary(self) -> str:
123
+ """Human-readable summary for agents."""
124
+ lines = [
125
+ f"Root: {self.root}",
126
+ f"Files: {len(self.files)}",
127
+ f"Symbols: {len(self.symbols)}",
128
+ f"Dependencies: {len(self.edges)} edges",
129
+ "",
130
+ "Top-level files:",
131
+ ]
132
+ for f in sorted(self.files)[:30]:
133
+ lines.append(f" {f}")
134
+ lines.append("")
135
+ lines.append("Symbols (sample):")
136
+ for s in self.symbols[:40]:
137
+ lines.append(f" {s.kind} {s.name} @ {s.file}:{s.line}")
138
+ return "\n".join(lines)
devsper/dev/sandbox.py ADDED
@@ -0,0 +1,203 @@
1
+ """
2
+ Code sandbox: write files, run shell commands, run tests, install dependencies.
3
+
4
+ Enforces timeout and resource limits for safe autonomous execution.
5
+ """
6
+
7
+ import os
8
+ import subprocess
9
+ import sys
10
+ import shlex
11
+ from pathlib import Path
12
+ try:
13
+ import resource
14
+ except ImportError:
15
+ resource = None # Windows has no resource module
16
+ from dataclasses import dataclass, field
17
+
18
+
19
+ @dataclass
20
+ class SandboxResult:
21
+ """Result of a sandboxed command or operation."""
22
+
23
+ success: bool
24
+ stdout: str = ""
25
+ stderr: str = ""
26
+ returncode: int = 0
27
+ timed_out: bool = False
28
+ error: str | None = None
29
+
30
+
31
+ @dataclass
32
+ class SandboxLimits:
33
+ """Resource limits for sandbox execution."""
34
+
35
+ timeout_seconds: int = 300
36
+ max_memory_mb: int | None = 100
37
+ max_cpu_seconds: int | None = 60 # soft CPU time
38
+
39
+
40
+ class Sandbox:
41
+ """
42
+ Isolated execution environment with timeout and resource limits.
43
+
44
+ Capabilities: write files, run shell commands, run tests, install dependencies.
45
+ """
46
+
47
+ def __init__(self, root: str | Path, limits: SandboxLimits | None = None):
48
+ self.root = Path(root).resolve()
49
+ self.limits = limits or SandboxLimits()
50
+
51
+ def _cwd(self) -> Path:
52
+ if not self.root.exists():
53
+ self.root.mkdir(parents=True, exist_ok=True)
54
+ return self.root
55
+
56
+ def write_file(self, path: str, content: str) -> SandboxResult:
57
+ """Write content to a file under sandbox root. Creates parent dirs."""
58
+ try:
59
+ p = (self.root / path).resolve()
60
+ if not str(p).startswith(str(self.root)):
61
+ return SandboxResult(
62
+ success=False,
63
+ error="Path escapes sandbox root",
64
+ )
65
+ p.parent.mkdir(parents=True, exist_ok=True)
66
+ p.write_text(content, encoding="utf-8")
67
+ return SandboxResult(success=True, stdout=f"Wrote {len(content)} chars to {path}")
68
+ except Exception as e:
69
+ return SandboxResult(success=False, error=str(e))
70
+
71
+ def read_file(self, path: str) -> SandboxResult:
72
+ """Read file content under sandbox root."""
73
+ try:
74
+ p = (self.root / path).resolve()
75
+ if not str(p).startswith(str(self.root)) or not p.is_file():
76
+ return SandboxResult(success=False, error=f"File not in sandbox or missing: {path}")
77
+ content = p.read_text(encoding="utf-8", errors="replace")
78
+ return SandboxResult(success=True, stdout=content)
79
+ except Exception as e:
80
+ return SandboxResult(success=False, error=str(e))
81
+
82
+ def run(
83
+ self,
84
+ command: str | list[str],
85
+ cwd: str | Path | None = None,
86
+ env: dict | None = None,
87
+ timeout_seconds: int | None = None,
88
+ ) -> SandboxResult:
89
+ """
90
+ Run a shell command in the sandbox with timeout and optional resource limits.
91
+ """
92
+ timeout = timeout_seconds or self.limits.timeout_seconds
93
+ work_dir = Path(cwd) if cwd else self._cwd()
94
+ if not work_dir.exists():
95
+ work_dir.mkdir(parents=True, exist_ok=True)
96
+ full_env = os.environ.copy()
97
+ if env:
98
+ full_env.update(env)
99
+
100
+ if isinstance(command, str):
101
+ cmd_list = shlex.split(command)
102
+ else:
103
+ cmd_list = list(command)
104
+
105
+ def _set_limits() -> None:
106
+ if resource is None:
107
+ return
108
+ if self.limits.max_memory_mb is not None:
109
+ try:
110
+ resource.setrlimit(
111
+ resource.RLIMIT_AS,
112
+ (self.limits.max_memory_mb * 1024 * 1024, -1),
113
+ )
114
+ except (ValueError, OSError):
115
+ pass
116
+ if self.limits.max_cpu_seconds is not None:
117
+ try:
118
+ resource.setrlimit(
119
+ resource.RLIMIT_CPU,
120
+ (self.limits.max_cpu_seconds, -1),
121
+ )
122
+ except (ValueError, OSError):
123
+ pass
124
+
125
+ try:
126
+ proc = subprocess.run(
127
+ cmd_list,
128
+ cwd=str(work_dir),
129
+ env=full_env,
130
+ capture_output=True,
131
+ text=True,
132
+ timeout=timeout,
133
+ preexec_fn=_set_limits if os.name != "nt" else None,
134
+ )
135
+ out = (proc.stdout or "") + (
136
+ "\n--- stderr ---\n" + (proc.stderr or "") if proc.stderr else ""
137
+ )
138
+ return SandboxResult(
139
+ success=proc.returncode == 0,
140
+ stdout=out.strip(),
141
+ stderr=proc.stderr or "",
142
+ returncode=proc.returncode,
143
+ )
144
+ except subprocess.TimeoutExpired:
145
+ return SandboxResult(
146
+ success=False,
147
+ timed_out=True,
148
+ error=f"Command timed out after {timeout}s",
149
+ )
150
+ except Exception as e:
151
+ return SandboxResult(success=False, error=str(e))
152
+
153
+ def install_dependencies(self, backend: bool = True, frontend: bool = False) -> SandboxResult:
154
+ """Install backend (pip) and optionally frontend (npm) dependencies."""
155
+ results: list[str] = []
156
+ if backend:
157
+ req = self.root / "backend" / "requirements.txt"
158
+ if not req.exists():
159
+ req = self.root / "requirements.txt"
160
+ if req.exists():
161
+ # Prefer uv when available (e.g. venvs without pip); else pip
162
+ r = self.run(
163
+ ["uv", "pip", "install", "-r", str(req)],
164
+ timeout_seconds=120,
165
+ )
166
+ if not r.success and r.error:
167
+ python = os.environ.get("PYTHON") or sys.executable or "python3"
168
+ r = self.run(
169
+ [python, "-m", "pip", "install", "-r", str(req)],
170
+ timeout_seconds=120,
171
+ )
172
+ results.append(f"pip: success={r.success}" + (f" {r.error}" if r.error else ""))
173
+ if frontend:
174
+ pkg = self.root / "frontend" / "package.json"
175
+ if pkg.exists():
176
+ r = self.run("npm install", cwd=str(self.root / "frontend"), timeout_seconds=120)
177
+ results.append(f"npm: success={r.success}" + (f" {r.error}" if r.error else ""))
178
+ return SandboxResult(
179
+ success=all("success=True" in s for s in results) or not results,
180
+ stdout="\n".join(results) or "No dependency files found",
181
+ )
182
+
183
+ def run_tests(self, path: str = "tests") -> SandboxResult:
184
+ """Run tests (pytest for backend, or path to test dir)."""
185
+ test_dir = self.root / path
186
+ if not test_dir.exists():
187
+ test_dir = self.root / "backend" / "tests"
188
+ if not test_dir.exists():
189
+ test_dir = self.root
190
+ env = os.environ.copy()
191
+ backend_dir = self.root / "backend"
192
+ if backend_dir.exists():
193
+ env["PYTHONPATH"] = str(backend_dir) + os.pathsep + env.get("PYTHONPATH", "")
194
+ python = os.environ.get("PYTHON") or sys.executable or "python3"
195
+ cmd = [
196
+ python,
197
+ "-m",
198
+ "pytest",
199
+ str(test_dir),
200
+ "-v",
201
+ "--tb=short",
202
+ ]
203
+ return self.run(cmd, timeout_seconds=60, env=env)