gobby 0.2.8__py3-none-any.whl → 0.2.11__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 (168) hide show
  1. gobby/__init__.py +1 -1
  2. gobby/adapters/__init__.py +6 -0
  3. gobby/adapters/base.py +11 -2
  4. gobby/adapters/claude_code.py +5 -28
  5. gobby/adapters/codex_impl/adapter.py +38 -43
  6. gobby/adapters/copilot.py +324 -0
  7. gobby/adapters/cursor.py +373 -0
  8. gobby/adapters/gemini.py +2 -26
  9. gobby/adapters/windsurf.py +359 -0
  10. gobby/agents/definitions.py +162 -2
  11. gobby/agents/isolation.py +33 -1
  12. gobby/agents/pty_reader.py +192 -0
  13. gobby/agents/registry.py +10 -1
  14. gobby/agents/runner.py +24 -8
  15. gobby/agents/sandbox.py +8 -3
  16. gobby/agents/session.py +4 -0
  17. gobby/agents/spawn.py +9 -2
  18. gobby/agents/spawn_executor.py +49 -61
  19. gobby/agents/spawners/command_builder.py +4 -4
  20. gobby/app_context.py +64 -0
  21. gobby/cli/__init__.py +4 -0
  22. gobby/cli/install.py +259 -4
  23. gobby/cli/installers/__init__.py +12 -0
  24. gobby/cli/installers/copilot.py +242 -0
  25. gobby/cli/installers/cursor.py +244 -0
  26. gobby/cli/installers/shared.py +3 -0
  27. gobby/cli/installers/windsurf.py +242 -0
  28. gobby/cli/pipelines.py +639 -0
  29. gobby/cli/sessions.py +3 -1
  30. gobby/cli/skills.py +209 -0
  31. gobby/cli/tasks/crud.py +6 -5
  32. gobby/cli/tasks/search.py +1 -1
  33. gobby/cli/ui.py +116 -0
  34. gobby/cli/utils.py +5 -17
  35. gobby/cli/workflows.py +38 -17
  36. gobby/config/app.py +5 -0
  37. gobby/config/features.py +0 -20
  38. gobby/config/skills.py +23 -2
  39. gobby/config/tasks.py +4 -0
  40. gobby/hooks/broadcaster.py +9 -0
  41. gobby/hooks/event_handlers/__init__.py +155 -0
  42. gobby/hooks/event_handlers/_agent.py +175 -0
  43. gobby/hooks/event_handlers/_base.py +92 -0
  44. gobby/hooks/event_handlers/_misc.py +66 -0
  45. gobby/hooks/event_handlers/_session.py +487 -0
  46. gobby/hooks/event_handlers/_tool.py +196 -0
  47. gobby/hooks/events.py +48 -0
  48. gobby/hooks/hook_manager.py +27 -3
  49. gobby/install/copilot/hooks/hook_dispatcher.py +203 -0
  50. gobby/install/cursor/hooks/hook_dispatcher.py +203 -0
  51. gobby/install/gemini/hooks/hook_dispatcher.py +8 -0
  52. gobby/install/windsurf/hooks/hook_dispatcher.py +205 -0
  53. gobby/llm/__init__.py +14 -1
  54. gobby/llm/claude.py +594 -43
  55. gobby/llm/service.py +149 -0
  56. gobby/mcp_proxy/importer.py +4 -41
  57. gobby/mcp_proxy/instructions.py +9 -27
  58. gobby/mcp_proxy/manager.py +13 -3
  59. gobby/mcp_proxy/models.py +1 -0
  60. gobby/mcp_proxy/registries.py +66 -5
  61. gobby/mcp_proxy/server.py +6 -2
  62. gobby/mcp_proxy/services/recommendation.py +2 -28
  63. gobby/mcp_proxy/services/tool_filter.py +7 -0
  64. gobby/mcp_proxy/services/tool_proxy.py +19 -1
  65. gobby/mcp_proxy/stdio.py +37 -21
  66. gobby/mcp_proxy/tools/agents.py +7 -0
  67. gobby/mcp_proxy/tools/artifacts.py +3 -3
  68. gobby/mcp_proxy/tools/hub.py +30 -1
  69. gobby/mcp_proxy/tools/orchestration/cleanup.py +5 -5
  70. gobby/mcp_proxy/tools/orchestration/monitor.py +1 -1
  71. gobby/mcp_proxy/tools/orchestration/orchestrate.py +8 -3
  72. gobby/mcp_proxy/tools/orchestration/review.py +17 -4
  73. gobby/mcp_proxy/tools/orchestration/wait.py +7 -7
  74. gobby/mcp_proxy/tools/pipelines/__init__.py +254 -0
  75. gobby/mcp_proxy/tools/pipelines/_discovery.py +67 -0
  76. gobby/mcp_proxy/tools/pipelines/_execution.py +281 -0
  77. gobby/mcp_proxy/tools/sessions/_crud.py +4 -4
  78. gobby/mcp_proxy/tools/sessions/_handoff.py +1 -1
  79. gobby/mcp_proxy/tools/skills/__init__.py +184 -30
  80. gobby/mcp_proxy/tools/spawn_agent.py +229 -14
  81. gobby/mcp_proxy/tools/task_readiness.py +27 -4
  82. gobby/mcp_proxy/tools/tasks/_context.py +8 -0
  83. gobby/mcp_proxy/tools/tasks/_crud.py +27 -1
  84. gobby/mcp_proxy/tools/tasks/_helpers.py +1 -1
  85. gobby/mcp_proxy/tools/tasks/_lifecycle.py +125 -8
  86. gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +2 -1
  87. gobby/mcp_proxy/tools/tasks/_search.py +1 -1
  88. gobby/mcp_proxy/tools/workflows/__init__.py +273 -0
  89. gobby/mcp_proxy/tools/workflows/_artifacts.py +225 -0
  90. gobby/mcp_proxy/tools/workflows/_import.py +112 -0
  91. gobby/mcp_proxy/tools/workflows/_lifecycle.py +332 -0
  92. gobby/mcp_proxy/tools/workflows/_query.py +226 -0
  93. gobby/mcp_proxy/tools/workflows/_resolution.py +78 -0
  94. gobby/mcp_proxy/tools/workflows/_terminal.py +175 -0
  95. gobby/mcp_proxy/tools/worktrees.py +54 -15
  96. gobby/memory/components/__init__.py +0 -0
  97. gobby/memory/components/ingestion.py +98 -0
  98. gobby/memory/components/search.py +108 -0
  99. gobby/memory/context.py +5 -5
  100. gobby/memory/manager.py +16 -25
  101. gobby/paths.py +51 -0
  102. gobby/prompts/loader.py +1 -35
  103. gobby/runner.py +131 -16
  104. gobby/servers/http.py +193 -150
  105. gobby/servers/routes/__init__.py +2 -0
  106. gobby/servers/routes/admin.py +56 -0
  107. gobby/servers/routes/mcp/endpoints/execution.py +33 -32
  108. gobby/servers/routes/mcp/endpoints/registry.py +8 -8
  109. gobby/servers/routes/mcp/hooks.py +10 -1
  110. gobby/servers/routes/pipelines.py +227 -0
  111. gobby/servers/websocket.py +314 -1
  112. gobby/sessions/analyzer.py +89 -3
  113. gobby/sessions/manager.py +5 -5
  114. gobby/sessions/transcripts/__init__.py +3 -0
  115. gobby/sessions/transcripts/claude.py +5 -0
  116. gobby/sessions/transcripts/codex.py +5 -0
  117. gobby/sessions/transcripts/gemini.py +5 -0
  118. gobby/skills/hubs/__init__.py +25 -0
  119. gobby/skills/hubs/base.py +234 -0
  120. gobby/skills/hubs/claude_plugins.py +328 -0
  121. gobby/skills/hubs/clawdhub.py +289 -0
  122. gobby/skills/hubs/github_collection.py +465 -0
  123. gobby/skills/hubs/manager.py +263 -0
  124. gobby/skills/hubs/skillhub.py +342 -0
  125. gobby/skills/parser.py +23 -0
  126. gobby/skills/sync.py +5 -4
  127. gobby/storage/artifacts.py +19 -0
  128. gobby/storage/memories.py +4 -4
  129. gobby/storage/migrations.py +118 -3
  130. gobby/storage/pipelines.py +367 -0
  131. gobby/storage/sessions.py +23 -4
  132. gobby/storage/skills.py +48 -8
  133. gobby/storage/tasks/_aggregates.py +2 -2
  134. gobby/storage/tasks/_lifecycle.py +4 -4
  135. gobby/storage/tasks/_models.py +7 -1
  136. gobby/storage/tasks/_queries.py +3 -3
  137. gobby/sync/memories.py +4 -3
  138. gobby/tasks/commits.py +48 -17
  139. gobby/tasks/external_validator.py +4 -17
  140. gobby/tasks/validation.py +13 -87
  141. gobby/tools/summarizer.py +18 -51
  142. gobby/utils/status.py +13 -0
  143. gobby/workflows/actions.py +80 -0
  144. gobby/workflows/context_actions.py +265 -27
  145. gobby/workflows/definitions.py +119 -1
  146. gobby/workflows/detection_helpers.py +23 -11
  147. gobby/workflows/enforcement/__init__.py +11 -1
  148. gobby/workflows/enforcement/blocking.py +96 -0
  149. gobby/workflows/enforcement/handlers.py +35 -1
  150. gobby/workflows/enforcement/task_policy.py +18 -0
  151. gobby/workflows/engine.py +26 -4
  152. gobby/workflows/evaluator.py +8 -5
  153. gobby/workflows/lifecycle_evaluator.py +59 -27
  154. gobby/workflows/loader.py +567 -30
  155. gobby/workflows/lobster_compat.py +147 -0
  156. gobby/workflows/pipeline_executor.py +801 -0
  157. gobby/workflows/pipeline_state.py +172 -0
  158. gobby/workflows/pipeline_webhooks.py +206 -0
  159. gobby/workflows/premature_stop.py +5 -0
  160. gobby/worktrees/git.py +135 -20
  161. {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/METADATA +56 -22
  162. {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/RECORD +166 -122
  163. gobby/hooks/event_handlers.py +0 -1008
  164. gobby/mcp_proxy/tools/workflows.py +0 -1023
  165. {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/WHEEL +0 -0
  166. {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/entry_points.txt +0 -0
  167. {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/licenses/LICENSE.md +0 -0
  168. {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env python3
2
+ """Hook Dispatcher - Routes Windsurf (Cascade) hooks to HookManager.
3
+
4
+ This is a thin wrapper script that receives hook calls from Windsurf
5
+ and routes them to the appropriate handler via HookManager.
6
+
7
+ Usage:
8
+ hook_dispatcher.py --type pre_user_prompt < input.json > output.json
9
+ hook_dispatcher.py --type pre_run_command --debug < input.json > output.json
10
+
11
+ Exit Codes:
12
+ 0 - Success
13
+ 1 - General error (logged, continues)
14
+ 2 - Block action (Windsurf interprets as deny)
15
+ """
16
+
17
+ import argparse
18
+ import json
19
+ import os
20
+ import sys
21
+ from pathlib import Path
22
+
23
+ # Default daemon configuration
24
+ DEFAULT_DAEMON_PORT = 60887
25
+ DEFAULT_CONFIG_PATH = "~/.gobby/config.yaml"
26
+
27
+
28
+ def get_daemon_url() -> str:
29
+ """Get the daemon HTTP URL from config file."""
30
+ config_path = Path(DEFAULT_CONFIG_PATH).expanduser()
31
+
32
+ if config_path.exists():
33
+ try:
34
+ import yaml
35
+
36
+ with open(config_path) as f:
37
+ config = yaml.safe_load(f) or {}
38
+ port = config.get("daemon_port", DEFAULT_DAEMON_PORT)
39
+ except Exception:
40
+ port = DEFAULT_DAEMON_PORT
41
+ else:
42
+ port = DEFAULT_DAEMON_PORT
43
+
44
+ return f"http://localhost:{port}"
45
+
46
+
47
+ def get_terminal_context() -> dict[str, str | int | None]:
48
+ """Capture terminal/process context for session correlation."""
49
+ context: dict[str, str | int | None] = {}
50
+
51
+ try:
52
+ context["parent_pid"] = os.getppid()
53
+ except Exception:
54
+ context["parent_pid"] = None
55
+
56
+ try:
57
+ context["tty"] = os.ttyname(0)
58
+ except Exception:
59
+ context["tty"] = None
60
+
61
+ context["term_session_id"] = os.environ.get("TERM_SESSION_ID")
62
+ context["iterm_session_id"] = os.environ.get("ITERM_SESSION_ID")
63
+ context["vscode_terminal_id"] = os.environ.get("VSCODE_GIT_ASKPASS_NODE")
64
+ context["tmux_pane"] = os.environ.get("TMUX_PANE")
65
+ context["kitty_window_id"] = os.environ.get("KITTY_WINDOW_ID")
66
+ context["alacritty_socket"] = os.environ.get("ALACRITTY_SOCKET")
67
+ context["term_program"] = os.environ.get("TERM_PROGRAM")
68
+
69
+ return context
70
+
71
+
72
+ def parse_arguments() -> argparse.Namespace:
73
+ """Parse command line arguments."""
74
+ parser = argparse.ArgumentParser(description="Windsurf Hook Dispatcher")
75
+ parser.add_argument(
76
+ "--type",
77
+ required=True,
78
+ help="Hook type (e.g., pre_user_prompt, pre_run_command)",
79
+ )
80
+ parser.add_argument(
81
+ "--debug",
82
+ action="store_true",
83
+ help="Enable debug logging",
84
+ )
85
+ return parser.parse_args()
86
+
87
+
88
+ def check_daemon_running(timeout: float = 0.5) -> bool:
89
+ """Check if gobby daemon is active and responding."""
90
+ try:
91
+ import httpx
92
+
93
+ daemon_url = get_daemon_url()
94
+ response = httpx.get(
95
+ f"{daemon_url}/admin/status",
96
+ timeout=timeout,
97
+ follow_redirects=False,
98
+ )
99
+ return response.status_code == 200
100
+ except Exception:
101
+ return False
102
+
103
+
104
+ def main() -> int:
105
+ """Main dispatcher execution."""
106
+ try:
107
+ args = parse_arguments()
108
+ except (argparse.ArgumentError, SystemExit):
109
+ print(json.dumps({}))
110
+ return 2
111
+
112
+ hook_type = args.type
113
+ debug_mode = args.debug
114
+
115
+ # Check if daemon is running
116
+ # Note: Windsurf doesn't have explicit session start/end, so pre_user_prompt is critical
117
+ if not check_daemon_running():
118
+ critical_hooks = {"pre_user_prompt"}
119
+ if hook_type in critical_hooks:
120
+ print(
121
+ f"Gobby daemon is not running. Start with 'gobby start' before continuing. "
122
+ f"({hook_type} requires daemon for session state management)",
123
+ file=sys.stderr,
124
+ )
125
+ return 2
126
+ else:
127
+ print(
128
+ json.dumps(
129
+ {"status": "daemon_not_running", "message": "gobby daemon is not running"}
130
+ )
131
+ )
132
+ return 0
133
+
134
+ import logging
135
+
136
+ logger = logging.getLogger("gobby.hooks.dispatcher.windsurf")
137
+ if debug_mode:
138
+ logging.basicConfig(level=logging.DEBUG)
139
+ else:
140
+ logging.basicConfig(level=logging.WARNING, handlers=[])
141
+
142
+ try:
143
+ input_data = json.load(sys.stdin)
144
+
145
+ # Inject terminal context for first prompt (acts as session start)
146
+ if hook_type == "pre_user_prompt":
147
+ input_data["terminal_context"] = get_terminal_context()
148
+
149
+ logger.info(f"[{hook_type}] Received input keys: {list(input_data.keys())}")
150
+
151
+ if debug_mode:
152
+ logger.debug(f"Input data: {input_data}")
153
+
154
+ except json.JSONDecodeError as e:
155
+ if debug_mode:
156
+ logger.error(f"JSON decode error: {e}")
157
+ print(json.dumps({}))
158
+ return 2
159
+
160
+ import httpx
161
+
162
+ daemon_url = get_daemon_url()
163
+ try:
164
+ response = httpx.post(
165
+ f"{daemon_url}/hooks/execute",
166
+ json={
167
+ "hook_type": hook_type,
168
+ "input_data": input_data,
169
+ "source": "windsurf",
170
+ },
171
+ timeout=90.0,
172
+ )
173
+
174
+ if response.status_code == 200:
175
+ result = response.json()
176
+
177
+ if debug_mode:
178
+ logger.debug(f"Output data: {result}")
179
+
180
+ # Check for block decision
181
+ if result.get("decision") == "deny":
182
+ reason = result.get("reason") or "Blocked by hook"
183
+ print(reason, file=sys.stderr)
184
+ return 2
185
+
186
+ if result and result != {}:
187
+ print(json.dumps(result))
188
+
189
+ return 0
190
+ else:
191
+ error_detail = response.text
192
+ logger.error(
193
+ f"Daemon returned error: status={response.status_code}, detail={error_detail}"
194
+ )
195
+ print(json.dumps({"status": "error", "message": f"Daemon error: {error_detail}"}))
196
+ return 1
197
+
198
+ except Exception as e:
199
+ logger.error(f"Hook execution failed: {e}", exc_info=True)
200
+ print(json.dumps({"status": "error", "message": str(e)}))
201
+ return 1
202
+
203
+
204
+ if __name__ == "__main__":
205
+ sys.exit(main())
gobby/llm/__init__.py CHANGED
@@ -10,7 +10,15 @@ Usage:
10
10
  """
11
11
 
12
12
  from gobby.llm.base import AuthMode, LLMProvider
13
- from gobby.llm.claude import MCPToolResult, ToolCall
13
+ from gobby.llm.claude import (
14
+ ChatEvent,
15
+ DoneEvent,
16
+ MCPToolResult,
17
+ TextChunk,
18
+ ToolCall,
19
+ ToolCallEvent,
20
+ ToolResultEvent,
21
+ )
14
22
  from gobby.llm.claude_executor import ClaudeExecutor
15
23
  from gobby.llm.executor import (
16
24
  AgentExecutor,
@@ -27,14 +35,19 @@ __all__ = [
27
35
  "AgentExecutor",
28
36
  "AgentResult",
29
37
  "AuthMode",
38
+ "ChatEvent",
30
39
  "ClaudeExecutor",
40
+ "DoneEvent",
31
41
  "LLMProvider",
32
42
  "LLMService",
33
43
  "MCPToolResult",
44
+ "TextChunk",
34
45
  "ToolCall",
46
+ "ToolCallEvent",
35
47
  "ToolCallRecord",
36
48
  "ToolHandler",
37
49
  "ToolResult",
50
+ "ToolResultEvent",
38
51
  "ToolSchema",
39
52
  "create_llm_service",
40
53
  ]