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/worktrees.py ADDED
@@ -0,0 +1,481 @@
1
+ """
2
+ Worktree management CLI commands.
3
+
4
+ Commands for managing git worktrees:
5
+ - create: Create a new worktree
6
+ - list: List worktrees
7
+ - show: Show worktree details
8
+ - delete: Delete a worktree
9
+ - spawn: Spawn an agent in a worktree
10
+ - claim: Claim a worktree for a session
11
+ - release: Release a worktree
12
+ - sync: Sync worktree with main branch
13
+ - stale: Detect stale worktrees
14
+ - cleanup: Clean up stale worktrees
15
+ """
16
+
17
+ import json
18
+
19
+ import click
20
+ import httpx
21
+
22
+ from gobby.cli.tasks._utils import get_task_manager, resolve_task_id
23
+ from gobby.cli.utils import resolve_project_ref, resolve_session_id
24
+ from gobby.storage.database import LocalDatabase
25
+ from gobby.storage.worktrees import LocalWorktreeManager
26
+
27
+
28
+ def get_worktree_manager() -> LocalWorktreeManager:
29
+ """Get initialized worktree manager."""
30
+ db = LocalDatabase()
31
+ return LocalWorktreeManager(db)
32
+
33
+
34
+ def get_daemon_url() -> str:
35
+ """Get daemon URL from config."""
36
+ from gobby.config.app import load_config
37
+
38
+ config = load_config()
39
+ return f"http://localhost:{config.daemon_port}"
40
+
41
+
42
+ @click.group()
43
+ def worktrees() -> None:
44
+ """Manage git worktrees for parallel development."""
45
+ pass
46
+
47
+
48
+ @worktrees.command("create")
49
+ @click.argument("branch_name")
50
+ @click.option("--base", "-b", "base_branch", default="main", help="Base branch to create from")
51
+ @click.option("--task", "-t", "task_id", help="Link worktree to a task")
52
+ @click.option("--json", "json_format", is_flag=True, help="Output as JSON")
53
+ def create_worktree(
54
+ branch_name: str,
55
+ base_branch: str,
56
+ task_id: str | None,
57
+ json_format: bool,
58
+ ) -> None:
59
+ """Create a new worktree for parallel development.
60
+
61
+ Examples:
62
+
63
+ gobby worktrees create feature/my-feature
64
+
65
+ gobby worktrees create bugfix/fix-123 --base develop --task #47
66
+ """
67
+ import os
68
+
69
+ daemon_url = get_daemon_url()
70
+
71
+ arguments = {
72
+ "branch_name": branch_name,
73
+ "base_branch": base_branch,
74
+ "project_path": os.getcwd(),
75
+ }
76
+
77
+ if task_id:
78
+ task_manager = get_task_manager()
79
+ resolved = resolve_task_id(task_manager, task_id)
80
+ if not resolved:
81
+ # resolve_task_id prints error
82
+ return
83
+ arguments["task_id"] = resolved.id
84
+
85
+ try:
86
+ response = httpx.post(
87
+ f"{daemon_url}/mcp/gobby-worktrees/tools/create_worktree",
88
+ json=arguments,
89
+ timeout=60.0,
90
+ )
91
+ response.raise_for_status()
92
+ result = response.json()
93
+ except httpx.ConnectError:
94
+ click.echo("Error: Cannot connect to Gobby daemon. Is it running?", err=True)
95
+ return
96
+ except httpx.HTTPStatusError as e:
97
+ click.echo(f"HTTP Error {e.response.status_code}: {e.response.text}", err=True)
98
+ return
99
+ except Exception as e:
100
+ click.echo(f"Error: {e}", err=True)
101
+ return
102
+
103
+ if json_format:
104
+ click.echo(json.dumps(result, indent=2, default=str))
105
+ return
106
+
107
+ if result.get("success"):
108
+ click.echo(f"Created worktree: {result.get('worktree_id', 'unknown')}")
109
+ click.echo(f" Path: {result.get('worktree_path', 'unknown')}")
110
+ click.echo(f" Branch: {result.get('branch_name', 'unknown')}")
111
+ else:
112
+ click.echo(f"Failed to create worktree: {result.get('error')}", err=True)
113
+
114
+
115
+ @worktrees.command("list")
116
+ @click.option("--status", "-s", help="Filter by status (active, stale, merged, abandoned)")
117
+ @click.option("--project", "-p", "project_ref", help="Filter by project (name or UUID)")
118
+ @click.option("--json", "json_format", is_flag=True, help="Output as JSON")
119
+ def list_worktrees(
120
+ status: str | None,
121
+ project_ref: str | None,
122
+ json_format: bool,
123
+ ) -> None:
124
+ """List worktrees."""
125
+ project_id = resolve_project_ref(project_ref) if project_ref else None
126
+ manager = get_worktree_manager()
127
+
128
+ worktrees_list = manager.list_worktrees(status=status, project_id=project_id)
129
+
130
+ if json_format:
131
+ click.echo(json.dumps([w.to_dict() for w in worktrees_list], indent=2, default=str))
132
+ return
133
+
134
+ if not worktrees_list:
135
+ click.echo("No worktrees found.")
136
+ return
137
+
138
+ click.echo(f"Found {len(worktrees_list)} worktree(s):\n")
139
+ for wt in worktrees_list:
140
+ status_icon = {
141
+ "active": "●",
142
+ "stale": "○",
143
+ "merged": "✓",
144
+ "abandoned": "✗",
145
+ }.get(wt.status, "?")
146
+
147
+ session_info = f" (session: {wt.agent_session_id[:8]})" if wt.agent_session_id else ""
148
+ click.echo(f"{status_icon} {wt.id} {wt.branch_name:<30} {wt.status:<10}{session_info}")
149
+
150
+
151
+ @worktrees.command("show")
152
+ @click.argument("worktree_ref")
153
+ @click.option("--json", "json_format", is_flag=True, help="Output as JSON")
154
+ def show_worktree(worktree_ref: str, json_format: bool) -> None:
155
+ """Show details for a worktree (UUID or prefix)."""
156
+ manager = get_worktree_manager()
157
+ worktree_id = resolve_worktree_id(manager, worktree_ref)
158
+ worktree = manager.get(worktree_id)
159
+
160
+ if not worktree:
161
+ click.echo(f"Worktree not found: {worktree_id}", err=True)
162
+ return
163
+
164
+ if json_format:
165
+ click.echo(json.dumps(worktree.to_dict(), indent=2, default=str))
166
+ return
167
+
168
+ click.echo(f"Worktree: {worktree.id}")
169
+ click.echo(f" Status: {worktree.status}")
170
+ click.echo(f" Branch: {worktree.branch_name}")
171
+ click.echo(f" Path: {worktree.worktree_path}")
172
+ click.echo(f" Base Branch: {worktree.base_branch}")
173
+ if worktree.project_id:
174
+ click.echo(f" Project: {worktree.project_id}")
175
+ if worktree.agent_session_id:
176
+ click.echo(f" Session: {worktree.agent_session_id}")
177
+ click.echo(f" Created: {worktree.created_at}")
178
+ click.echo(f" Updated: {worktree.updated_at}")
179
+
180
+
181
+ @worktrees.command("delete")
182
+ @click.argument("worktree_ref")
183
+ @click.option("--force", "-f", is_flag=True, help="Force delete even if active")
184
+ @click.option("--yes", "-y", is_flag=True, help="Skip confirmation prompt")
185
+ def delete_worktree(worktree_ref: str, force: bool, yes: bool) -> None:
186
+ """Delete a worktree (UUID or prefix)."""
187
+ if not yes:
188
+ click.confirm("Are you sure you want to delete this worktree?", abort=True)
189
+
190
+ manager = get_worktree_manager()
191
+ try:
192
+ worktree_id = resolve_worktree_id(manager, worktree_ref)
193
+ except click.ClickException as e:
194
+ click.echo(str(e), err=True)
195
+ return
196
+
197
+ daemon_url = get_daemon_url()
198
+
199
+ try:
200
+ response = httpx.post(
201
+ f"{daemon_url}/mcp/gobby-worktrees/tools/delete_worktree",
202
+ json={"worktree_id": worktree_id, "force": force},
203
+ timeout=30.0,
204
+ )
205
+ response.raise_for_status()
206
+ result = response.json()
207
+ except httpx.ConnectError:
208
+ click.echo("Error: Cannot connect to Gobby daemon. Is it running?", err=True)
209
+ return
210
+ except httpx.HTTPStatusError as e:
211
+ click.echo(f"HTTP Error {e.response.status_code}: {e.response.text}", err=True)
212
+ return
213
+ except Exception as e:
214
+ click.echo(f"Error: {e}", err=True)
215
+ return
216
+
217
+ if result.get("success"):
218
+ click.echo(f"Deleted worktree: {worktree_id}")
219
+ else:
220
+ click.echo(f"Failed to delete worktree: {result.get('error')}", err=True)
221
+
222
+
223
+ # ... spawn command is unchanged ...
224
+
225
+
226
+ @worktrees.command("claim")
227
+ @click.argument("worktree_ref")
228
+ @click.argument("session_id")
229
+ def claim_worktree(worktree_ref: str, session_id: str) -> None:
230
+ """Claim a worktree for a session (UUID or prefix)."""
231
+ try:
232
+ session_id = resolve_session_id(session_id)
233
+ except click.ClickException as e:
234
+ e.show()
235
+ raise SystemExit(1) from e
236
+
237
+ manager = get_worktree_manager()
238
+ try:
239
+ worktree_id = resolve_worktree_id(manager, worktree_ref)
240
+ except click.ClickException as e:
241
+ raise SystemExit(1) from e
242
+
243
+ result = manager.claim(worktree_id, session_id)
244
+ if result:
245
+ click.echo(f"Claimed worktree {worktree_id} for session {session_id}")
246
+ else:
247
+ click.echo(f"Failed to claim worktree {worktree_id}", err=True)
248
+
249
+
250
+ @worktrees.command("release")
251
+ @click.argument("worktree_ref")
252
+ def release_worktree(worktree_ref: str) -> None:
253
+ """Release a worktree (UUID or prefix)."""
254
+ manager = get_worktree_manager()
255
+ try:
256
+ worktree_id = resolve_worktree_id(manager, worktree_ref)
257
+ except click.ClickException as e:
258
+ raise SystemExit(1) from e
259
+
260
+ result = manager.release(worktree_id)
261
+ if result:
262
+ click.echo(f"Released worktree {worktree_id}")
263
+ else:
264
+ click.echo(f"Failed to release worktree {worktree_id}", err=True)
265
+
266
+
267
+ @worktrees.command("sync")
268
+ @click.argument("worktree_ref")
269
+ @click.option(
270
+ "--source", "-s", "source_branch", help="Source branch to sync from (default: base branch)"
271
+ )
272
+ @click.option("--json", "json_format", is_flag=True, help="Output as JSON")
273
+ def sync_worktree(worktree_ref: str, source_branch: str | None, json_format: bool) -> None:
274
+ """Sync worktree with its base branch (UUID or prefix)."""
275
+ manager = get_worktree_manager()
276
+ try:
277
+ worktree_id = resolve_worktree_id(manager, worktree_ref)
278
+ except click.ClickException as e:
279
+ click.echo(str(e), err=True)
280
+ return
281
+
282
+ daemon_url = get_daemon_url()
283
+
284
+ arguments = {"worktree_id": worktree_id}
285
+ if source_branch:
286
+ arguments["source_branch"] = source_branch
287
+
288
+ try:
289
+ response = httpx.post(
290
+ f"{daemon_url}/mcp/gobby-worktrees/tools/sync_worktree",
291
+ json=arguments,
292
+ timeout=60.0,
293
+ )
294
+ response.raise_for_status()
295
+ result = response.json()
296
+ except httpx.ConnectError:
297
+ click.echo("Error: Cannot connect to Gobby daemon. Is it running?", err=True)
298
+ return
299
+ except httpx.HTTPStatusError as e:
300
+ click.echo(f"HTTP Error {e.response.status_code}: {e.response.text}", err=True)
301
+ return
302
+ except Exception as e:
303
+ click.echo(f"Error: {e}", err=True)
304
+ return
305
+
306
+ if json_format:
307
+ click.echo(json.dumps(result, indent=2, default=str))
308
+ return
309
+
310
+ if result.get("success"):
311
+ click.echo(f"Synced worktree {worktree_id}")
312
+ if result.get("commits_behind"):
313
+ click.echo(f" Commits merged: {result['commits_behind']}")
314
+ else:
315
+ click.echo(f"Failed to sync worktree: {result.get('error')}", err=True)
316
+
317
+
318
+ # ... stale/cleanup/stats commands ...
319
+
320
+
321
+ def resolve_worktree_id(manager: LocalWorktreeManager, worktree_ref: str) -> str:
322
+ """Resolve worktree reference (UUID or prefix) to full ID."""
323
+ # Optimization: check 36 chars?
324
+ if len(worktree_ref) == 36 and manager.get(worktree_ref):
325
+ return worktree_ref
326
+
327
+ # Use list listing since local manager doesn't expose prefix search easily
328
+ all_worktrees = manager.list_worktrees()
329
+ matches = [w for w in all_worktrees if w.id.startswith(worktree_ref)]
330
+
331
+ if not matches:
332
+ raise click.ClickException(f"Worktree not found: {worktree_ref}")
333
+
334
+ if len(matches) > 1:
335
+ click.echo(f"Ambiguous worktree reference '{worktree_ref}' matches:", err=True)
336
+ for w in matches:
337
+ click.echo(f" {w.id[:8]} ({w.branch_name})", err=True)
338
+ raise click.ClickException(f"Ambiguous worktree reference: {worktree_ref}")
339
+
340
+ return matches[0].id
341
+
342
+
343
+ @worktrees.command("stale")
344
+ @click.option("--days", "-d", default=7, help="Days of inactivity to consider stale")
345
+ @click.option("--json", "json_format", is_flag=True, help="Output as JSON")
346
+ def detect_stale(days: int, json_format: bool) -> None:
347
+ """Detect stale worktrees."""
348
+ daemon_url = get_daemon_url()
349
+ # Convert days to hours for MCP tool
350
+ hours = days * 24
351
+
352
+ try:
353
+ response = httpx.post(
354
+ f"{daemon_url}/mcp/gobby-worktrees/tools/detect_stale_worktrees",
355
+ json={"hours": hours},
356
+ timeout=30.0,
357
+ )
358
+ response.raise_for_status()
359
+ result = response.json()
360
+ except httpx.ConnectError:
361
+ click.echo("Error: Cannot connect to Gobby daemon. Is it running?", err=True)
362
+ return
363
+ except httpx.HTTPStatusError as e:
364
+ click.echo(f"HTTP Error {e.response.status_code}: {e.response.text}", err=True)
365
+ return
366
+ except Exception as e:
367
+ click.echo(f"Error: {e}", err=True)
368
+ return
369
+
370
+ if json_format:
371
+ click.echo(json.dumps(result, indent=2, default=str))
372
+ return
373
+
374
+ stale = result.get("stale_worktrees", [])
375
+ if not stale:
376
+ click.echo(f"No stale worktrees found (inactive > {days} days)")
377
+ return
378
+
379
+ click.echo(f"Found {len(stale)} stale worktree(s) (inactive > {days} days):\n")
380
+ for wt in stale:
381
+ click.echo(
382
+ f" {wt['id']}: {wt['branch_name']} (last updated: {wt.get('updated_at', 'unknown')})"
383
+ )
384
+
385
+
386
+ @worktrees.command("cleanup")
387
+ @click.option("--days", "-d", default=7, help="Days of inactivity to consider stale")
388
+ @click.option("--dry-run", is_flag=True, help="Show what would be cleaned up")
389
+ @click.option("--yes", "-y", is_flag=True, help="Skip confirmation prompt")
390
+ def cleanup_worktrees(days: int, dry_run: bool, yes: bool) -> None:
391
+ """Clean up stale worktrees."""
392
+ daemon_url = get_daemon_url()
393
+ # Convert days to hours for MCP tool
394
+ hours = days * 24
395
+
396
+ if dry_run:
397
+ # Just detect stale - no confirmation needed
398
+ try:
399
+ response = httpx.post(
400
+ f"{daemon_url}/mcp/gobby-worktrees/tools/detect_stale_worktrees",
401
+ json={"hours": hours},
402
+ timeout=30.0,
403
+ )
404
+ response.raise_for_status()
405
+ result = response.json()
406
+ stale = result.get("stale_worktrees", [])
407
+ click.echo(f"Would cleanup {len(stale)} stale worktree(s)")
408
+ for wt in stale:
409
+ click.echo(f" {wt['id']}: {wt['branch_name']}")
410
+ except Exception as e:
411
+ click.echo(f"Error: {e}", err=True)
412
+ return
413
+
414
+ # Confirm before actual cleanup unless --yes is provided
415
+ if not yes:
416
+ click.confirm("Are you sure you want to cleanup stale worktrees?", abort=True)
417
+
418
+ try:
419
+ response = httpx.post(
420
+ f"{daemon_url}/mcp/gobby-worktrees/tools/cleanup_stale_worktrees",
421
+ json={"hours": hours, "dry_run": False},
422
+ timeout=120.0,
423
+ )
424
+ response.raise_for_status()
425
+ result = response.json()
426
+ except httpx.ConnectError:
427
+ click.echo("Error: Cannot connect to Gobby daemon. Is it running?", err=True)
428
+ return
429
+ except httpx.HTTPStatusError as e:
430
+ click.echo(f"HTTP Error {e.response.status_code}: {e.response.text}", err=True)
431
+ return
432
+ except Exception as e:
433
+ click.echo(f"Error: {e}", err=True)
434
+ return
435
+
436
+ if result.get("success"):
437
+ cleaned = result.get("count", 0)
438
+ click.echo(f"Cleaned up {cleaned} stale worktree(s)")
439
+ else:
440
+ click.echo(f"Failed to cleanup worktrees: {result.get('error')}", err=True)
441
+
442
+
443
+ @worktrees.command("stats")
444
+ @click.option("--json", "json_format", is_flag=True, help="Output as JSON")
445
+ def worktree_stats(json_format: bool) -> None:
446
+ """Show worktree statistics."""
447
+ import os
448
+
449
+ daemon_url = get_daemon_url()
450
+
451
+ try:
452
+ response = httpx.post(
453
+ f"{daemon_url}/mcp/gobby-worktrees/tools/get_worktree_stats",
454
+ json={"project_path": os.getcwd()},
455
+ timeout=10.0,
456
+ )
457
+ response.raise_for_status()
458
+ result = response.json()
459
+ except httpx.ConnectError:
460
+ click.echo("Error: Cannot connect to Gobby daemon. Is it running?", err=True)
461
+ return
462
+ except httpx.HTTPStatusError as e:
463
+ click.echo(f"HTTP Error {e.response.status_code}: {e.response.text}", err=True)
464
+ return
465
+ except Exception as e:
466
+ click.echo(f"Error: {e}", err=True)
467
+ return
468
+
469
+ if json_format:
470
+ click.echo(json.dumps(result, indent=2, default=str))
471
+ return
472
+
473
+ counts = result.get("counts", {})
474
+ total = result.get("total", 0)
475
+ click.echo("Worktree Statistics:")
476
+ click.echo(f" Total: {total}")
477
+ click.echo(f" Active: {counts.get('active', 0)}")
478
+ click.echo(f" Stale: {counts.get('stale', 0)}")
479
+ click.echo(f" Merged: {counts.get('merged', 0)}")
480
+ click.echo(f" Abandoned: {counts.get('abandoned', 0)}")
481
+ click.echo(f" With Sessions: {counts.get('with_sessions', 0)}")
@@ -0,0 +1,129 @@
1
+ """
2
+ Configuration package for Gobby daemon.
3
+
4
+ This package provides Pydantic config models for all daemon settings.
5
+ Configuration classes are organized into submodules by functionality:
6
+
7
+ Module structure:
8
+ - app.py: Main DaemonConfig aggregator and utility functions
9
+ - logging.py: LoggingSettings
10
+ - servers.py: WebSocket and MCP proxy configs
11
+ - llm_providers.py: LLM provider configurations
12
+ - persistence.py: Memory storage configs
13
+ - tasks.py: Task expansion, validation, and workflow configs
14
+ - extensions.py: Hook extension configs (webhooks, plugins)
15
+ - sessions.py: Session lifecycle and tracking configs
16
+ - features.py: MCP proxy feature configs (code execution, tool recommendation)
17
+ """
18
+
19
+ # Core configuration and utilities from app.py
20
+ from gobby.config.app import (
21
+ DaemonConfig,
22
+ expand_env_vars,
23
+ load_config,
24
+ save_config,
25
+ )
26
+
27
+ # Extension configs
28
+ from gobby.config.extensions import (
29
+ HookExtensionsConfig,
30
+ PluginItemConfig,
31
+ PluginsConfig,
32
+ WebhookEndpointConfig,
33
+ WebhooksConfig,
34
+ WebSocketBroadcastConfig,
35
+ )
36
+
37
+ # Feature configs
38
+ from gobby.config.features import (
39
+ ImportMCPServerConfig,
40
+ MetricsConfig,
41
+ ProjectVerificationConfig,
42
+ RecommendToolsConfig,
43
+ ToolSummarizerConfig,
44
+ )
45
+
46
+ # LLM provider configs
47
+ from gobby.config.llm_providers import (
48
+ LLMProviderConfig,
49
+ LLMProvidersConfig,
50
+ )
51
+
52
+ # Logging configs
53
+ from gobby.config.logging import LoggingSettings
54
+
55
+ # Persistence configs
56
+ from gobby.config.persistence import (
57
+ MemoryConfig,
58
+ MemorySyncConfig,
59
+ )
60
+
61
+ # Server configs
62
+ from gobby.config.servers import (
63
+ MCPClientProxyConfig,
64
+ WebSocketSettings,
65
+ )
66
+
67
+ # Session configs
68
+ from gobby.config.sessions import (
69
+ ContextInjectionConfig,
70
+ MessageTrackingConfig,
71
+ SessionLifecycleConfig,
72
+ SessionSummaryConfig,
73
+ TitleSynthesisConfig,
74
+ )
75
+
76
+ # Task configs
77
+ from gobby.config.tasks import (
78
+ CompactHandoffConfig,
79
+ GobbyTasksConfig,
80
+ PatternCriteriaConfig,
81
+ TaskExpansionConfig,
82
+ TaskValidationConfig,
83
+ WorkflowConfig,
84
+ )
85
+
86
+ __all__ = [
87
+ # Core
88
+ "DaemonConfig",
89
+ "expand_env_vars",
90
+ "load_config",
91
+ "save_config",
92
+ # Extension configs
93
+ "HookExtensionsConfig",
94
+ "PluginItemConfig",
95
+ "PluginsConfig",
96
+ "WebhookEndpointConfig",
97
+ "WebhooksConfig",
98
+ "WebSocketBroadcastConfig",
99
+ # Feature configs
100
+ "ImportMCPServerConfig",
101
+ "MetricsConfig",
102
+ "ProjectVerificationConfig",
103
+ "RecommendToolsConfig",
104
+ "ToolSummarizerConfig",
105
+ # LLM provider configs
106
+ "LLMProviderConfig",
107
+ "LLMProvidersConfig",
108
+ # Logging configs
109
+ "LoggingSettings",
110
+ # Persistence configs
111
+ "MemoryConfig",
112
+ "MemorySyncConfig",
113
+ # Server configs
114
+ "MCPClientProxyConfig",
115
+ "WebSocketSettings",
116
+ # Session configs
117
+ "ContextInjectionConfig",
118
+ "MessageTrackingConfig",
119
+ "SessionLifecycleConfig",
120
+ "SessionSummaryConfig",
121
+ "TitleSynthesisConfig",
122
+ # Task configs
123
+ "CompactHandoffConfig",
124
+ "GobbyTasksConfig",
125
+ "PatternCriteriaConfig",
126
+ "TaskExpansionConfig",
127
+ "TaskValidationConfig",
128
+ "WorkflowConfig",
129
+ ]