gobby 0.2.7__tar.gz → 0.2.9__tar.gz

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 (437) hide show
  1. {gobby-0.2.7 → gobby-0.2.9}/PKG-INFO +1 -1
  2. {gobby-0.2.7 → gobby-0.2.9}/pyproject.toml +1 -1
  3. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/__init__.py +1 -1
  4. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/adapters/claude_code.py +99 -61
  5. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/adapters/gemini.py +140 -38
  6. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/isolation.py +130 -0
  7. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/registry.py +11 -0
  8. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/session.py +1 -0
  9. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawn_executor.py +43 -13
  10. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawners/macos.py +26 -1
  11. gobby-0.2.9/src/gobby/app_context.py +59 -0
  12. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/__init__.py +0 -2
  13. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/memory.py +185 -0
  14. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/utils.py +5 -17
  15. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/clones/git.py +177 -0
  16. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/features.py +0 -20
  17. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/skills.py +31 -0
  18. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/tasks.py +4 -0
  19. gobby-0.2.9/src/gobby/hooks/event_handlers/__init__.py +155 -0
  20. gobby-0.2.9/src/gobby/hooks/event_handlers/_agent.py +175 -0
  21. gobby-0.2.9/src/gobby/hooks/event_handlers/_base.py +87 -0
  22. gobby-0.2.9/src/gobby/hooks/event_handlers/_misc.py +66 -0
  23. gobby-0.2.9/src/gobby/hooks/event_handlers/_session.py +573 -0
  24. gobby-0.2.9/src/gobby/hooks/event_handlers/_tool.py +196 -0
  25. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/hook_manager.py +21 -1
  26. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/install/gemini/hooks/hook_dispatcher.py +74 -15
  27. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/claude.py +377 -42
  28. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/importer.py +4 -41
  29. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/instructions.py +2 -2
  30. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/manager.py +13 -3
  31. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/registries.py +35 -4
  32. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/services/recommendation.py +2 -28
  33. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/agent_messaging.py +93 -44
  34. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/agents.py +45 -9
  35. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/artifacts.py +46 -12
  36. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/sessions/_commits.py +31 -24
  37. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/sessions/_crud.py +5 -5
  38. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/sessions/_handoff.py +45 -41
  39. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/sessions/_messages.py +35 -7
  40. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/spawn_agent.py +44 -6
  41. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/task_readiness.py +27 -4
  42. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_context.py +18 -0
  43. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_crud.py +13 -6
  44. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_lifecycle.py +29 -14
  45. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_session.py +22 -7
  46. gobby-0.2.9/src/gobby/mcp_proxy/tools/workflows/__init__.py +266 -0
  47. gobby-0.2.9/src/gobby/mcp_proxy/tools/workflows/_artifacts.py +225 -0
  48. gobby-0.2.9/src/gobby/mcp_proxy/tools/workflows/_import.py +112 -0
  49. gobby-0.2.9/src/gobby/mcp_proxy/tools/workflows/_lifecycle.py +321 -0
  50. gobby-0.2.9/src/gobby/mcp_proxy/tools/workflows/_query.py +207 -0
  51. gobby-0.2.9/src/gobby/mcp_proxy/tools/workflows/_resolution.py +78 -0
  52. gobby-0.2.9/src/gobby/mcp_proxy/tools/workflows/_terminal.py +139 -0
  53. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/worktrees.py +32 -7
  54. gobby-0.2.9/src/gobby/memory/components/ingestion.py +98 -0
  55. gobby-0.2.9/src/gobby/memory/components/search.py +108 -0
  56. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/extractor.py +15 -1
  57. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/manager.py +16 -25
  58. gobby-0.2.9/src/gobby/paths.py +51 -0
  59. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/prompts/loader.py +1 -35
  60. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/runner.py +36 -10
  61. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/http.py +186 -149
  62. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/admin.py +12 -0
  63. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/mcp/endpoints/execution.py +15 -7
  64. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/mcp/endpoints/registry.py +8 -8
  65. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/mcp/hooks.py +50 -3
  66. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/websocket.py +57 -1
  67. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sessions/analyzer.py +4 -4
  68. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sessions/manager.py +9 -0
  69. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sessions/transcripts/gemini.py +100 -34
  70. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/skills/parser.py +23 -0
  71. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/skills/sync.py +5 -4
  72. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/artifacts.py +19 -0
  73. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/database.py +9 -2
  74. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/memories.py +32 -21
  75. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/migrations.py +46 -4
  76. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/sessions.py +4 -2
  77. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/skills.py +87 -7
  78. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tasks/external_validator.py +4 -17
  79. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tasks/validation.py +13 -87
  80. gobby-0.2.9/src/gobby/tools/__init__.py +0 -0
  81. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tools/summarizer.py +18 -51
  82. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/status.py +13 -0
  83. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/actions.py +5 -0
  84. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/context_actions.py +21 -24
  85. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/detection_helpers.py +38 -24
  86. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/enforcement/__init__.py +11 -1
  87. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/enforcement/blocking.py +109 -1
  88. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/enforcement/handlers.py +35 -1
  89. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/engine.py +96 -0
  90. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/evaluator.py +110 -0
  91. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/hooks.py +41 -0
  92. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/lifecycle_evaluator.py +2 -1
  93. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/memory_actions.py +11 -0
  94. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/safe_evaluator.py +8 -0
  95. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/summary_actions.py +123 -50
  96. {gobby-0.2.7 → gobby-0.2.9}/src/gobby.egg-info/PKG-INFO +1 -1
  97. {gobby-0.2.7 → gobby-0.2.9}/src/gobby.egg-info/SOURCES.txt +18 -26
  98. {gobby-0.2.7 → gobby-0.2.9}/tests/test_import_pathing_trap.py +5 -2
  99. {gobby-0.2.7 → gobby-0.2.9}/tests/test_runner.py +24 -22
  100. gobby-0.2.7/src/gobby/cli/tui.py +0 -34
  101. gobby-0.2.7/src/gobby/hooks/event_handlers.py +0 -909
  102. gobby-0.2.7/src/gobby/mcp_proxy/tools/workflows.py +0 -973
  103. gobby-0.2.7/src/gobby/tui/__init__.py +0 -5
  104. gobby-0.2.7/src/gobby/tui/api_client.py +0 -278
  105. gobby-0.2.7/src/gobby/tui/app.py +0 -329
  106. gobby-0.2.7/src/gobby/tui/screens/__init__.py +0 -25
  107. gobby-0.2.7/src/gobby/tui/screens/agents.py +0 -333
  108. gobby-0.2.7/src/gobby/tui/screens/chat.py +0 -450
  109. gobby-0.2.7/src/gobby/tui/screens/dashboard.py +0 -377
  110. gobby-0.2.7/src/gobby/tui/screens/memory.py +0 -305
  111. gobby-0.2.7/src/gobby/tui/screens/metrics.py +0 -231
  112. gobby-0.2.7/src/gobby/tui/screens/orchestrator.py +0 -903
  113. gobby-0.2.7/src/gobby/tui/screens/sessions.py +0 -412
  114. gobby-0.2.7/src/gobby/tui/screens/tasks.py +0 -440
  115. gobby-0.2.7/src/gobby/tui/screens/workflows.py +0 -289
  116. gobby-0.2.7/src/gobby/tui/screens/worktrees.py +0 -174
  117. gobby-0.2.7/src/gobby/tui/widgets/__init__.py +0 -21
  118. gobby-0.2.7/src/gobby/tui/widgets/chat.py +0 -210
  119. gobby-0.2.7/src/gobby/tui/widgets/conductor.py +0 -104
  120. gobby-0.2.7/src/gobby/tui/widgets/menu.py +0 -132
  121. gobby-0.2.7/src/gobby/tui/widgets/message_panel.py +0 -160
  122. gobby-0.2.7/src/gobby/tui/widgets/review_gate.py +0 -224
  123. gobby-0.2.7/src/gobby/tui/widgets/task_tree.py +0 -99
  124. gobby-0.2.7/src/gobby/tui/widgets/token_budget.py +0 -166
  125. gobby-0.2.7/src/gobby/tui/ws_client.py +0 -258
  126. {gobby-0.2.7 → gobby-0.2.9}/LICENSE.md +0 -0
  127. {gobby-0.2.7 → gobby-0.2.9}/README.md +0 -0
  128. {gobby-0.2.7 → gobby-0.2.9}/setup.cfg +0 -0
  129. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/adapters/__init__.py +0 -0
  130. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/adapters/base.py +0 -0
  131. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/adapters/codex_impl/__init__.py +0 -0
  132. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/adapters/codex_impl/adapter.py +0 -0
  133. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/adapters/codex_impl/client.py +0 -0
  134. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/adapters/codex_impl/protocol.py +0 -0
  135. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/adapters/codex_impl/types.py +0 -0
  136. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/__init__.py +0 -0
  137. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/codex_session.py +0 -0
  138. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/constants.py +0 -0
  139. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/context.py +0 -0
  140. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/definitions.py +0 -0
  141. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/gemini_session.py +0 -0
  142. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/runner.py +0 -0
  143. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/sandbox.py +0 -0
  144. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawn.py +0 -0
  145. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawners/__init__.py +0 -0
  146. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawners/base.py +0 -0
  147. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawners/command_builder.py +0 -0
  148. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawners/cross_platform.py +0 -0
  149. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawners/embedded.py +0 -0
  150. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawners/headless.py +0 -0
  151. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawners/linux.py +0 -0
  152. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawners/prompt_manager.py +0 -0
  153. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawners/windows.py +0 -0
  154. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/tty_config.py +0 -0
  155. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/autonomous/__init__.py +0 -0
  156. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/autonomous/progress_tracker.py +0 -0
  157. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/autonomous/stop_registry.py +0 -0
  158. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/autonomous/stuck_detector.py +0 -0
  159. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/__main__.py +0 -0
  160. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/agents.py +0 -0
  161. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/artifacts.py +0 -0
  162. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/clones.py +0 -0
  163. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/conductor.py +0 -0
  164. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/daemon.py +0 -0
  165. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/extensions.py +0 -0
  166. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/github.py +0 -0
  167. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/init.py +0 -0
  168. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/install.py +0 -0
  169. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/installers/__init__.py +0 -0
  170. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/installers/antigravity.py +0 -0
  171. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/installers/claude.py +0 -0
  172. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/installers/codex.py +0 -0
  173. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/installers/gemini.py +0 -0
  174. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/installers/git_hooks.py +0 -0
  175. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/installers/shared.py +0 -0
  176. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/linear.py +0 -0
  177. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/mcp.py +0 -0
  178. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/mcp_proxy.py +0 -0
  179. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/merge.py +0 -0
  180. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/projects.py +0 -0
  181. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/sessions.py +0 -0
  182. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/skills.py +0 -0
  183. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/tasks/__init__.py +0 -0
  184. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/tasks/_utils.py +0 -0
  185. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/tasks/ai.py +0 -0
  186. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/tasks/commits.py +0 -0
  187. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/tasks/crud.py +0 -0
  188. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/tasks/deps.py +0 -0
  189. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/tasks/labels.py +0 -0
  190. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/tasks/main.py +0 -0
  191. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/tasks/search.py +0 -0
  192. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/workflows.py +0 -0
  193. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/worktrees.py +0 -0
  194. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/clones/__init__.py +0 -0
  195. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/conductor/__init__.py +0 -0
  196. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/conductor/alerts.py +0 -0
  197. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/conductor/loop.py +0 -0
  198. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/conductor/monitors/__init__.py +0 -0
  199. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/conductor/monitors/agents.py +0 -0
  200. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/conductor/monitors/tasks.py +0 -0
  201. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/conductor/pricing.py +0 -0
  202. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/conductor/token_tracker.py +0 -0
  203. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/__init__.py +0 -0
  204. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/app.py +0 -0
  205. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/extensions.py +0 -0
  206. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/llm_providers.py +0 -0
  207. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/logging.py +0 -0
  208. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/mcp.py +0 -0
  209. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/persistence.py +0 -0
  210. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/search.py +0 -0
  211. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/servers.py +0 -0
  212. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/sessions.py +0 -0
  213. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/__init__.py +0 -0
  214. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/artifact_capture.py +0 -0
  215. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/broadcaster.py +0 -0
  216. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/events.py +0 -0
  217. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/git.py +0 -0
  218. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/health_monitor.py +0 -0
  219. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/hook_types.py +0 -0
  220. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/plugins.py +0 -0
  221. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/session_coordinator.py +0 -0
  222. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/skill_manager.py +0 -0
  223. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/verification_runner.py +0 -0
  224. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/webhooks.py +0 -0
  225. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/install/claude/hooks/HOOK_SCHEMAS.md +0 -0
  226. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/install/claude/hooks/hook_dispatcher.py +0 -0
  227. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/install/claude/hooks/validate_settings.py +0 -0
  228. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/install/claude/hooks-template.json +0 -0
  229. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/install/codex/hooks/hook_dispatcher.py +0 -0
  230. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/install/gemini/hooks-template.json +0 -0
  231. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/install/shared/plugins/code_guardian.py +0 -0
  232. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/install/shared/plugins/example_notify.py +0 -0
  233. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/integrations/__init__.py +0 -0
  234. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/integrations/github.py +0 -0
  235. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/integrations/linear.py +0 -0
  236. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/__init__.py +0 -0
  237. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/base.py +0 -0
  238. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/claude_executor.py +0 -0
  239. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/codex.py +0 -0
  240. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/codex_executor.py +0 -0
  241. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/executor.py +0 -0
  242. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/factory.py +0 -0
  243. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/gemini.py +0 -0
  244. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/litellm.py +0 -0
  245. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/litellm_executor.py +0 -0
  246. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/resolver.py +0 -0
  247. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/service.py +0 -0
  248. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/__init__.py +0 -0
  249. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/actions.py +0 -0
  250. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/daemon_control.py +0 -0
  251. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/lazy.py +0 -0
  252. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/metrics.py +0 -0
  253. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/models.py +0 -0
  254. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/schema_hash.py +0 -0
  255. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/semantic_search.py +0 -0
  256. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/server.py +0 -0
  257. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/services/__init__.py +0 -0
  258. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/services/fallback.py +0 -0
  259. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/services/server_mgmt.py +0 -0
  260. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/services/system.py +0 -0
  261. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/services/tool_filter.py +0 -0
  262. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/services/tool_proxy.py +0 -0
  263. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/stdio.py +0 -0
  264. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/__init__.py +0 -0
  265. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/clones.py +0 -0
  266. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/hub.py +0 -0
  267. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/internal.py +0 -0
  268. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/memory.py +0 -0
  269. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/merge.py +0 -0
  270. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/metrics.py +0 -0
  271. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/orchestration/__init__.py +0 -0
  272. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/orchestration/cleanup.py +0 -0
  273. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/orchestration/monitor.py +0 -0
  274. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/orchestration/orchestrate.py +0 -0
  275. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/orchestration/review.py +0 -0
  276. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/orchestration/utils.py +0 -0
  277. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/orchestration/wait.py +0 -0
  278. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/sessions/__init__.py +0 -0
  279. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/sessions/_factory.py +0 -0
  280. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/skills/__init__.py +0 -0
  281. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/task_dependencies.py +0 -0
  282. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/task_github.py +0 -0
  283. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/task_linear.py +0 -0
  284. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/task_orchestration.py +0 -0
  285. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/task_sync.py +0 -0
  286. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/task_validation.py +0 -0
  287. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/__init__.py +0 -0
  288. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_expansion.py +0 -0
  289. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_factory.py +0 -0
  290. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_helpers.py +0 -0
  291. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +0 -0
  292. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_resolution.py +0 -0
  293. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_search.py +0 -0
  294. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/transports/__init__.py +0 -0
  295. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/transports/base.py +0 -0
  296. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/transports/factory.py +0 -0
  297. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/transports/http.py +0 -0
  298. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/transports/stdio.py +0 -0
  299. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/transports/websocket.py +0 -0
  300. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/backends/__init__.py +0 -0
  301. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/backends/mem0.py +0 -0
  302. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/backends/memu.py +0 -0
  303. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/backends/null.py +0 -0
  304. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/backends/openmemory.py +0 -0
  305. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/backends/sqlite.py +0 -0
  306. {gobby-0.2.7/src/gobby/tools → gobby-0.2.9/src/gobby/memory/components}/__init__.py +0 -0
  307. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/context.py +0 -0
  308. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/ingestion/__init__.py +0 -0
  309. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/ingestion/multimodal.py +0 -0
  310. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/protocol.py +0 -0
  311. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/search/__init__.py +0 -0
  312. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/search/coordinator.py +0 -0
  313. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/search/text.py +0 -0
  314. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/services/__init__.py +0 -0
  315. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/services/crossref.py +0 -0
  316. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/viz.py +0 -0
  317. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/prompts/__init__.py +0 -0
  318. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/prompts/models.py +0 -0
  319. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/py.typed +0 -0
  320. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/search/__init__.py +0 -0
  321. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/search/backends/__init__.py +0 -0
  322. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/search/backends/embedding.py +0 -0
  323. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/search/embeddings.py +0 -0
  324. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/search/models.py +0 -0
  325. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/search/protocol.py +0 -0
  326. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/search/tfidf.py +0 -0
  327. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/search/unified.py +0 -0
  328. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/__init__.py +0 -0
  329. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/models.py +0 -0
  330. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/__init__.py +0 -0
  331. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/dependencies.py +0 -0
  332. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/mcp/__init__.py +0 -0
  333. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/mcp/endpoints/__init__.py +0 -0
  334. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/mcp/endpoints/discovery.py +0 -0
  335. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/mcp/endpoints/server.py +0 -0
  336. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/mcp/plugins.py +0 -0
  337. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/mcp/tools.py +0 -0
  338. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/mcp/webhooks.py +0 -0
  339. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/sessions.py +0 -0
  340. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sessions/__init__.py +0 -0
  341. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sessions/lifecycle.py +0 -0
  342. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sessions/processor.py +0 -0
  343. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sessions/summary.py +0 -0
  344. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sessions/transcripts/__init__.py +0 -0
  345. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sessions/transcripts/base.py +0 -0
  346. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sessions/transcripts/claude.py +0 -0
  347. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sessions/transcripts/codex.py +0 -0
  348. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/skills/__init__.py +0 -0
  349. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/skills/loader.py +0 -0
  350. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/skills/manager.py +0 -0
  351. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/skills/search.py +0 -0
  352. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/skills/updater.py +0 -0
  353. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/skills/validator.py +0 -0
  354. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/__init__.py +0 -0
  355. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/agents.py +0 -0
  356. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/artifact_classifier.py +0 -0
  357. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/clones.py +0 -0
  358. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/compaction.py +0 -0
  359. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/inter_session_messages.py +0 -0
  360. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/mcp.py +0 -0
  361. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/merge_resolutions.py +0 -0
  362. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/projects.py +0 -0
  363. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/session_messages.py +0 -0
  364. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/session_tasks.py +0 -0
  365. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/task_dependencies.py +0 -0
  366. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/tasks/__init__.py +0 -0
  367. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/tasks/_aggregates.py +0 -0
  368. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/tasks/_crud.py +0 -0
  369. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/tasks/_id.py +0 -0
  370. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/tasks/_lifecycle.py +0 -0
  371. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/tasks/_manager.py +0 -0
  372. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/tasks/_models.py +0 -0
  373. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/tasks/_ordering.py +0 -0
  374. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/tasks/_path_cache.py +0 -0
  375. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/tasks/_queries.py +0 -0
  376. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/tasks/_search.py +0 -0
  377. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/workflow_audit.py +0 -0
  378. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/worktrees.py +0 -0
  379. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sync/__init__.py +0 -0
  380. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sync/github.py +0 -0
  381. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sync/linear.py +0 -0
  382. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sync/memories.py +0 -0
  383. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sync/tasks.py +0 -0
  384. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tasks/__init__.py +0 -0
  385. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tasks/build_verification.py +0 -0
  386. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tasks/commits.py +0 -0
  387. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tasks/enhanced_validator.py +0 -0
  388. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tasks/escalation.py +0 -0
  389. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tasks/issue_extraction.py +0 -0
  390. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tasks/tree_builder.py +0 -0
  391. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tasks/validation_history.py +0 -0
  392. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tasks/validation_models.py +0 -0
  393. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/__init__.py +0 -0
  394. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/daemon_client.py +0 -0
  395. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/git.py +0 -0
  396. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/id.py +0 -0
  397. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/json_helpers.py +0 -0
  398. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/logging.py +0 -0
  399. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/machine_id.py +0 -0
  400. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/metrics.py +0 -0
  401. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/project_context.py +0 -0
  402. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/project_init.py +0 -0
  403. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/validation.py +0 -0
  404. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/version.py +0 -0
  405. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/__init__.py +0 -0
  406. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/approval_flow.py +0 -0
  407. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/artifact_actions.py +0 -0
  408. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/audit_helpers.py +0 -0
  409. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/autonomous_actions.py +0 -0
  410. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/definitions.py +0 -0
  411. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/enforcement/commit_policy.py +0 -0
  412. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/enforcement/task_policy.py +0 -0
  413. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/git_utils.py +0 -0
  414. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/llm_actions.py +0 -0
  415. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/loader.py +0 -0
  416. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/mcp_actions.py +0 -0
  417. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/premature_stop.py +0 -0
  418. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/session_actions.py +0 -0
  419. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/state_actions.py +0 -0
  420. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/state_manager.py +0 -0
  421. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/stop_signal_actions.py +0 -0
  422. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/task_actions.py +0 -0
  423. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/task_sync_actions.py +0 -0
  424. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/templates.py +0 -0
  425. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/todo_actions.py +0 -0
  426. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/webhook.py +0 -0
  427. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/webhook_actions.py +0 -0
  428. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/webhook_executor.py +0 -0
  429. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/worktrees/__init__.py +0 -0
  430. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/worktrees/git.py +0 -0
  431. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/worktrees/merge/__init__.py +0 -0
  432. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/worktrees/merge/conflict_parser.py +0 -0
  433. {gobby-0.2.7 → gobby-0.2.9}/src/gobby/worktrees/merge/resolver.py +0 -0
  434. {gobby-0.2.7 → gobby-0.2.9}/src/gobby.egg-info/dependency_links.txt +0 -0
  435. {gobby-0.2.7 → gobby-0.2.9}/src/gobby.egg-info/entry_points.txt +0 -0
  436. {gobby-0.2.7 → gobby-0.2.9}/src/gobby.egg-info/requires.txt +0 -0
  437. {gobby-0.2.7 → gobby-0.2.9}/src/gobby.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gobby
