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,379 @@
1
+ """
2
+ Linear integration tools for gobby-tasks registry.
3
+
4
+ Provides MCP tools for syncing between gobby tasks and Linear issues:
5
+ - import_linear_issues: Import Linear issues as gobby tasks
6
+ - sync_task_to_linear: Sync a task to its linked Linear issue
7
+ - create_issue_for_task: Create a Linear issue for a task
8
+ - link_linear_team: Set default Linear team for the project
9
+ - get_linear_status: Get Linear integration status
10
+
11
+ All tools delegate to LinearSyncService which uses the official Linear MCP server.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import logging
17
+ from typing import TYPE_CHECKING, Any
18
+
19
+ from gobby.mcp_proxy.tools.internal import InternalToolRegistry
20
+ from gobby.sync.linear import (
21
+ LinearNotFoundError,
22
+ LinearRateLimitError,
23
+ LinearSyncError,
24
+ LinearSyncService,
25
+ )
26
+ from gobby.utils.project_context import get_project_context
27
+
28
+ if TYPE_CHECKING:
29
+ from gobby.mcp_proxy.manager import MCPClientManager
30
+ from gobby.storage.projects import LocalProjectManager
31
+ from gobby.storage.tasks import LocalTaskManager
32
+
33
+ __all__ = ["create_linear_sync_registry"]
34
+
35
+ logger = logging.getLogger(__name__)
36
+
37
+
38
+ def create_linear_sync_registry(
39
+ task_manager: LocalTaskManager,
40
+ mcp_manager: MCPClientManager,
41
+ project_manager: LocalProjectManager,
42
+ project_id: str | None = None,
43
+ ) -> InternalToolRegistry:
44
+ """
45
+ Create a Linear sync tool registry.
46
+
47
+ Args:
48
+ task_manager: LocalTaskManager instance for task CRUD.
49
+ mcp_manager: MCPClientManager for Linear MCP server access.
50
+ project_manager: LocalProjectManager for project config.
51
+ project_id: Default project ID (optional, uses context if not provided).
52
+
53
+ Returns:
54
+ InternalToolRegistry with Linear sync tools registered.
55
+ """
56
+ registry = InternalToolRegistry(
57
+ name="gobby-tasks",
58
+ description="Linear integration tools",
59
+ )
60
+
61
+ def get_current_project_id() -> str | None:
62
+ """Get the current project ID from context."""
63
+ if project_id:
64
+ return project_id
65
+ ctx = get_project_context()
66
+ if ctx and ctx.get("id"):
67
+ pid_str: str = ctx["id"]
68
+ return pid_str
69
+ return None
70
+
71
+ def get_sync_service(team_id: str | None = None) -> LinearSyncService:
72
+ """Create LinearSyncService with current project context."""
73
+ pid = get_current_project_id()
74
+ if not pid:
75
+ raise ValueError("No project context - run from a gobby project directory")
76
+
77
+ return LinearSyncService(
78
+ mcp_manager=mcp_manager,
79
+ task_manager=task_manager,
80
+ project_id=pid,
81
+ linear_team_id=team_id,
82
+ )
83
+
84
+ # --- Linear Sync Tools ---
85
+
86
+ @registry.tool(
87
+ name="import_linear_issues",
88
+ description=(
89
+ "Import Linear issues as gobby tasks. "
90
+ "Each issue becomes a task linked to the original Linear issue."
91
+ ),
92
+ )
93
+ async def import_linear_issues(
94
+ team_id: str | None = None,
95
+ state: str | None = None,
96
+ labels: str | None = None,
97
+ ) -> dict[str, Any]:
98
+ """Import Linear issues as gobby tasks.
99
+
100
+ Args:
101
+ team_id: Linear team ID to filter issues (uses project default if not set)
102
+ state: Issue state filter (e.g., 'Todo', 'In Progress', 'Done')
103
+ labels: Comma-separated labels to filter issues (optional)
104
+
105
+ Returns:
106
+ Dict with 'tasks' list of created task dicts and 'count' of imported issues.
107
+ """
108
+ try:
109
+ service = get_sync_service(team_id)
110
+ label_list = labels.split(",") if labels else None
111
+
112
+ tasks = await service.import_linear_issues(
113
+ team_id=team_id,
114
+ state=state,
115
+ labels=label_list,
116
+ )
117
+
118
+ return {
119
+ "success": True,
120
+ "tasks": tasks,
121
+ "count": len(tasks),
122
+ "team_id": team_id,
123
+ }
124
+ except LinearRateLimitError as e:
125
+ return {
126
+ "success": False,
127
+ "error": str(e),
128
+ "error_type": "rate_limit",
129
+ "reset_at": e.reset_at,
130
+ }
131
+ except LinearNotFoundError as e:
132
+ return {
133
+ "success": False,
134
+ "error": str(e),
135
+ "error_type": "not_found",
136
+ "resource": e.resource,
137
+ }
138
+ except LinearSyncError as e:
139
+ return {
140
+ "success": False,
141
+ "error": str(e),
142
+ "error_type": "sync_error",
143
+ }
144
+ except RuntimeError as e:
145
+ return {
146
+ "success": False,
147
+ "error": str(e),
148
+ "error_type": "unavailable",
149
+ }
150
+
151
+ @registry.tool(
152
+ name="sync_task_to_linear",
153
+ description=(
154
+ "Sync a gobby task to its linked Linear issue. "
155
+ "Updates the issue title and description to match the task."
156
+ ),
157
+ )
158
+ async def sync_task_to_linear(task_id: str) -> dict[str, Any]:
159
+ """Sync a task to its linked Linear issue.
160
+
161
+ Args:
162
+ task_id: ID of the task to sync
163
+
164
+ Returns:
165
+ Dict with sync result including updated issue info.
166
+ """
167
+ try:
168
+ service = get_sync_service()
169
+ result = await service.sync_task_to_linear(task_id)
170
+
171
+ return {
172
+ "success": True,
173
+ "task_id": task_id,
174
+ "linear_result": result,
175
+ }
176
+ except ValueError as e:
177
+ return {
178
+ "success": False,
179
+ "error": str(e),
180
+ "error_type": "invalid_task",
181
+ }
182
+ except LinearRateLimitError as e:
183
+ return {
184
+ "success": False,
185
+ "error": str(e),
186
+ "error_type": "rate_limit",
187
+ "reset_at": e.reset_at,
188
+ }
189
+ except LinearNotFoundError as e:
190
+ return {
191
+ "success": False,
192
+ "error": str(e),
193
+ "error_type": "not_found",
194
+ "resource": e.resource,
195
+ }
196
+ except LinearSyncError as e:
197
+ return {
198
+ "success": False,
199
+ "error": str(e),
200
+ "error_type": "sync_error",
201
+ }
202
+ except RuntimeError as e:
203
+ return {
204
+ "success": False,
205
+ "error": str(e),
206
+ "error_type": "unavailable",
207
+ }
208
+
209
+ @registry.tool(
210
+ name="create_linear_issue_for_task",
211
+ description=(
212
+ "Create a Linear issue from a gobby task. Links the issue to the task for tracking."
213
+ ),
214
+ )
215
+ async def create_linear_issue_for_task(
216
+ task_id: str,
217
+ team_id: str | None = None,
218
+ ) -> dict[str, Any]:
219
+ """Create a Linear issue for a task.
220
+
221
+ Args:
222
+ task_id: ID of the task to create issue for
223
+ team_id: Linear team ID (uses task's team_id or project default if not set)
224
+
225
+ Returns:
226
+ Dict with issue info including ID.
227
+ """
228
+ try:
229
+ service = get_sync_service(team_id)
230
+ result = await service.create_issue_for_task(
231
+ task_id=task_id,
232
+ team_id=team_id,
233
+ )
234
+
235
+ return {
236
+ "success": True,
237
+ "task_id": task_id,
238
+ "issue_id": result.get("id"),
239
+ "linear_result": result,
240
+ }
241
+ except ValueError as e:
242
+ return {
243
+ "success": False,
244
+ "error": str(e),
245
+ "error_type": "invalid_task",
246
+ }
247
+ except LinearRateLimitError as e:
248
+ return {
249
+ "success": False,
250
+ "error": str(e),
251
+ "error_type": "rate_limit",
252
+ "reset_at": e.reset_at,
253
+ }
254
+ except LinearNotFoundError as e:
255
+ return {
256
+ "success": False,
257
+ "error": str(e),
258
+ "error_type": "not_found",
259
+ "resource": e.resource,
260
+ }
261
+ except LinearSyncError as e:
262
+ return {
263
+ "success": False,
264
+ "error": str(e),
265
+ "error_type": "sync_error",
266
+ }
267
+ except RuntimeError as e:
268
+ return {
269
+ "success": False,
270
+ "error": str(e),
271
+ "error_type": "unavailable",
272
+ }
273
+
274
+ @registry.tool(
275
+ name="link_linear_team",
276
+ description=(
277
+ "Link a Linear team to the current project. "
278
+ "Sets the default team for Linear operations."
279
+ ),
280
+ )
281
+ async def link_linear_team(team_id: str) -> dict[str, Any]:
282
+ """Link a Linear team to the current project.
283
+
284
+ Args:
285
+ team_id: Linear team ID to link
286
+
287
+ Returns:
288
+ Dict with link result.
289
+ """
290
+ pid = get_current_project_id()
291
+ if not pid:
292
+ return {
293
+ "success": False,
294
+ "error": "No project context - run from a gobby project directory",
295
+ }
296
+
297
+ # Update project with linear_team_id
298
+ project_manager.update(pid, linear_team_id=team_id)
299
+
300
+ return {
301
+ "success": True,
302
+ "project_id": pid,
303
+ "linear_team_id": team_id,
304
+ "message": f"Linked project to Linear team: {team_id}",
305
+ }
306
+
307
+ @registry.tool(
308
+ name="unlink_linear_team",
309
+ description="Remove Linear team link from the current project.",
310
+ )
311
+ async def unlink_linear_team() -> dict[str, Any]:
312
+ """Unlink Linear team from the current project.
313
+
314
+ Returns:
315
+ Dict with unlink result.
316
+ """
317
+ pid = get_current_project_id()
318
+ if not pid:
319
+ return {
320
+ "success": False,
321
+ "error": "No project context - run from a gobby project directory",
322
+ }
323
+
324
+ # Clear linear_team_id from project
325
+ project_manager.update(pid, linear_team_id=None)
326
+
327
+ return {
328
+ "success": True,
329
+ "project_id": pid,
330
+ "message": "Unlinked Linear team from project",
331
+ }
332
+
333
+ @registry.tool(
334
+ name="get_linear_status",
335
+ description=(
336
+ "Get Linear integration status for the current project. "
337
+ "Shows linked team, MCP server availability, and task links."
338
+ ),
339
+ )
340
+ async def get_linear_status() -> dict[str, Any]:
341
+ """Get Linear integration status.
342
+
343
+ Returns:
344
+ Dict with Linear status including linked team and availability.
345
+ """
346
+ pid = get_current_project_id()
347
+ if not pid:
348
+ return {
349
+ "success": False,
350
+ "error": "No project context - run from a gobby project directory",
351
+ }
352
+
353
+ project = project_manager.get(pid)
354
+ linear_team_id = getattr(project, "linear_team_id", None) if project else None
355
+
356
+ # Check Linear MCP availability
357
+ from gobby.integrations.linear import LinearIntegration
358
+
359
+ linear = LinearIntegration(mcp_manager)
360
+ available = linear.is_available()
361
+ unavailable_reason = linear.get_unavailable_reason() if not available else None
362
+
363
+ # Count linked tasks (direct query since list_tasks doesn't support filters)
364
+ row = task_manager.db.fetchone(
365
+ "SELECT COUNT(*) as count FROM tasks WHERE project_id = ? AND linear_issue_id IS NOT NULL",
366
+ (pid,),
367
+ )
368
+ linked_count = row["count"] if row else 0
369
+
370
+ return {
371
+ "success": True,
372
+ "project_id": pid,
373
+ "linear_team_id": linear_team_id,
374
+ "linear_available": available,
375
+ "unavailable_reason": unavailable_reason,
376
+ "linked_tasks_count": linked_count,
377
+ }
378
+
379
+ return registry
@@ -0,0 +1,77 @@
1
+ """Task orchestration tool registry."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import TYPE_CHECKING
7
+
8
+ from gobby.mcp_proxy.tools.internal import InternalToolRegistry
9
+ from gobby.mcp_proxy.tools.orchestration.cleanup import register_cleanup
10
+ from gobby.mcp_proxy.tools.orchestration.monitor import register_monitor
11
+ from gobby.mcp_proxy.tools.orchestration.orchestrate import register_orchestrator
12
+ from gobby.mcp_proxy.tools.orchestration.review import register_reviewer
13
+ from gobby.mcp_proxy.tools.orchestration.utils import get_current_project_id
14
+
15
+ if TYPE_CHECKING:
16
+ from gobby.agents.runner import AgentRunner
17
+ from gobby.storage.tasks import LocalTaskManager
18
+ from gobby.storage.worktrees import LocalWorktreeManager
19
+ from gobby.worktrees.git import WorktreeGitManager
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ def create_orchestration_registry(
25
+ task_manager: LocalTaskManager,
26
+ worktree_storage: LocalWorktreeManager,
27
+ git_manager: WorktreeGitManager | None = None,
28
+ agent_runner: AgentRunner | None = None,
29
+ project_id: str | None = None,
30
+ ) -> InternalToolRegistry:
31
+ """Create registry with orchestration tools."""
32
+ registry = InternalToolRegistry(
33
+ name="gobby-orchestration",
34
+ description="Task orchestration, monitoring, and review tools",
35
+ )
36
+
37
+ # Determine default project ID if not provided
38
+ default_project_id = project_id or get_current_project_id()
39
+
40
+ # Register orchestration tools
41
+ register_orchestrator(
42
+ registry=registry,
43
+ task_manager=task_manager,
44
+ worktree_storage=worktree_storage,
45
+ git_manager=git_manager,
46
+ agent_runner=agent_runner,
47
+ default_project_id=default_project_id,
48
+ )
49
+
50
+ # Register monitor tools
51
+ register_monitor(
52
+ registry=registry,
53
+ task_manager=task_manager,
54
+ worktree_storage=worktree_storage,
55
+ agent_runner=agent_runner,
56
+ default_project_id=default_project_id,
57
+ )
58
+
59
+ # Register review tools
60
+ register_reviewer(
61
+ registry=registry,
62
+ task_manager=task_manager,
63
+ worktree_storage=worktree_storage,
64
+ agent_runner=agent_runner,
65
+ default_project_id=default_project_id,
66
+ )
67
+
68
+ # Register cleanup tools
69
+ register_cleanup(
70
+ registry=registry,
71
+ task_manager=task_manager,
72
+ worktree_storage=worktree_storage,
73
+ git_manager=git_manager,
74
+ default_project_id=default_project_id,
75
+ )
76
+
77
+ return registry