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,242 @@
1
+ """
2
+ Windsurf (Cascade) installation for Gobby hooks.
3
+
4
+ This module handles installing and uninstalling Gobby hooks for Windsurf.
5
+ """
6
+
7
+ import json
8
+ import logging
9
+ import os
10
+ import tempfile
11
+ import time
12
+ from pathlib import Path
13
+ from shutil import copy2
14
+ from typing import Any
15
+
16
+ from gobby.cli.utils import get_install_dir
17
+
18
+ from .shared import install_shared_content
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ def install_windsurf(project_path: Path) -> dict[str, Any]:
24
+ """Install Gobby integration for Windsurf (hooks, workflows).
25
+
26
+ Args:
27
+ project_path: Path to the project root
28
+
29
+ Returns:
30
+ Dict with installation results including success status and installed items
31
+ """
32
+ hooks_installed: list[str] = []
33
+ result: dict[str, Any] = {
34
+ "success": False,
35
+ "hooks_installed": hooks_installed,
36
+ "workflows_installed": [],
37
+ "error": None,
38
+ }
39
+
40
+ windsurf_path = project_path / ".windsurf"
41
+ hooks_file = windsurf_path / "hooks.json"
42
+
43
+ # Ensure .windsurf subdirectories exist
44
+ windsurf_path.mkdir(parents=True, exist_ok=True)
45
+ hooks_dir = windsurf_path / "hooks"
46
+ hooks_dir.mkdir(parents=True, exist_ok=True)
47
+
48
+ # Get source files
49
+ install_dir = get_install_dir()
50
+ windsurf_install_dir = install_dir / "windsurf"
51
+ install_hooks_dir = windsurf_install_dir / "hooks"
52
+
53
+ # Hook files to copy
54
+ hook_files = {
55
+ "hook_dispatcher.py": True, # Make executable
56
+ }
57
+
58
+ source_hooks_template = windsurf_install_dir / "hooks-template.json"
59
+
60
+ # Verify all source files exist
61
+ missing_files = []
62
+ for filename in hook_files.keys():
63
+ source_file = install_hooks_dir / filename
64
+ if not source_file.exists():
65
+ missing_files.append(str(source_file))
66
+
67
+ if not source_hooks_template.exists():
68
+ missing_files.append(str(source_hooks_template))
69
+
70
+ if missing_files:
71
+ result["error"] = f"Missing source files: {missing_files}"
72
+ return result
73
+
74
+ # Copy hook files
75
+ try:
76
+ for filename, make_executable in hook_files.items():
77
+ source_file = install_hooks_dir / filename
78
+ target_file = hooks_dir / filename
79
+
80
+ if target_file.exists():
81
+ target_file.unlink()
82
+
83
+ copy2(source_file, target_file)
84
+ if make_executable:
85
+ target_file.chmod(0o755)
86
+ except OSError as e:
87
+ logger.error(f"Failed to copy hook files: {e}")
88
+ result["error"] = f"Failed to copy hook files: {e}"
89
+ return result
90
+
91
+ # Install shared content (workflows) to .gobby/
92
+ try:
93
+ gobby_path = project_path / ".gobby"
94
+ shared = install_shared_content(gobby_path, project_path)
95
+ result["workflows_installed"] = shared.get("workflows", [])
96
+ except Exception as e:
97
+ logger.warning(f"Failed to install shared content: {e}")
98
+ # Non-fatal - continue with hooks installation
99
+
100
+ # Backup existing hooks.json if it exists
101
+ backup_file = None
102
+ if hooks_file.exists():
103
+ timestamp = int(time.time())
104
+ backup_file = windsurf_path / f"hooks.json.{timestamp}.backup"
105
+ try:
106
+ copy2(hooks_file, backup_file)
107
+ except OSError as e:
108
+ logger.error(f"Failed to create backup of hooks.json: {e}")
109
+ result["error"] = f"Failed to create backup: {e}"
110
+ return result
111
+
112
+ # Load existing hooks or create empty
113
+ existing_hooks: dict[str, Any] = {"hooks": {}}
114
+ if hooks_file.exists():
115
+ try:
116
+ with open(hooks_file) as f:
117
+ existing_hooks = json.load(f)
118
+ except json.JSONDecodeError as e:
119
+ logger.warning(f"Failed to parse hooks.json, starting fresh: {e}")
120
+ except OSError as e:
121
+ logger.error(f"Failed to read hooks.json: {e}")
122
+ result["error"] = f"Failed to read hooks.json: {e}"
123
+ return result
124
+
125
+ # Ensure structure
126
+ if "hooks" not in existing_hooks:
127
+ existing_hooks["hooks"] = {}
128
+
129
+ # Load Gobby hooks from template
130
+ try:
131
+ with open(source_hooks_template) as f:
132
+ gobby_hooks_str = f.read()
133
+ except OSError as e:
134
+ logger.error(f"Failed to read hooks template: {e}")
135
+ result["error"] = f"Failed to read hooks template: {e}"
136
+ return result
137
+
138
+ # Replace $PROJECT_PATH with absolute project path
139
+ abs_project_path = str(project_path.resolve())
140
+ gobby_hooks_str = gobby_hooks_str.replace("$PROJECT_PATH", abs_project_path)
141
+
142
+ try:
143
+ gobby_hooks = json.loads(gobby_hooks_str)
144
+ except json.JSONDecodeError as e:
145
+ logger.error(f"Failed to parse hooks template: {e}")
146
+ result["error"] = f"Failed to parse hooks template: {e}"
147
+ return result
148
+
149
+ # Merge Gobby hooks
150
+ new_hooks = gobby_hooks.get("hooks", {})
151
+ for hook_type, hook_config in new_hooks.items():
152
+ existing_hooks["hooks"][hook_type] = hook_config
153
+ hooks_installed.append(hook_type)
154
+
155
+ # Write merged hooks back using atomic write
156
+ try:
157
+ fd, temp_path = tempfile.mkstemp(dir=str(windsurf_path), suffix=".tmp", prefix="hooks_")
158
+ try:
159
+ with os.fdopen(fd, "w") as f:
160
+ json.dump(existing_hooks, f, indent=2)
161
+ f.flush()
162
+ os.fsync(f.fileno())
163
+ # Atomic replace
164
+ os.replace(temp_path, hooks_file)
165
+ except Exception:
166
+ if os.path.exists(temp_path):
167
+ os.unlink(temp_path)
168
+ raise
169
+ except OSError as e:
170
+ logger.error(f"Failed to write hooks.json: {e}")
171
+ if backup_file and backup_file.exists():
172
+ try:
173
+ copy2(backup_file, hooks_file)
174
+ logger.info("Restored hooks.json from backup after write failure")
175
+ except OSError as restore_error:
176
+ logger.error(f"Failed to restore from backup: {restore_error}")
177
+ result["error"] = f"Failed to write hooks.json: {e}"
178
+ return result
179
+
180
+ result["success"] = True
181
+ return result
182
+
183
+
184
+ def uninstall_windsurf(project_path: Path) -> dict[str, Any]:
185
+ """Uninstall Gobby integration from Windsurf.
186
+
187
+ Args:
188
+ project_path: Path to the project root
189
+
190
+ Returns:
191
+ Dict with uninstallation results
192
+ """
193
+ result: dict[str, Any] = {
194
+ "success": False,
195
+ "hooks_removed": [],
196
+ "files_removed": [],
197
+ "error": None,
198
+ }
199
+
200
+ windsurf_path = project_path / ".windsurf"
201
+ hooks_file = windsurf_path / "hooks.json"
202
+ hooks_dir = windsurf_path / "hooks"
203
+
204
+ # Remove hook dispatcher
205
+ dispatcher = hooks_dir / "hook_dispatcher.py"
206
+ if dispatcher.exists():
207
+ try:
208
+ dispatcher.unlink()
209
+ result["files_removed"].append(str(dispatcher))
210
+ except OSError as e:
211
+ logger.warning(f"Failed to remove {dispatcher}: {e}")
212
+
213
+ # Remove hooks from hooks.json
214
+ if hooks_file.exists():
215
+ try:
216
+ with open(hooks_file) as f:
217
+ hooks_config = json.load(f)
218
+
219
+ # Remove all Gobby hooks (those that reference hook_dispatcher.py)
220
+ if "hooks" in hooks_config:
221
+ hooks_to_remove = []
222
+ for hook_type, hook_list in hooks_config["hooks"].items():
223
+ if isinstance(hook_list, list):
224
+ for hook in hook_list:
225
+ cmd = hook.get("command", "")
226
+ if "hook_dispatcher.py" in cmd:
227
+ hooks_to_remove.append(hook_type)
228
+ break
229
+
230
+ for hook_type in hooks_to_remove:
231
+ del hooks_config["hooks"][hook_type]
232
+ result["hooks_removed"].append(hook_type)
233
+
234
+ # Write back
235
+ with open(hooks_file, "w") as f:
236
+ json.dump(hooks_config, f, indent=2)
237
+
238
+ except (json.JSONDecodeError, OSError) as e:
239
+ logger.warning(f"Failed to update hooks.json: {e}")
240
+
241
+ result["success"] = True
242
+ return result