3
- Version: 0.2.7
3
+ Version: 0.2.9
4
4
  Summary: A local-first daemon to unify your AI coding tools. Session tracking and handoffs across Claude Code, Gemini CLI, and Codex. An MCP proxy that discovers tools without flooding context. Task management with dependencies, validation, and TDD expansion. Agent spawning and worktree orchestration. Persistent memory, extensible workflows, and hooks.
5
5
  Author-email: Josh Wilhelmi <josh@gobby.ai>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "gobby"
3
- version = "0.2.7"
3
+ version = "0.2.9"
4
4
  description = "A local-first daemon to unify your AI coding tools. Session tracking and handoffs across Claude Code, Gemini CLI, and Codex. An MCP proxy that discovers tools without flooding context. Task management with dependencies, validation, and TDD expansion. Agent spawning and worktree orchestration. Persistent memory, extensible workflows, and hooks."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -1,3 +1,3 @@
1
1
  """Gobby - Local-first daemon for multi-CLI session management."""
2
2
 
3
- __version__ = "0.2.7"
3
+ __version__ = "0.2.9"
@@ -61,13 +61,10 @@ class ClaudeCodeAdapter(BaseAdapter):
61
61
  """Initialize the Claude Code adapter.
62
62
 
63
63
  Args:
64
- hook_manager: Reference to HookManager for strangler fig delegation.
64
+ hook_manager: Reference to HookManager for delegation.
65
65
  If None, the adapter can only translate (not handle events).
66
66
  """
