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
gobby/app_context.py ADDED
@@ -0,0 +1,64 @@
1
+ """
2
+ Service container for dependency injection in Gobby daemon.
3
+
4
+ Holds references to singleton services to avoid prop-drilling in HTTPServer
5
+ and other components.
6
+ """
7
+
8
+ from dataclasses import dataclass
9
+ from typing import Any
10
+
11
+ from gobby.config.app import DaemonConfig
12
+ from gobby.llm import LLMService
13
+ from gobby.memory.manager import MemoryManager
14
+ from gobby.storage.clones import LocalCloneManager
15
+ from gobby.storage.database import DatabaseProtocol
16
+ from gobby.storage.sessions import LocalSessionManager
17
+ from gobby.storage.tasks import LocalTaskManager
18
+ from gobby.storage.worktrees import LocalWorktreeManager
19
+ from gobby.sync.memories import MemorySyncManager
20
+ from gobby.sync.tasks import TaskSyncManager
21
+
22
+
23
+ @dataclass
24
+ class ServiceContainer:
25
+ """Container for daemon services."""
26
+
27
+ # Core Infrastructure
28
+ config: DaemonConfig
29
+ database: DatabaseProtocol
30
+
31
+ # Core Managers
32
+ session_manager: LocalSessionManager
33
+ task_manager: LocalTaskManager
34
+
35
+ # Sync Managers
36
+ task_sync_manager: TaskSyncManager | None = None
37
+ memory_sync_manager: MemorySyncManager | None = None
38
+
39
+ # Advanced Features
40
+ memory_manager: MemoryManager | None = None
41
+ llm_service: LLMService | None = None
42
+
43
+ # MCP & Agents
44
+ mcp_manager: Any | None = None # MCPClientManager
45
+ mcp_db_manager: Any | None = None # LocalMCPManager
46
+ metrics_manager: Any | None = None # ToolMetricsManager
47
+ agent_runner: Any | None = None # AgentRunner
48
+ message_processor: Any | None = None # SessionMessageProcessor
49
+ message_manager: Any | None = None # LocalSessionMessageManager
50
+
51
+ # Validation & Git
52
+ task_validator: Any | None = None # TaskValidator
53
+ worktree_storage: LocalWorktreeManager | None = None
54
+ clone_storage: LocalCloneManager | None = None
55
+ git_manager: Any | None = None # WorktreeGitManager
56
+
57
+ # Pipelines
58
+ pipeline_executor: Any | None = None # PipelineExecutor
59
+ workflow_loader: Any | None = None # WorkflowLoader
60
+ pipeline_execution_manager: Any | None = None # LocalPipelineExecutionManager
61
+
62
+ # Context
63
+ project_id: str | None = None
64
+ websocket_server: Any | None = None
gobby/cli/__init__.py CHANGED
@@ -20,10 +20,12 @@ from .mcp import mcp_server
20
20
  from .mcp_proxy import mcp_proxy
21
21
  from .memory import memory
22
22
  from .merge import merge
23
+ from .pipelines import pipelines
23
24
  from .projects import projects
24
25
  from .sessions import sessions
25
26
  from .skills import skills
26
27
  from .tasks import tasks
28
+ from .ui import ui
27
29
  from .workflows import workflows
28
30
  from .worktrees import worktrees
29
31
 
@@ -61,6 +63,7 @@ cli.add_command(mcp_proxy)
61
63
  cli.add_command(projects)
62
64
  cli.add_command(workflows)
63
65
  cli.add_command(merge)
66
+ cli.add_command(pipelines)
64
67
  cli.add_command(artifacts)
65
68
  cli.add_command(github)
66
69
  cli.add_command(linear)
@@ -69,3 +72,4 @@ cli.add_command(conductor)
69
72
  cli.add_command(hooks)
70
73
  cli.add_command(plugins)
71
74
  cli.add_command(webhooks)
