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
gobby/cli/linear.py ADDED
@@ -0,0 +1,250 @@
1
+ """
2
+ CLI commands for Linear integration.
3
+
4
+ Provides commands for syncing gobby tasks with Linear issues.
5
+ """
6
+
7
+ import asyncio
8
+ import json
9
+ import logging
10
+
11
+ import click
12
+
13
+ from gobby.cli.tasks._utils import resolve_task_id
14
+ from gobby.integrations.linear import LinearIntegration
15
+ from gobby.mcp_proxy.manager import MCPClientManager
16
+ from gobby.storage.database import LocalDatabase
17
+ from gobby.storage.projects import LocalProjectManager
18
+ from gobby.storage.tasks import LocalTaskManager
19
+ from gobby.sync.linear import LinearSyncService
20
+ from gobby.utils.project_context import get_project_context
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ def get_linear_deps() -> tuple[LocalTaskManager, MCPClientManager, LocalProjectManager, str]:
26
+ """Get dependencies for Linear commands."""
27
+ db = LocalDatabase()
28
+ task_manager = LocalTaskManager(db)
29
+ project_manager = LocalProjectManager(db)
30
+ mcp_manager = MCPClientManager()
31
+
32
+ ctx = get_project_context()
33
+ if not ctx or not ctx.get("id"):
34
+ raise click.ClickException("Not in a gobby project directory. Run 'gobby init' first.")
35
+
36
+ project_id: str = ctx["id"]
37
+ return task_manager, mcp_manager, project_manager, project_id
38
+
39
+
40
+ def get_sync_service(team_id: str | None = None) -> LinearSyncService:
41
+ """Create LinearSyncService for CLI commands."""
42
+ task_manager, mcp_manager, _, project_id = get_linear_deps()
43
+ return LinearSyncService(
44
+ mcp_manager=mcp_manager,
45
+ task_manager=task_manager,
46
+ project_id=project_id,
47
+ linear_team_id=team_id,
48
+ )
49
+
50
+
51
+ @click.group()
52
+ def linear() -> None:
53
+ """Linear integration commands."""
54
+ pass
55
+
56
+
57
+ @linear.command("status")
58
+ @click.option("--json", "json_format", is_flag=True, help="Output as JSON")
59
+ def linear_status(json_format: bool) -> None:
60
+ """Show Linear integration status."""
61
+ try:
62
+ task_manager, mcp_manager, project_manager, project_id = get_linear_deps()
63
+
64
+ # Get project info
65
+ project = project_manager.get(project_id)
66
+ linear_team_id = project.linear_team_id if project else None
67
+
68
+ # Check Linear MCP availability
69
+ linear = LinearIntegration(mcp_manager)
70
+ available = linear.is_available()
71
+ unavailable_reason = linear.get_unavailable_reason() if not available else None
72
+
73
+ # Count linked tasks
74
+ row = task_manager.db.fetchone(
75
+ "SELECT COUNT(*) as count FROM tasks WHERE project_id = ? AND linear_issue_id IS NOT NULL",
76
+ (project_id,),
77
+ )
78
+ linked_count = row["count"] if row else 0
79
+
80
+ if json_format:
81
+ click.echo(
82
+ json.dumps(
83
+ {
84
+ "project_id": project_id,
85
+ "linear_team_id": linear_team_id,
86
+ "linear_available": available,
87
+ "unavailable_reason": unavailable_reason,
88
+ "linked_tasks_count": linked_count,
89
+ },
90
+ indent=2,
91
+ )
92
+ )
93
+ else:
94
+ click.echo("Linear Integration Status")
95
+ click.echo("=" * 40)
96
+ click.echo(f"Project ID: {project_id}")
97
+ click.echo(f"Linked team: {linear_team_id or '(not linked)'}")
98
+ click.echo(f"Linear MCP available: {'✓' if available else '✗'}")
99
+ if not available:
100
+ click.echo(f" Reason: {unavailable_reason}")
101
+ click.echo(f"Linked tasks: {linked_count}")
102
+
103
+ except click.ClickException:
104
+ raise
105
+ except Exception as e:
106
+ raise click.ClickException(str(e)) from None
107
+
108
+
109
+ @linear.command("link")
110
+ @click.argument("team_id")
111
+ def linear_link(team_id: str) -> None:
112
+ """Link a Linear team to this project.
113
+
114
+ TEAM_ID is the Linear team identifier (e.g., 'ENG-123' or UUID).
115
+ """
116
+ try:
117
+ _, _, project_manager, project_id = get_linear_deps()
118
+
119
+ project_manager.update(project_id, linear_team_id=team_id)
120
+ click.echo(f"✓ Linked project to Linear team: {team_id}")
121
+
122
+ except click.ClickException:
123
+ raise
124
+ except Exception as e:
125
+ raise click.ClickException(str(e)) from None
126
+
127
+
128
+ @linear.command("unlink")
129
+ def linear_unlink() -> None:
130
+ """Remove Linear team link from this project."""
131
+ try:
132
+ _, _, project_manager, project_id = get_linear_deps()
133
+
134
+ project_manager.update(project_id, linear_team_id=None)
135
+ click.echo("✓ Unlinked Linear team from project")
136
+
137
+ except click.ClickException:
138
+ raise
139
+ except Exception as e:
140
+ raise click.ClickException(str(e)) from None
141
+
142
+
143
+ @linear.command("import")
144
+ @click.argument("team_id", required=False)
145
+ @click.option("--state", help="Issue state filter (e.g., 'Todo', 'In Progress')")
146
+ @click.option("--labels", help="Comma-separated labels to filter issues")
147
+ @click.option("--json", "json_format", is_flag=True, help="Output as JSON")
148
+ def linear_import(
149
+ team_id: str | None, state: str | None, labels: str | None, json_format: bool
150
+ ) -> None:
151
+ """Import Linear issues as gobby tasks.
152
+
153
+ If TEAM_ID is not specified, uses the linked team.
154
+ """
155
+ try:
156
+ task_manager, mcp_manager, project_manager, project_id = get_linear_deps()
157
+
158
+ # Get team from argument or project config
159
+ if not team_id:
160
+ project = project_manager.get(project_id)
161
+ team_id = project.linear_team_id if project else None
162
+ if not team_id:
163
+ raise click.ClickException(
164
+ "No team specified and project not linked to a Linear team. "
165
+ "Use 'gobby linear link <team_id>' first or specify the team."
166
+ )
167
+
168
+ service = LinearSyncService(
169
+ mcp_manager=mcp_manager,
170
+ task_manager=task_manager,
171
+ project_id=project_id,
172
+ linear_team_id=team_id,
173
+ )
174
+
175
+ # Run async import
176
+ label_list = labels.split(",") if labels else None
177
+ tasks = asyncio.run(
178
+ service.import_linear_issues(team_id=team_id, state=state, labels=label_list)
179
+ )
180
+
181
+ if json_format:
182
+ click.echo(json.dumps({"tasks": tasks, "count": len(tasks)}, indent=2))
183
+ else:
184
+ click.echo(f"✓ Imported {len(tasks)} issues from Linear team {team_id}")
185
+ for task in tasks:
186
+ click.echo(f" - {task.get('id', 'unknown')}: {task.get('title', 'Untitled')}")
187
+
188
+ except click.ClickException:
189
+ raise
190
+ except Exception as e:
191
+ raise click.ClickException(str(e)) from None
192
+
193
+
194
+ @linear.command("sync")
195
+ @click.argument("task_id")
196
+ @click.option("--json", "json_format", is_flag=True, help="Output as JSON")
197
+ def linear_sync(task_id: str, json_format: bool) -> None:
198
+ """Sync a task to its linked Linear issue.
199
+
200
+ Updates the Linear issue title and description to match the task.
201
+ """
202
+ try:
203
+ task_manager, _, _, _ = get_linear_deps()
204
+ resolved = resolve_task_id(task_manager, task_id)
205
+ if not resolved:
206
+ return
207
+
208
+ service = get_sync_service()
209
+ result = asyncio.run(service.sync_task_to_linear(resolved.id))
210
+
211
+ if json_format:
212
+ click.echo(json.dumps(result, indent=2))
213
+ else:
214
+ click.echo(f"✓ Synced task {task_id} to Linear")
215
+
216
+ except click.ClickException:
217
+ raise
218
+ except ValueError as e:
219
+ raise click.ClickException(str(e)) from None
220
+ except Exception as e:
221
+ raise click.ClickException(str(e)) from None
222
+
223
+
224
+ @linear.command("create")
225
+ @click.argument("task_id")
226
+ @click.option("--team", "team_id", help="Linear team ID")
227
+ @click.option("--json", "json_format", is_flag=True, help="Output as JSON")
228
+ def linear_create(task_id: str, team_id: str | None, json_format: bool) -> None:
229
+ """Create a Linear issue from a gobby task."""
230
+ try:
231
+ task_manager, _, _, _ = get_linear_deps()
232
+ resolved = resolve_task_id(task_manager, task_id)
233
+ if not resolved:
234
+ return
235
+
236
+ service = get_sync_service(team_id)
237
+ result = asyncio.run(service.create_issue_for_task(task_id=resolved.id, team_id=team_id))
238
+
239
+ if json_format:
240
+ click.echo(json.dumps(result, indent=2))
241
+ else:
242
+ issue_id = result.get("id", "unknown")
243
+ click.echo(f"✓ Created Linear issue {issue_id} for task {task_id}")
244
+
245
+ except click.ClickException:
246
+ raise
247
+ except ValueError as e:
248
+ raise click.ClickException(str(e)) from None
249
+ except Exception as e:
250
+ raise click.ClickException(str(e)) from None
gobby/cli/mcp.py ADDED
@@ -0,0 +1,30 @@
1
+ """
2
+ MCP server commands.
3
+ """
4
+
5
+ import asyncio
6
+ import logging
7
+
8
+ import click
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ @click.command()
14
+ @click.pass_context
15
+ def mcp_server(ctx: click.Context) -> None:
16
+ """
17
+ Run stdio MCP server for AI CLI integration.
18
+
19
+ This command starts a stdio-based MCP server that:
20
+ - Auto-starts the daemon if not running
21
+ - Provides daemon lifecycle tools (start/stop/restart)
22
+ - Proxies all HTTP MCP tools from the daemon
23
+
24
+ Example usage:
25
+ claude mcp add --transport stdio gobby -- gobby mcp-server
26
+ """
27
+ from gobby.mcp_proxy.stdio import main as mcp_main
28
+
29
+ # Run the stdio MCP server
30
+ asyncio.run(mcp_main())