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,337 @@
1
+ """
2
+ Internal tool registry for Gobby built-in tools.
3
+
4
+ Provides a registry system for internal tools that can be accessed via the
5
+ downstream proxy pattern (call_tool, list_tools, get_tool_schema) without
6
+ being registered directly on the FastMCP server.
7
+
8
+ This enables progressive disclosure for internal tools and reduces the
9
+ number of tools exposed on the main MCP server.
10
+ """
11
+
12
+ import inspect
13
+ import logging
14
+ import types
15
+ from collections.abc import Callable
16
+ from dataclasses import dataclass
17
+ from typing import Any, Union, get_args, get_origin
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ def _get_json_schema_type(annotation: Any) -> str:
23
+ """
24
+ Convert a Python type annotation to a JSON schema type string.
25
+
26
+ Handles:
27
+ - Basic types: int, bool, str, dict, list
28
+ - Generic types: dict[str, Any], list[str], etc.
29
+ - Union types: str | None, dict[str, Any] | None, etc.
30
+ - typing.Union: Union[str, None], etc.
31
+
32
+ Args:
33
+ annotation: Python type annotation
34
+
35
+ Returns:
36
+ JSON schema type string ("string", "integer", "boolean", "object", "array")
37
+ """
38
+ if annotation is inspect.Parameter.empty:
39
+ return "string"
40
+
41
+ # Handle Union types (X | Y or Union[X, Y])
42
+ origin = get_origin(annotation)
43
+ if origin is Union or origin is types.UnionType:
44
+ # Get non-None types from the union
45
+ args = [arg for arg in get_args(annotation) if arg is not type(None)]
46
+ if args:
47
+ # Use the first non-None type
48
+ return _get_json_schema_type(args[0])
49
+ return "string"
50
+
51
+ # Handle generic types like dict[str, Any], list[str]
52
+ if origin is dict:
53
+ return "object"
54
+ if origin is list:
55
+ return "array"
56
+
57
+ # Handle basic types
58
+ if annotation is int:
59
+ return "integer"
60
+ if annotation is bool:
61
+ return "boolean"
62
+ if annotation is dict:
63
+ return "object"
64
+ if annotation is list:
65
+ return "array"
66
+ if annotation is str:
67
+ return "string"
68
+
69
+ # Default to string for unknown types
70
+ return "string"
71
+
72
+
73
+ @dataclass
74
+ class InternalTool:
75
+ """Represents an internal tool with its metadata and implementation."""
76
+
77
+ name: str
78
+ description: str
79
+ input_schema: dict[str, Any]
80
+ func: Callable[..., Any]
81
+
82
+
83
+ class InternalToolRegistry:
84
+ """
85
+ Registry for a domain of internal tools (e.g., gobby-tasks).
86
+
87
+ Each registry represents a logical grouping of tools that can be
88
+ discovered and called via the proxy pattern.
89
+ """
90
+
91
+ def __init__(self, name: str, description: str = ""):
92
+ """
93
+ Initialize a tool registry.
94
+
95
+ Args:
96
+ name: Server name (e.g., "gobby-tasks")
97
+ description: Human-readable description of this tool domain
98
+ """
99
+ self.name = name
100
+ self.description = description
101
+ self._tools: dict[str, InternalTool] = {}
102
+
103
+ def register(
104
+ self,
105
+ name: str,
106
+ description: str,
107
+ input_schema: dict[str, Any],
108
+ func: Callable[..., Any],
109
+ ) -> None:
110
+ """
111
+ Register a tool with the registry.
112
+
113
+ Args:
114
+ name: Tool name
115
+ description: Tool description (for progressive disclosure)
116
+ input_schema: JSON Schema for the tool's input parameters
117
+ func: The callable that implements the tool (sync or async)
118
+ """
119
+ self._tools[name] = InternalTool(
120
+ name=name,
121
+ description=description,
122
+ input_schema=input_schema,
123
+ func=func,
124
+ )
125
+ logger.debug(f"Registered internal tool '{name}' on '{self.name}'")
126
+
127
+ def tool(
128
+ self,
129
+ name: str | None = None,
130
+ description: str | None = None,
131
+ ) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
132
+ """
133
+ Decorator to register a function as a tool.
134
+
135
+ Args:
136
+ name: Optional tool name (defaults to function name)
137
+ description: Optional description (defaults to docstring)
138
+
139
+ Returns:
140
+ Decorator function
141
+ """
142
+
143
+ def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
144
+ tool_name = name or func.__name__
145
+ tool_desc = description or func.__doc__ or ""
146
+
147
+ # Simple schema generation (placeholder for fuller introspection)
148
+ # In a real system, we'd inspect signature to build JSON schema
149
+ # For now, we'll assume the decorated function is well-behaved
150
+ # or rely on manual registration if complex schema needed.
151
+ # But wait, tasks.py usage implies we need schema extraction.
152
+ # Extract schema from function signature using type annotations
153
+ sig = inspect.signature(func)
154
+ properties = {}
155
+ required = []
156
+
157
+ for param_name, param in sig.parameters.items():
158
+ if param_name == "self":
159
+ continue
160
+
161
+ param_type = _get_json_schema_type(param.annotation)
162
+ properties[param_name] = {"type": param_type}
163
+
164
+ if param.default == inspect.Parameter.empty:
165
+ required.append(param_name)
166
+
167
+ input_schema = {"type": "object", "properties": properties, "required": required}
168
+
169
+ self.register(
170
+ name=tool_name,
171
+ description=tool_desc.strip(),
172
+ input_schema=input_schema,
173
+ func=func,
174
+ )
175
+ return func
176
+
177
+ return decorator
178
+
179
+ async def call(self, name: str, args: dict[str, Any]) -> Any:
180
+ """
181
+ Call a tool by name with the given arguments.
182
+
183
+ Args:
184
+ name: Tool name
185
+ args: Tool arguments
186
+
187
+ Returns:
188
+ Tool execution result
189
+
190
+ Raises:
191
+ ValueError: If tool not found
192
+ Exception: If tool execution fails
193
+ """
194
+ tool = self._tools.get(name)
195
+ if not tool:
196
+ available = ", ".join(self._tools.keys())
197
+ raise ValueError(f"Tool '{name}' not found on '{self.name}'. Available: {available}")
198
+
199
+ # Call the function (handle both sync and async)
200
+ if inspect.iscoroutinefunction(tool.func):
201
+ return await tool.func(**args)
202
+ return tool.func(**args)
203
+
204
+ def list_tools(self) -> list[dict[str, str]]:
205
+ """
206
+ List all tools with lightweight metadata.
207
+
208
+ Returns:
209
+ List of {name, brief} dicts for progressive disclosure
210
+ """
211
+ return [
212
+ {
213
+ "name": tool.name,
214
+ "brief": tool.description[:100] if tool.description else "No description",
215
+ }
216
+ for tool in self._tools.values()
217
+ ]
218
+
219
+ def get_schema(self, name: str) -> dict[str, Any] | None:
220
+ """
221
+ Get full schema for a specific tool.
222
+
223
+ Args:
224
+ name: Tool name
225
+
226
+ Returns:
227
+ Dict with name, description, and inputSchema, or None if not found
228
+ """
229
+ tool = self._tools.get(name)
230
+ if not tool:
231
+ return None
232
+
233
+ return {
234
+ "name": tool.name,
235
+ "description": tool.description,
236
+ "inputSchema": tool.input_schema,
237
+ }
238
+
239
+ def __len__(self) -> int:
240
+ """Return number of registered tools."""
241
+ return len(self._tools)
242
+
243
+
244
+ class InternalRegistryManager:
245
+ """
246
+ Manages multiple internal registries (gobby-tasks, gobby-hooks, etc.).
247
+
248
+ Provides routing logic to dispatch calls to the appropriate registry
249
+ based on server name prefix.
250
+ """
251
+
252
+ INTERNAL_PREFIX = "gobby-"
253
+
254
+ def __init__(self) -> None:
255
+ self._registries: dict[str, InternalToolRegistry] = {}
256
+
257
+ def add_registry(self, registry: InternalToolRegistry) -> None:
258
+ """
259
+ Add a registry to the manager.
260
+
261
+ Args:
262
+ registry: The registry to add
263
+ """
264
+ self._registries[registry.name] = registry
265
+ logger.info(f"Added internal registry '{registry.name}' with {len(registry)} tools")
266
+
267
+ def is_internal(self, server_name: str | None) -> bool:
268
+ """
269
+ Check if a server name refers to an internal registry.
270
+
271
+ Args:
272
+ server_name: Server name to check
273
+
274
+ Returns:
275
+ True if server_name starts with 'gobby-'
276
+ """
277
+ if server_name is None:
278
+ return False
279
+ return server_name.startswith(self.INTERNAL_PREFIX)
280
+
281
+ def get_registry(self, server_name: str) -> InternalToolRegistry | None:
282
+ """
283
+ Get a registry by name.
284
+
285
+ Args:
286
+ server_name: Registry name (e.g., "gobby-tasks")
287
+
288
+ Returns:
289
+ The registry if found, None otherwise
290
+ """
291
+ return self._registries.get(server_name)
292
+
293
+ def list_servers(self) -> list[dict[str, Any]]:
294
+ """
295
+ List all internal servers with metadata.
296
+
297
+ Returns:
298
+ List of server info dicts
299
+ """
300
+ return [
301
+ {
302
+ "name": registry.name,
303
+ "description": registry.description,
304
+ "tool_count": len(registry),
305
+ }
306
+ for registry in self._registries.values()
307
+ ]
308
+
309
+ def get_all_registries(self) -> list[InternalToolRegistry]:
310
+ """
311
+ Get all registered registries.
312
+
313
+ Returns:
314
+ List of all registries
315
+ """
316
+ return list(self._registries.values())
317
+
318
+ def find_tool_server(self, tool_name: str) -> str | None:
319
+ """
320
+ Find which internal server owns a tool.
321
+
322
+ Searches all internal registries to find a tool by name.
323
+
324
+ Args:
325
+ tool_name: Name of the tool to find
326
+
327
+ Returns:
328
+ Server name if found, None otherwise
329
+ """
330
+ for registry in self._registries.values():
331
+ if tool_name in registry._tools:
332
+ return registry.name
333
+ return None
334
+
335
+ def __len__(self) -> int:
336
+ """Return number of registries."""
337
+ return len(self._registries)