gobby 0.2.6__py3-none-any.whl → 0.2.8__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 (198) hide show
  1. gobby/__init__.py +1 -1
  2. gobby/adapters/__init__.py +2 -1
  3. gobby/adapters/claude_code.py +96 -35
  4. gobby/adapters/codex_impl/__init__.py +28 -0
  5. gobby/adapters/codex_impl/adapter.py +722 -0
  6. gobby/adapters/codex_impl/client.py +679 -0
  7. gobby/adapters/codex_impl/protocol.py +20 -0
  8. gobby/adapters/codex_impl/types.py +68 -0
  9. gobby/adapters/gemini.py +140 -38
  10. gobby/agents/definitions.py +11 -1
  11. gobby/agents/isolation.py +525 -0
  12. gobby/agents/registry.py +11 -0
  13. gobby/agents/sandbox.py +261 -0
  14. gobby/agents/session.py +1 -0
  15. gobby/agents/spawn.py +42 -287
  16. gobby/agents/spawn_executor.py +415 -0
  17. gobby/agents/spawners/__init__.py +24 -0
  18. gobby/agents/spawners/command_builder.py +189 -0
  19. gobby/agents/spawners/embedded.py +21 -2
  20. gobby/agents/spawners/headless.py +21 -2
  21. gobby/agents/spawners/macos.py +26 -1
  22. gobby/agents/spawners/prompt_manager.py +125 -0
  23. gobby/cli/__init__.py +0 -2
  24. gobby/cli/install.py +4 -4
  25. gobby/cli/installers/claude.py +6 -0
  26. gobby/cli/installers/gemini.py +6 -0
  27. gobby/cli/installers/shared.py +103 -4
  28. gobby/cli/memory.py +185 -0
  29. gobby/cli/sessions.py +1 -1
  30. gobby/cli/utils.py +9 -2
  31. gobby/clones/git.py +177 -0
  32. gobby/config/__init__.py +12 -97
  33. gobby/config/app.py +10 -94
  34. gobby/config/extensions.py +2 -2
  35. gobby/config/features.py +7 -130
  36. gobby/config/skills.py +31 -0
  37. gobby/config/tasks.py +4 -28
  38. gobby/hooks/__init__.py +0 -13
  39. gobby/hooks/event_handlers.py +150 -8
  40. gobby/hooks/hook_manager.py +21 -3
  41. gobby/hooks/plugins.py +1 -1
  42. gobby/hooks/webhooks.py +1 -1
  43. gobby/install/gemini/hooks/hook_dispatcher.py +74 -15
  44. gobby/llm/resolver.py +3 -2
  45. gobby/mcp_proxy/importer.py +62 -4
  46. gobby/mcp_proxy/instructions.py +4 -2
  47. gobby/mcp_proxy/registries.py +22 -8
  48. gobby/mcp_proxy/services/recommendation.py +43 -11
  49. gobby/mcp_proxy/tools/agent_messaging.py +93 -44
  50. gobby/mcp_proxy/tools/agents.py +76 -740
  51. gobby/mcp_proxy/tools/artifacts.py +43 -9
  52. gobby/mcp_proxy/tools/clones.py +0 -385
  53. gobby/mcp_proxy/tools/memory.py +2 -2
  54. gobby/mcp_proxy/tools/sessions/__init__.py +14 -0
  55. gobby/mcp_proxy/tools/sessions/_commits.py +239 -0
  56. gobby/mcp_proxy/tools/sessions/_crud.py +253 -0
  57. gobby/mcp_proxy/tools/sessions/_factory.py +63 -0
  58. gobby/mcp_proxy/tools/sessions/_handoff.py +503 -0
  59. gobby/mcp_proxy/tools/sessions/_messages.py +166 -0
  60. gobby/mcp_proxy/tools/skills/__init__.py +14 -29
  61. gobby/mcp_proxy/tools/spawn_agent.py +455 -0
  62. gobby/mcp_proxy/tools/tasks/_context.py +18 -0
  63. gobby/mcp_proxy/tools/tasks/_crud.py +13 -6
  64. gobby/mcp_proxy/tools/tasks/_lifecycle.py +79 -30
  65. gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +1 -1
  66. gobby/mcp_proxy/tools/tasks/_session.py +22 -7
  67. gobby/mcp_proxy/tools/workflows.py +84 -34
  68. gobby/mcp_proxy/tools/worktrees.py +32 -350
  69. gobby/memory/extractor.py +15 -1
  70. gobby/memory/ingestion/__init__.py +5 -0
  71. gobby/memory/ingestion/multimodal.py +221 -0
  72. gobby/memory/manager.py +62 -283
  73. gobby/memory/search/__init__.py +10 -0
  74. gobby/memory/search/coordinator.py +248 -0
  75. gobby/memory/services/__init__.py +5 -0
  76. gobby/memory/services/crossref.py +142 -0
  77. gobby/prompts/loader.py +5 -2
  78. gobby/runner.py +13 -0
  79. gobby/servers/http.py +1 -4
  80. gobby/servers/routes/admin.py +14 -0
  81. gobby/servers/routes/mcp/endpoints/__init__.py +61 -0
  82. gobby/servers/routes/mcp/endpoints/discovery.py +405 -0
  83. gobby/servers/routes/mcp/endpoints/execution.py +568 -0
  84. gobby/servers/routes/mcp/endpoints/registry.py +378 -0
  85. gobby/servers/routes/mcp/endpoints/server.py +304 -0
  86. gobby/servers/routes/mcp/hooks.py +51 -4
  87. gobby/servers/routes/mcp/tools.py +48 -1506
  88. gobby/servers/websocket.py +57 -1
  89. gobby/sessions/analyzer.py +2 -2
  90. gobby/sessions/lifecycle.py +1 -1
  91. gobby/sessions/manager.py +9 -0
  92. gobby/sessions/processor.py +10 -0
  93. gobby/sessions/transcripts/base.py +1 -0
  94. gobby/sessions/transcripts/claude.py +15 -5
  95. gobby/sessions/transcripts/gemini.py +100 -34
  96. gobby/skills/parser.py +30 -2
  97. gobby/storage/database.py +9 -2
  98. gobby/storage/memories.py +32 -21
  99. gobby/storage/migrations.py +174 -368
  100. gobby/storage/sessions.py +45 -7
  101. gobby/storage/skills.py +80 -7
  102. gobby/storage/tasks/_lifecycle.py +18 -3
  103. gobby/sync/memories.py +1 -1
  104. gobby/tasks/external_validator.py +1 -1
  105. gobby/tasks/validation.py +22 -20
  106. gobby/tools/summarizer.py +91 -10
  107. gobby/utils/project_context.py +2 -3
  108. gobby/utils/status.py +13 -0
  109. gobby/workflows/actions.py +221 -1217
  110. gobby/workflows/artifact_actions.py +31 -0
  111. gobby/workflows/autonomous_actions.py +11 -0
  112. gobby/workflows/context_actions.py +50 -1
  113. gobby/workflows/detection_helpers.py +38 -24
  114. gobby/workflows/enforcement/__init__.py +47 -0
  115. gobby/workflows/enforcement/blocking.py +281 -0
  116. gobby/workflows/enforcement/commit_policy.py +283 -0
  117. gobby/workflows/enforcement/handlers.py +269 -0
  118. gobby/workflows/enforcement/task_policy.py +542 -0
  119. gobby/workflows/engine.py +93 -0
  120. gobby/workflows/evaluator.py +110 -0
  121. gobby/workflows/git_utils.py +106 -0
  122. gobby/workflows/hooks.py +41 -0
  123. gobby/workflows/llm_actions.py +30 -0
  124. gobby/workflows/mcp_actions.py +20 -1
  125. gobby/workflows/memory_actions.py +91 -0
  126. gobby/workflows/safe_evaluator.py +191 -0
  127. gobby/workflows/session_actions.py +44 -0
  128. gobby/workflows/state_actions.py +60 -1
  129. gobby/workflows/stop_signal_actions.py +55 -0
  130. gobby/workflows/summary_actions.py +217 -51
  131. gobby/workflows/task_sync_actions.py +347 -0
  132. gobby/workflows/todo_actions.py +34 -1
  133. gobby/workflows/webhook_actions.py +185 -0
  134. {gobby-0.2.6.dist-info → gobby-0.2.8.dist-info}/METADATA +6 -1
  135. {gobby-0.2.6.dist-info → gobby-0.2.8.dist-info}/RECORD +139 -163
  136. {gobby-0.2.6.dist-info → gobby-0.2.8.dist-info}/WHEEL +1 -1
  137. gobby/adapters/codex.py +0 -1332
  138. gobby/cli/tui.py +0 -34
  139. gobby/install/claude/commands/gobby/bug.md +0 -51
  140. gobby/install/claude/commands/gobby/chore.md +0 -51
  141. gobby/install/claude/commands/gobby/epic.md +0 -52
  142. gobby/install/claude/commands/gobby/eval.md +0 -235
  143. gobby/install/claude/commands/gobby/feat.md +0 -49
  144. gobby/install/claude/commands/gobby/nit.md +0 -52
  145. gobby/install/claude/commands/gobby/ref.md +0 -52
  146. gobby/mcp_proxy/tools/session_messages.py +0 -1055
  147. gobby/prompts/defaults/expansion/system.md +0 -119
  148. gobby/prompts/defaults/expansion/user.md +0 -48
  149. gobby/prompts/defaults/external_validation/agent.md +0 -72
  150. gobby/prompts/defaults/external_validation/external.md +0 -63
  151. gobby/prompts/defaults/external_validation/spawn.md +0 -83
  152. gobby/prompts/defaults/external_validation/system.md +0 -6
  153. gobby/prompts/defaults/features/import_mcp.md +0 -22
  154. gobby/prompts/defaults/features/import_mcp_github.md +0 -17
  155. gobby/prompts/defaults/features/import_mcp_search.md +0 -16
  156. gobby/prompts/defaults/features/recommend_tools.md +0 -32
  157. gobby/prompts/defaults/features/recommend_tools_hybrid.md +0 -35
  158. gobby/prompts/defaults/features/recommend_tools_llm.md +0 -30
  159. gobby/prompts/defaults/features/server_description.md +0 -20
  160. gobby/prompts/defaults/features/server_description_system.md +0 -6
  161. gobby/prompts/defaults/features/task_description.md +0 -31
  162. gobby/prompts/defaults/features/task_description_system.md +0 -6
  163. gobby/prompts/defaults/features/tool_summary.md +0 -17
  164. gobby/prompts/defaults/features/tool_summary_system.md +0 -6
  165. gobby/prompts/defaults/handoff/compact.md +0 -63
  166. gobby/prompts/defaults/handoff/session_end.md +0 -57
  167. gobby/prompts/defaults/memory/extract.md +0 -61
  168. gobby/prompts/defaults/research/step.md +0 -58
  169. gobby/prompts/defaults/validation/criteria.md +0 -47
  170. gobby/prompts/defaults/validation/validate.md +0 -38
  171. gobby/storage/migrations_legacy.py +0 -1359
  172. gobby/tui/__init__.py +0 -5
  173. gobby/tui/api_client.py +0 -278
  174. gobby/tui/app.py +0 -329
  175. gobby/tui/screens/__init__.py +0 -25
  176. gobby/tui/screens/agents.py +0 -333
  177. gobby/tui/screens/chat.py +0 -450
  178. gobby/tui/screens/dashboard.py +0 -377
  179. gobby/tui/screens/memory.py +0 -305
  180. gobby/tui/screens/metrics.py +0 -231
  181. gobby/tui/screens/orchestrator.py +0 -903
  182. gobby/tui/screens/sessions.py +0 -412
  183. gobby/tui/screens/tasks.py +0 -440
  184. gobby/tui/screens/workflows.py +0 -289
  185. gobby/tui/screens/worktrees.py +0 -174
  186. gobby/tui/widgets/__init__.py +0 -21
  187. gobby/tui/widgets/chat.py +0 -210
  188. gobby/tui/widgets/conductor.py +0 -104
  189. gobby/tui/widgets/menu.py +0 -132
  190. gobby/tui/widgets/message_panel.py +0 -160
  191. gobby/tui/widgets/review_gate.py +0 -224
  192. gobby/tui/widgets/task_tree.py +0 -99
  193. gobby/tui/widgets/token_budget.py +0 -166
  194. gobby/tui/ws_client.py +0 -258
  195. gobby/workflows/task_enforcement_actions.py +0 -1343
  196. {gobby-0.2.6.dist-info → gobby-0.2.8.dist-info}/entry_points.txt +0 -0
  197. {gobby-0.2.6.dist-info → gobby-0.2.8.dist-info}/licenses/LICENSE.md +0 -0
  198. {gobby-0.2.6.dist-info → gobby-0.2.8.dist-info}/top_level.txt +0 -0