75
+ cli.add_command(ui)
gobby/cli/install.py CHANGED
@@ -3,6 +3,7 @@ Installation commands for hooks.
3
3
  """
4
4
 
5
5
  import logging
6
+ import os
6
7
  import shutil
7
8
  import sys
8
9
  from pathlib import Path
@@ -15,12 +16,18 @@ from .installers import (
15
16
  install_antigravity,
16
17
  install_claude,
17
18
  install_codex_notify,
19
+ install_copilot,
20
+ install_cursor,
18
21
  install_default_mcp_servers,
19
22
  install_gemini,
20
23
  install_git_hooks,
24
+ install_windsurf,
21
25
  uninstall_claude,
22
26
  uninstall_codex_notify,
27
+ uninstall_copilot,
28
+ uninstall_cursor,
23
29
  uninstall_gemini,
30
+ uninstall_windsurf,
24
31
  )
25
32
  from .utils import get_install_dir
26
33
 
@@ -75,6 +82,37 @@ def _is_codex_cli_installed() -> bool:
75
82
  return shutil.which("codex") is not None
76
83
 
77
84
 
85
+ def _is_cursor_installed() -> bool:
86
+ """Check if Cursor is installed."""
87
+ # Cursor is an IDE, check for common install locations
88
+ if sys.platform == "darwin":
89
+ return Path("/Applications/Cursor.app").exists()
90
+ elif sys.platform == "win32":
91
+ return Path(os.environ.get("LOCALAPPDATA", ""), "Programs", "cursor").exists()
92
+ else:
93
+ # Linux - check common locations
94
+ return (Path.home() / ".local" / "share" / "cursor").exists() or shutil.which(
95
+ "cursor"
96
+ ) is not None
97
+
98
+
99
+ def _is_windsurf_installed() -> bool:
100
+ """Check if Windsurf (Codeium) is installed."""
101
+ # Windsurf is an IDE
102
+ if sys.platform == "darwin":
103
+ return Path("/Applications/Windsurf.app").exists()
104
+ elif sys.platform == "win32":
105
+ return Path(os.environ.get("LOCALAPPDATA", ""), "Programs", "windsurf").exists()
106
+ else:
107
+ return shutil.which("windsurf") is not None
108
+
109
+
110
+ def _is_copilot_cli_installed() -> bool:
111
+ """Check if GitHub Copilot CLI is installed."""
112
+ # Check for gh copilot extension or standalone CLI
113
+ return shutil.which("gh") is not None or shutil.which("github-copilot-cli") is not None
114
+
115
+
78
116
  @click.command("install")
79
117
  @click.option(
80
118
  "--claude",
@@ -94,6 +132,24 @@ def _is_codex_cli_installed() -> bool:
94
132
  is_flag=True,
95
133
  help="Configure Codex notify integration (interactive Codex)",
96
134
  )
135
+ @click.option(
136
+ "--cursor",
137
+ "cursor_flag",
138
+ is_flag=True,
139
+ help="Install Cursor hooks",
140
+ )
141
+ @click.option(
142
+ "--windsurf",
143
+ "windsurf_flag",
144
+ is_flag=True,
145
+ help="Install Windsurf (Cascade) hooks",
146
+ )
147
+ @click.option(
148
+ "--copilot",
149
+ "copilot_flag",
150
+ is_flag=True,
151
+ help="Install GitHub Copilot CLI hooks",
152
+ )
97
153
  @click.option(
98
154
  "--hooks",
99
155
  "--git-hooks",
@@ -118,6 +174,9 @@ def install(
118
174
  claude_flag: bool,
119
175
  gemini_flag: bool,
120
176
  codex_flag: bool,
177
+ cursor_flag: bool,
178
+ windsurf_flag: bool,
179
+ copilot_flag: bool,
121
180
  hooks_flag: bool,
122
181
  all_flag: bool,
123
182
  antigravity_flag: bool,
@@ -143,6 +202,9 @@ def install(
143
202
  not claude_flag
144
203
  and not gemini_flag
145
204
  and not codex_flag
205
+ and not cursor_flag
206
+ and not windsurf_flag
207
+ and not copilot_flag
146
208
  and not hooks_flag
147
209
  and not all_flag
148
210
  and not antigravity_flag
@@ -162,6 +224,12 @@ def install(
162
224
  clis_to_install.append("gemini")
163
225
  if codex_detected:
164
226
  clis_to_install.append("codex")
227
+ if _is_cursor_installed():
228
+ clis_to_install.append("cursor")
229
+ if _is_windsurf_installed():
230
+ clis_to_install.append("windsurf")
231
+ if _is_copilot_cli_installed():
232
+ clis_to_install.append("copilot")
165
233
 
166
234
  # Check for git
167
235
  if (project_path / ".git").exists():
@@ -174,8 +242,11 @@ def install(
174
242
  click.echo(" - Claude Code: npm install -g @anthropic-ai/claude-code")
175
243
  click.echo(" - Gemini CLI: npm install -g @google/gemini-cli")
176
244
  click.echo(" - Codex CLI: npm install -g @openai/codex")
245
+ click.echo(" - Cursor: https://cursor.com")
246
+ click.echo(" - Windsurf: https://codeium.com/windsurf")
247
+ click.echo(" - Copilot CLI: gh extension install github/gh-copilot")
177
248
  click.echo(
178
- "\nYou can still install manually with --claude, --gemini, or --codex flags."
249
+ "\nYou can still install manually with --claude, --gemini, --codex, --cursor, --windsurf, or --copilot flags."
179
250
  )
180
251
  sys.exit(1)
181
252
  else:
@@ -185,6 +256,12 @@ def install(
185
256
  clis_to_install.append("gemini")
186
257
  if codex_flag:
187
258
  clis_to_install.append("codex")
259
+ if cursor_flag:
260
+ clis_to_install.append("cursor")
261
+ if windsurf_flag:
262
+ clis_to_install.append("windsurf")
263
+ if copilot_flag:
264
+ clis_to_install.append("copilot")
188
265
  if antigravity_flag:
189
266
  clis_to_install.append("antigravity")
190
267
 
@@ -346,6 +423,66 @@ def install(
346
423
  click.echo(f"Failed: {result['error']}", err=True)
347
424
  click.echo("")
348
425
 
426
+ # Install Cursor hooks
427
+ if "cursor" in clis_to_install:
428
+ click.echo("-" * 40)
429
+ click.echo("Cursor")
430
+ click.echo("-" * 40)
431
+
432
+ result = install_cursor(project_path)
433
+ results["cursor"] = result
434
+
435
+ if result["success"]:
436
+ click.echo(f"Installed {len(result['hooks_installed'])} hooks")
437
+ for hook in result["hooks_installed"]:
438
+ click.echo(f" - {hook}")
439
+ if result.get("workflows_installed"):
440
+ click.echo(f"Installed {len(result['workflows_installed'])} workflows")
441
+ click.echo(f"Configuration: {project_path / '.cursor' / 'hooks.json'}")
442
+ else:
443
+ click.echo(f"Failed: {result['error']}", err=True)
444
+ click.echo("")
445
+
446
+ # Install Windsurf hooks
447
+ if "windsurf" in clis_to_install:
448
+ click.echo("-" * 40)
449
+ click.echo("Windsurf (Cascade)")
450
+ click.echo("-" * 40)
451
+
452
+ result = install_windsurf(project_path)
453
+ results["windsurf"] = result
454
+
455
+ if result["success"]:
456
+ click.echo(f"Installed {len(result['hooks_installed'])} hooks")
457
+ for hook in result["hooks_installed"]:
458
+ click.echo(f" - {hook}")
459
+ if result.get("workflows_installed"):
460
+ click.echo(f"Installed {len(result['workflows_installed'])} workflows")
461
+ click.echo(f"Configuration: {project_path / '.windsurf' / 'hooks.json'}")
462
+ else:
463
+ click.echo(f"Failed: {result['error']}", err=True)
464
+ click.echo("")
465
+
466
+ # Install Copilot CLI hooks
467
+ if "copilot" in clis_to_install:
468
+ click.echo("-" * 40)
469
+ click.echo("GitHub Copilot CLI")
470
+ click.echo("-" * 40)
471
+
472
+ result = install_copilot(project_path)
473
+ results["copilot"] = result
474
+
475
+ if result["success"]:
476
+ click.echo(f"Installed {len(result['hooks_installed'])} hooks")
477
+ for hook in result["hooks_installed"]:
478
+ click.echo(f" - {hook}")
479
+ if result.get("workflows_installed"):
480
+ click.echo(f"Installed {len(result['workflows_installed'])} workflows")
481
+ click.echo(f"Configuration: {project_path / '.copilot' / 'hooks.json'}")
482
+ else:
483
+ click.echo(f"Failed: {result['error']}", err=True)
484
+ click.echo("")
485
+
349
486
  # Install Git Hooks
350
487
  if hooks_flag:
351
488
  click.echo("-" * 40)
@@ -467,6 +604,24 @@ def install(
467
604
  is_flag=True,
468
605
  help="Uninstall Codex notify integration",
469
606
  )
607
+ @click.option(
608
+ "--cursor",
609
+ "cursor_flag",
610
+ is_flag=True,
611
+ help="Uninstall Cursor hooks",
612
+ )
613
+ @click.option(
614
+ "--windsurf",
615
+ "windsurf_flag",
616
+ is_flag=True,
617
+ help="Uninstall Windsurf hooks",
618
+ )
619
+ @click.option(
620
+ "--copilot",
621
+ "copilot_flag",
622
+ is_flag=True,
623
+ help="Uninstall Copilot CLI hooks",
624
+ )
470
625
  @click.option(
471
626
  "--all",
472
627
  "all_flag",
@@ -475,11 +630,19 @@ def install(
475
630
  help="Uninstall hooks from all CLIs (default behavior when no flags specified)",
476
631
  )
477
632
  @click.confirmation_option(prompt="Are you sure you want to uninstall Gobby hooks?")
478
- def uninstall(claude_flag: bool, gemini_flag: bool, codex_flag: bool, all_flag: bool) -> None:
633
+ def uninstall(
634
+ claude_flag: bool,
635
+ gemini_flag: bool,
636
+ codex_flag: bool,
637
+ cursor_flag: bool,
638
+ windsurf_flag: bool,
639
+ copilot_flag: bool,
640
+ all_flag: bool,
641
+ ) -> None:
479
642
  """Uninstall Gobby hooks from AI coding CLIs.
