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,73 @@
1
+ """Extract the public API surface: non-_ prefixed functions and classes with signatures."""
2
+
3
+ import ast
4
+ from pathlib import Path
5
+
6
+ from devsper.tools.base import Tool
7
+ from devsper.tools.registry import register
8
+
9
+
10
+ class ApiSurfaceExtractorTool(Tool):
11
+ """
12
+ Extract public API: top-level functions and classes that do not start with underscore.
13
+ """
14
+
15
+ name = "api_surface_extractor"
16
+ description = "Extract public API surface (non-_ names) with argument lists."
17
+ input_schema = {
18
+ "type": "object",
19
+ "properties": {
20
+ "path": {"type": "string", "description": "Path to file or directory"},
21
+ "max_symbols": {"type": "integer", "description": "Max symbols (default 80)"},
22
+ },
23
+ "required": ["path"],
24
+ }
25
+
26
+ def _arg_list(self, node: ast.FunctionDef | ast.AsyncFunctionDef) -> str:
27
+ args = [a.arg for a in node.args.args if a.arg != "self"]
28
+ if node.args.vararg:
29
+ args.append("*" + node.args.vararg.arg)
30
+ if node.args.kwarg:
31
+ args.append("**" + node.args.kwarg.arg)
32
+ return ", ".join(args)
33
+
34
+ def run(self, **kwargs) -> str:
35
+ path = kwargs.get("path")
36
+ max_symbols = kwargs.get("max_symbols", 80)
37
+ if not path or not isinstance(path, str):
38
+ return "Error: path must be a non-empty string"
39
+ if not isinstance(max_symbols, int) or max_symbols < 1:
40
+ max_symbols = 80
41
+ p = Path(path).resolve()
42
+ if not p.exists():
43
+ return f"Error: path not found: {path}"
44
+ files = [p] if p.is_file() and p.suffix == ".py" else list(p.rglob("*.py")) if p.is_dir() else []
45
+ symbols = []
46
+ for f in files:
47
+ if not f.is_file():
48
+ continue
49
+ try:
50
+ tree = ast.parse(f.read_text(encoding="utf-8", errors="replace"))
51
+ rel = f.relative_to(p if p.is_dir() else f.parent)
52
+ for node in ast.iter_child_nodes(tree):
53
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
54
+ if node.name.startswith("_"):
55
+ continue
56
+ kind = "class" if isinstance(node, ast.ClassDef) else "def"
57
+ if kind == "def":
58
+ sig = f"{node.name}({self._arg_list(node)})" # type: ignore[arg-type]
59
+ else:
60
+ sig = node.name
61
+ symbols.append({"file": str(rel), "kind": kind, "signature": sig})
62
+ if len(symbols) >= max_symbols:
63
+ break
64
+ except (SyntaxError, OSError):
65
+ continue
66
+ if len(symbols) >= max_symbols:
67
+ break
68
+ import json
69
+
70
+ return json.dumps({"api": symbols}, indent=2)
71
+
72
+
73
+ register(ApiSurfaceExtractorTool())
@@ -0,0 +1,65 @@
1
+ """Analyze codebase architecture: package layout, entry points, and high-level structure."""
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 ArchitectureAnalyzerTool(Tool):
10
+ """
11
+ Report high-level architecture: top-level packages, __main__ and main-like files, and layout.
12
+ """
13
+
14
+ name = "architecture_analyzer"
15
+ description = "Analyze codebase architecture: packages, entry points, layout."
16
+ input_schema = {
17
+ "type": "object",
18
+ "properties": {
19
+ "path": {"type": "string", "description": "Root path"},
20
+ "max_depth": {"type": "integer", "description": "Max depth for layout (default 3)"},
21
+ },
22
+ "required": ["path"],
23
+ }
24
+
25
+ def run(self, **kwargs) -> str:
26
+ path = kwargs.get("path")
27
+ max_depth = kwargs.get("max_depth", 3)
28
+ if not path or not isinstance(path, str):
29
+ return "Error: path must be a non-empty string"
30
+ if not isinstance(max_depth, int) or max_depth < 1:
31
+ max_depth = 3
32
+ root = Path(path).resolve()
33
+ if not root.exists() or not root.is_dir():
34
+ return f"Error: path must be an existing directory: {path}"
35
+ top_dirs = []
36
+ for d in root.iterdir():
37
+ if d.is_dir() and not d.name.startswith(".") and d.name != "__pycache__":
38
+ top_dirs.append(d.name)
39
+ main_files = []
40
+ for p in root.rglob("*.py"):
41
+ if not p.is_file():
42
+ continue
43
+ name = p.name.lower()
44
+ if name in ("__main__.py", "main.py", "run.py", "app.py"):
45
+ main_files.append(str(p.relative_to(root)).replace("\\", "/"))
46
+ layout = []
47
+ for d in sorted(top_dirs)[:20]:
48
+ sub = root / d
49
+ if sub.is_dir():
50
+ py_count = sum(1 for _ in sub.rglob("*.py"))
51
+ layout.append(f" {d}/ ({py_count} .py files)")
52
+ lines = [
53
+ "Architecture summary",
54
+ "=" * 40,
55
+ "Top-level packages: " + ", ".join(sorted(top_dirs)[:15]),
56
+ "",
57
+ "Entry-point-like files: " + ", ".join(main_files[:10]) or "none found",
58
+ "",
59
+ "Layout (top-level):",
60
+ "\n".join(layout),
61
+ ]
62
+ return "\n".join(lines)
63
+
64
+
65
+ register(ArchitectureAnalyzerTool())
@@ -0,0 +1,71 @@
1
+ """Index a codebase: list modules, files, and top-level symbols (functions/classes)."""
2
+
3
+ import ast
4
+ from pathlib import Path
5
+
6
+ from devsper.tools.base import Tool
7
+ from devsper.tools.registry import register
8
+
9
+
10
+ class CodebaseIndexerTool(Tool):
11
+ """
12
+ Build a simple index of a Python codebase: files, modules, and top-level function/class names.
13
+ """
14
+
15
+ name = "codebase_indexer"
16
+ description = "Index a Python codebase: list files and top-level functions/classes."
17
+ input_schema = {
18
+ "type": "object",
19
+ "properties": {
20
+ "path": {"type": "string", "description": "Root path to index"},
21
+ "max_depth": {"type": "integer", "description": "Max directory depth (default 5)"},
22
+ "extensions": {
23
+ "type": "array",
24
+ "items": {"type": "string"},
25
+ "description": "File extensions to include (default ['.py'])",
26
+ },
27
+ },
28
+ "required": ["path"],
29
+ }
30
+
31
+ def _index_file(self, p: Path) -> list[str]:
32
+ symbols = []
33
+ try:
34
+ tree = ast.parse(p.read_text(encoding="utf-8", errors="replace"))
35
+ for node in ast.walk(tree):
36
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
37
+ if node.col_offset == 0:
38
+ kind = "class" if isinstance(node, ast.ClassDef) else "function"
39
+ symbols.append(f"{kind}:{node.name}")
40
+ except (SyntaxError, OSError):
41
+ pass
42
+ return symbols
43
+
44
+ def run(self, **kwargs) -> str:
45
+ path = kwargs.get("path")
46
+ max_depth = kwargs.get("max_depth", 5)
47
+ extensions = kwargs.get("extensions") or [".py"]
48
+ if not path or not isinstance(path, str):
49
+ return "Error: path must be a non-empty string"
50
+ if not isinstance(max_depth, int) or max_depth < 1:
51
+ max_depth = 5
52
+ root = Path(path).resolve()
53
+ if not root.exists() or not root.is_dir():
54
+ return f"Error: path must be an existing directory: {path}"
55
+ index = []
56
+ for depth, _ in enumerate(root.rglob("*")):
57
+ if depth > 1000:
58
+ break
59
+ for p in root.rglob("*"):
60
+ if p.is_file() and p.suffix.lower() in extensions:
61
+ rel = p.relative_to(root)
62
+ if len(rel.parts) > max_depth:
63
+ continue
64
+ symbols = self._index_file(p)
65
+ index.append({"file": str(rel), "symbols": symbols})
66
+ import json
67
+
68
+ return json.dumps({"root": str(root), "index": index[:200]}, indent=2)
69
+
70
+
71
+ register(CodebaseIndexerTool())
@@ -0,0 +1,67 @@
1
+ """Build a dependency graph from Python files: module -> imported modules."""
2
+
3
+ import ast
4
+ from pathlib import Path
5
+
6
+ from devsper.tools.base import Tool
7
+ from devsper.tools.registry import register
8
+
9
+
10
+ class DependencyGraphBuilderTool(Tool):
11
+ """
12
+ Build a module-level dependency graph: for each .py file, list its imports (internal or external).
13
+ """
14
+
15
+ name = "dependency_graph_builder"
16
+ description = "Build a dependency graph from Python files (module -> imports)."
17
+ input_schema = {
18
+ "type": "object",
19
+ "properties": {
20
+ "path": {"type": "string", "description": "Root path of the codebase"},
21
+ "max_files": {"type": "integer", "description": "Max files to scan (default 150)"},
22
+ },
23
+ "required": ["path"],
24
+ }
25
+
26
+ def _imports(self, p: Path) -> list[str]:
27
+ imports = []
28
+ try:
29
+ tree = ast.parse(p.read_text(encoding="utf-8", errors="replace"))
30
+ for node in ast.walk(tree):
31
+ if isinstance(node, ast.Import):
32
+ for alias in node.names:
33
+ imports.append(alias.name.split(".")[0])
34
+ elif isinstance(node, ast.ImportFrom):
35
+ if node.module:
36
+ imports.append(node.module.split(".")[0])
37
+ except (SyntaxError, OSError):
38
+ pass
39
+ return list(dict.fromkeys(imports))
40
+
41
+ def run(self, **kwargs) -> str:
42
+ path = kwargs.get("path")
43
+ max_files = kwargs.get("max_files", 150)
44
+ if not path or not isinstance(path, str):
45
+ return "Error: path must be a non-empty string"
46
+ if not isinstance(max_files, int) or max_files < 1:
47
+ max_files = 150
48
+ root = Path(path).resolve()
49
+ if not root.exists() or not root.is_dir():
50
+ return f"Error: path must be an existing directory: {path}"
51
+ edges = []
52
+ count = 0
53
+ for p in root.rglob("*.py"):
54
+ if count >= max_files:
55
+ break
56
+ if not p.is_file():
57
+ continue
58
+ count += 1
59
+ mod = str(p.relative_to(root)).replace("\\", "/").replace(".py", "").replace("/", ".")
60
+ for imp in self._imports(p):
61
+ edges.append({"from": mod, "to": imp})
62
+ import json
63
+
64
+ return json.dumps({"nodes": "inferred from edges", "edges": edges[:300]}, indent=2)
65
+
66
+
67
+ register(DependencyGraphBuilderTool())
@@ -0,0 +1,62 @@
1
+ """Detect simple design patterns in Python code: singleton-like, context manager, etc."""
2
+
3
+ import ast
4
+ from pathlib import Path
5
+
6
+ from devsper.tools.base import Tool
7
+ from devsper.tools.registry import register
8
+
9
+
10
+ class DesignPatternDetectorTool(Tool):
11
+ """
12
+ Detect heuristic design patterns: class with __enter__/__exit__, base class usage, etc.
13
+ """
14
+
15
+ name = "design_pattern_detector"
16
+ description = "Detect simple design patterns in Python code (context manager, inheritance)."
17
+ input_schema = {
18
+ "type": "object",
19
+ "properties": {
20
+ "path": {"type": "string", "description": "Path to file or directory"},
21
+ "max_results": {"type": "integer", "description": "Max results (default 30)"},
22
+ },
23
+ "required": ["path"],
24
+ }
25
+
26
+ def run(self, **kwargs) -> str:
27
+ path = kwargs.get("path")
28
+ max_results = kwargs.get("max_results", 30)
29
+ if not path or not isinstance(path, str):
30
+ return "Error: path must be a non-empty string"
31
+ if not isinstance(max_results, int) or max_results < 1:
32
+ max_results = 30
33
+ p = Path(path).resolve()
34
+ if not p.exists():
35
+ return f"Error: path not found: {path}"
36
+ files = [p] if p.is_file() and p.suffix == ".py" else list(p.rglob("*.py")) if p.is_dir() else []
37
+ patterns = []
38
+ for f in files:
39
+ if not f.is_file():
40
+ continue
41
+ try:
42
+ tree = ast.parse(f.read_text(encoding="utf-8", errors="replace"))
43
+ rel = str(f.relative_to(p if p.is_dir() else f.parent)).replace("\\", "/")
44
+ for node in ast.walk(tree):
45
+ if isinstance(node, ast.ClassDef):
46
+ names = {n.name for n in ast.iter_child_nodes(node) if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef))}
47
+ if "__enter__" in names and "__exit__" in names:
48
+ patterns.append({"file": rel, "pattern": "context_manager", "class": node.name})
49
+ if node.bases:
50
+ patterns.append({"file": rel, "pattern": "inheritance", "class": node.name, "bases": [ast.unparse(b) for b in node.bases[:3]]})
51
+ if len(patterns) >= max_results:
52
+ break
53
+ except (SyntaxError, OSError):
54
+ continue
55
+ if len(patterns) >= max_results:
56
+ break
57
+ import json
58
+
59
+ return json.dumps({"patterns": patterns}, indent=2)
60
+
61
+
62
+ register(DesignPatternDetectorTool())
@@ -0,0 +1,68 @@
1
+ """Detect large functions by line count and list them with locations."""
2
+
3
+ import ast
4
+ from pathlib import Path
5
+
6
+ from devsper.tools.base import Tool
7
+ from devsper.tools.registry import register
8
+
9
+
10
+ class LargeFunctionDetectorTool(Tool):
11
+ """
12
+ List functions that exceed a line-count threshold (default 50 lines).
13
+ """
14
+
15
+ name = "large_function_detector"
16
+ description = "Detect large functions by line count in Python files."
17
+ input_schema = {
18
+ "type": "object",
19
+ "properties": {
20
+ "path": {"type": "string", "description": "Path to file or directory"},
21
+ "min_lines": {"type": "integer", "description": "Minimum lines to flag (default 50)"},
22
+ "max_results": {"type": "integer", "description": "Max results (default 25)"},
23
+ },
24
+ "required": ["path"],
25
+ }
26
+
27
+ def run(self, **kwargs) -> str:
28
+ path = kwargs.get("path")
29
+ min_lines = kwargs.get("min_lines", 50)
30
+ max_results = kwargs.get("max_results", 25)
31
+ if not path or not isinstance(path, str):
32
+ return "Error: path must be a non-empty string"
33
+ if not isinstance(min_lines, int) or min_lines < 1:
34
+ min_lines = 50
35
+ if not isinstance(max_results, int) or max_results < 1:
36
+ max_results = 25
37
+ p = Path(path).resolve()
38
+ if not p.exists():
39
+ return f"Error: path not found: {path}"
40
+ files = [p] if p.is_file() and p.suffix == ".py" else list(p.rglob("*.py")) if p.is_dir() else []
41
+ large = []
42
+ for f in files:
43
+ if not f.is_file():
44
+ continue
45
+ try:
46
+ text = f.read_text(encoding="utf-8", errors="replace")
47
+ tree = ast.parse(text)
48
+ rel = str(f.relative_to(p if p.is_dir() else f.parent)).replace("\\", "/")
49
+ for node in ast.walk(tree):
50
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
51
+ end = node.end_lineno or node.lineno
52
+ line_count = end - node.lineno + 1
53
+ if line_count >= min_lines:
54
+ large.append({"file": rel, "function": node.name, "lines": line_count, "line_start": node.lineno})
55
+ if len(large) >= max_results:
56
+ break
57
+ except (SyntaxError, OSError):
58
+ continue
59
+ if len(large) >= max_results:
60
+ break
61
+ if not large:
62
+ return f"No functions with >= {min_lines} lines found."
63
+ import json
64
+
65
+ return json.dumps({"large_functions": large}, indent=2)
66
+
67
+
68
+ register(LargeFunctionDetectorTool())
@@ -0,0 +1,56 @@
1
+ """Map module responsibility using docstrings and top-level exports."""
2
+
3
+ import ast
4
+ from pathlib import Path
5
+
6
+ from devsper.tools.base import Tool
7
+ from devsper.tools.registry import register
8
+
9
+
10
+ class ModuleResponsibilityMapperTool(Tool):
11
+ """
12
+ Map each module to a short responsibility summary (docstring or first line).
13
+ """
14
+
15
+ name = "module_responsibility_mapper"
16
+ description = "Map modules to responsibility summaries (docstring/first line)."
17
+ input_schema = {
18
+ "type": "object",
19
+ "properties": {
20
+ "path": {"type": "string", "description": "Root path"},
21
+ "max_modules": {"type": "integer", "description": "Max modules (default 50)"},
22
+ },
23
+ "required": ["path"],
24
+ }
25
+
26
+ def run(self, **kwargs) -> str:
27
+ path = kwargs.get("path")
28
+ max_modules = kwargs.get("max_modules", 50)
29
+ if not path or not isinstance(path, str):
30
+ return "Error: path must be a non-empty string"
31
+ if not isinstance(max_modules, int) or max_modules < 1:
32
+ max_modules = 50
33
+ root = Path(path).resolve()
34
+ if not root.exists() or not root.is_dir():
35
+ return f"Error: path must be an existing directory: {path}"
36
+ results = []
37
+ for p in root.rglob("*.py"):
38
+ if len(results) >= max_modules:
39
+ break
40
+ if not p.is_file():
41
+ continue
42
+ try:
43
+ text = p.read_text(encoding="utf-8", errors="replace")
44
+ tree = ast.parse(text)
45
+ doc = ast.get_docstring(tree) or ""
46
+ first = text.strip().split("\n")[0][:60] if text.strip() else ""
47
+ summary = (doc.split("\n")[0] if doc else first).strip() or "(no summary)"
48
+ results.append({"module": str(p.relative_to(root)).replace("\\", "/"), "responsibility": summary[:200]})
49
+ except (SyntaxError, OSError):
50
+ continue
51
+ import json
52
+
53
+ return json.dumps({"modules": results}, indent=2)
54
+
55
+
56
+ register(ModuleResponsibilityMapperTool())
@@ -0,0 +1,44 @@
1
+ """Split codebase analysis into batches for parallel/swarm processing."""
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+ from devsper.tools.base import Tool
7
+ from devsper.tools.registry import register
8
+
9
+
10
+ class ParallelCodebaseAnalysisTool(Tool):
11
+ """
12
+ Split a codebase into file batches for parallel analysis (e.g. by swarm workers).
13
+ """
14
+
15
+ name = "parallel_codebase_analysis"
16
+ description = "Split codebase into batches for parallel analysis. Returns batch plan."
17
+ input_schema = {
18
+ "type": "object",
19
+ "properties": {
20
+ "path": {"type": "string", "description": "Root path"},
21
+ "batch_size": {"type": "integer", "description": "Files per batch (default 10)"},
22
+ "extension": {"type": "string", "description": "File extension (default .py)"},
23
+ },
24
+ "required": ["path"],
25
+ }
26
+
27
+ def run(self, **kwargs) -> str:
28
+ path = kwargs.get("path")
29
+ batch_size = kwargs.get("batch_size", 10)
30
+ extension = kwargs.get("extension", ".py")
31
+ if not path or not isinstance(path, str):
32
+ return "Error: path must be a non-empty string"
33
+ if not isinstance(batch_size, int) or batch_size < 1:
34
+ batch_size = 10
35
+ root = Path(path).resolve()
36
+ if not root.exists() or not root.is_dir():
37
+ return f"Error: path must be an existing directory: {path}"
38
+ files = [str(p.relative_to(root)).replace("\\", "/") for p in root.rglob(f"*{extension}") if p.is_file()]
39
+ batches = [files[i : i + batch_size] for i in range(0, len(files), batch_size)]
40
+ result = {"root": str(root), "total_files": len(files), "batch_size": batch_size, "num_batches": len(batches), "batches": batches}
41
+ return json.dumps(result, indent=2)
42
+
43
+
44
+ register(ParallelCodebaseAnalysisTool())
@@ -0,0 +1,81 @@
1
+ """Detect refactor candidates: long functions, high branch count, deep nesting."""
2
+
3
+ import ast
4
+ from pathlib import Path
5
+
6
+ from devsper.tools.base import Tool
7
+ from devsper.tools.registry import register
8
+
9
+
10
+ class RefactorCandidateDetectorTool(Tool):
11
+ """
12
+ Detect refactor candidates: functions with many lines, many branches, or deep nesting.
13
+ """
14
+
15
+ name = "refactor_candidate_detector"
16
+ description = "Detect refactor candidates: long functions, high complexity, deep nesting."
17
+ input_schema = {
18
+ "type": "object",
19
+ "properties": {
20
+ "path": {"type": "string", "description": "Path to file or directory"},
21
+ "min_lines": {"type": "integer", "description": "Flag functions with >= N lines (default 30)"},
22
+ "min_branches": {"type": "integer", "description": "Flag functions with >= N branches (default 8)"},
23
+ },
24
+ "required": ["path"],
25
+ }
26
+
27
+ def _complexity(self, node: ast.AST) -> tuple[int, int]:
28
+ branches = 0
29
+ max_nest = 0
30
+
31
+ def visit(n: ast.AST, depth: int) -> None:
32
+ nonlocal branches, max_nest
33
+ max_nest = max(max_nest, depth)
34
+ if isinstance(n, (ast.If, ast.While, ast.For, ast.With, ast.Try, ast.Assert)):
35
+ branches += 1
36
+ if isinstance(n, ast.comprehension):
37
+ branches += 1
38
+ for c in ast.iter_child_nodes(n):
39
+ visit(c, depth + 1)
40
+
41
+ visit(node, 0)
42
+ return branches, max_nest
43
+
44
+ def run(self, **kwargs) -> str:
45
+ path = kwargs.get("path")
46
+ min_lines = kwargs.get("min_lines", 30)
47
+ min_branches = kwargs.get("min_branches", 8)
48
+ if not path or not isinstance(path, str):
49
+ return "Error: path must be a non-empty string"
50
+ if not isinstance(min_lines, int) or min_lines < 1:
51
+ min_lines = 30
52
+ if not isinstance(min_branches, int) or min_branches < 1:
53
+ min_branches = 8
54
+ p = Path(path).resolve()
55
+ if not p.exists():
56
+ return f"Error: path not found: {path}"
57
+ files = [p] if p.is_file() and p.suffix == ".py" else list(p.rglob("*.py")) if p.is_dir() else []
58
+ candidates = []
59
+ for f in files:
60
+ if not f.is_file():
61
+ continue
62
+ try:
63
+ text = f.read_text(encoding="utf-8", errors="replace")
64
+ tree = ast.parse(text)
65
+ lines = text.splitlines()
66
+ rel = str(f.relative_to(p if p.is_dir() else f.parent)).replace("\\", "/")
67
+ for node in ast.walk(tree):
68
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
69
+ start, end = node.lineno, node.end_lineno or node.lineno
70
+ line_count = end - start + 1
71
+ branches, nest = self._complexity(node)
72
+ if line_count >= min_lines or branches >= min_branches:
73
+ candidates.append({"file": rel, "name": node.name, "lines": line_count, "branches": branches, "max_nesting": nest})
74
+ except (SyntaxError, OSError):
75
+ continue
76
+ import json
77
+
78
+ return json.dumps({"candidates": candidates[:40]}, indent=2)
79
+
80
+
81
+ register(RefactorCandidateDetectorTool())
@@ -0,0 +1,61 @@
1
+ """Build a semantic-style index: modules and their docstrings / first line summary."""
2
+
3
+ import ast
4
+ from pathlib import Path
5
+
6
+ from devsper.tools.base import Tool
7
+ from devsper.tools.registry import register
8
+
9
+
10
+ class RepositorySemanticIndexTool(Tool):
11
+ """
12
+ Index modules with docstrings and first-line summaries for semantic search (keyword match).
13
+ """
14
+
15
+ name = "repository_semantic_index"
16
+ description = "Build a semantic index of a repo: module docstrings and summaries."
17
+ input_schema = {
18
+ "type": "object",
19
+ "properties": {
20
+ "path": {"type": "string", "description": "Root path to index"},
21
+ "max_files": {"type": "integer", "description": "Max files to index (default 100)"},
22
+ },
23
+ "required": ["path"],
24
+ }
25
+
26
+ def _extract_summary(self, p: Path) -> dict:
27
+ try:
28
+ text = p.read_text(encoding="utf-8", errors="replace")
29
+ tree = ast.parse(text)
30
+ doc = ast.get_docstring(tree)
31
+ first_line = text.strip().split("\n")[0][:80] if text.strip() else ""
32
+ return {"docstring": (doc or "")[:300], "first_line": first_line}
33
+ except (SyntaxError, OSError):
34
+ return {"docstring": "", "first_line": ""}
35
+
36
+ def run(self, **kwargs) -> str:
37
+ path = kwargs.get("path")
38
+ max_files = kwargs.get("max_files", 100)
39
+ if not path or not isinstance(path, str):
40
+ return "Error: path must be a non-empty string"
41
+ if not isinstance(max_files, int) or max_files < 1:
42
+ max_files = 100
43
+ root = Path(path).resolve()
44
+ if not root.exists() or not root.is_dir():
45
+ return f"Error: path must be an existing directory: {path}"
46
+ entries = []
47
+ for p in root.rglob("*.py"):
48
+ if len(entries) >= max_files:
49
+ break
50
+ if not p.is_file():
51
+ continue
52
+ rel = p.relative_to(root)
53
+ summary = self._extract_summary(p)
54
+ if summary["docstring"] or summary["first_line"]:
55
+ entries.append({"file": str(rel), **summary})
56
+ import json
57
+
58
+ return json.dumps({"root": str(root), "entries": entries}, indent=2)
59
+
60
+
61
+ register(RepositorySemanticIndexTool())