67
67
  self._hook_manager = hook_manager
68
- # Phase 2C: Use new handle() path with unified HookEvent model
69
- # Note: systemMessage handoff notification bug exists in both paths (see plan-multi-cli.md)
70
- self._use_legacy = False
71
68
 
72
69
  def translate_to_hook_event(self, native_event: dict[str, Any]) -> HookEvent:
73
70
  """Convert Claude Code native event to unified HookEvent.
@@ -104,6 +101,10 @@ class ClaudeCodeAdapter(BaseAdapter):
104
101
  is_failure = hook_type == "post-tool-use-failure"
105
102
  metadata = {"is_failure": is_failure} if is_failure else {}
106
103
 
104
+ # Normalize event data for CLI-agnostic processing
105
+ # This allows downstream code to use consistent field names
106
+ normalized_data = self._normalize_event_data(input_data)
107
+
107
108
  return HookEvent(
108
109
  event_type=event_type,
109
110
  session_id=session_id,
@@ -111,10 +112,46 @@ class ClaudeCodeAdapter(BaseAdapter):
111
112
  timestamp=datetime.now(UTC),
112
113
  machine_id=input_data.get("machine_id"),
113
114
  cwd=input_data.get("cwd"),
114
- data=input_data,
115
+ data=normalized_data,
115
116
  metadata=metadata,
116
117
  )
117
118
 
119
+ def _normalize_event_data(self, input_data: dict[str, Any]) -> dict[str, Any]:
120
+ """Normalize Claude Code event data for CLI-agnostic processing.
121
+
122
+ This method enriches the input_data with normalized fields so downstream
123
+ code doesn't need to handle Claude-specific formats.
124
+
125
+ Normalizations performed:
126
+ 1. tool_input.server_name/tool_name → mcp_server/mcp_tool (for MCP calls)
127
+ 2. tool_result → tool_output
128
+
129
+ Args:
130
+ input_data: Raw input data from Claude Code
131
+
132
+ Returns:
133
+ Enriched data dict with normalized fields added
134
+ """
135
+ # Start with a copy to avoid mutating original
136
+ data = dict(input_data)
137
+
138
+ # Get tool info
139
+ tool_name = data.get("tool_name", "")
140
+ tool_input = data.get("tool_input", {}) or {}
141
+
142
+ # 1. Extract MCP info from nested tool_input for call_tool calls
143
+ if tool_name in ("call_tool", "mcp__gobby__call_tool"):
144
+ if "mcp_server" not in data:
145
+ data["mcp_server"] = tool_input.get("server_name")
146
+ if "mcp_tool" not in data:
147
+ data["mcp_tool"] = tool_input.get("tool_name")
148
+
149
+ # 2. Normalize tool_result → tool_output
150
+ if "tool_result" in data and "tool_output" not in data:
151
+ data["tool_output"] = data["tool_result"]
152
+
153
+ return data
154
+
118
155
  # Map Claude Code hook types to hookEventName for hookSpecificOutput
119
156
  HOOK_EVENT_NAME_MAP: dict[str, str] = {
120
157
  "session-start": "SessionStart",
@@ -193,44 +230,65 @@ class ClaudeCodeAdapter(BaseAdapter):
193
230
  # Add session identifiers from metadata
194
231
  # Note: "session_id" in metadata is Gobby's internal platform session ID
195
232
  # "external_id" in metadata is the CLI's session UUID
233
+ # "session_ref" is the short #N format for easier reference
234
+ # Token optimization: Only inject full metadata on first hook per session
196
235
  if response.metadata:
197
236
  gobby_session_id = response.metadata.get("session_id")
237
+ session_ref = response.metadata.get("session_ref")
198
238
  external_id = response.metadata.get("external_id")
239
+ is_first_hook = response.metadata.get("_first_hook_for_session", False)
240
+
199
241
  if gobby_session_id:
200
- # Build context with all available identifiers
201
- # Use clear naming: Gobby Session ID for MCP calls, External ID for transcripts
202
- context_lines = [f"Gobby Session ID: {gobby_session_id}"]
203
- if external_id:
204
- context_lines.append(f"External ID: {external_id}")
205
- if response.metadata.get("parent_session_id"):
206
- context_lines.append(
207
- f"parent_session_id: {response.metadata['parent_session_id']}"
208
- )
209
- if response.metadata.get("machine_id"):
210
- context_lines.append(f"machine_id: {response.metadata['machine_id']}")
211
- if response.metadata.get("project_id"):
212
- context_lines.append(f"project_id: {response.metadata['project_id']}")
213
- # Add terminal context (non-null values only)
214
- if response.metadata.get("terminal_term_program"):
215
- context_lines.append(f"terminal: {response.metadata['terminal_term_program']}")
216
- if response.metadata.get("terminal_tty"):
217
- context_lines.append(f"tty: {response.metadata['terminal_tty']}")
218
- if response.metadata.get("terminal_parent_pid"):
219
- context_lines.append(f"parent_pid: {response.metadata['terminal_parent_pid']}")
220
- # Add terminal-specific session IDs (only one will be present)
221
- for key in [
222
- "terminal_iterm_session_id",
223
- "terminal_term_session_id",
224
- "terminal_kitty_window_id",
225
- "terminal_tmux_pane",
226
- "terminal_vscode_terminal_id",
227
- "terminal_alacritty_socket",
228
- ]:
229
- if response.metadata.get(key):
230
- # Use friendlier names in output
231
- friendly_name = key.replace("terminal_", "").replace("_", " ")
232
- context_lines.append(f"{friendly_name}: {response.metadata[key]}")
233
- additional_context_parts.append("\n".join(context_lines))
242
+ if is_first_hook:
243
+ # First hook: inject full metadata (~60-100 tokens)
244
+ context_lines = []
245
+ if session_ref:
246
+ context_lines.append(
247
+ f"Gobby Session ID: {session_ref} (or {gobby_session_id})"
248
+ )
249
+ else:
250
+ context_lines.append(f"Gobby Session ID: {gobby_session_id}")
251
+ if external_id:
252
+ context_lines.append(
253
+ f"CLI-Specific Session ID (external_id): {external_id}"
254
+ )
255
+ if response.metadata.get("parent_session_id"):
256
+ context_lines.append(
257
+ f"parent_session_id: {response.metadata['parent_session_id']}"
258
+ )
259
+ if response.metadata.get("machine_id"):
260
+ context_lines.append(f"machine_id: {response.metadata['machine_id']}")
261
+ if response.metadata.get("project_id"):
262
+ context_lines.append(f"project_id: {response.metadata['project_id']}")
263
+ # Add terminal context (non-null values only)
264
+ if response.metadata.get("terminal_term_program"):
265
+ context_lines.append(
266
+ f"terminal: {response.metadata['terminal_term_program']}"
267
+ )
268
+ if response.metadata.get("terminal_tty"):
269
+ context_lines.append(f"tty: {response.metadata['terminal_tty']}")
270
+ if response.metadata.get("terminal_parent_pid"):
271
+ context_lines.append(
272
+ f"parent_pid: {response.metadata['terminal_parent_pid']}"
273
+ )
274
+ # Add terminal-specific session IDs (only one will be present)
275
+ for key in [
276
+ "terminal_iterm_session_id",
277
+ "terminal_term_session_id",
278
+ "terminal_kitty_window_id",
279
+ "terminal_tmux_pane",
280
+ "terminal_vscode_terminal_id",
281
+ "terminal_alacritty_socket",
282
+ ]:
283
+ if response.metadata.get(key):
284
+ # Use friendlier names in output
285
+ friendly_name = key.replace("terminal_", "").replace("_", " ")
286
+ context_lines.append(f"{friendly_name}: {response.metadata[key]}")
287
+ additional_context_parts.append("\n".join(context_lines))
288
+ else:
289
+ # Subsequent hooks: inject minimal session ref only (~8 tokens)
290
+ if session_ref:
291
+ additional_context_parts.append(f"Gobby Session ID: {session_ref}")
234
292
 
235
293
  # Build hookSpecificOutput if we have any context to inject
236
294
  # Only include hookSpecificOutput for hook types that Claude Code's schema accepts
@@ -249,14 +307,6 @@ class ClaudeCodeAdapter(BaseAdapter):
249
307
  ) -> dict[str, Any]:
250
308
  """Main entry point for HTTP endpoint.