480
643
 
481
644
  By default (no flags), uninstalls from all CLIs that have hooks installed.
482
- Use --claude, --gemini, or --codex to uninstall only from specific CLIs.
645
+ Use --claude, --gemini, --codex, --cursor, --windsurf, or --copilot to uninstall only from specific CLIs.
483
646
 
484
647
  Uninstalls from project-level directories in current working directory.
485
648
  """
@@ -487,7 +650,15 @@ def uninstall(claude_flag: bool, gemini_flag: bool, codex_flag: bool, all_flag:
487
650
 
488
651
  # Determine which CLIs to uninstall
489
652
  # If no flags specified, act like --all
490
- if not claude_flag and not gemini_flag and not codex_flag and not all_flag:
653
+ if (
654
+ not claude_flag
655
+ and not gemini_flag
656
+ and not codex_flag
657
+ and not cursor_flag
658
+ and not windsurf_flag
659
+ and not copilot_flag
660
+ and not all_flag
661
+ ):
491
662
  all_flag = True
492
663
 
493
664
  # Build list of CLIs to uninstall
@@ -498,6 +669,9 @@ def uninstall(claude_flag: bool, gemini_flag: bool, codex_flag: bool, all_flag:
498
669
  claude_settings = project_path / ".claude" / "settings.json"
499
670
  gemini_settings = project_path / ".gemini" / "settings.json"
500
671
  codex_notify = Path.home() / ".gobby" / "hooks" / "codex" / "hook_dispatcher.py"
672
+ cursor_hooks = project_path / ".cursor" / "hooks.json"
673
+ windsurf_hooks = project_path / ".windsurf" / "hooks.json"
674
+ copilot_hooks = project_path / ".copilot" / "hooks.json"
501
675
 
502
676
  if claude_settings.exists():
503
677
  clis_to_uninstall.append("claude")
@@ -505,11 +679,20 @@ def uninstall(claude_flag: bool, gemini_flag: bool, codex_flag: bool, all_flag:
505
679
  clis_to_uninstall.append("gemini")
506
680
  if codex_notify.exists():
507
681
  clis_to_uninstall.append("codex")
682
+ if cursor_hooks.exists():
683
+ clis_to_uninstall.append("cursor")
684
+ if windsurf_hooks.exists():
685
+ clis_to_uninstall.append("windsurf")
686
+ if copilot_hooks.exists():
687
+ clis_to_uninstall.append("copilot")
508
688
 
509
689
  if not clis_to_uninstall:
510
690
  click.echo("No Gobby hooks found to uninstall.")
511
691
  click.echo(f"\nChecked: {project_path / '.claude'}")
512
692
  click.echo(f" {project_path / '.gemini'}")
693
+ click.echo(f" {project_path / '.cursor'}")
694
+ click.echo(f" {project_path / '.windsurf'}")
695
+ click.echo(f" {project_path / '.copilot'}")
513
696
  click.echo(f" {codex_notify}")
514
697
  sys.exit(0)
515
698
  else:
@@ -519,6 +702,12 @@ def uninstall(claude_flag: bool, gemini_flag: bool, codex_flag: bool, all_flag:
519
702
  clis_to_uninstall.append("gemini")
520
703
  if codex_flag:
521
704
  clis_to_uninstall.append("codex")
705
+ if cursor_flag:
706
+ clis_to_uninstall.append("cursor")
707
+ if windsurf_flag:
708
+ clis_to_uninstall.append("windsurf")
709
+ if copilot_flag:
710
+ clis_to_uninstall.append("copilot")
522
711
 
523
712
  click.echo("=" * 60)
524
713
  click.echo(" Gobby Hooks Uninstallation")
@@ -597,6 +786,72 @@ def uninstall(claude_flag: bool, gemini_flag: bool, codex_flag: bool, all_flag:
597
786
  click.echo(f"Failed: {result['error']}", err=True)
598
787
  click.echo("")
599
788
 
789
+ # Uninstall Cursor hooks
790
+ if "cursor" in clis_to_uninstall:
791
+ click.echo("-" * 40)
792
+ click.echo("Cursor")
793
+ click.echo("-" * 40)
794
+
795
+ result = uninstall_cursor(project_path)
796
+ results["cursor"] = result
797
+
798
+ if result["success"]:
799
+ if result["hooks_removed"]:
800
+ click.echo(f"Removed {len(result['hooks_removed'])} hooks from hooks.json")
801
+ for hook in result["hooks_removed"]:
802
+ click.echo(f" - {hook}")
803
+ if result["files_removed"]:
804
+ click.echo(f"Removed {len(result['files_removed'])} files")
805
+ if not result["hooks_removed"] and not result["files_removed"]:
806
+ click.echo(" (no hooks found to remove)")
807
+ else:
808
+ click.echo(f"Failed: {result['error']}", err=True)
809
+ click.echo("")
810
+
811
+ # Uninstall Windsurf hooks
812
+ if "windsurf" in clis_to_uninstall:
813
+ click.echo("-" * 40)
814
+ click.echo("Windsurf")
815
+ click.echo("-" * 40)
816
+
817
+ result = uninstall_windsurf(project_path)
818
+ results["windsurf"] = result
819
+
820
+ if result["success"]:
821
+ if result["hooks_removed"]:
822
+ click.echo(f"Removed {len(result['hooks_removed'])} hooks from hooks.json")
823
+ for hook in result["hooks_removed"]:
824
+ click.echo(f" - {hook}")
825
+ if result["files_removed"]:
826
+ click.echo(f"Removed {len(result['files_removed'])} files")
827
+ if not result["hooks_removed"] and not result["files_removed"]:
828
+ click.echo(" (no hooks found to remove)")
829
+ else:
830
+ click.echo(f"Failed: {result['error']}", err=True)
831
+ click.echo("")
832
+
833
+ # Uninstall Copilot hooks
834
+ if "copilot" in clis_to_uninstall:
835
+ click.echo("-" * 40)
836
+ click.echo("Copilot CLI")
837
+ click.echo("-" * 40)
838
+
839
+ result = uninstall_copilot(project_path)
840
+ results["copilot"] = result
841
+
842
+ if result["success"]:
843
+ if result["hooks_removed"]:
844
+ click.echo(f"Removed {len(result['hooks_removed'])} hooks from hooks.json")
845
+ for hook in result["hooks_removed"]:
846
+ click.echo(f" - {hook}")
847
+ if result["files_removed"]:
848
+ click.echo(f"Removed {len(result['files_removed'])} files")
849
+ if not result["hooks_removed"] and not result["files_removed"]:
850
+ click.echo(" (no hooks found to remove)")
851
+ else:
852
+ click.echo(f"Failed: {result['error']}", err=True)
853
+ click.echo("")
854
+
600
855
  # Summary
601
856
  click.echo("=" * 60)
602
857
  click.echo(" Summary")
@@ -8,6 +8,8 @@ using the strangler fig pattern for incremental migration.
8
8
  from .antigravity import install_antigravity
9
9
  from .claude import install_claude, uninstall_claude
10
10
  from .codex import install_codex_notify, uninstall_codex_notify
11
+ from .copilot import install_copilot, uninstall_copilot
12
+ from .cursor import install_cursor, uninstall_cursor
11
13
  from .gemini import install_gemini, uninstall_gemini
12
14
  from .git_hooks import install_git_hooks
13
15
  from .shared import (
@@ -15,6 +17,7 @@ from .shared import (
15
17
  install_default_mcp_servers,
16
18
  install_shared_content,
17
19
  )
20
+ from .windsurf import install_windsurf, uninstall_windsurf
18
21
 
19
22
  __all__ = [
20
23
  # Shared
@@ -30,6 +33,15 @@ __all__ = [
30
33
  # Codex
31
34
  "install_codex_notify",
32
35
  "uninstall_codex_notify",
36
+ # Cursor
37
+ "install_cursor",
38
+ "uninstall_cursor",
39
+ # Windsurf
40
+ "install_windsurf",
41
+ "uninstall_windsurf",
42
+ # Copilot
43
+ "install_copilot",
44
+ "uninstall_copilot",
33
45
  # Git Hooks
34
46
  "install_git_hooks",
35
47
  # Antigravity