gobby 0.2.5__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 (383) hide show
  1. gobby/__init__.py +3 -0
  2. gobby/adapters/__init__.py +30 -0
  3. gobby/adapters/base.py +93 -0
  4. gobby/adapters/claude_code.py +276 -0
  5. gobby/adapters/codex.py +1292 -0
  6. gobby/adapters/gemini.py +343 -0
  7. gobby/agents/__init__.py +37 -0
  8. gobby/agents/codex_session.py +120 -0
  9. gobby/agents/constants.py +112 -0
  10. gobby/agents/context.py +362 -0
  11. gobby/agents/definitions.py +133 -0
  12. gobby/agents/gemini_session.py +111 -0
  13. gobby/agents/registry.py +618 -0
  14. gobby/agents/runner.py +968 -0
  15. gobby/agents/session.py +259 -0
  16. gobby/agents/spawn.py +916 -0
  17. gobby/agents/spawners/__init__.py +77 -0
  18. gobby/agents/spawners/base.py +142 -0
  19. gobby/agents/spawners/cross_platform.py +266 -0
  20. gobby/agents/spawners/embedded.py +225 -0
  21. gobby/agents/spawners/headless.py +226 -0
  22. gobby/agents/spawners/linux.py +125 -0
  23. gobby/agents/spawners/macos.py +277 -0
  24. gobby/agents/spawners/windows.py +308 -0
  25. gobby/agents/tty_config.py +319 -0
  26. gobby/autonomous/__init__.py +32 -0
  27. gobby/autonomous/progress_tracker.py +447 -0
  28. gobby/autonomous/stop_registry.py +269 -0
  29. gobby/autonomous/stuck_detector.py +383 -0
  30. gobby/cli/__init__.py +67 -0
  31. gobby/cli/__main__.py +8 -0
  32. gobby/cli/agents.py +529 -0
  33. gobby/cli/artifacts.py +266 -0
  34. gobby/cli/daemon.py +329 -0
  35. gobby/cli/extensions.py +526 -0
  36. gobby/cli/github.py +263 -0
  37. gobby/cli/init.py +53 -0
  38. gobby/cli/install.py +614 -0
  39. gobby/cli/installers/__init__.py +37 -0
  40. gobby/cli/installers/antigravity.py +65 -0
  41. gobby/cli/installers/claude.py +363 -0
  42. gobby/cli/installers/codex.py +192 -0
  43. gobby/cli/installers/gemini.py +294 -0
  44. gobby/cli/installers/git_hooks.py +377 -0
  45. gobby/cli/installers/shared.py +737 -0
  46. gobby/cli/linear.py +250 -0
  47. gobby/cli/mcp.py +30 -0
  48. gobby/cli/mcp_proxy.py +698 -0
  49. gobby/cli/memory.py +304 -0
  50. gobby/cli/merge.py +384 -0
  51. gobby/cli/projects.py +79 -0
  52. gobby/cli/sessions.py +622 -0
  53. gobby/cli/tasks/__init__.py +30 -0
  54. gobby/cli/tasks/_utils.py +658 -0
  55. gobby/cli/tasks/ai.py +1025 -0
  56. gobby/cli/tasks/commits.py +169 -0
  57. gobby/cli/tasks/crud.py +685 -0
  58. gobby/cli/tasks/deps.py +135 -0
  59. gobby/cli/tasks/labels.py +63 -0
  60. gobby/cli/tasks/main.py +273 -0
  61. gobby/cli/tasks/search.py +178 -0
  62. gobby/cli/tui.py +34 -0
  63. gobby/cli/utils.py +513 -0
  64. gobby/cli/workflows.py +927 -0
  65. gobby/cli/worktrees.py +481 -0
  66. gobby/config/__init__.py +129 -0
  67. gobby/config/app.py +551 -0
  68. gobby/config/extensions.py +167 -0
  69. gobby/config/features.py +472 -0
  70. gobby/config/llm_providers.py +98 -0
  71. gobby/config/logging.py +66 -0
  72. gobby/config/mcp.py +346 -0
  73. gobby/config/persistence.py +247 -0
  74. gobby/config/servers.py +141 -0
  75. gobby/config/sessions.py +250 -0
  76. gobby/config/tasks.py +784 -0
  77. gobby/hooks/__init__.py +104 -0
  78. gobby/hooks/artifact_capture.py +213 -0
  79. gobby/hooks/broadcaster.py +243 -0
  80. gobby/hooks/event_handlers.py +723 -0
  81. gobby/hooks/events.py +218 -0
  82. gobby/hooks/git.py +169 -0
  83. gobby/hooks/health_monitor.py +171 -0
  84. gobby/hooks/hook_manager.py +856 -0
  85. gobby/hooks/hook_types.py +575 -0
  86. gobby/hooks/plugins.py +813 -0
  87. gobby/hooks/session_coordinator.py +396 -0
  88. gobby/hooks/verification_runner.py +268 -0
  89. gobby/hooks/webhooks.py +339 -0
  90. gobby/install/claude/commands/gobby/bug.md +51 -0
  91. gobby/install/claude/commands/gobby/chore.md +51 -0
  92. gobby/install/claude/commands/gobby/epic.md +52 -0
  93. gobby/install/claude/commands/gobby/eval.md +235 -0
  94. gobby/install/claude/commands/gobby/feat.md +49 -0
  95. gobby/install/claude/commands/gobby/nit.md +52 -0
  96. gobby/install/claude/commands/gobby/ref.md +52 -0
  97. gobby/install/claude/hooks/HOOK_SCHEMAS.md +632 -0
  98. gobby/install/claude/hooks/hook_dispatcher.py +364 -0
  99. gobby/install/claude/hooks/validate_settings.py +102 -0
  100. gobby/install/claude/hooks-template.json +118 -0
  101. gobby/install/codex/hooks/hook_dispatcher.py +153 -0
  102. gobby/install/codex/prompts/forget.md +7 -0
  103. gobby/install/codex/prompts/memories.md +7 -0
  104. gobby/install/codex/prompts/recall.md +7 -0
  105. gobby/install/codex/prompts/remember.md +13 -0
  106. gobby/install/gemini/hooks/hook_dispatcher.py +268 -0
  107. gobby/install/gemini/hooks-template.json +138 -0
  108. gobby/install/shared/plugins/code_guardian.py +456 -0
  109. gobby/install/shared/plugins/example_notify.py +331 -0
  110. gobby/integrations/__init__.py +10 -0
  111. gobby/integrations/github.py +145 -0
  112. gobby/integrations/linear.py +145 -0
  113. gobby/llm/__init__.py +40 -0
  114. gobby/llm/base.py +120 -0
  115. gobby/llm/claude.py +578 -0
  116. gobby/llm/claude_executor.py +503 -0
  117. gobby/llm/codex.py +322 -0
  118. gobby/llm/codex_executor.py +513 -0
  119. gobby/llm/executor.py +316 -0
  120. gobby/llm/factory.py +34 -0
  121. gobby/llm/gemini.py +258 -0
  122. gobby/llm/gemini_executor.py +339 -0
  123. gobby/llm/litellm.py +287 -0
  124. gobby/llm/litellm_executor.py +303 -0
  125. gobby/llm/resolver.py +499 -0
  126. gobby/llm/service.py +236 -0
  127. gobby/mcp_proxy/__init__.py +29 -0
  128. gobby/mcp_proxy/actions.py +175 -0
  129. gobby/mcp_proxy/daemon_control.py +198 -0
  130. gobby/mcp_proxy/importer.py +436 -0
  131. gobby/mcp_proxy/lazy.py +325 -0
  132. gobby/mcp_proxy/manager.py +798 -0
  133. gobby/mcp_proxy/metrics.py +609 -0
  134. gobby/mcp_proxy/models.py +139 -0
  135. gobby/mcp_proxy/registries.py +215 -0
  136. gobby/mcp_proxy/schema_hash.py +381 -0
  137. gobby/mcp_proxy/semantic_search.py +706 -0
  138. gobby/mcp_proxy/server.py +549 -0
  139. gobby/mcp_proxy/services/__init__.py +0 -0
  140. gobby/mcp_proxy/services/fallback.py +306 -0
  141. gobby/mcp_proxy/services/recommendation.py +224 -0
  142. gobby/mcp_proxy/services/server_mgmt.py +214 -0
  143. gobby/mcp_proxy/services/system.py +72 -0
  144. gobby/mcp_proxy/services/tool_filter.py +231 -0
  145. gobby/mcp_proxy/services/tool_proxy.py +309 -0
  146. gobby/mcp_proxy/stdio.py +565 -0
  147. gobby/mcp_proxy/tools/__init__.py +27 -0
  148. gobby/mcp_proxy/tools/agents.py +1103 -0
  149. gobby/mcp_proxy/tools/artifacts.py +207 -0
  150. gobby/mcp_proxy/tools/hub.py +335 -0
  151. gobby/mcp_proxy/tools/internal.py +337 -0
  152. gobby/mcp_proxy/tools/memory.py +543 -0
  153. gobby/mcp_proxy/tools/merge.py +422 -0
  154. gobby/mcp_proxy/tools/metrics.py +283 -0
  155. gobby/mcp_proxy/tools/orchestration/__init__.py +23 -0
  156. gobby/mcp_proxy/tools/orchestration/cleanup.py +619 -0
  157. gobby/mcp_proxy/tools/orchestration/monitor.py +380 -0
  158. gobby/mcp_proxy/tools/orchestration/orchestrate.py +746 -0
  159. gobby/mcp_proxy/tools/orchestration/review.py +736 -0
  160. gobby/mcp_proxy/tools/orchestration/utils.py +16 -0
  161. gobby/mcp_proxy/tools/session_messages.py +1056 -0
  162. gobby/mcp_proxy/tools/task_dependencies.py +219 -0
  163. gobby/mcp_proxy/tools/task_expansion.py +591 -0
  164. gobby/mcp_proxy/tools/task_github.py +393 -0
  165. gobby/mcp_proxy/tools/task_linear.py +379 -0
  166. gobby/mcp_proxy/tools/task_orchestration.py +77 -0
  167. gobby/mcp_proxy/tools/task_readiness.py +522 -0
  168. gobby/mcp_proxy/tools/task_sync.py +351 -0
  169. gobby/mcp_proxy/tools/task_validation.py +843 -0
  170. gobby/mcp_proxy/tools/tasks/__init__.py +25 -0
  171. gobby/mcp_proxy/tools/tasks/_context.py +112 -0
  172. gobby/mcp_proxy/tools/tasks/_crud.py +516 -0
  173. gobby/mcp_proxy/tools/tasks/_factory.py +176 -0
  174. gobby/mcp_proxy/tools/tasks/_helpers.py +129 -0
  175. gobby/mcp_proxy/tools/tasks/_lifecycle.py +517 -0
  176. gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +301 -0
  177. gobby/mcp_proxy/tools/tasks/_resolution.py +55 -0
  178. gobby/mcp_proxy/tools/tasks/_search.py +215 -0
  179. gobby/mcp_proxy/tools/tasks/_session.py +125 -0
  180. gobby/mcp_proxy/tools/workflows.py +973 -0
  181. gobby/mcp_proxy/tools/worktrees.py +1264 -0
  182. gobby/mcp_proxy/transports/__init__.py +0 -0
  183. gobby/mcp_proxy/transports/base.py +95 -0
  184. gobby/mcp_proxy/transports/factory.py +44 -0
  185. gobby/mcp_proxy/transports/http.py +139 -0
  186. gobby/mcp_proxy/transports/stdio.py +213 -0
  187. gobby/mcp_proxy/transports/websocket.py +136 -0
  188. gobby/memory/backends/__init__.py +116 -0
  189. gobby/memory/backends/mem0.py +408 -0
  190. gobby/memory/backends/memu.py +485 -0
  191. gobby/memory/backends/null.py +111 -0
  192. gobby/memory/backends/openmemory.py +537 -0
  193. gobby/memory/backends/sqlite.py +304 -0
  194. gobby/memory/context.py +87 -0
  195. gobby/memory/manager.py +1001 -0
  196. gobby/memory/protocol.py +451 -0
  197. gobby/memory/search/__init__.py +66 -0
  198. gobby/memory/search/text.py +127 -0
  199. gobby/memory/viz.py +258 -0
  200. gobby/prompts/__init__.py +13 -0
  201. gobby/prompts/defaults/expansion/system.md +119 -0
  202. gobby/prompts/defaults/expansion/user.md +48 -0
  203. gobby/prompts/defaults/external_validation/agent.md +72 -0
  204. gobby/prompts/defaults/external_validation/external.md +63 -0
  205. gobby/prompts/defaults/external_validation/spawn.md +83 -0
  206. gobby/prompts/defaults/external_validation/system.md +6 -0
  207. gobby/prompts/defaults/features/import_mcp.md +22 -0
  208. gobby/prompts/defaults/features/import_mcp_github.md +17 -0
  209. gobby/prompts/defaults/features/import_mcp_search.md +16 -0
  210. gobby/prompts/defaults/features/recommend_tools.md +32 -0
  211. gobby/prompts/defaults/features/recommend_tools_hybrid.md +35 -0
  212. gobby/prompts/defaults/features/recommend_tools_llm.md +30 -0
  213. gobby/prompts/defaults/features/server_description.md +20 -0
  214. gobby/prompts/defaults/features/server_description_system.md +6 -0
  215. gobby/prompts/defaults/features/task_description.md +31 -0
  216. gobby/prompts/defaults/features/task_description_system.md +6 -0
  217. gobby/prompts/defaults/features/tool_summary.md +17 -0
  218. gobby/prompts/defaults/features/tool_summary_system.md +6 -0
  219. gobby/prompts/defaults/research/step.md +58 -0
  220. gobby/prompts/defaults/validation/criteria.md +47 -0
  221. gobby/prompts/defaults/validation/validate.md +38 -0
  222. gobby/prompts/loader.py +346 -0
  223. gobby/prompts/models.py +113 -0
  224. gobby/py.typed +0 -0
  225. gobby/runner.py +488 -0
  226. gobby/search/__init__.py +23 -0
  227. gobby/search/protocol.py +104 -0
  228. gobby/search/tfidf.py +232 -0
  229. gobby/servers/__init__.py +7 -0
  230. gobby/servers/http.py +636 -0
  231. gobby/servers/models.py +31 -0
  232. gobby/servers/routes/__init__.py +23 -0
  233. gobby/servers/routes/admin.py +416 -0
  234. gobby/servers/routes/dependencies.py +118 -0
  235. gobby/servers/routes/mcp/__init__.py +24 -0
  236. gobby/servers/routes/mcp/hooks.py +135 -0
  237. gobby/servers/routes/mcp/plugins.py +121 -0
  238. gobby/servers/routes/mcp/tools.py +1337 -0
  239. gobby/servers/routes/mcp/webhooks.py +159 -0
  240. gobby/servers/routes/sessions.py +582 -0
  241. gobby/servers/websocket.py +766 -0
  242. gobby/sessions/__init__.py +13 -0
  243. gobby/sessions/analyzer.py +322 -0
  244. gobby/sessions/lifecycle.py +240 -0
  245. gobby/sessions/manager.py +563 -0
  246. gobby/sessions/processor.py +225 -0
  247. gobby/sessions/summary.py +532 -0
  248. gobby/sessions/transcripts/__init__.py +41 -0
  249. gobby/sessions/transcripts/base.py +125 -0
  250. gobby/sessions/transcripts/claude.py +386 -0
  251. gobby/sessions/transcripts/codex.py +143 -0
  252. gobby/sessions/transcripts/gemini.py +195 -0
  253. gobby/storage/__init__.py +21 -0
  254. gobby/storage/agents.py +409 -0
  255. gobby/storage/artifact_classifier.py +341 -0
  256. gobby/storage/artifacts.py +285 -0
  257. gobby/storage/compaction.py +67 -0
  258. gobby/storage/database.py +357 -0
  259. gobby/storage/inter_session_messages.py +194 -0
  260. gobby/storage/mcp.py +680 -0
  261. gobby/storage/memories.py +562 -0
  262. gobby/storage/merge_resolutions.py +550 -0
  263. gobby/storage/migrations.py +860 -0
  264. gobby/storage/migrations_legacy.py +1359 -0
  265. gobby/storage/projects.py +166 -0
  266. gobby/storage/session_messages.py +251 -0
  267. gobby/storage/session_tasks.py +97 -0
  268. gobby/storage/sessions.py +817 -0
  269. gobby/storage/task_dependencies.py +223 -0
  270. gobby/storage/tasks/__init__.py +42 -0
  271. gobby/storage/tasks/_aggregates.py +180 -0
  272. gobby/storage/tasks/_crud.py +449 -0
  273. gobby/storage/tasks/_id.py +104 -0
  274. gobby/storage/tasks/_lifecycle.py +311 -0
  275. gobby/storage/tasks/_manager.py +889 -0
  276. gobby/storage/tasks/_models.py +300 -0
  277. gobby/storage/tasks/_ordering.py +119 -0
  278. gobby/storage/tasks/_path_cache.py +110 -0
  279. gobby/storage/tasks/_queries.py +343 -0
  280. gobby/storage/tasks/_search.py +143 -0
  281. gobby/storage/workflow_audit.py +393 -0
  282. gobby/storage/worktrees.py +547 -0
  283. gobby/sync/__init__.py +29 -0
  284. gobby/sync/github.py +333 -0
  285. gobby/sync/linear.py +304 -0
  286. gobby/sync/memories.py +284 -0
  287. gobby/sync/tasks.py +641 -0
  288. gobby/tasks/__init__.py +8 -0
  289. gobby/tasks/build_verification.py +193 -0
  290. gobby/tasks/commits.py +633 -0
  291. gobby/tasks/context.py +747 -0
  292. gobby/tasks/criteria.py +342 -0
  293. gobby/tasks/enhanced_validator.py +226 -0
  294. gobby/tasks/escalation.py +263 -0
  295. gobby/tasks/expansion.py +626 -0
  296. gobby/tasks/external_validator.py +764 -0
  297. gobby/tasks/issue_extraction.py +171 -0
  298. gobby/tasks/prompts/expand.py +327 -0
  299. gobby/tasks/research.py +421 -0
  300. gobby/tasks/tdd.py +352 -0
  301. gobby/tasks/tree_builder.py +263 -0
  302. gobby/tasks/validation.py +712 -0
  303. gobby/tasks/validation_history.py +357 -0
  304. gobby/tasks/validation_models.py +89 -0
  305. gobby/tools/__init__.py +0 -0
  306. gobby/tools/summarizer.py +170 -0
  307. gobby/tui/__init__.py +5 -0
  308. gobby/tui/api_client.py +281 -0
  309. gobby/tui/app.py +327 -0
  310. gobby/tui/screens/__init__.py +25 -0
  311. gobby/tui/screens/agents.py +333 -0
  312. gobby/tui/screens/chat.py +450 -0
  313. gobby/tui/screens/dashboard.py +377 -0
  314. gobby/tui/screens/memory.py +305 -0
  315. gobby/tui/screens/metrics.py +231 -0
  316. gobby/tui/screens/orchestrator.py +904 -0
  317. gobby/tui/screens/sessions.py +412 -0
  318. gobby/tui/screens/tasks.py +442 -0
  319. gobby/tui/screens/workflows.py +289 -0
  320. gobby/tui/screens/worktrees.py +174 -0
  321. gobby/tui/widgets/__init__.py +21 -0
  322. gobby/tui/widgets/chat.py +210 -0
  323. gobby/tui/widgets/conductor.py +104 -0
  324. gobby/tui/widgets/menu.py +132 -0
  325. gobby/tui/widgets/message_panel.py +160 -0
  326. gobby/tui/widgets/review_gate.py +224 -0
  327. gobby/tui/widgets/task_tree.py +99 -0
  328. gobby/tui/widgets/token_budget.py +166 -0
  329. gobby/tui/ws_client.py +258 -0
  330. gobby/utils/__init__.py +3 -0
  331. gobby/utils/daemon_client.py +235 -0
  332. gobby/utils/git.py +222 -0
  333. gobby/utils/id.py +38 -0
  334. gobby/utils/json_helpers.py +161 -0
  335. gobby/utils/logging.py +376 -0
  336. gobby/utils/machine_id.py +135 -0
  337. gobby/utils/metrics.py +589 -0
  338. gobby/utils/project_context.py +182 -0
  339. gobby/utils/project_init.py +263 -0
  340. gobby/utils/status.py +256 -0
  341. gobby/utils/validation.py +80 -0
  342. gobby/utils/version.py +23 -0
  343. gobby/workflows/__init__.py +4 -0
  344. gobby/workflows/actions.py +1310 -0
  345. gobby/workflows/approval_flow.py +138 -0
  346. gobby/workflows/artifact_actions.py +103 -0
  347. gobby/workflows/audit_helpers.py +110 -0
  348. gobby/workflows/autonomous_actions.py +286 -0
  349. gobby/workflows/context_actions.py +394 -0
  350. gobby/workflows/definitions.py +130 -0
  351. gobby/workflows/detection_helpers.py +208 -0
  352. gobby/workflows/engine.py +485 -0
  353. gobby/workflows/evaluator.py +669 -0
  354. gobby/workflows/git_utils.py +96 -0
  355. gobby/workflows/hooks.py +169 -0
  356. gobby/workflows/lifecycle_evaluator.py +613 -0
  357. gobby/workflows/llm_actions.py +70 -0
  358. gobby/workflows/loader.py +333 -0
  359. gobby/workflows/mcp_actions.py +60 -0
  360. gobby/workflows/memory_actions.py +272 -0
  361. gobby/workflows/premature_stop.py +164 -0
  362. gobby/workflows/session_actions.py +139 -0
  363. gobby/workflows/state_actions.py +123 -0
  364. gobby/workflows/state_manager.py +104 -0
  365. gobby/workflows/stop_signal_actions.py +163 -0
  366. gobby/workflows/summary_actions.py +344 -0
  367. gobby/workflows/task_actions.py +249 -0
  368. gobby/workflows/task_enforcement_actions.py +901 -0
  369. gobby/workflows/templates.py +52 -0
  370. gobby/workflows/todo_actions.py +84 -0
  371. gobby/workflows/webhook.py +223 -0
  372. gobby/workflows/webhook_executor.py +399 -0
  373. gobby/worktrees/__init__.py +5 -0
  374. gobby/worktrees/git.py +690 -0
  375. gobby/worktrees/merge/__init__.py +20 -0
  376. gobby/worktrees/merge/conflict_parser.py +177 -0
  377. gobby/worktrees/merge/resolver.py +485 -0
  378. gobby-0.2.5.dist-info/METADATA +351 -0
  379. gobby-0.2.5.dist-info/RECORD +383 -0
  380. gobby-0.2.5.dist-info/WHEEL +5 -0
  381. gobby-0.2.5.dist-info/entry_points.txt +2 -0
  382. gobby-0.2.5.dist-info/licenses/LICENSE.md +193 -0
  383. gobby-0.2.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,549 @@
