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.
- gobby/__init__.py +1 -1
- gobby/adapters/__init__.py +6 -0
- gobby/adapters/base.py +11 -2
- gobby/adapters/claude_code.py +5 -28
- gobby/adapters/codex_impl/adapter.py +38 -43
- gobby/adapters/copilot.py +324 -0
- gobby/adapters/cursor.py +373 -0
- gobby/adapters/gemini.py +2 -26
- gobby/adapters/windsurf.py +359 -0
- gobby/agents/definitions.py +162 -2
- gobby/agents/isolation.py +33 -1
- gobby/agents/pty_reader.py +192 -0
- gobby/agents/registry.py +10 -1
- gobby/agents/runner.py +24 -8
- gobby/agents/sandbox.py +8 -3
- gobby/agents/session.py +4 -0
- gobby/agents/spawn.py +9 -2
- gobby/agents/spawn_executor.py +49 -61
- gobby/agents/spawners/command_builder.py +4 -4
- gobby/app_context.py +64 -0
- gobby/cli/__init__.py +4 -0
- gobby/cli/install.py +259 -4
- gobby/cli/installers/__init__.py +12 -0
- gobby/cli/installers/copilot.py +242 -0
- gobby/cli/installers/cursor.py +244 -0
- gobby/cli/installers/shared.py +3 -0
- gobby/cli/installers/windsurf.py +242 -0
- gobby/cli/pipelines.py +639 -0
- gobby/cli/sessions.py +3 -1
- gobby/cli/skills.py +209 -0
- gobby/cli/tasks/crud.py +6 -5
- gobby/cli/tasks/search.py +1 -1
- gobby/cli/ui.py +116 -0
- gobby/cli/utils.py +5 -17
- gobby/cli/workflows.py +38 -17
- gobby/config/app.py +5 -0
- gobby/config/features.py +0 -20
- gobby/config/skills.py +23 -2
- gobby/config/tasks.py +4 -0
- gobby/hooks/broadcaster.py +9 -0
- gobby/hooks/event_handlers/__init__.py +155 -0
- gobby/hooks/event_handlers/_agent.py +175 -0
- gobby/hooks/event_handlers/_base.py +92 -0
- gobby/hooks/event_handlers/_misc.py +66 -0
- gobby/hooks/event_handlers/_session.py +487 -0
- gobby/hooks/event_handlers/_tool.py +196 -0
- gobby/hooks/events.py +48 -0
- gobby/hooks/hook_manager.py +27 -3
- gobby/install/copilot/hooks/hook_dispatcher.py +203 -0
- gobby/install/cursor/hooks/hook_dispatcher.py +203 -0
- gobby/install/gemini/hooks/hook_dispatcher.py +8 -0
- gobby/install/windsurf/hooks/hook_dispatcher.py +205 -0
- gobby/llm/__init__.py +14 -1
- gobby/llm/claude.py +594 -43
- gobby/llm/service.py +149 -0
- gobby/mcp_proxy/importer.py +4 -41
- gobby/mcp_proxy/instructions.py +9 -27
- gobby/mcp_proxy/manager.py +13 -3
- gobby/mcp_proxy/models.py +1 -0
- gobby/mcp_proxy/registries.py +66 -5
- gobby/mcp_proxy/server.py +6 -2
- gobby/mcp_proxy/services/recommendation.py +2 -28
- gobby/mcp_proxy/services/tool_filter.py +7 -0
- gobby/mcp_proxy/services/tool_proxy.py +19 -1
- gobby/mcp_proxy/stdio.py +37 -21
- gobby/mcp_proxy/tools/agents.py +7 -0
- gobby/mcp_proxy/tools/artifacts.py +3 -3
- gobby/mcp_proxy/tools/hub.py +30 -1
- gobby/mcp_proxy/tools/orchestration/cleanup.py +5 -5
- gobby/mcp_proxy/tools/orchestration/monitor.py +1 -1
- gobby/mcp_proxy/tools/orchestration/orchestrate.py +8 -3
- gobby/mcp_proxy/tools/orchestration/review.py +17 -4
- gobby/mcp_proxy/tools/orchestration/wait.py +7 -7
- gobby/mcp_proxy/tools/pipelines/__init__.py +254 -0
- gobby/mcp_proxy/tools/pipelines/_discovery.py +67 -0
- gobby/mcp_proxy/tools/pipelines/_execution.py +281 -0
- gobby/mcp_proxy/tools/sessions/_crud.py +4 -4
- gobby/mcp_proxy/tools/sessions/_handoff.py +1 -1
- gobby/mcp_proxy/tools/skills/__init__.py +184 -30
- gobby/mcp_proxy/tools/spawn_agent.py +229 -14
- gobby/mcp_proxy/tools/task_readiness.py +27 -4
- gobby/mcp_proxy/tools/tasks/_context.py +8 -0
- gobby/mcp_proxy/tools/tasks/_crud.py +27 -1
- gobby/mcp_proxy/tools/tasks/_helpers.py +1 -1
- gobby/mcp_proxy/tools/tasks/_lifecycle.py +125 -8
- gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +2 -1
- gobby/mcp_proxy/tools/tasks/_search.py +1 -1
- gobby/mcp_proxy/tools/workflows/__init__.py +273 -0
- gobby/mcp_proxy/tools/workflows/_artifacts.py +225 -0
- gobby/mcp_proxy/tools/workflows/_import.py +112 -0
- gobby/mcp_proxy/tools/workflows/_lifecycle.py +332 -0
- gobby/mcp_proxy/tools/workflows/_query.py +226 -0
- gobby/mcp_proxy/tools/workflows/_resolution.py +78 -0
- gobby/mcp_proxy/tools/workflows/_terminal.py +175 -0
- gobby/mcp_proxy/tools/worktrees.py +54 -15
- gobby/memory/components/__init__.py +0 -0
- gobby/memory/components/ingestion.py +98 -0
- gobby/memory/components/search.py +108 -0
- gobby/memory/context.py +5 -5
- gobby/memory/manager.py +16 -25
- gobby/paths.py +51 -0
- gobby/prompts/loader.py +1 -35
- gobby/runner.py +131 -16
- gobby/servers/http.py +193 -150
- gobby/servers/routes/__init__.py +2 -0
- gobby/servers/routes/admin.py +56 -0
- gobby/servers/routes/mcp/endpoints/execution.py +33 -32
- gobby/servers/routes/mcp/endpoints/registry.py +8 -8
- gobby/servers/routes/mcp/hooks.py +10 -1
- gobby/servers/routes/pipelines.py +227 -0
- gobby/servers/websocket.py +314 -1
- gobby/sessions/analyzer.py +89 -3
- gobby/sessions/manager.py +5 -5
- gobby/sessions/transcripts/__init__.py +3 -0
- gobby/sessions/transcripts/claude.py +5 -0
- gobby/sessions/transcripts/codex.py +5 -0
- gobby/sessions/transcripts/gemini.py +5 -0
- gobby/skills/hubs/__init__.py +25 -0
- gobby/skills/hubs/base.py +234 -0
- gobby/skills/hubs/claude_plugins.py +328 -0
- gobby/skills/hubs/clawdhub.py +289 -0
- gobby/skills/hubs/github_collection.py +465 -0
- gobby/skills/hubs/manager.py +263 -0
- gobby/skills/hubs/skillhub.py +342 -0
- gobby/skills/parser.py +23 -0
- gobby/skills/sync.py +5 -4
- gobby/storage/artifacts.py +19 -0
- gobby/storage/memories.py +4 -4
- gobby/storage/migrations.py +118 -3
- gobby/storage/pipelines.py +367 -0
- gobby/storage/sessions.py +23 -4
- gobby/storage/skills.py +48 -8
- gobby/storage/tasks/_aggregates.py +2 -2
- gobby/storage/tasks/_lifecycle.py +4 -4
- gobby/storage/tasks/_models.py +7 -1
- gobby/storage/tasks/_queries.py +3 -3
- gobby/sync/memories.py +4 -3
- gobby/tasks/commits.py +48 -17
- gobby/tasks/external_validator.py +4 -17
- gobby/tasks/validation.py +13 -87
- gobby/tools/summarizer.py +18 -51
- gobby/utils/status.py +13 -0
- gobby/workflows/actions.py +80 -0
- gobby/workflows/context_actions.py +265 -27
- gobby/workflows/definitions.py +119 -1
- gobby/workflows/detection_helpers.py +23 -11
- gobby/workflows/enforcement/__init__.py +11 -1
- gobby/workflows/enforcement/blocking.py +96 -0
- gobby/workflows/enforcement/handlers.py +35 -1
- gobby/workflows/enforcement/task_policy.py +18 -0
- gobby/workflows/engine.py +26 -4
- gobby/workflows/evaluator.py +8 -5
- gobby/workflows/lifecycle_evaluator.py +59 -27
- gobby/workflows/loader.py +567 -30
- gobby/workflows/lobster_compat.py +147 -0
- gobby/workflows/pipeline_executor.py +801 -0
- gobby/workflows/pipeline_state.py +172 -0
- gobby/workflows/pipeline_webhooks.py +206 -0
- gobby/workflows/premature_stop.py +5 -0
- gobby/worktrees/git.py +135 -20
- {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/METADATA +56 -22
- {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/RECORD +166 -122
- gobby/hooks/event_handlers.py +0 -1008
- gobby/mcp_proxy/tools/workflows.py +0 -1023
- {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/WHEEL +0 -0
- {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/entry_points.txt +0 -0
- {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/licenses/LICENSE.md +0 -0
- {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 --
|
|
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(
|
|
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 --
|
|
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
|
|
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")
|
gobby/cli/installers/__init__.py
CHANGED
|
@@ -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
|