251
309
 
252
- Strangler fig pattern:
253
- - Phase 2A-2B: Delegates to existing execute() — validates translation only
254
- - Phase 2C+: Calls new handle() with HookEvent
255
-
256
- Note: This method is synchronous for Phase 2A-2B compatibility with
257
- the existing execute() method. In Phase 2C+, it will become async
258
- when handle() is implemented as async.
259
-
260
310
  Args:
261
311
  native_event: Raw payload from Claude Code's hook_dispatcher.py
262
312
  hook_manager: HookManager instance for processing.
@@ -264,22 +314,10 @@ class ClaudeCodeAdapter(BaseAdapter):
264
314
  Returns:
265
315
  Response dict in Claude Code's expected format.
266
316
  """
267
- # Always translate (validates our mapping is correct)
317
+ # Translate to HookEvent
268
318
  hook_event = self.translate_to_hook_event(native_event)
269
319
 
270
- # Phase 2C+: Use new HookEvent-based handler
271
- # Legacy execute() path removed as HookManager.execute is deprecated/removed.
320
+ # Use HookEvent-based handler
272
321
  hook_type = native_event.get("hook_type", "")
273
322
  hook_response = hook_manager.handle(hook_event)
274
323
  return self.translate_from_hook_response(hook_response, hook_type=hook_type)
275
-
276
- def set_legacy_mode(self, use_legacy: bool) -> None:
277
- """Toggle between legacy and new code paths.
278
-
279
- This method is used during the strangler fig migration to switch
280
- between delegating to execute() and calling handle() directly.
281
-
282
- Args:
283
- use_legacy: If True, use legacy execute() path. If False, use new handle() path.
284
- """
285
- self._use_legacy = use_legacy
@@ -76,21 +76,39 @@ class GeminiAdapter(BaseAdapter):
76
76
 
77
77
  # Tool name mapping: Gemini tool names -> normalized names
78
78
  # Gemini uses different tool names than Claude Code
79
+ # This enables workflows to use Claude Code naming conventions
79
80
  TOOL_MAP: dict[str, str] = {
81
+ # Shell/Bash
80
82
  "run_shell_command": "Bash",
81
83
  "RunShellCommand": "Bash",
84
+ "ShellTool": "Bash",
85
+ # File read
82
86
  "read_file": "Read",
83
87
  "ReadFile": "Read",
84
88
  "ReadFileTool": "Read",
89
+ # File write
85
90
  "write_file": "Write",
86
91
  "WriteFile": "Write",
87
92
  "WriteFileTool": "Write",
93
+ # File edit
88
94
  "edit_file": "Edit",
89
95
  "EditFile": "Edit",
90
96
  "EditFileTool": "Edit",
97
+ # Search/Glob/Grep
91
98
  "GlobTool": "Glob",
92
99
  "GrepTool": "Grep",
93
- "ShellTool": "Bash",
100
+ "search_file_content": "Grep",
101
+ "SearchText": "Grep",
102
+ # MCP tools (Gobby MCP server)
103
+ "call_tool": "mcp__gobby__call_tool",
104
+ "list_mcp_servers": "mcp__gobby__list_mcp_servers",
105
+ "list_tools": "mcp__gobby__list_tools",
106
+ "get_tool_schema": "mcp__gobby__get_tool_schema",
107
+ "search_tools": "mcp__gobby__search_tools",
108
+ "recommend_tools": "mcp__gobby__recommend_tools",
109
+ # Skill and agent tools
110
+ "activate_skill": "Skill",
111
+ "delegate_to_agent": "Task",
94
112
  }
95
113
 
96
114
  def __init__(self, hook_manager: "HookManager | None" = None):
@@ -135,6 +153,55 @@ class GeminiAdapter(BaseAdapter):
135
153
  """