gobby/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Gobby - Local-first daemon for multi-CLI session management."""
2
2
 
3
- __version__ = "0.2.3"
3
+ __version__ = "0.2.7"
@@ -17,7 +17,8 @@ Adapters:
17
17
 
18
18
  from gobby.adapters.base import BaseAdapter
19
19
  from gobby.adapters.claude_code import ClaudeCodeAdapter
20
- from gobby.adapters.codex import CodexAdapter, CodexAppServerClient, CodexNotifyAdapter
20
+ from gobby.adapters.codex_impl.adapter import CodexAdapter, CodexNotifyAdapter
21
+ from gobby.adapters.codex_impl.client import CodexAppServerClient
21
22
  from gobby.adapters.gemini import GeminiAdapter
22
23
 
23
24
  __all__ = [
@@ -104,6 +104,10 @@ class ClaudeCodeAdapter(BaseAdapter):
104
104
  is_failure = hook_type == "post-tool-use-failure"
105
105
  metadata = {"is_failure": is_failure} if is_failure else {}
106
106
 
107
+ # Normalize event data for CLI-agnostic processing
108
+ # This allows downstream code to use consistent field names
109
+ normalized_data = self._normalize_event_data(input_data)
110
+
107
111
  return HookEvent(
108
112
  event_type=event_type,
109
113
  session_id=session_id,
@@ -111,10 +115,46 @@ class ClaudeCodeAdapter(BaseAdapter):
111
115
  timestamp=datetime.now(UTC),
112
116
  machine_id=input_data.get("machine_id"),
113
117
  cwd=input_data.get("cwd"),
114
- data=input_data,
118
+ data=normalized_data,
115
119
  metadata=metadata,
116
120
  )
117
121
 
122
+ def _normalize_event_data(self, input_data: dict[str, Any]) -> dict[str, Any]:
123
+ """Normalize Claude Code event data for CLI-agnostic processing.
124
+
125
+ This method enriches the input_data with normalized fields so downstream
126
+ code doesn't need to handle Claude-specific formats.
127
+
128
+ Normalizations performed:
129
+ 1. tool_input.server_name/tool_name → mcp_server/mcp_tool (for MCP calls)
130
+ 2. tool_result → tool_output
131
+
132
+ Args:
133
+ input_data: Raw input data from Claude Code
134
+
135
+ Returns:
136
+ Enriched data dict with normalized fields added
137
+ """
138
+ # Start with a copy to avoid mutating original
139
+ data = dict(input_data)
140
+
141
+ # Get tool info
142
+ tool_name = data.get("tool_name", "")
143
+ tool_input = data.get("tool_input", {}) or {}
144
+
145
+ # 1. Extract MCP info from nested tool_input for call_tool calls
146
+ if tool_name in ("call_tool", "mcp__gobby__call_tool"):
147
+ if "mcp_server" not in data:
148
+ data["mcp_server"] = tool_input.get("server_name")
149
+ if "mcp_tool" not in data:
150
+ data["mcp_tool"] = tool_input.get("tool_name")
151
+
152
+ # 2. Normalize tool_result → tool_output
153
+ if "tool_result" in data and "tool_output" not in data:
154
+ data["tool_output"] = data["tool_result"]
155
+
156
+ return data
157
+
118
158
  # Map Claude Code hook types to hookEventName for hookSpecificOutput
119
159
  HOOK_EVENT_NAME_MAP: dict[str, str] = {
120
160
  "session-start": "SessionStart",
@@ -193,44 +233,65 @@ class ClaudeCodeAdapter(BaseAdapter):
193
233
  # Add session identifiers from metadata
194
234
  # Note: "session_id" in metadata is Gobby's internal platform session ID
195
235
  # "external_id" in metadata is the CLI's session UUID
236
+ # "session_ref" is the short #N format for easier reference
237
+ # Token optimization: Only inject full metadata on first hook per session
196
238
  if response.metadata:
197
239
  gobby_session_id = response.metadata.get("session_id")
240
+ session_ref = response.metadata.get("session_ref")
198
241
  external_id = response.metadata.get("external_id")
242
+ is_first_hook = response.metadata.get("_first_hook_for_session", False)
243
+
199
244
  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))
245
+ if is_first_hook:
246
+ # First hook: inject full metadata (~60-100 tokens)
247
+ context_lines = []
248
+ if session_ref:
249
+ context_lines.append(
250
+ f"Gobby Session ID: {session_ref} (or {gobby_session_id})"
251
+ )
252
+ else:
253
+ context_lines.append(f"Gobby Session ID: {gobby_session_id}")
254
+ if external_id:
255
+ context_lines.append(
256
+ f"CLI-Specific Session ID (external_id): {external_id}"
257
+ )
258
+ if response.metadata.get("parent_session_id"):
259
+ context_lines.append(
260
+ f"parent_session_id: {response.metadata['parent_session_id']}"
261
+ )
262
+ if response.metadata.get("machine_id"):
263
+ context_lines.append(f"machine_id: {response.metadata['machine_id']}")
264
+ if response.metadata.get("project_id"):
265
+ context_lines.append(f"project_id: {response.metadata['project_id']}")
266
+ # Add terminal context (non-null values only)
267
+ if response.metadata.get("terminal_term_program"):
268
+ context_lines.append(
269
+ f"terminal: {response.metadata['terminal_term_program']}"
270
+ )
271
+ if response.metadata.get("terminal_tty"):
272
+ context_lines.append(f"tty: {response.metadata['terminal_tty']}")
273
+ if response.metadata.get("terminal_parent_pid"):
274
+ context_lines.append(
275
+ f"parent_pid: {response.metadata['terminal_parent_pid']}"
276
+ )
277
+ # Add terminal-specific session IDs (only one will be present)
278
+ for key in [
279
+ "terminal_iterm_session_id",
280
+ "terminal_term_session_id",
281
+ "terminal_kitty_window_id",
282
+ "terminal_tmux_pane",
283
+ "terminal_vscode_terminal_id",
284
+ "terminal_alacritty_socket",
285
+ ]:
286
+ if response.metadata.get(key):
287
+ # Use friendlier names in output
288
+ friendly_name = key.replace("terminal_", "").replace("_", " ")
289
+ context_lines.append(f"{friendly_name}: {response.metadata[key]}")
290
+ additional_context_parts.append("\n".join(context_lines))
291
+ else:
292
+ # Subsequent hooks: inject minimal session ref only (~8 tokens)
293
+ if session_ref:
294
+ additional_context_parts.append(f"Gobby Session ID: {session_ref}")
234
295
 
235
296
  # Build hookSpecificOutput if we have any context to inject
236
297
  # Only include hookSpecificOutput for hook types that Claude Code's schema accepts
@@ -0,0 +1,28 @@
1
+ """
2
+ Codex adapter implementation package.
3
+
4
+ This package contains the decomposed implementation of the Codex adapter,
5
+ extracted from the monolithic codex.py using the Strangler Fig pattern.
6
+
7
+ Modules:
8
+ - types.py: Type definitions and data classes
9
+ - protocol.py: Protocol/interface definitions
10
+ - client.py: CodexAppServerClient implementation
11
+ - adapter.py: CodexAdapter and CodexNotifyAdapter implementations
12
+
13
+ Importer analysis (from codex.py):
14
+ - src/gobby/servers/http.py: imports CodexAdapter
15
+ - src/gobby/servers/routes/mcp/hooks.py: imports CodexNotifyAdapter
16
+ - src/gobby/adapters/__init__.py: imports CodexAdapter, CodexAppServerClient, CodexNotifyAdapter
17
+ - tests/adapters/test_codex.py: imports various items for testing
18
+
19
+ Migration strategy:
20
+ 1. Extract types/dataclasses to types.py
21
+ 2. Extract protocol definitions to protocol.py
22
+ 3. Extract CodexAppServerClient to client.py
23
+ 4. Extract adapters to adapter.py
24
+ 5. Update codex.py to re-export from submodules
25
+ """
26
+
27
+ # Phase 3: Placeholders - exports will be added as code is migrated
28
+ __all__: list[str] = []