1
+ """
2
+ Gobby Daemon Tools MCP Server.
3
+ """
4
+
5
+ import logging
6
+ from datetime import UTC
7
+ from typing import Any
8
+
9
+ from mcp.server.fastmcp import FastMCP
10
+
11
+ from gobby.config.app import DaemonConfig
12
+ from gobby.mcp_proxy.manager import MCPClientManager
13
+ from gobby.mcp_proxy.services.recommendation import RecommendationService, SearchMode
14
+ from gobby.mcp_proxy.services.server_mgmt import ServerManagementService
15
+ from gobby.mcp_proxy.services.system import SystemService
16
+ from gobby.mcp_proxy.services.tool_proxy import ToolProxyService
17
+
18
+ logger = logging.getLogger("gobby.mcp.server")
19
+
20
+
21
+ class GobbyDaemonTools:
22
+ """Handler for Gobby Daemon MCP tools (Refactored to use services)."""
23
+
24
+ def __init__(
25
+ self,
26
+ mcp_manager: MCPClientManager,
27
+ daemon_port: int,
28
+ websocket_port: int,
29
+ start_time: float,
30
+ internal_manager: Any,
31
+ config: DaemonConfig | None = None,
32
+ llm_service: Any | None = None,
33
+ session_manager: Any | None = None,
34
+ memory_manager: Any | None = None,
35
+ config_manager: Any | None = None,
36
+ semantic_search: Any | None = None,
37
+ tool_filter: Any | None = None,
38
+ fallback_resolver: Any | None = None,
39
+ ):
40
+ self.config = config
41
+ self.internal_manager = internal_manager
42
+ self._mcp_manager = mcp_manager # Store for project_id access
43
+ self._semantic_search = semantic_search # Store for direct search access
44
+
45
+ # Initialize services
46
+ self.system_service = SystemService(mcp_manager, daemon_port, websocket_port, start_time)
47
+ self.tool_proxy = ToolProxyService(
48
+ mcp_manager,
49
+ internal_manager=internal_manager,
50
+ tool_filter=tool_filter,
51
+ fallback_resolver=fallback_resolver,
52
+ )
53
+ self.server_mgmt = ServerManagementService(mcp_manager, config_manager, config)
54
+ self.recommendation = RecommendationService(
55
+ llm_service,
56
+ mcp_manager,
57
+ semantic_search=semantic_search,
58
+ project_id=mcp_manager.project_id,
59
+ config=config.recommend_tools if config else None,
60
+ )
61
+
62
+ # --- System Tools ---
63
+
64
+ async def status(self) -> dict[str, Any]:
65
+ """Get daemon status."""
66
+ return self.system_service.get_status()
67
+
68
+ async def list_mcp_servers(self) -> dict[str, Any]:
69
+ """List configured MCP servers."""
70
+ status = self.system_service.get_status()
71
+ servers_info = status.get("mcp_servers", {})
72
+
73
+ server_list = []
74
+ for name, info in servers_info.items():
75
+ server_list.append(
76
+ {
77
+ "name": name,
78
+ "state": info["state"],
79
+ "connected": info["state"] == "connected",
80
+ # Additional fields can be fetched from config if we had access
81
+ }
82
+ )
83
+
84
+ return {
85
+ "success": True,
86
+ "servers": server_list,
87
+ "total_count": len(server_list),
88
+ "connected_count": len([s for s in server_list if s["connected"]]),
89
+ }
90
+
91
+ # --- Tool Proxying ---
92
+
93
+ async def call_tool(
94
+ self,
95
+ server_name: str,
96
+ tool_name: str,
97
+ arguments: dict[str, Any] | None = None,
98
+ ) -> Any:
99
+ """Call a tool."""
100
+ return await self.tool_proxy.call_tool(server_name, tool_name, arguments)
101
+
102
+ async def list_tools(self, server: str, session_id: str | None = None) -> dict[str, Any]:
103
+ """List tools for a specific server, optionally filtered by workflow phase restrictions."""
104
+ return await self.tool_proxy.list_tools(server, session_id=session_id)
105
+
106
+ async def get_tool_schema(self, server_name: str, tool_name: str) -> dict[str, Any]:
107
+ """Get tool schema."""
108
+ return await self.tool_proxy.get_tool_schema(server_name, tool_name)
109
+
110
+ async def read_mcp_resource(self, server_name: str, resource_uri: str) -> Any:
111
+ """Read resource."""
112
+ return await self.tool_proxy.read_resource(server_name, resource_uri)
113
+
114
+ # --- Server Management ---
115
+
116
+ async def add_mcp_server(
117
+ self,
118
+ name: str,
119
+ transport: str,
120
+ url: str | None = None,
121
+ headers: dict[str, str] | None = None,
122
+ command: str | None = None,
123
+ args: list[str] | None = None,
124
+ env: dict[str, str] | None = None,
125
+ enabled: bool = True,
126
+ ) -> dict[str, Any]:
127
+ """Add server."""
128
+ return await self.server_mgmt.add_server(
129
+ name, transport, url, command, args, env, headers, enabled
130
+ )
131
+
132
+ async def remove_mcp_server(self, name: str) -> dict[str, Any]:
133
+ """Remove server."""
134
+ return await self.server_mgmt.remove_server(name)
135
+
136
+ async def import_mcp_server(
137
+ self,
138
+ from_project: str | None = None,
139
+ servers: list[str] | None = None,
140
+ github_url: str | None = None,
141
+ query: str | None = None,
142
+ ) -> dict[str, Any]:
143
+ """Import server."""
144
+ return await self.server_mgmt.import_server(from_project, github_url, query, servers)
145
+
146
+ # --- Recommendation ---
147
+
148
+ async def recommend_tools(
149
+ self,
150
+ task_description: str,
151
+ agent_id: str | None = None,
152
+ search_mode: SearchMode = "llm",
153
+ top_k: int = 10,
154
+ min_similarity: float = 0.3,
155
+ project_id: str | None = None,
156
+ ) -> dict[str, Any]:
157
+ """Recommend tools for a task.
158
+
159
+ Args:
160
+ task_description: What the user wants to accomplish
161
+ agent_id: Optional agent profile for filtering (reserved)
162
+ search_mode: How to search - "llm" (default), "semantic", or "hybrid"
163
+ top_k: Maximum recommendations to return (semantic/hybrid modes)
164
+ min_similarity: Minimum similarity threshold (semantic/hybrid modes)
165
+ project_id: Project ID for semantic/hybrid search
166
+
167
+ Returns:
168
+ Dict with tool recommendations
169
+ """
170
+ return await self.recommendation.recommend_tools(
171
+ task_description,
172
+ agent_id=agent_id,
173
+ search_mode=search_mode,
174
+ top_k=top_k,
175
+ min_similarity=min_similarity,
176
+ project_id=project_id,
177
+ )
178
+
179
+ # --- Fallback Resolver ---
180
+
181
+ async def get_tool_alternatives(
182
+ self,
183
+ server_name: str,
184
+ tool_name: str,
185
+ error_message: str | None = None,
186
+ top_k: int = 5,
187
+ ) -> dict[str, Any]:
188
+ """Get alternative tool suggestions when a tool fails.
189
+
190
+ Uses semantic similarity and historical success rates to suggest
191
+ similar tools that might work as alternatives.
192
+
193
+ Args:
194
+ server_name: Server where the tool failed
195
+ tool_name: Name of the failed tool
196
+ error_message: Optional error message for context-aware matching
197
+ top_k: Maximum number of alternatives to return (default: 5)
198
+
199
+ Returns:
200
+ Dict with alternative tool suggestions and scores
201
+ """
202
+ project_id = self._mcp_manager.project_id
203
+ if not project_id:
204
+ return {
205
+ "success": False,
206
+ "error": "No project_id available. Run 'gobby init' first.",
207
+ }
208
+
209
+ fallback_resolver = self.tool_proxy._fallback_resolver
210
+ if not fallback_resolver:
211
+ return {
212
+ "success": False,
213
+ "error": "Fallback resolver not configured",
214
+ }
215
+
216
+ try:
217
+ suggestions = await fallback_resolver.find_alternatives_for_error(
218
+ server_name=server_name,
219
+ tool_name=tool_name,
220
+ error_message=error_message or "Tool call failed",
221
+ project_id=project_id,
222
+ top_k=top_k,
223
+ )
224
+
225
+ return {
226
+ "success": True,
227
+ "failed_tool": f"{server_name}/{tool_name}",
228
+ "alternatives": suggestions,
229
+ "count": len(suggestions),
230
+ }
231
+ except Exception as e:
232
+ logger.error(f"Failed to get tool alternatives: {e}")
233
+ return {"success": False, "error": str(e)}
234
+
235
+ # --- Semantic Search ---
236
+
237
+ async def search_tools(
238
+ self,
239
+ query: str,
240
+ top_k: int = 10,
241
+ min_similarity: float = 0.0,
242
+ server: str | None = None,
243
+ ) -> dict[str, Any]:
244
+ """Search for tools using semantic similarity.
245
+
246
+ Args:
247
+ query: Natural language query describing the tool you need
248
+ top_k: Maximum number of results to return (default: 10)
249
+ min_similarity: Minimum similarity threshold (default: 0.0)
250
+ server: Optional server name to filter results
251
+
252
+ Returns:
253
+ Dict with search results and metadata
254
+ """
255
+ if not self._semantic_search:
256
+ return {
257
+ "success": False,
258
+ "error": "Semantic search not configured",
259
+ "query": query,
260
+ }
261
+
262
+ project_id = self._mcp_manager.project_id
263
+ if not project_id:
264
+ return {
265
+ "success": False,
266
+ "error": "No project_id available. Run 'gobby init' first.",
267
+ "query": query,
268
+ }
269
+
270
+ try:
271
+ results = await self._semantic_search.search_tools(
272
+ query=query,
273
+ project_id=project_id,
274
+ top_k=top_k,
275
+ min_similarity=min_similarity,
276
+ server_filter=server,
277
+ )
278
+
279
+ return {
280
+ "success": True,
281
+ "query": query,
282
+ "results": [r.to_dict() for r in results],
283
+ "total_results": len(results),
284
+ }
285
+ except Exception as e:
286
+ logger.error(f"Semantic search failed: {e}")
287
+ return {"success": False, "error": str(e), "query": query}
288
+
289
+ # --- Hook Extensions ---
290
+
291
+ async def list_hook_handlers(self) -> dict[str, Any]:
292
+ """List registered hook handlers from plugins.
293
+
294
+ Returns:
295
+ Dict with handler information organized by event type
296
+ """
297
+ # Access plugin registry via internal_manager if available
298
+ if not self.internal_manager:
299
+ return {
300
+ "success": False,
301
+ "error": "Internal manager not available",
302
+ }
303
+
304
+ # Get hook_manager from app state if available (set during HTTP server startup)
305
+ hook_manager = getattr(self.internal_manager, "_hook_manager", None)
306
+ if not hook_manager:
307
+ return {
308
+ "success": True,
309
+ "handlers": {},
310
+ "message": "No hook manager available - plugins not loaded",
311
+ }
312
+
313
+ plugin_loader = getattr(hook_manager, "plugin_loader", None)
314
+ if not plugin_loader:
315
+ return {
316
+ "success": True,
317
+ "handlers": {},
318
+ "message": "Plugin system not initialized",
319
+ }
320
+
321
+ # Get handlers organized by event type
322
+ from gobby.hooks.events import HookEventType
323
+
324
+ handlers_by_event: dict[str, list[dict[str, Any]]] = {}
325
+
326
+ for event_type in HookEventType:
327
+ handlers = plugin_loader.registry.get_handlers(event_type)
328
+ if handlers:
329
+ handlers_by_event[event_type.value] = [
330
+ {
331
+ "plugin": h.plugin.name,
332
+ "method": h.method.__name__,
333
+ "priority": h.priority,
334
+ "is_pre_handler": h.priority < 50,
335
+ }
336
+ for h in handlers
337
+ ]
338
+
339
+ return {
340
+ "success": True,
341
+ "handlers": handlers_by_event,
342
+ "total_handlers": sum(len(h) for h in handlers_by_event.values()),
343
+ }
344
+
345
+ async def test_hook_event(
346
+ self,
347
+ event_type: str,
348
+ source: str = "claude",
349
+ data: dict[str, Any] | None = None,
350
+ ) -> dict[str, Any]:
351
+ """Test a hook event by sending it through the hook system.
352
+
353
+ Args:
354
+ event_type: Hook event type (e.g., "session_start", "before_tool")
355
+ source: Source CLI to simulate (claude, gemini, codex)
356
+ data: Optional additional data for the event
357
+
358
+ Returns:
359
+ Hook execution result
360
+ """
361
+ if not self.internal_manager:
362
+ return {
363
+ "success": False,
364
+ "error": "Internal manager not available",
365
+ }
366
+
367
+ hook_manager = getattr(self.internal_manager, "_hook_manager", None)
368
+ if not hook_manager:
369
+ return {
370
+ "success": False,
371
+ "error": "No hook manager available",
372
+ }
373
+
374
+ # Build test event
375
+ from datetime import datetime
376
+
377
+ from gobby.hooks.events import HookEvent, HookEventType, SessionSource
378
+
379
+ try:
380
+ hook_event_type = HookEventType(event_type)
381
+ except ValueError:
382
+ return {
383
+ "success": False,
384
+ "error": f"Invalid event type: {event_type}",
385
+ "valid_types": [e.value for e in HookEventType],
386
+ }
387
+
388
+ # Map source string to SessionSource enum
389
+ try:
390
+ session_source = SessionSource(source)
391
+ except ValueError:
392
+ session_source = SessionSource.CLAUDE # Default to Claude
393
+
394
+ test_data = {
395
+ "session_id": "test-mcp-event",
396
+ "source": source,
397
+ **(data or {}),
398
+ }
399
+
400
+ event = HookEvent(
401
+ event_type=hook_event_type,
402
+ session_id="test-mcp-event",
403
+ source=session_source,
404
+ timestamp=datetime.now(UTC),
405
+ data=test_data,
406
+ )
407
+
408
+ # Process through hook manager
409
+ try:
410
+ result = hook_manager.process_event(event)
411
+ return {
412
+ "success": True,
413
+ "event_type": event_type,
414
+ "continue": result.get("continue", True),
415
+ "reason": result.get("reason"),
416
+ "inject_context": result.get("inject_context"),
417
+ }
418
+ except Exception as e:
419
+ return {
420
+ "success": False,
421
+ "error": str(e),
422
+ }
423
+
424
+ async def list_plugins(self) -> dict[str, Any]:
425
+ """List loaded Python plugins.
426
+
427
+ Returns:
428
+ Dict with plugin information
429
+ """
430
+ if not self.internal_manager:
431
+ return {
432
+ "success": False,
433
+ "error": "Internal manager not available",
434
+ }
435
+
436
+ hook_manager = getattr(self.internal_manager, "_hook_manager", None)
437
+ if not hook_manager:
438
+ return {
439
+ "success": True,
440
+ "enabled": False,
441
+ "plugins": [],
442
+ "message": "Plugin system not initialized",
443
+ }
444
+
445
+ plugin_loader = getattr(hook_manager, "plugin_loader", None)
446
+ if not plugin_loader:
447
+ return {
448
+ "success": True,
449
+ "enabled": False,
450
+ "plugins": [],
451
+ "message": "No plugin loader available",
452
+ }
453
+
454
+ plugins = plugin_loader.registry.list_plugins()
455
+
456
+ return {
457
+ "success": True,
458
+ "enabled": plugin_loader.config.enabled,
459
+ "plugins": plugins,
460
+ "plugin_dirs": plugin_loader.config.plugin_dirs,
461
+ }
462
+
463
+ async def reload_plugin(self, name: str) -> dict[str, Any]:
464
+ """Reload a plugin by name.
465
+
466
+ Args:
467
+ name: Plugin name to reload
468
+
469
+ Returns:
470
+ Reload result
471
+ """
472
+ if not self.internal_manager:
473
+ return {
474
+ "success": False,
475
+ "error": "Internal manager not available",
476
+ }
477
+
478
+ hook_manager = getattr(self.internal_manager, "_hook_manager", None)
479
+ if not hook_manager:
480
+ return {
481
+ "success": False,
482
+ "error": "Plugin system not initialized",
483
+ }
484
+
485
+ plugin_loader = getattr(hook_manager, "plugin_loader", None)
486
+ if not plugin_loader:
487
+ return {
488
+ "success": False,
489
+ "error": "No plugin loader available",
490
+ }
491
+
492
+ try:
493
+ plugin = plugin_loader.reload_plugin(name)
494
+ if plugin is None:
495
+ return {
496
+ "success": False,
497
+ "error": f"Plugin not found or reload failed: {name}",
498
+ }
499
+
500
+ return {
501
+ "success": True,
502
+ "name": plugin.name,
503
+ "version": plugin.version,
504
+ "description": plugin.description,
505
+ }
506
+ except Exception as e:
507
+ logger.error(f"Plugin reload failed: {e}")
508
+ return {
509
+ "success": False,
510
+ "error": str(e),
511
+ }
512
+
513
+
514
+ def create_mcp_server(tools_handler: GobbyDaemonTools) -> FastMCP:
515
+ """Create the FastMCP server instance for the HTTP daemon."""
516
+ mcp = FastMCP("gobby")
517
+
518
+ # System tools
519
+ mcp.add_tool(tools_handler.status)
520
+ mcp.add_tool(tools_handler.list_mcp_servers)
521
+
522
+ # Tool Proxy
523
+ mcp.add_tool(tools_handler.call_tool)
524
+ mcp.add_tool(tools_handler.list_tools)
525
+ mcp.add_tool(tools_handler.get_tool_schema)
526
+ # read_mcp_resource is a tool that proxies resource reading
527
+ mcp.add_tool(tools_handler.read_mcp_resource)
528
+
529
+ # Server Management
530
+ mcp.add_tool(tools_handler.add_mcp_server)
531
+ mcp.add_tool(tools_handler.remove_mcp_server)
532
+ mcp.add_tool(tools_handler.import_mcp_server)
533
+
534
+ # Recommendation
535
+ mcp.add_tool(tools_handler.recommend_tools)
536
+
537
+ # Fallback Resolver
538
+ mcp.add_tool(tools_handler.get_tool_alternatives)
539
+
540
+ # Semantic Search
541
+ mcp.add_tool(tools_handler.search_tools)
542
+
543
+ # Hook Extensions
544
+ mcp.add_tool(tools_handler.list_hook_handlers)
545
+ mcp.add_tool(tools_handler.test_hook_event)
546
+ mcp.add_tool(tools_handler.list_plugins)
547
+ mcp.add_tool(tools_handler.reload_plugin)
548
+
549
+ return mcp
File without changes