136
154
  return self.TOOL_MAP.get(gemini_tool_name, gemini_tool_name)
137
155
 
156
+ def _normalize_event_data(self, input_data: dict[str, Any]) -> dict[str, Any]:
157
+ """Normalize Gemini event data for CLI-agnostic processing.
158
+
159
+ This method enriches the input_data with normalized fields so downstream
160
+ code doesn't need to handle Gemini-specific formats.
161
+
162
+ Normalizations performed:
163
+ 1. mcp_context.server_name/tool_name → mcp_server/mcp_tool (top-level)
164
+ 2. tool_response → tool_output
165
+ 3. function_name → tool_name (if not already present)
166
+ 4. parameters/args → tool_input (if not already present)
167
+
168
+ Args:
169
+ input_data: Raw input data from Gemini CLI
170
+
171
+ Returns:
172
+ Enriched data dict with normalized fields added
173
+ """
174
+ # Start with a copy to avoid mutating original
175
+ data = dict(input_data)
176
+
177
+ # 1. Flatten mcp_context to top-level mcp_server/mcp_tool
178
+ mcp_context = data.get("mcp_context")
179
+ if mcp_context and isinstance(mcp_context, dict):
180
+ if "mcp_server" not in data:
181
+ data["mcp_server"] = mcp_context.get("server_name")
182
+ if "mcp_tool" not in data:
183
+ data["mcp_tool"] = mcp_context.get("tool_name")
184
+
185
+ # 2. Normalize tool_response → tool_output
186
+ if "tool_response" in data and "tool_output" not in data:
187
+ data["tool_output"] = data["tool_response"]
188
+
189
+ # 3. Normalize function_name → tool_name
190
+ if "function_name" in data and "tool_name" not in data:
191
+ data["tool_name"] = self.normalize_tool_name(data["function_name"])
192
+ elif "tool_name" in data:
193
+ # Normalize existing tool_name
194
+ data["tool_name"] = self.normalize_tool_name(data["tool_name"])
195
+
196
+ # 4. Normalize parameters/args → tool_input
197
+ if "tool_input" not in data:
198
+ if "parameters" in data:
199
+ data["tool_input"] = data["parameters"]
200
+ elif "args" in data:
201
+ data["tool_input"] = data["args"]
202
+
203
+ return data
204
+
138
205
  def translate_to_hook_event(self, native_event: dict[str, Any]) -> HookEvent:
