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
@@ -0,0 +1,62 @@
1
+ """Estimate test coverage: ratio of test files to source files and test/source layout."""
2
+
3
+ from pathlib import Path
4
+
5
+ from devsper.tools.base import Tool
6
+ from devsper.tools.registry import register
7
+
8
+
9
+ class TestCoverageEstimatorTool(Tool):
10
+ """
11
+ Estimate test coverage heuristically: count test files vs source files and list test dirs.
12
+ """
13
+
14
+ name = "test_coverage_estimator"
15
+ description = "Estimate test coverage: test file count vs source file count and layout."
16
+ input_schema = {
17
+ "type": "object",
18
+ "properties": {
19
+ "path": {"type": "string", "description": "Root path of the repo"},
20
+ "test_dir_names": {
21
+ "type": "array",
22
+ "items": {"type": "string"},
23
+ "description": "Dir names that indicate tests (default: tests, test)",
24
+ },
25
+ },
26
+ "required": ["path"],
27
+ }
28
+
29
+ def run(self, **kwargs) -> str:
30
+ path = kwargs.get("path")
31
+ test_dir_names = kwargs.get("test_dir_names") or ["tests", "test"]
32
+ if not path or not isinstance(path, str):
33
+ return "Error: path must be a non-empty string"
34
+ root = Path(path).resolve()
35
+ if not root.exists() or not root.is_dir():
36
+ return f"Error: path must be an existing directory: {path}"
37
+ test_dirs = set(n.lower() for n in test_dir_names if isinstance(n, str))
38
+ source_files = []
39
+ test_files = []
40
+ for p in root.rglob("*.py"):
41
+ if not p.is_file():
42
+ continue
43
+ rel = p.relative_to(root)
44
+ parts = [x.lower() for x in rel.parts]
45
+ is_test = any(d in parts for d in test_dirs) or rel.name.startswith("test_") or rel.name.endswith("_test.py")
46
+ if is_test:
47
+ test_files.append(str(rel))
48
+ else:
49
+ source_files.append(str(rel))
50
+ ratio = len(test_files) / len(source_files) if source_files else 0
51
+ lines = [
52
+ "Test coverage estimate",
53
+ "=" * 40,
54
+ f"Source files: {len(source_files)}",
55
+ f"Test files: {len(test_files)}",
56
+ f"Ratio (test/source): {ratio:.2f}",
57
+ "Test dirs/files (sample): " + ", ".join(test_files[:15]),
58
+ ]
59
+ return "\n".join(lines)
60
+
61
+
62
+ register(TestCoverageEstimatorTool())
@@ -0,0 +1,12 @@
1
+ """Coding tools: run Python, format, lint, analyze, refactor, etc."""
2
+
3
+ from devsper.tools.coding.run_python import RunPythonTool
4
+ from devsper.tools.coding.format_python import FormatPythonTool
5
+ from devsper.tools.coding.lint_python import LintPythonTool
6
+ from devsper.tools.coding.generate_unit_tests import GenerateUnitTestsTool
7
+ from devsper.tools.coding.analyze_code_complexity import AnalyzeCodeComplexityTool
8
+ from devsper.tools.coding.extract_functions import ExtractFunctionsTool
9
+ from devsper.tools.coding.generate_docstrings import GenerateDocstringsTool
10
+ from devsper.tools.coding.refactor_function import RefactorFunctionTool
11
+ from devsper.tools.coding.dependency_analyzer import DependencyAnalyzerTool
12
+ from devsper.tools.coding.repo_structure_map import RepoStructureMapTool
@@ -0,0 +1,48 @@
1
+ """Analyze code complexity: line count, cyclomatic complexity approximation, nesting."""
2
+
3
+ import ast
4
+
5
+ from devsper.tools.base import Tool
6
+ from devsper.tools.registry import register
7
+
8
+
9
+ class AnalyzeCodeComplexityTool(Tool):
10
+ """Report simple complexity metrics: lines, number of branches, max nesting."""
11
+
12
+ name = "analyze_code_complexity"
13
+ description = "Analyze Python code: line count, branch count, nesting depth."
14
+ input_schema = {
15
+ "type": "object",
16
+ "properties": {"code": {"type": "string", "description": "Python code to analyze"}},
17
+ "required": ["code"],
18
+ }
19
+
20
+ def run(self, **kwargs) -> str:
21
+ code = kwargs.get("code")
22
+ if not code or not isinstance(code, str):
23
+ return "Error: code must be a non-empty string"
24
+ try:
25
+ tree = ast.parse(code)
26
+ lines = len(code.splitlines())
27
+ branches = 0
28
+ max_nest = 0
29
+
30
+ def visit(node: ast.AST, depth: int) -> None:
31
+ nonlocal branches, max_nest
32
+ max_nest = max(max_nest, depth)
33
+ if isinstance(node, (ast.If, ast.While, ast.For, ast.With, ast.Try, ast.Assert)):
34
+ branches += 1
35
+ if isinstance(node, ast.comprehension):
36
+ branches += 1
37
+ for child in ast.iter_child_nodes(node):
38
+ visit(child, depth + 1)
39
+
40
+ visit(tree, 0)
41
+ return f"Lines: {lines}\nBranches (approx): {branches}\nMax nesting depth: {max_nest}"
42
+ except SyntaxError as e:
43
+ return f"Syntax error: {e}"
44
+ except Exception as e:
45
+ return f"Error: {e}"
46
+
47
+
48
+ register(AnalyzeCodeComplexityTool())
@@ -0,0 +1,42 @@
1
+ """Analyze import dependencies in Python code."""
2
+
3
+ import ast
4
+
5
+ from devsper.tools.base import Tool
6
+ from devsper.tools.registry import register
7
+
8
+
9
+ class DependencyAnalyzerTool(Tool):
10
+ """List top-level import names (modules and aliases) from Python code."""
11
+
12
+ name = "dependency_analyzer"
13
+ description = "Extract import statements and dependency names from Python code."
14
+ input_schema = {
15
+ "type": "object",
16
+ "properties": {"code": {"type": "string", "description": "Python source code"}},
17
+ "required": ["code"],
18
+ }
19
+
20
+ def run(self, **kwargs) -> str:
21
+ code = kwargs.get("code")
22
+ if not code or not isinstance(code, str):
23
+ return "Error: code must be a non-empty string"
24
+ try:
25
+ tree = ast.parse(code)
26
+ imports = []
27
+ for node in ast.walk(tree):
28
+ if isinstance(node, ast.Import):
29
+ for alias in node.names:
30
+ imports.append(f"import {alias.name}" + (f" as {alias.asname}" if alias.asname else ""))
31
+ elif isinstance(node, ast.ImportFrom):
32
+ mod = node.module or ""
33
+ names = ", ".join(a.name + (f" as {a.asname}" if a.asname else "") for a in node.names)
34
+ imports.append(f"from {mod} import {names}")
35
+ return "\n".join(imports) if imports else "No imports found."
36
+ except SyntaxError as e:
37
+ return f"Syntax error: {e}"
38
+ except Exception as e:
39
+ return f"Error: {e}"
40
+
41
+
42
+ register(DependencyAnalyzerTool())
@@ -0,0 +1,38 @@
1
+ """Extract function names and signatures from Python code."""
2
+
3
+ import ast
4
+
5
+ from devsper.tools.base import Tool
6
+ from devsper.tools.registry import register
7
+
8
+
9
+ class ExtractFunctionsTool(Tool):
10
+ """List function names and their parameter names from Python code."""
11
+
12
+ name = "extract_functions"
13
+ description = "Extract function names and parameter lists from Python code."
14
+ input_schema = {
15
+ "type": "object",
16
+ "properties": {"code": {"type": "string", "description": "Python source code"}},
17
+ "required": ["code"],
18
+ }
19
+
20
+ def run(self, **kwargs) -> str:
21
+ code = kwargs.get("code")
22
+ if not code or not isinstance(code, str):
23
+ return "Error: code must be a non-empty string"
24
+ try:
25
+ tree = ast.parse(code)
26
+ result = []
27
+ for node in ast.walk(tree):
28
+ if isinstance(node, ast.FunctionDef):
29
+ args = [a.arg for a in node.args.args]
30
+ result.append(f"{node.name}({', '.join(args)})")
31
+ return "\n".join(result) if result else "No functions found."
32
+ except SyntaxError as e:
33
+ return f"Syntax error: {e}"
34
+ except Exception as e:
35
+ return f"Error: {e}"
36
+
37
+
38
+ register(ExtractFunctionsTool())
@@ -0,0 +1,50 @@
1
+ """Format Python code using black if available, else return as-is."""
2
+
3
+ import subprocess
4
+ import tempfile
5
+ from pathlib import Path
6
+
7
+ from devsper.tools.base import Tool
8
+ from devsper.tools.registry import register
9
+
10
+
11
+ class FormatPythonTool(Tool):
12
+ """Format Python code. Uses black if installed, otherwise returns code unchanged."""
13
+
14
+ name = "format_python"
15
+ description = "Format Python code with black. Returns formatted code or original if black not installed."
16
+ input_schema = {
17
+ "type": "object",
18
+ "properties": {"code": {"type": "string", "description": "Python code to format"}},
19
+ "required": ["code"],
20
+ }
21
+
22
+ def run(self, **kwargs) -> str:
23
+ code = kwargs.get("code")
24
+ if code is None:
25
+ return "Error: code is required"
26
+ if not isinstance(code, str):
27
+ code = str(code)
28
+ try:
29
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
30
+ f.write(code)
31
+ path = f.name
32
+ try:
33
+ result = subprocess.run(
34
+ ["black", "-q", path],
35
+ capture_output=True,
36
+ text=True,
37
+ timeout=10,
38
+ )
39
+ if result.returncode == 0:
40
+ return Path(path).read_text(encoding="utf-8")
41
+ return code
42
+ finally:
43
+ Path(path).unlink(missing_ok=True)
44
+ except FileNotFoundError:
45
+ return code
46
+ except Exception as e:
47
+ return f"Error: {e}\nOriginal code:\n{code}"
48
+
49
+
50
+ register(FormatPythonTool())
@@ -0,0 +1,40 @@
1
+ """Add or suggest docstring placeholders for functions that lack them."""
2
+
3
+ import ast
4
+
5
+ from devsper.tools.base import Tool
6
+ from devsper.tools.registry import register
7
+
8
+
9
+ class GenerateDocstringsTool(Tool):
10
+ """List functions missing docstrings and suggest a one-line placeholder."""
11
+
12
+ name = "generate_docstrings"
13
+ description = "Find functions without docstrings and suggest a placeholder docstring."
14
+ input_schema = {
15
+ "type": "object",
16
+ "properties": {"code": {"type": "string", "description": "Python source code"}},
17
+ "required": ["code"],
18
+ }
19
+
20
+ def run(self, **kwargs) -> str:
21
+ code = kwargs.get("code")
22
+ if not code or not isinstance(code, str):
23
+ return "Error: code must be a non-empty string"
24
+ try:
25
+ tree = ast.parse(code)
26
+ result = []
27
+ for node in ast.walk(tree):
28
+ if isinstance(node, ast.FunctionDef):
29
+ doc = ast.get_docstring(node)
30
+ if not doc:
31
+ args = [a.arg for a in node.args.args]
32
+ result.append(f"{node.name}({', '.join(args)}): \"\"\"TODO: describe.\"\"\"")
33
+ return "\n".join(result) if result else "All functions have docstrings."
34
+ except SyntaxError as e:
35
+ return f"Syntax error: {e}"
36
+ except Exception as e:
37
+ return f"Error: {e}"
38
+
39
+
40
+ register(GenerateDocstringsTool())
@@ -0,0 +1,42 @@
1
+ """Generate skeleton unit test code for a function (template-based)."""
2
+
3
+ import ast
4
+
5
+ from devsper.tools.base import Tool
6
+ from devsper.tools.registry import register
7
+
8
+
9
+ class GenerateUnitTestsTool(Tool):
10
+ """Generate a minimal pytest-style test skeleton for a function. No LLM."""
11
+
12
+ name = "generate_unit_tests"
13
+ description = "Generate skeleton pytest tests for a function. Uses function name and params."
14
+ input_schema = {
15
+ "type": "object",
16
+ "properties": {"code": {"type": "string", "description": "Python function or module code"}},
17
+ "required": ["code"],
18
+ }
19
+
20
+ def run(self, **kwargs) -> str:
21
+ code = kwargs.get("code")
22
+ if not code or not isinstance(code, str):
23
+ return "Error: code must be a non-empty string"
24
+ try:
25
+ tree = ast.parse(code)
26
+ tests = []
27
+ for node in ast.walk(tree):
28
+ if isinstance(node, ast.FunctionDef):
29
+ params = [a.arg for a in node.args.args if a.arg != "self"]
30
+ test_name = f"test_{node.name}"
31
+ args_str = ", ".join(repr(f"arg_{p}") for p in params)
32
+ tests.append(f"def {test_name}():\n assert {node.name}({args_str}) is not None # TODO\n")
33
+ if not tests:
34
+ return "No function definitions found in code."
35
+ return "import pytest\n\n" + "\n".join(tests)
36
+ except SyntaxError as e:
37
+ return f"Syntax error: {e}"
38
+ except Exception as e:
39
+ return f"Error: {e}"
40
+
41
+
42
+ register(GenerateUnitTestsTool())
@@ -0,0 +1,51 @@
1
+ """Lint Python code using ruff if available."""
2
+
3
+ import subprocess
4
+ import tempfile
5
+ from pathlib import Path
6
+
7
+ from devsper.tools.base import Tool
8
+ from devsper.tools.registry import register
9
+
10
+
11
+ class LintPythonTool(Tool):
12
+ """Lint Python code. Uses ruff if installed; otherwise returns a message."""
13
+
14
+ name = "lint_python"
15
+ description = "Lint Python code with ruff. Returns lint output or suggests installing ruff."
16
+ input_schema = {
17
+ "type": "object",
18
+ "properties": {"code": {"type": "string", "description": "Python code to lint"}},
19
+ "required": ["code"],
20
+ }
21
+
22
+ def run(self, **kwargs) -> str:
23
+ code = kwargs.get("code")
24
+ if code is None:
25
+ return "Error: code is required"
26
+ if not isinstance(code, str):
27
+ code = str(code)
28
+ try:
29
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
30
+ f.write(code)
31
+ path = f.name
32
+ try:
33
+ result = subprocess.run(
34
+ ["ruff", "check", path],
35
+ capture_output=True,
36
+ text=True,
37
+ timeout=10,
38
+ )
39
+ out = result.stdout or result.stderr or ""
40
+ if result.returncode == 0 and not out:
41
+ return "No issues found."
42
+ return out.strip() or "No output from ruff."
43
+ finally:
44
+ Path(path).unlink(missing_ok=True)
45
+ except FileNotFoundError:
46
+ return "ruff not installed. Install with: pip install ruff"
47
+ except Exception as e:
48
+ return f"Error: {e}"
49
+
50
+
51
+ register(LintPythonTool())
@@ -0,0 +1,41 @@
1
+ """Suggest a refactor: rename and normalize style (placeholder / heuristic)."""
2
+
3
+ import ast
4
+
5
+ from devsper.tools.base import Tool
6
+ from devsper.tools.registry import register
7
+
8
+
9
+ class RefactorFunctionTool(Tool):
10
+ """Suggest simple refactors: PEP8 name style, single return. No code rewrite."""
11
+
12
+ name = "refactor_function"
13
+ description = "Suggest refactoring tips for a function: naming, single return."
14
+ input_schema = {
15
+ "type": "object",
16
+ "properties": {"code": {"type": "string", "description": "Python function code"}},
17
+ "required": ["code"],
18
+ }
19
+
20
+ def run(self, **kwargs) -> str:
21
+ code = kwargs.get("code")
22
+ if not code or not isinstance(code, str):
23
+ return "Error: code must be a non-empty string"
24
+ try:
25
+ tree = ast.parse(code)
26
+ tips = []
27
+ for node in ast.walk(tree):
28
+ if isinstance(node, ast.FunctionDef):
29
+ if not node.name.islower() and "_" not in node.name:
30
+ tips.append(f"Consider snake_case for function name: {node.name}")
31
+ returns = sum(1 for n in ast.walk(node) if isinstance(n, ast.Return))
32
+ if returns > 1:
33
+ tips.append("Multiple returns: consider single exit point.")
34
+ return "\n".join(tips) if tips else "No refactor suggestions."
35
+ except SyntaxError as e:
36
+ return f"Syntax error: {e}"
37
+ except Exception as e:
38
+ return f"Error: {e}"
39
+
40
+
41
+ register(RefactorFunctionTool())
@@ -0,0 +1,54 @@
1
+ """Map directory structure (files and folders) under a path."""
2
+
3
+ from pathlib import Path
4
+
5
+ from devsper.tools.base import Tool
6
+ from devsper.tools.registry import register
7
+
8
+
9
+ class RepoStructureMapTool(Tool):
10
+ """List directory tree structure (files and dirs) under a given path."""
11
+
12
+ name = "repo_structure_map"
13
+ description = "Map repo/directory structure. Returns indented list of files and dirs."
14
+ input_schema = {
15
+ "type": "object",
16
+ "properties": {
17
+ "path": {"type": "string", "description": "Root path to map"},
18
+ "max_depth": {"type": "integer", "description": "Max depth (default 4)"},
19
+ },
20
+ "required": ["path"],
21
+ }
22
+
23
+ def run(self, **kwargs) -> str:
24
+ path = kwargs.get("path")
25
+ max_depth = kwargs.get("max_depth", 4)
26
+ if not path or not isinstance(path, str):
27
+ return "Error: path must be a non-empty string"
28
+ if not isinstance(max_depth, int) or max_depth < 1:
29
+ max_depth = 4
30
+ root = Path(path)
31
+ if not root.exists() or not root.is_dir():
32
+ return f"Error: path must be an existing directory: {path}"
33
+ lines = []
34
+
35
+ def walk(p: Path, prefix: str, depth: int) -> None:
36
+ if depth > max_depth:
37
+ return
38
+ try:
39
+ entries = sorted(p.iterdir(), key=lambda x: (not x.is_dir(), x.name.lower()))
40
+ except PermissionError:
41
+ return
42
+ for i, e in enumerate(entries):
43
+ is_last = i == len(entries) - 1
44
+ branch = "└── " if is_last else "├── "
45
+ lines.append(prefix + branch + e.name)
46
+ if e.is_dir():
47
+ ext = " " if is_last else "│ "
48
+ walk(e, prefix + ext, depth + 1)
49
+ lines.append(root.name + "/")
50
+ walk(root, "", 1)
51
+ return "\n".join(lines)
52
+
53
+
54
+ register(RepoStructureMapTool())
@@ -0,0 +1,53 @@
1
+ """Run Python code in a subprocess and return stdout/stderr."""
2
+
3
+ import subprocess
4
+ import tempfile
5
+ from pathlib import Path
6
+
7
+ from devsper.tools.base import Tool
8
+ from devsper.tools.registry import register
9
+
10
+
11
+ class RunPythonTool(Tool):
12
+ """Execute Python code in a subprocess. Returns combined stdout and stderr."""
13
+
14
+ name = "run_python"
15
+ description = "Run Python code string in a subprocess. Returns stdout and stderr."
16
+ input_schema = {
17
+ "type": "object",
18
+ "properties": {"code": {"type": "string", "description": "Python code to execute"}},
19
+ "required": ["code"],
20
+ }
21
+
22
+ def run(self, **kwargs) -> str:
23
+ code = kwargs.get("code")
24
+ if not code or not isinstance(code, str):
25
+ return "Error: code must be a non-empty string"
26
+ try:
27
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
28
+ f.write(code)
29
+ path = f.name
30
+ try:
31
+ result = subprocess.run(
32
+ ["python", path],
33
+ capture_output=True,
34
+ text=True,
35
+ timeout=30,
36
+ cwd=Path(path).parent,
37
+ )
38
+ out = result.stdout or ""
39
+ err = result.stderr or ""
40
+ if err:
41
+ out = out + "\n--- stderr ---\n" + err
42
+ if result.returncode != 0:
43
+ out = f"[exit code {result.returncode}]\n" + out
44
+ return out.strip() or "(no output)"
45
+ finally:
46
+ Path(path).unlink(missing_ok=True)
47
+ except subprocess.TimeoutExpired:
48
+ return "Error: execution timed out (30s)"
49
+ except Exception as e:
50
+ return f"Error: {e}"
51
+
52
+
53
+ register(RunPythonTool())
@@ -0,0 +1,12 @@
1
+ """Data tools: CSV, JSON, dataframe stats, schema, sampling, etc."""
2
+
3
+ from devsper.tools.data.csv_summary import CsvSummaryTool
4
+ from devsper.tools.data.json_query import JsonQueryTool
5
+ from devsper.tools.data.json_pretty_print import JsonPrettyPrintTool
6
+ from devsper.tools.data.dataframe_stats import DataframeStatsTool
7
+ from devsper.tools.data.dataframe_filter import DataframeFilterTool
8
+ from devsper.tools.data.dataframe_groupby import DataframeGroupbyTool
9
+ from devsper.tools.data.column_type_detection import ColumnTypeDetectionTool
10
+ from devsper.tools.data.missing_value_report import MissingValueReportTool
11
+ from devsper.tools.data.dataset_sampling import DatasetSamplingTool
12
+ from devsper.tools.data.dataset_schema import DatasetSchemaTool
@@ -0,0 +1,64 @@
1
+ """Detect column types (numeric, string, etc.) for a CSV. Works with stdlib or pandas."""
2
+
3
+ import csv
4
+ from pathlib import Path
5
+
6
+ from devsper.tools.base import Tool
7
+ from devsper.tools.registry import register
8
+
9
+
10
+ class ColumnTypeDetectionTool(Tool):
11
+ """Infer column types from a CSV: int, float, or str based on sample rows."""
12
+
13
+ name = "column_type_detection"
14
+ description = "Detect column types (int, float, str) from a CSV file."
15
+ input_schema = {
16
+ "type": "object",
17
+ "properties": {"path": {"type": "string", "description": "Path to CSV file"}},
18
+ "required": ["path"],
19
+ }
20
+
21
+ def run(self, **kwargs) -> str:
22
+ path = kwargs.get("path")
23
+ if not path or not isinstance(path, str):
24
+ return "Error: path must be a non-empty string"
25
+ p = Path(path)
26
+ if not p.exists() or not p.is_file():
27
+ return f"Error: file not found: {path}"
28
+ try:
29
+ with p.open(encoding="utf-8", errors="replace", newline="") as f:
30
+ reader = csv.reader(f)
31
+ rows = list(reader)
32
+ if not rows:
33
+ return "Empty CSV."
34
+ header = rows[0]
35
+ data = rows[1:][:100]
36
+ result = []
37
+ for col_idx, col_name in enumerate(header):
38
+ values = [row[col_idx] if col_idx < len(row) else "" for row in data]
39
+ types = []
40
+ for v in values:
41
+ v = (v or "").strip()
42
+ if not v:
43
+ continue
44
+ try:
45
+ int(v)
46
+ types.append("int")
47
+ except ValueError:
48
+ try:
49
+ float(v)
50
+ types.append("float")
51
+ except ValueError:
52
+ types.append("str")
53
+ inferred = "str"
54
+ if all(t == "int" for t in types):
55
+ inferred = "int"
56
+ elif types and "str" not in types:
57
+ inferred = "float"
58
+ result.append(f"{col_name}: {inferred}")
59
+ return "\n".join(result)
60
+ except Exception as e:
61
+ return f"Error: {e}"
62
+
63
+
64
+ register(ColumnTypeDetectionTool())
@@ -0,0 +1,52 @@
1
+ """Summarize a CSV file: row count, column names, sample rows."""
2
+
3
+ import csv
4
+ from pathlib import Path
5
+
6
+ from devsper.tools.base import Tool
7
+ from devsper.tools.registry import register
8
+
9
+
10
+ class CsvSummaryTool(Tool):
11
+ """Summarize a CSV file: columns, row count, and first few rows."""
12
+
13
+ name = "csv_summary"
14
+ description = "Summarize a CSV file: column names, row count, and first N rows."
15
+ input_schema = {
16
+ "type": "object",
17
+ "properties": {
18
+ "path": {"type": "string", "description": "Path to the CSV file"},
19
+ "sample_rows": {"type": "integer", "description": "Number of rows to show (default 5)"},
20
+ },
21
+ "required": ["path"],
22
+ }
23
+
24
+ def run(self, **kwargs) -> str:
25
+ path = kwargs.get("path")
26
+ sample_rows = kwargs.get("sample_rows", 5)
27
+ if not path or not isinstance(path, str):
28
+ return "Error: path must be a non-empty string"
29
+ if not isinstance(sample_rows, int) or sample_rows < 1:
30
+ sample_rows = 5
31
+ p = Path(path)
32
+ if not p.exists() or not p.is_file():
33
+ return f"Error: file not found: {path}"
34
+ try:
35
+ with p.open(encoding="utf-8", errors="replace", newline="") as f:
36
+ reader = csv.reader(f)
37
+ rows = list(reader)
38
+ if not rows:
39
+ return "Empty CSV."
40
+ header = rows[0]
41
+ data = rows[1:]
42
+ n = len(data)
43
+ sample = data[:sample_rows]
44
+ lines = [f"Columns: {', '.join(header)}", f"Row count: {n}", "Sample:"]
45
+ for i, row in enumerate(sample, 1):
46
+ lines.append(f" {i}: {row}")
47
+ return "\n".join(lines)
48
+ except Exception as e:
49
+ return f"Error: {e}"
50
+
51
+
52
+ register(CsvSummaryTool())