139
206
  """Convert Gemini CLI native event to unified HookEvent.
140
207
 
@@ -202,6 +269,10 @@ class GeminiAdapter(BaseAdapter):
202
269
  else:
203
270
  metadata = {}
204
271
 
272
+ # Normalize event data for CLI-agnostic processing
273
+ # This allows downstream code to use consistent field names
274
+ normalized_data = self._normalize_event_data(input_data)
275
+
205
276
  return HookEvent(
206
277
  event_type=event_type,
207
278
  session_id=session_id,
@@ -209,7 +280,7 @@ class GeminiAdapter(BaseAdapter):
209
280
  timestamp=timestamp,
210
281
  machine_id=machine_id,
211
282
  cwd=input_data.get("cwd"),
212
- data=input_data,
283
+ data=normalized_data,
213
284
  metadata=metadata,
214
285
  )
215
286
 
@@ -254,46 +325,77 @@ class GeminiAdapter(BaseAdapter):
254
325
  if response.context:
255
326
  hook_specific["additionalContext"] = response.context
256
327
 
257
- # Add session/terminal context for SessionStart only
258
- if hook_type == "SessionStart" and response.metadata:
328
+ # Add session/terminal context for hooks that support additionalContext
329
+ # Parity with Claude Code: inject on SessionStart, BeforeAgent, BeforeTool, AfterTool
330
+ hooks_with_context = {"SessionStart", "BeforeAgent", "BeforeTool", "AfterTool"}
331
+ if hook_type in hooks_with_context and response.metadata:
259
332
  session_id = response.metadata.get("session_id")
333
+ session_ref = response.metadata.get("session_ref")
334
+ external_id = response.metadata.get("external_id")
335
+ is_first_hook = response.metadata.get("_first_hook_for_session", False)
336
+
260
337
  if session_id:
261
338
  hook_event_name = self.HOOK_EVENT_NAME_MAP.get(hook_type, "Unknown")
262
- context_lines = [f"session_id: {session_id}"]
263
- if response.metadata.get("parent_session_id"):
264
- context_lines.append(
265
- f"parent_session_id: {response.metadata['parent_session_id']}"
339
+
340
+ if is_first_hook:
341
+ # First hook: inject full metadata (~60-100 tokens)
342
+ context_lines = []
343
+ if session_ref:
344
+ context_lines.append(f"Gobby Session ID: {session_ref} (or {session_id})")
345
+ else:
346
+ context_lines.append(f"Gobby Session ID: {session_id}")
347
+ if external_id:
348
+ context_lines.append(
349
+ f"CLI-Specific Session ID (external_id): {external_id}"
350
+ )
351
+ if response.metadata.get("parent_session_id"):
352
+ context_lines.append(
353
+ f"parent_session_id: {response.metadata['parent_session_id']}"
354
+ )
355
+ if response.metadata.get("machine_id"):
356
+ context_lines.append(f"machine_id: {response.metadata['machine_id']}")
357
+ if response.metadata.get("project_id"):
358
+ context_lines.append(f"project_id: {response.metadata['project_id']}")
359
+ # Add terminal context (non-null values only)
360
+ if response.metadata.get("terminal_term_program"):
361
+ context_lines.append(
362
+ f"terminal: {response.metadata['terminal_term_program']}"
363
+ )
364
+ if response.metadata.get("terminal_tty"):
365
+ context_lines.append(f"tty: {response.metadata['terminal_tty']}")
366
+ if response.metadata.get("terminal_parent_pid"):
367
+ context_lines.append(
368
+ f"parent_pid: {response.metadata['terminal_parent_pid']}"
369
+ )
370
+ # Add terminal-specific session IDs
371
+ for key in [
372
+ "terminal_iterm_session_id",
373
+ "terminal_term_session_id",
374
+ "terminal_kitty_window_id",
375
+ "terminal_tmux_pane",
376
+ "terminal_vscode_terminal_id",
377
+ "terminal_alacritty_socket",
378
+ ]:
379
+ if response.metadata.get(key):
380
+ friendly_name = key.replace("terminal_", "").replace("_", " ")
381
+ context_lines.append(f"{friendly_name}: {response.metadata[key]}")
382
+
383
+ hook_specific["hookEventName"] = hook_event_name
384
+ # Append to existing additionalContext if present
385
+ existing = hook_specific.get("additionalContext", "")
386
+ new_context = "\n".join(context_lines)
387
+ hook_specific["additionalContext"] = (
388
+ f"{existing}\n{new_context}" if existing else new_context
266
389
  )
267
- if response.metadata.get("machine_id"):
268
- context_lines.append(f"machine_id: {response.metadata['machine_id']}")
269
- if response.metadata.get("project_id"):
270
- context_lines.append(f"project_id: {response.metadata['project_id']}")
271
- # Add terminal context (non-null values only)
272
- if response.metadata.get("terminal_term_program"):
273
- context_lines.append(f"terminal: {response.metadata['terminal_term_program']}")
274
- if response.metadata.get("terminal_tty"):
275
- context_lines.append(f"tty: {response.metadata['terminal_tty']}")
276
- if response.metadata.get("terminal_parent_pid"):
277
- context_lines.append(f"parent_pid: {response.metadata['terminal_parent_pid']}")
278
- # Add terminal-specific session IDs
279
- for key in [
280
- "terminal_iterm_session_id",
281
- "terminal_term_session_id",
282
- "terminal_kitty_window_id",
283
- "terminal_tmux_pane",
284
- "terminal_vscode_terminal_id",
285
- "terminal_alacritty_socket",
286
- ]:
287
- if response.metadata.get(key):
288
- friendly_name = key.replace("terminal_", "").replace("_", " ")
289
- context_lines.append(f"{friendly_name}: {response.metadata[key]}")
290
- hook_specific["hookEventName"] = hook_event_name
291
- # Append to existing additionalContext if present
292
- existing = hook_specific.get("additionalContext", "")
293
- new_context = "\n".join(context_lines)
294
- hook_specific["additionalContext"] = (
295
- f"{existing}\n{new_context}" if existing else new_context
296
- )
390
+ else:
391
+ # Subsequent hooks: inject minimal session ref only (~8 tokens)
392
+ if session_ref:
393
+ hook_specific["hookEventName"] = hook_event_name
394
+ existing = hook_specific.get("additionalContext", "")
395
+ minimal_context = f"Gobby Session ID: {session_ref}"
396
+ hook_specific["additionalContext"] = (
397
+ f"{existing}\n{minimal_context}" if existing else minimal_context
398
+ )
297
399
 
298
400
  # Handle BeforeModel-specific output (llm_request modification)
299
401
  if hook_type == "BeforeModel" and response.modify_args:
@@ -197,6 +197,13 @@ class WorktreeIsolationHandler(IsolationHandler):
197
197
  task_id=config.task_id,
198
198
  )
199
199
 
200
+ # Copy CLI hooks to worktree so hooks fire correctly
201
+ await self._copy_cli_hooks(
202
+ main_repo_path=self._git_manager.repo_path,
203
+ worktree_path=worktree_path,
204
+ provider=config.provider,
205
+ )
206
+
200
207
  return IsolationContext(
201
208
  cwd=worktree.worktree_path,
202
209
  branch_name=worktree.branch_name,
@@ -235,6 +242,64 @@ Commit your changes to the worktree branch when done.
235
242
  worktree_dir = tempfile.gettempdir()
236
243
  return f"{worktree_dir}/gobby-worktrees/{project_name}/{safe_branch}"
237
244
 
245
+ async def _copy_cli_hooks(
246
+ self,
247
+ main_repo_path: str,
248
+ worktree_path: str,
249
+ provider: str,
250
+ ) -> None:
251
+ """
252
+ Copy CLI-specific hooks to the worktree.
253
+
254
+ Without these hooks, the spawned agent won't trigger SessionStart
255
+ and other lifecycle hooks, breaking Gobby integration.
256
+
257
+ Args:
258
+ main_repo_path: Path to the main repository
259
+ worktree_path: Path to the newly created worktree
260
+ provider: CLI provider (gemini, claude, codex)
261
+ """
262
+ import asyncio
263
+ import logging
264
+ import shutil
265
+ from pathlib import Path
266
+
267
+ logger = logging.getLogger(__name__)
268
+
269
+ # Map provider to CLI hook directory
270
+ cli_dirs = {
271
+ "gemini": ".gemini",
272
+ "claude": ".claude",
273
+ "codex": ".codex",
274
+ }
275
+
276
+ cli_dir = cli_dirs.get(provider)
277
+ if not cli_dir:
278
+ logger.debug(f"No CLI hooks directory defined for provider: {provider}")
279
+ return
280
+
281
+ src_path = Path(main_repo_path) / cli_dir
282
+ dst_path = Path(worktree_path) / cli_dir
283
+
284
+ if not src_path.exists():
285
+ logger.debug(f"CLI hooks directory not found in main repo: {src_path}")
286
+ return
287
+
288
+ try:
289
+ # Copy entire CLI hooks directory (non-blocking)
290
+ await asyncio.to_thread(shutil.copytree, src_path, dst_path, dirs_exist_ok=True)
291
+ logger.info(f"Copied CLI hooks from {src_path} to {dst_path}")
292
+ except shutil.Error:
293
+ logger.warning(
294
+ f"Failed to copy CLI hooks: provider={provider}, src={src_path}, dst={dst_path}",
295
+ exc_info=True,
296
+ )
297
+ except OSError:
298
+ logger.warning(
299
+ f"Filesystem error copying CLI hooks: provider={provider}, src={src_path}, dst={dst_path}",
300
+ exc_info=True,
301
+ )
302
+
238
303
 
239
304
  class CloneIsolationHandler(IsolationHandler):
240
305
  """
@@ -310,6 +375,13 @@ class CloneIsolationHandler(IsolationHandler):
310
375
  task_id=config.task_id,
311
376
  )
312
377
 
378
+ # Copy CLI hooks to clone so hooks fire correctly
379
+ await self._copy_cli_hooks(
380
+ source_repo_path=config.project_path,
381
+ clone_path=clone_path,
382
+ provider=config.provider,
383
+ )
384
+
313
385
  return IsolationContext(
314
386
  cwd=clone.clone_path,
315
387
  branch_name=clone.branch_name,
@@ -348,6 +420,64 @@ Push your changes when ready to share with the original.
348
420
  clone_dir = tempfile.gettempdir()
349
421
  return f"{clone_dir}/gobby-clones/{project_name}/{safe_branch}"
350
422
 
423
+ async def _copy_cli_hooks(
424
+ self,
425
+ source_repo_path: str,
426
+ clone_path: str,
427
+ provider: str,
428
+ ) -> None:
429
+ """
430
+ Copy CLI-specific hooks to the clone.
431
+
432
+ Without these hooks, the spawned agent won't trigger SessionStart
433
+ and other lifecycle hooks, breaking Gobby integration.
434
+
435
+ Args:
436
+ source_repo_path: Path to the source repository
437
+ clone_path: Path to the newly created clone
438
+ provider: CLI provider (gemini, claude, codex)
439
+ """
440
+ import asyncio
441
+ import logging
442
+ import shutil
443
+ from pathlib import Path
444
+
445
+ logger = logging.getLogger(__name__)
446
+
447
+ # Map provider to CLI hook directory
448
+ cli_dirs = {
449
+ "gemini": ".gemini",
450
+ "claude": ".claude",
451
+ "codex": ".codex",
452
+ }
453
+
454
+ cli_dir = cli_dirs.get(provider)
455
+ if not cli_dir:
456
+ logger.debug(f"No CLI hooks directory defined for provider: {provider}")
457
+ return
458
+
459
+ src_path = Path(source_repo_path) / cli_dir
460
+ dst_path = Path(clone_path) / cli_dir
461
+
462
+ if not src_path.exists():
463
+ logger.debug(f"CLI hooks directory not found in source repo: {src_path}")
464
+ return
465
+
466
+ try:
467
+ # Copy entire CLI hooks directory (non-blocking)
468
+ await asyncio.to_thread(shutil.copytree, src_path, dst_path, dirs_exist_ok=True)
469
+ logger.info(f"Copied CLI hooks from {src_path} to {dst_path}")
470
+ except shutil.Error:
471
+ logger.warning(
472
+ f"Failed to copy CLI hooks: provider={provider}, src={src_path}, dst={dst_path}",
473
+ exc_info=True,
474
+ )
475
+ except OSError:
476
+ logger.warning(
477
+ f"Filesystem error copying CLI hooks: provider={provider}, src={src_path}, dst={dst_path}",
478
+ exc_info=True,
479
+ )
480
+
351
481
 
352
482
  def get_isolation_handler(
353
483
  mode: Literal["current", "worktree", "clone"],
@@ -137,6 +137,17 @@ class RunningAgentRegistry:
137
137
  with self._event_callbacks_lock:
138
138
  self._event_callbacks.append(callback)
139
139
 
140
+ def emit_event(self, event_type: str, run_id: str, data: dict[str, Any]) -> None:
141
+ """
142
+ Emit a custom event to all registered callbacks.
143
+
144
+ Args:
145
+ event_type: Type of event (e.g., terminal_output)
146
+ run_id: Agent run ID
147
+ data: Additional event data
148
+ """
149
+ self._emit_event(event_type, run_id, data)
150
+
140
151
  def _emit_event(self, event_type: str, run_id: str, data: dict[str, Any]) -> None:
141
152
  """
142
153
  Emit an event to all registered callbacks.
@@ -196,6 +196,7 @@ class ChildSessionManager:
196
196
  parent_session_id=config.parent_session_id,
197
197
  agent_depth=child_depth,
198
198
  spawned_by_agent_id=config.agent_id,
199
+ workflow_name=config.workflow_name,
199
200
  )
200
201
 
201
202
  child_id = child.id