gobby 0.2.5__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 +3 -0
- gobby/adapters/__init__.py +30 -0
- gobby/adapters/base.py +93 -0
- gobby/adapters/claude_code.py +276 -0
- gobby/adapters/codex.py +1292 -0
- gobby/adapters/gemini.py +343 -0
- gobby/agents/__init__.py +37 -0
- gobby/agents/codex_session.py +120 -0
- gobby/agents/constants.py +112 -0
- gobby/agents/context.py +362 -0
- gobby/agents/definitions.py +133 -0
- gobby/agents/gemini_session.py +111 -0
- gobby/agents/registry.py +618 -0
- gobby/agents/runner.py +968 -0
- gobby/agents/session.py +259 -0
- gobby/agents/spawn.py +916 -0
- gobby/agents/spawners/__init__.py +77 -0
- gobby/agents/spawners/base.py +142 -0
- gobby/agents/spawners/cross_platform.py +266 -0
- gobby/agents/spawners/embedded.py +225 -0
- gobby/agents/spawners/headless.py +226 -0
- gobby/agents/spawners/linux.py +125 -0
- gobby/agents/spawners/macos.py +277 -0
- gobby/agents/spawners/windows.py +308 -0
- gobby/agents/tty_config.py +319 -0
- gobby/autonomous/__init__.py +32 -0
- gobby/autonomous/progress_tracker.py +447 -0
- gobby/autonomous/stop_registry.py +269 -0
- gobby/autonomous/stuck_detector.py +383 -0
- gobby/cli/__init__.py +67 -0
- gobby/cli/__main__.py +8 -0
- gobby/cli/agents.py +529 -0
- gobby/cli/artifacts.py +266 -0
- gobby/cli/daemon.py +329 -0
- gobby/cli/extensions.py +526 -0
- gobby/cli/github.py +263 -0
- gobby/cli/init.py +53 -0
- gobby/cli/install.py +614 -0
- gobby/cli/installers/__init__.py +37 -0
- gobby/cli/installers/antigravity.py +65 -0
- gobby/cli/installers/claude.py +363 -0
- gobby/cli/installers/codex.py +192 -0
- gobby/cli/installers/gemini.py +294 -0
- gobby/cli/installers/git_hooks.py +377 -0
- gobby/cli/installers/shared.py +737 -0
- gobby/cli/linear.py +250 -0
- gobby/cli/mcp.py +30 -0
- gobby/cli/mcp_proxy.py +698 -0
- gobby/cli/memory.py +304 -0
- gobby/cli/merge.py +384 -0
- gobby/cli/projects.py +79 -0
- gobby/cli/sessions.py +622 -0
- gobby/cli/tasks/__init__.py +30 -0
- gobby/cli/tasks/_utils.py +658 -0
- gobby/cli/tasks/ai.py +1025 -0
- gobby/cli/tasks/commits.py +169 -0
- gobby/cli/tasks/crud.py +685 -0
- gobby/cli/tasks/deps.py +135 -0
- gobby/cli/tasks/labels.py +63 -0
- gobby/cli/tasks/main.py +273 -0
- gobby/cli/tasks/search.py +178 -0
- gobby/cli/tui.py +34 -0
- gobby/cli/utils.py +513 -0
- gobby/cli/workflows.py +927 -0
- gobby/cli/worktrees.py +481 -0
- gobby/config/__init__.py +129 -0
- gobby/config/app.py +551 -0
- gobby/config/extensions.py +167 -0
- gobby/config/features.py +472 -0
- gobby/config/llm_providers.py +98 -0
- gobby/config/logging.py +66 -0
- gobby/config/mcp.py +346 -0
- gobby/config/persistence.py +247 -0
- gobby/config/servers.py +141 -0
- gobby/config/sessions.py +250 -0
- gobby/config/tasks.py +784 -0
- gobby/hooks/__init__.py +104 -0
- gobby/hooks/artifact_capture.py +213 -0
- gobby/hooks/broadcaster.py +243 -0
- gobby/hooks/event_handlers.py +723 -0
- gobby/hooks/events.py +218 -0
- gobby/hooks/git.py +169 -0
- gobby/hooks/health_monitor.py +171 -0
- gobby/hooks/hook_manager.py +856 -0
- gobby/hooks/hook_types.py +575 -0
- gobby/hooks/plugins.py +813 -0
- gobby/hooks/session_coordinator.py +396 -0
- gobby/hooks/verification_runner.py +268 -0
- gobby/hooks/webhooks.py +339 -0
- gobby/install/claude/commands/gobby/bug.md +51 -0
- gobby/install/claude/commands/gobby/chore.md +51 -0
- gobby/install/claude/commands/gobby/epic.md +52 -0
- gobby/install/claude/commands/gobby/eval.md +235 -0
- gobby/install/claude/commands/gobby/feat.md +49 -0
- gobby/install/claude/commands/gobby/nit.md +52 -0
- gobby/install/claude/commands/gobby/ref.md +52 -0
- gobby/install/claude/hooks/HOOK_SCHEMAS.md +632 -0
- gobby/install/claude/hooks/hook_dispatcher.py +364 -0
- gobby/install/claude/hooks/validate_settings.py +102 -0
- gobby/install/claude/hooks-template.json +118 -0
- gobby/install/codex/hooks/hook_dispatcher.py +153 -0
- gobby/install/codex/prompts/forget.md +7 -0
- gobby/install/codex/prompts/memories.md +7 -0
- gobby/install/codex/prompts/recall.md +7 -0
- gobby/install/codex/prompts/remember.md +13 -0
- gobby/install/gemini/hooks/hook_dispatcher.py +268 -0
- gobby/install/gemini/hooks-template.json +138 -0
- gobby/install/shared/plugins/code_guardian.py +456 -0
- gobby/install/shared/plugins/example_notify.py +331 -0
- gobby/integrations/__init__.py +10 -0
- gobby/integrations/github.py +145 -0
- gobby/integrations/linear.py +145 -0
- gobby/llm/__init__.py +40 -0
- gobby/llm/base.py +120 -0
- gobby/llm/claude.py +578 -0
- gobby/llm/claude_executor.py +503 -0
- gobby/llm/codex.py +322 -0
- gobby/llm/codex_executor.py +513 -0
- gobby/llm/executor.py +316 -0
- gobby/llm/factory.py +34 -0
- gobby/llm/gemini.py +258 -0
- gobby/llm/gemini_executor.py +339 -0
- gobby/llm/litellm.py +287 -0
- gobby/llm/litellm_executor.py +303 -0
- gobby/llm/resolver.py +499 -0
- gobby/llm/service.py +236 -0
- gobby/mcp_proxy/__init__.py +29 -0
- gobby/mcp_proxy/actions.py +175 -0
- gobby/mcp_proxy/daemon_control.py +198 -0
- gobby/mcp_proxy/importer.py +436 -0
- gobby/mcp_proxy/lazy.py +325 -0
- gobby/mcp_proxy/manager.py +798 -0
- gobby/mcp_proxy/metrics.py +609 -0
- gobby/mcp_proxy/models.py +139 -0
- gobby/mcp_proxy/registries.py +215 -0
- gobby/mcp_proxy/schema_hash.py +381 -0
- gobby/mcp_proxy/semantic_search.py +706 -0
- gobby/mcp_proxy/server.py +549 -0
- gobby/mcp_proxy/services/__init__.py +0 -0
- gobby/mcp_proxy/services/fallback.py +306 -0
- gobby/mcp_proxy/services/recommendation.py +224 -0
- gobby/mcp_proxy/services/server_mgmt.py +214 -0
- gobby/mcp_proxy/services/system.py +72 -0
- gobby/mcp_proxy/services/tool_filter.py +231 -0
- gobby/mcp_proxy/services/tool_proxy.py +309 -0
- gobby/mcp_proxy/stdio.py +565 -0
- gobby/mcp_proxy/tools/__init__.py +27 -0
- gobby/mcp_proxy/tools/agents.py +1103 -0
- gobby/mcp_proxy/tools/artifacts.py +207 -0
- gobby/mcp_proxy/tools/hub.py +335 -0
- gobby/mcp_proxy/tools/internal.py +337 -0
- gobby/mcp_proxy/tools/memory.py +543 -0
- gobby/mcp_proxy/tools/merge.py +422 -0
- gobby/mcp_proxy/tools/metrics.py +283 -0
- gobby/mcp_proxy/tools/orchestration/__init__.py +23 -0
- gobby/mcp_proxy/tools/orchestration/cleanup.py +619 -0
- gobby/mcp_proxy/tools/orchestration/monitor.py +380 -0
- gobby/mcp_proxy/tools/orchestration/orchestrate.py +746 -0
- gobby/mcp_proxy/tools/orchestration/review.py +736 -0
- gobby/mcp_proxy/tools/orchestration/utils.py +16 -0
- gobby/mcp_proxy/tools/session_messages.py +1056 -0
- gobby/mcp_proxy/tools/task_dependencies.py +219 -0
- gobby/mcp_proxy/tools/task_expansion.py +591 -0
- gobby/mcp_proxy/tools/task_github.py +393 -0
- gobby/mcp_proxy/tools/task_linear.py +379 -0
- gobby/mcp_proxy/tools/task_orchestration.py +77 -0
- gobby/mcp_proxy/tools/task_readiness.py +522 -0
- gobby/mcp_proxy/tools/task_sync.py +351 -0
- gobby/mcp_proxy/tools/task_validation.py +843 -0
- gobby/mcp_proxy/tools/tasks/__init__.py +25 -0
- gobby/mcp_proxy/tools/tasks/_context.py +112 -0
- gobby/mcp_proxy/tools/tasks/_crud.py +516 -0
- gobby/mcp_proxy/tools/tasks/_factory.py +176 -0
- gobby/mcp_proxy/tools/tasks/_helpers.py +129 -0
- gobby/mcp_proxy/tools/tasks/_lifecycle.py +517 -0
- gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +301 -0
- gobby/mcp_proxy/tools/tasks/_resolution.py +55 -0
- gobby/mcp_proxy/tools/tasks/_search.py +215 -0
- gobby/mcp_proxy/tools/tasks/_session.py +125 -0
- gobby/mcp_proxy/tools/workflows.py +973 -0
- gobby/mcp_proxy/tools/worktrees.py +1264 -0
- gobby/mcp_proxy/transports/__init__.py +0 -0
- gobby/mcp_proxy/transports/base.py +95 -0
- gobby/mcp_proxy/transports/factory.py +44 -0
- gobby/mcp_proxy/transports/http.py +139 -0
- gobby/mcp_proxy/transports/stdio.py +213 -0
- gobby/mcp_proxy/transports/websocket.py +136 -0
- gobby/memory/backends/__init__.py +116 -0
- gobby/memory/backends/mem0.py +408 -0
- gobby/memory/backends/memu.py +485 -0
- gobby/memory/backends/null.py +111 -0
- gobby/memory/backends/openmemory.py +537 -0
- gobby/memory/backends/sqlite.py +304 -0
- gobby/memory/context.py +87 -0
- gobby/memory/manager.py +1001 -0
- gobby/memory/protocol.py +451 -0
- gobby/memory/search/__init__.py +66 -0
- gobby/memory/search/text.py +127 -0
- gobby/memory/viz.py +258 -0
- gobby/prompts/__init__.py +13 -0
- gobby/prompts/defaults/expansion/system.md +119 -0
- gobby/prompts/defaults/expansion/user.md +48 -0
- gobby/prompts/defaults/external_validation/agent.md +72 -0
- gobby/prompts/defaults/external_validation/external.md +63 -0
- gobby/prompts/defaults/external_validation/spawn.md +83 -0
- gobby/prompts/defaults/external_validation/system.md +6 -0
- gobby/prompts/defaults/features/import_mcp.md +22 -0
- gobby/prompts/defaults/features/import_mcp_github.md +17 -0
- gobby/prompts/defaults/features/import_mcp_search.md +16 -0
- gobby/prompts/defaults/features/recommend_tools.md +32 -0
- gobby/prompts/defaults/features/recommend_tools_hybrid.md +35 -0
- gobby/prompts/defaults/features/recommend_tools_llm.md +30 -0
- gobby/prompts/defaults/features/server_description.md +20 -0
- gobby/prompts/defaults/features/server_description_system.md +6 -0
- gobby/prompts/defaults/features/task_description.md +31 -0
- gobby/prompts/defaults/features/task_description_system.md +6 -0
- gobby/prompts/defaults/features/tool_summary.md +17 -0
- gobby/prompts/defaults/features/tool_summary_system.md +6 -0
- gobby/prompts/defaults/research/step.md +58 -0
- gobby/prompts/defaults/validation/criteria.md +47 -0
- gobby/prompts/defaults/validation/validate.md +38 -0
- gobby/prompts/loader.py +346 -0
- gobby/prompts/models.py +113 -0
- gobby/py.typed +0 -0
- gobby/runner.py +488 -0
- gobby/search/__init__.py +23 -0
- gobby/search/protocol.py +104 -0
- gobby/search/tfidf.py +232 -0
- gobby/servers/__init__.py +7 -0
- gobby/servers/http.py +636 -0
- gobby/servers/models.py +31 -0
- gobby/servers/routes/__init__.py +23 -0
- gobby/servers/routes/admin.py +416 -0
- gobby/servers/routes/dependencies.py +118 -0
- gobby/servers/routes/mcp/__init__.py +24 -0
- gobby/servers/routes/mcp/hooks.py +135 -0
- gobby/servers/routes/mcp/plugins.py +121 -0
- gobby/servers/routes/mcp/tools.py +1337 -0
- gobby/servers/routes/mcp/webhooks.py +159 -0
- gobby/servers/routes/sessions.py +582 -0
- gobby/servers/websocket.py +766 -0
- gobby/sessions/__init__.py +13 -0
- gobby/sessions/analyzer.py +322 -0
- gobby/sessions/lifecycle.py +240 -0
- gobby/sessions/manager.py +563 -0
- gobby/sessions/processor.py +225 -0
- gobby/sessions/summary.py +532 -0
- gobby/sessions/transcripts/__init__.py +41 -0
- gobby/sessions/transcripts/base.py +125 -0
- gobby/sessions/transcripts/claude.py +386 -0
- gobby/sessions/transcripts/codex.py +143 -0
- gobby/sessions/transcripts/gemini.py +195 -0
- gobby/storage/__init__.py +21 -0
- gobby/storage/agents.py +409 -0
- gobby/storage/artifact_classifier.py +341 -0
- gobby/storage/artifacts.py +285 -0
- gobby/storage/compaction.py +67 -0
- gobby/storage/database.py +357 -0
- gobby/storage/inter_session_messages.py +194 -0
- gobby/storage/mcp.py +680 -0
- gobby/storage/memories.py +562 -0
- gobby/storage/merge_resolutions.py +550 -0
- gobby/storage/migrations.py +860 -0
- gobby/storage/migrations_legacy.py +1359 -0
- gobby/storage/projects.py +166 -0
- gobby/storage/session_messages.py +251 -0
- gobby/storage/session_tasks.py +97 -0
- gobby/storage/sessions.py +817 -0
- gobby/storage/task_dependencies.py +223 -0
- gobby/storage/tasks/__init__.py +42 -0
- gobby/storage/tasks/_aggregates.py +180 -0
- gobby/storage/tasks/_crud.py +449 -0
- gobby/storage/tasks/_id.py +104 -0
- gobby/storage/tasks/_lifecycle.py +311 -0
- gobby/storage/tasks/_manager.py +889 -0
- gobby/storage/tasks/_models.py +300 -0
- gobby/storage/tasks/_ordering.py +119 -0
- gobby/storage/tasks/_path_cache.py +110 -0
- gobby/storage/tasks/_queries.py +343 -0
- gobby/storage/tasks/_search.py +143 -0
- gobby/storage/workflow_audit.py +393 -0
- gobby/storage/worktrees.py +547 -0
- gobby/sync/__init__.py +29 -0
- gobby/sync/github.py +333 -0
- gobby/sync/linear.py +304 -0
- gobby/sync/memories.py +284 -0
- gobby/sync/tasks.py +641 -0
- gobby/tasks/__init__.py +8 -0
- gobby/tasks/build_verification.py +193 -0
- gobby/tasks/commits.py +633 -0
- gobby/tasks/context.py +747 -0
- gobby/tasks/criteria.py +342 -0
- gobby/tasks/enhanced_validator.py +226 -0
- gobby/tasks/escalation.py +263 -0
- gobby/tasks/expansion.py +626 -0
- gobby/tasks/external_validator.py +764 -0
- gobby/tasks/issue_extraction.py +171 -0
- gobby/tasks/prompts/expand.py +327 -0
- gobby/tasks/research.py +421 -0
- gobby/tasks/tdd.py +352 -0
- gobby/tasks/tree_builder.py +263 -0
- gobby/tasks/validation.py +712 -0
- gobby/tasks/validation_history.py +357 -0
- gobby/tasks/validation_models.py +89 -0
- gobby/tools/__init__.py +0 -0
- gobby/tools/summarizer.py +170 -0
- gobby/tui/__init__.py +5 -0
- gobby/tui/api_client.py +281 -0
- gobby/tui/app.py +327 -0
- gobby/tui/screens/__init__.py +25 -0
- gobby/tui/screens/agents.py +333 -0
- gobby/tui/screens/chat.py +450 -0
- gobby/tui/screens/dashboard.py +377 -0
- gobby/tui/screens/memory.py +305 -0
- gobby/tui/screens/metrics.py +231 -0
- gobby/tui/screens/orchestrator.py +904 -0
- gobby/tui/screens/sessions.py +412 -0
- gobby/tui/screens/tasks.py +442 -0
- gobby/tui/screens/workflows.py +289 -0
- gobby/tui/screens/worktrees.py +174 -0
- gobby/tui/widgets/__init__.py +21 -0
- gobby/tui/widgets/chat.py +210 -0
- gobby/tui/widgets/conductor.py +104 -0
- gobby/tui/widgets/menu.py +132 -0
- gobby/tui/widgets/message_panel.py +160 -0
- gobby/tui/widgets/review_gate.py +224 -0
- gobby/tui/widgets/task_tree.py +99 -0
- gobby/tui/widgets/token_budget.py +166 -0
- gobby/tui/ws_client.py +258 -0
- gobby/utils/__init__.py +3 -0
- gobby/utils/daemon_client.py +235 -0
- gobby/utils/git.py +222 -0
- gobby/utils/id.py +38 -0
- gobby/utils/json_helpers.py +161 -0
- gobby/utils/logging.py +376 -0
- gobby/utils/machine_id.py +135 -0
- gobby/utils/metrics.py +589 -0
- gobby/utils/project_context.py +182 -0
- gobby/utils/project_init.py +263 -0
- gobby/utils/status.py +256 -0
- gobby/utils/validation.py +80 -0
- gobby/utils/version.py +23 -0
- gobby/workflows/__init__.py +4 -0
- gobby/workflows/actions.py +1310 -0
- gobby/workflows/approval_flow.py +138 -0
- gobby/workflows/artifact_actions.py +103 -0
- gobby/workflows/audit_helpers.py +110 -0
- gobby/workflows/autonomous_actions.py +286 -0
- gobby/workflows/context_actions.py +394 -0
- gobby/workflows/definitions.py +130 -0
- gobby/workflows/detection_helpers.py +208 -0
- gobby/workflows/engine.py +485 -0
- gobby/workflows/evaluator.py +669 -0
- gobby/workflows/git_utils.py +96 -0
- gobby/workflows/hooks.py +169 -0
- gobby/workflows/lifecycle_evaluator.py +613 -0
- gobby/workflows/llm_actions.py +70 -0
- gobby/workflows/loader.py +333 -0
- gobby/workflows/mcp_actions.py +60 -0
- gobby/workflows/memory_actions.py +272 -0
- gobby/workflows/premature_stop.py +164 -0
- gobby/workflows/session_actions.py +139 -0
- gobby/workflows/state_actions.py +123 -0
- gobby/workflows/state_manager.py +104 -0
- gobby/workflows/stop_signal_actions.py +163 -0
- gobby/workflows/summary_actions.py +344 -0
- gobby/workflows/task_actions.py +249 -0
- gobby/workflows/task_enforcement_actions.py +901 -0
- gobby/workflows/templates.py +52 -0
- gobby/workflows/todo_actions.py +84 -0
- gobby/workflows/webhook.py +223 -0
- gobby/workflows/webhook_executor.py +399 -0
- gobby/worktrees/__init__.py +5 -0
- gobby/worktrees/git.py +690 -0
- gobby/worktrees/merge/__init__.py +20 -0
- gobby/worktrees/merge/conflict_parser.py +177 -0
- gobby/worktrees/merge/resolver.py +485 -0
- gobby-0.2.5.dist-info/METADATA +351 -0
- gobby-0.2.5.dist-info/RECORD +383 -0
- gobby-0.2.5.dist-info/WHEEL +5 -0
- gobby-0.2.5.dist-info/entry_points.txt +2 -0
- gobby-0.2.5.dist-info/licenses/LICENSE.md +193 -0
- gobby-0.2.5.dist-info/top_level.txt +1 -0
gobby/cli/install.py
ADDED
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Installation commands for hooks.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import shutil
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from shutil import copy2
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
|
|
14
|
+
from .installers import (
|
|
15
|
+
install_antigravity,
|
|
16
|
+
install_claude,
|
|
17
|
+
install_codex_notify,
|
|
18
|
+
install_default_mcp_servers,
|
|
19
|
+
install_gemini,
|
|
20
|
+
install_git_hooks,
|
|
21
|
+
uninstall_claude,
|
|
22
|
+
uninstall_codex_notify,
|
|
23
|
+
uninstall_gemini,
|
|
24
|
+
)
|
|
25
|
+
from .utils import get_install_dir
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _ensure_daemon_config() -> dict[str, Any]:
|
|
31
|
+
"""Ensure daemon config exists at ~/.gobby/config.yaml.
|
|
32
|
+
|
|
33
|
+
If config doesn't exist, copies the shared config template.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Dict with 'created' (bool) and 'path' (str) keys
|
|
37
|
+
"""
|
|
38
|
+
config_path = Path("~/.gobby/config.yaml").expanduser()
|
|
39
|
+
|
|
40
|
+
if config_path.exists():
|
|
41
|
+
return {"created": False, "path": str(config_path)}
|
|
42
|
+
|
|
43
|
+
# Ensure directory exists
|
|
44
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
45
|
+
|
|
46
|
+
# Copy shared config template
|
|
47
|
+
shared_config = get_install_dir() / "shared" / "config" / "config.yaml"
|
|
48
|
+
if shared_config.exists():
|
|
49
|
+
copy2(shared_config, config_path)
|
|
50
|
+
# Set restrictive permissions
|
|
51
|
+
config_path.chmod(0o600)
|
|
52
|
+
return {"created": True, "path": str(config_path), "source": "shared"}
|
|
53
|
+
|
|
54
|
+
# Fallback: generate from Pydantic defaults
|
|
55
|
+
from gobby.config.app import generate_default_config
|
|
56
|
+
|
|
57
|
+
generate_default_config(str(config_path))
|
|
58
|
+
# Set restrictive permissions (same as copied template)
|
|
59
|
+
config_path.chmod(0o600)
|
|
60
|
+
return {"created": True, "path": str(config_path), "source": "generated"}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _is_claude_code_installed() -> bool:
|
|
64
|
+
"""Check if Claude Code CLI is installed."""
|
|
65
|
+
return shutil.which("claude") is not None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _is_gemini_cli_installed() -> bool:
|
|
69
|
+
"""Check if Gemini CLI is installed."""
|
|
70
|
+
return shutil.which("gemini") is not None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _is_codex_cli_installed() -> bool:
|
|
74
|
+
"""Check if OpenAI Codex CLI is installed."""
|
|
75
|
+
return shutil.which("codex") is not None
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@click.command("install")
|
|
79
|
+
@click.option(
|
|
80
|
+
"--claude",
|
|
81
|
+
"claude_flag",
|
|
82
|
+
is_flag=True,
|
|
83
|
+
help="Install Claude Code hooks only",
|
|
84
|
+
)
|
|
85
|
+
@click.option(
|
|
86
|
+
"--gemini",
|
|
87
|
+
"gemini_flag",
|
|
88
|
+
is_flag=True,
|
|
89
|
+
help="Install Gemini CLI hooks only",
|
|
90
|
+
)
|
|
91
|
+
@click.option(
|
|
92
|
+
"--codex",
|
|
93
|
+
"codex_flag",
|
|
94
|
+
is_flag=True,
|
|
95
|
+
help="Configure Codex notify integration (interactive Codex)",
|
|
96
|
+
)
|
|
97
|
+
@click.option(
|
|
98
|
+
"--hooks",
|
|
99
|
+
"--git-hooks",
|
|
100
|
+
"hooks_flag",
|
|
101
|
+
is_flag=True,
|
|
102
|
+
help="Install Git hooks for task auto-sync (pre-commit, post-merge, post-checkout)",
|
|
103
|
+
)
|
|
104
|
+
@click.option(
|
|
105
|
+
"--all",
|
|
106
|
+
"all_flag",
|
|
107
|
+
is_flag=True,
|
|
108
|
+
default=False,
|
|
109
|
+
help="Install hooks for all detected CLIs (default behavior when no flags specified)",
|
|
110
|
+
)
|
|
111
|
+
@click.option(
|
|
112
|
+
"--antigravity",
|
|
113
|
+
"antigravity_flag",
|
|
114
|
+
is_flag=True,
|
|
115
|
+
help="Install Antigravity agent hooks (internal)",
|
|
116
|
+
)
|
|
117
|
+
def install(
|
|
118
|
+
claude_flag: bool,
|
|
119
|
+
gemini_flag: bool,
|
|
120
|
+
codex_flag: bool,
|
|
121
|
+
hooks_flag: bool,
|
|
122
|
+
all_flag: bool,
|
|
123
|
+
antigravity_flag: bool,
|
|
124
|
+
) -> None:
|
|
125
|
+
"""Install Gobby hooks to AI coding CLIs and Git.
|
|
126
|
+
|
|
127
|
+
By default (no flags), installs to all detected CLIs.
|
|
128
|
+
Use --claude, --gemini, --codex to install only to specific CLIs.
|
|
129
|
+
Use --hooks to install Git hooks for task auto-sync.
|
|
130
|
+
"""
|
|
131
|
+
project_path = Path.cwd()
|
|
132
|
+
|
|
133
|
+
# Determine which CLIs to install
|
|
134
|
+
# If no flags specified, act like --all (but don't force git hooks unless implied or explicit)
|
|
135
|
+
# Actually, let's keep git hooks opt-in or part of --all?
|
|
136
|
+
# Let's make --all include git hooks if we are in a git repo?
|
|
137
|
+
# For safety, let's make git hooks explicit or part of --all if user approves?
|
|
138
|
+
# Requirement: "Users must run this command explicitly to enable auto-sync"
|
|
139
|
+
# So --all might NOT include hooks by default in this logic unless we change policy.
|
|
140
|
+
# Let's explicitly check flags.
|
|
141
|
+
|
|
142
|
+
if (
|
|
143
|
+
not claude_flag
|
|
144
|
+
and not gemini_flag
|
|
145
|
+
and not codex_flag
|
|
146
|
+
and not hooks_flag
|
|
147
|
+
and not all_flag
|
|
148
|
+
and not antigravity_flag
|
|
149
|
+
):
|
|
150
|
+
all_flag = True
|
|
151
|
+
|
|
152
|
+
codex_detected = _is_codex_cli_installed()
|
|
153
|
+
|
|
154
|
+
# Build list of CLIs to install
|
|
155
|
+
clis_to_install = []
|
|
156
|
+
|
|
157
|
+
if all_flag:
|
|
158
|
+
# Auto-detect installed CLIs
|
|
159
|
+
if _is_claude_code_installed():
|
|
160
|
+
clis_to_install.append("claude")
|
|
161
|
+
if _is_gemini_cli_installed():
|
|
162
|
+
clis_to_install.append("gemini")
|
|
163
|
+
if codex_detected:
|
|
164
|
+
clis_to_install.append("codex")
|
|
165
|
+
|
|
166
|
+
# Check for git
|
|
167
|
+
if (project_path / ".git").exists():
|
|
168
|
+
hooks_flag = True # Include git hooks in --all? Or leave separate?
|
|
169
|
+
# Let's include them in --all for "complete setup", but maybe log it clearly.
|
|
170
|
+
|
|
171
|
+
if not clis_to_install and not hooks_flag:
|
|
172
|
+
click.echo("No supported AI coding CLIs detected.")
|
|
173
|
+
click.echo("\nSupported CLIs:")
|
|
174
|
+
click.echo(" - Claude Code: npm install -g @anthropic-ai/claude-code")
|
|
175
|
+
click.echo(" - Gemini CLI: npm install -g @google/gemini-cli")
|
|
176
|
+
click.echo(" - Codex CLI: npm install -g @openai/codex")
|
|
177
|
+
click.echo(
|
|
178
|
+
"\nYou can still install manually with --claude, --gemini, or --codex flags."
|
|
179
|
+
)
|
|
180
|
+
sys.exit(1)
|
|
181
|
+
else:
|
|
182
|
+
if claude_flag:
|
|
183
|
+
clis_to_install.append("claude")
|
|
184
|
+
if gemini_flag:
|
|
185
|
+
clis_to_install.append("gemini")
|
|
186
|
+
if codex_flag:
|
|
187
|
+
clis_to_install.append("codex")
|
|
188
|
+
if antigravity_flag:
|
|
189
|
+
clis_to_install.append("antigravity")
|
|
190
|
+
|
|
191
|
+
# Get install directory info
|
|
192
|
+
install_dir = get_install_dir()
|
|
193
|
+
is_dev_mode = "src" in str(install_dir)
|
|
194
|
+
|
|
195
|
+
click.echo("=" * 60)
|
|
196
|
+
click.echo(" Gobby Hooks Installation")
|
|
197
|
+
click.echo("=" * 60)
|
|
198
|
+
click.echo(f"\nProject: {project_path}")
|
|
199
|
+
if is_dev_mode:
|
|
200
|
+
click.echo("Mode: Development (using source directory)")
|
|
201
|
+
|
|
202
|
+
# Ensure daemon config exists
|
|
203
|
+
config_result = _ensure_daemon_config()
|
|
204
|
+
if config_result["created"]:
|
|
205
|
+
click.echo(f"Created daemon config: {config_result['path']}")
|
|
206
|
+
|
|
207
|
+
# Install default external MCP servers (GitHub, Linear, context7)
|
|
208
|
+
mcp_result = install_default_mcp_servers()
|
|
209
|
+
if mcp_result["success"]:
|
|
210
|
+
if mcp_result["servers_added"]:
|
|
211
|
+
click.echo(f"Added MCP servers to proxy: {', '.join(mcp_result['servers_added'])}")
|
|
212
|
+
if mcp_result["servers_skipped"]:
|
|
213
|
+
click.echo(
|
|
214
|
+
f"MCP servers already configured: {', '.join(mcp_result['servers_skipped'])}"
|
|
215
|
+
)
|
|
216
|
+
else:
|
|
217
|
+
click.echo(f"Warning: Failed to configure MCP servers: {mcp_result['error']}")
|
|
218
|
+
|
|
219
|
+
toggles = list(clis_to_install)
|
|
220
|
+
if hooks_flag:
|
|
221
|
+
toggles.append("git-hooks")
|
|
222
|
+
|
|
223
|
+
click.echo(f"Components to configure: {', '.join(toggles)}")
|
|
224
|
+
click.echo("")
|
|
225
|
+
|
|
226
|
+
# Track results
|
|
227
|
+
results = {}
|
|
228
|
+
|
|
229
|
+
# Install Claude Code hooks
|
|
230
|
+
if "claude" in clis_to_install:
|
|
231
|
+
click.echo("-" * 40)
|
|
232
|
+
click.echo("Claude Code")
|
|
233
|
+
click.echo("-" * 40)
|
|
234
|
+
|
|
235
|
+
result = install_claude(project_path)
|
|
236
|
+
results["claude"] = result
|
|
237
|
+
|
|
238
|
+
if result["success"]:
|
|
239
|
+
click.echo(f"Installed {len(result['hooks_installed'])} hooks")
|
|
240
|
+
for hook in result["hooks_installed"]:
|
|
241
|
+
click.echo(f" - {hook}")
|
|
242
|
+
|
|
243
|
+
if result.get("workflows_installed"):
|
|
244
|
+
click.echo(f"Installed {len(result['workflows_installed'])} workflows")
|
|
245
|
+
for workflow in result["workflows_installed"]:
|
|
246
|
+
click.echo(f" - {workflow}")
|
|
247
|
+
if result.get("commands_installed"):
|
|
248
|
+
click.echo(f"Installed {len(result['commands_installed'])} skills/commands")
|
|
249
|
+
for cmd in result["commands_installed"]:
|
|
250
|
+
click.echo(f" - {cmd}")
|
|
251
|
+
if result.get("plugins_installed"):
|
|
252
|
+
click.echo(
|
|
253
|
+
f"Installed {len(result['plugins_installed'])} plugins to ~/.gobby/plugins/"
|
|
254
|
+
)
|
|
255
|
+
for plugin in result["plugins_installed"]:
|
|
256
|
+
click.echo(f" - {plugin}")
|
|
257
|
+
if result.get("mcp_configured"):
|
|
258
|
+
click.echo("Configured MCP server: ~/.claude.json")
|
|
259
|
+
elif result.get("mcp_already_configured"):
|
|
260
|
+
click.echo("MCP server already configured: ~/.claude.json")
|
|
261
|
+
click.echo(f"Configuration: {project_path / '.claude' / 'settings.json'}")
|
|
262
|
+
else:
|
|
263
|
+
click.echo(f"Failed: {result['error']}", err=True)
|
|
264
|
+
click.echo("")
|
|
265
|
+
|
|
266
|
+
# Install Gemini CLI hooks
|
|
267
|
+
if "gemini" in clis_to_install:
|
|
268
|
+
click.echo("-" * 40)
|
|
269
|
+
click.echo("Gemini CLI")
|
|
270
|
+
click.echo("-" * 40)
|
|
271
|
+
|
|
272
|
+
result = install_gemini(project_path)
|
|
273
|
+
results["gemini"] = result
|
|
274
|
+
|
|
275
|
+
if result["success"]:
|
|
276
|
+
click.echo(f"Installed {len(result['hooks_installed'])} hooks")
|
|
277
|
+
for hook in result["hooks_installed"]:
|
|
278
|
+
click.echo(f" - {hook}")
|
|
279
|
+
|
|
280
|
+
if result.get("workflows_installed"):
|
|
281
|
+
click.echo(f"Installed {len(result['workflows_installed'])} workflows")
|
|
282
|
+
for workflow in result["workflows_installed"]:
|
|
283
|
+
click.echo(f" - {workflow}")
|
|
284
|
+
if result.get("commands_installed"):
|
|
285
|
+
click.echo(f"Installed {len(result['commands_installed'])} skills/commands")
|
|
286
|
+
for cmd in result["commands_installed"]:
|
|
287
|
+
click.echo(f" - {cmd}")
|
|
288
|
+
if result.get("plugins_installed"):
|
|
289
|
+
click.echo(
|
|
290
|
+
f"Installed {len(result['plugins_installed'])} plugins to ~/.gobby/plugins/"
|
|
291
|
+
)
|
|
292
|
+
for plugin in result["plugins_installed"]:
|
|
293
|
+
click.echo(f" - {plugin}")
|
|
294
|
+
if result.get("mcp_configured"):
|
|
295
|
+
click.echo("Configured MCP server: ~/.gemini/settings.json")
|
|
296
|
+
elif result.get("mcp_already_configured"):
|
|
297
|
+
click.echo("MCP server already configured: ~/.gemini/settings.json")
|
|
298
|
+
click.echo(f"Configuration: {project_path / '.gemini' / 'settings.json'}")
|
|
299
|
+
else:
|
|
300
|
+
click.echo(f"Failed: {result['error']}", err=True)
|
|
301
|
+
click.echo("")
|
|
302
|
+
|
|
303
|
+
# Configure Codex notify integration (interactive Codex)
|
|
304
|
+
if "codex" in clis_to_install:
|
|
305
|
+
click.echo("-" * 40)
|
|
306
|
+
click.echo("Codex")
|
|
307
|
+
click.echo("-" * 40)
|
|
308
|
+
|
|
309
|
+
if not codex_detected:
|
|
310
|
+
click.echo("Codex CLI not detected in PATH (`codex`).", err=True)
|
|
311
|
+
click.echo("Install Codex first, then re-run:")
|
|
312
|
+
click.echo(" npm install -g @openai/codex\n")
|
|
313
|
+
results["codex"] = {"success": False, "error": "Codex CLI not detected"}
|
|
314
|
+
else:
|
|
315
|
+
result = install_codex_notify()
|
|
316
|
+
results["codex"] = result
|
|
317
|
+
|
|
318
|
+
if result["success"]:
|
|
319
|
+
click.echo("Installed Codex notify integration")
|
|
320
|
+
for file_path in result["files_installed"]:
|
|
321
|
+
click.echo(f" - {file_path}")
|
|
322
|
+
if result.get("config_updated"):
|
|
323
|
+
click.echo("Updated: ~/.codex/config.toml (set `notify = ...`)")
|
|
324
|
+
else:
|
|
325
|
+
click.echo("~/.codex/config.toml already configured")
|
|
326
|
+
|
|
327
|
+
if result.get("workflows_installed"):
|
|
328
|
+
click.echo(f"Installed {len(result['workflows_installed'])} workflows")
|
|
329
|
+
for workflow in result["workflows_installed"]:
|
|
330
|
+
click.echo(f" - {workflow}")
|
|
331
|
+
if result.get("commands_installed"):
|
|
332
|
+
click.echo(f"Installed {len(result['commands_installed'])} commands")
|
|
333
|
+
for cmd in result["commands_installed"]:
|
|
334
|
+
click.echo(f" - {cmd}")
|
|
335
|
+
if result.get("plugins_installed"):
|
|
336
|
+
click.echo(
|
|
337
|
+
f"Installed {len(result['plugins_installed'])} plugins to ~/.gobby/plugins/"
|
|
338
|
+
)
|
|
339
|
+
for plugin in result["plugins_installed"]:
|
|
340
|
+
click.echo(f" - {plugin}")
|
|
341
|
+
if result.get("mcp_configured"):
|
|
342
|
+
click.echo("Configured MCP server: ~/.codex/config.toml")
|
|
343
|
+
elif result.get("mcp_already_configured"):
|
|
344
|
+
click.echo("MCP server already configured: ~/.codex/config.toml")
|
|
345
|
+
else:
|
|
346
|
+
click.echo(f"Failed: {result['error']}", err=True)
|
|
347
|
+
click.echo("")
|
|
348
|
+
|
|
349
|
+
# Install Git Hooks
|
|
350
|
+
if hooks_flag:
|
|
351
|
+
click.echo("-" * 40)
|
|
352
|
+
click.echo("Git Hooks (Task Auto-Sync)")
|
|
353
|
+
click.echo("-" * 40)
|
|
354
|
+
|
|
355
|
+
result = install_git_hooks(project_path)
|
|
356
|
+
results["git-hooks"] = result
|
|
357
|
+
|
|
358
|
+
if result["success"]:
|
|
359
|
+
if result.get("installed"):
|
|
360
|
+
click.echo("Installed git hooks:")
|
|
361
|
+
for hook in result["installed"]:
|
|
362
|
+
click.echo(f" - {hook}")
|
|
363
|
+
if result.get("skipped"):
|
|
364
|
+
click.echo("Skipped:")
|
|
365
|
+
for hook in result["skipped"]:
|
|
366
|
+
click.echo(f" - {hook}")
|
|
367
|
+
if not result.get("installed") and not result.get("skipped"):
|
|
368
|
+
click.echo("No hooks to install")
|
|
369
|
+
else:
|
|
370
|
+
click.echo(f"Failed: {result['error']}", err=True)
|
|
371
|
+
click.echo("")
|
|
372
|
+
|
|
373
|
+
# Install Antigravity hooks
|
|
374
|
+
# Note: Antigravity is an internal configuration, so we treat it similarly to Gemini
|
|
375
|
+
if "antigravity" in clis_to_install:
|
|
376
|
+
click.echo("-" * 40)
|
|
377
|
+
click.echo("Antigravity Agent")
|
|
378
|
+
click.echo("-" * 40)
|
|
379
|
+
|
|
380
|
+
result = install_antigravity(project_path)
|
|
381
|
+
results["antigravity"] = result
|
|
382
|
+
|
|
383
|
+
if result["success"]:
|
|
384
|
+
click.echo(f"Installed {len(result['hooks_installed'])} hooks")
|
|
385
|
+
for hook in result["hooks_installed"]:
|
|
386
|
+
click.echo(f" - {hook}")
|
|
387
|
+
|
|
388
|
+
if result.get("workflows_installed"):
|
|
389
|
+
click.echo(f"Installed {len(result['workflows_installed'])} workflows")
|
|
390
|
+
for workflow in result["workflows_installed"]:
|
|
391
|
+
click.echo(f" - {workflow}")
|
|
392
|
+
if result.get("commands_installed"):
|
|
393
|
+
click.echo(f"Installed {len(result['commands_installed'])} skills/commands")
|
|
394
|
+
for cmd in result["commands_installed"]:
|
|
395
|
+
click.echo(f" - {cmd}")
|
|
396
|
+
if result.get("plugins_installed"):
|
|
397
|
+
click.echo(
|
|
398
|
+
f"Installed {len(result['plugins_installed'])} plugins to ~/.gobby/plugins/"
|
|
399
|
+
)
|
|
400
|
+
for plugin in result["plugins_installed"]:
|
|
401
|
+
click.echo(f" - {plugin}")
|
|
402
|
+
if result.get("mcp_configured"):
|
|
403
|
+
click.echo("Configured MCP server: ~/.gemini/antigravity/mcp_config.json")
|
|
404
|
+
elif result.get("mcp_already_configured"):
|
|
405
|
+
click.echo("MCP server already configured: ~/.gemini/antigravity/mcp_config.json")
|
|
406
|
+
click.echo(f"Configuration: {project_path / '.antigravity' / 'settings.json'}")
|
|
407
|
+
else:
|
|
408
|
+
click.echo(f"Failed: {result['error']}", err=True)
|
|
409
|
+
click.echo("")
|
|
410
|
+
|
|
411
|
+
# Summary
|
|
412
|
+
click.echo("=" * 60)
|
|
413
|
+
click.echo(" Summary")
|
|
414
|
+
click.echo("=" * 60)
|
|
415
|
+
|
|
416
|
+
all_success = all(r.get("success", False) for r in results.values())
|
|
417
|
+
|
|
418
|
+
if all_success:
|
|
419
|
+
click.echo("\nInstallation completed successfully!")
|
|
420
|
+
else:
|
|
421
|
+
failed = [cli for cli, r in results.items() if not r.get("success", False)]
|
|
422
|
+
click.echo(f"\nSome installations failed: {', '.join(failed)}")
|
|
423
|
+
|
|
424
|
+
click.echo("\nNext steps:")
|
|
425
|
+
click.echo(" 1. Ensure the Gobby daemon is running:")
|
|
426
|
+
click.echo(" gobby start")
|
|
427
|
+
click.echo(" 2. Start a new session in your AI coding CLI")
|
|
428
|
+
click.echo(" 3. Your sessions will now be tracked locally")
|
|
429
|
+
|
|
430
|
+
# Show MCP server API key instructions
|
|
431
|
+
click.echo("\nMCP Servers (via Gobby proxy):")
|
|
432
|
+
click.echo(" The following MCP servers are available through the Gobby proxy.")
|
|
433
|
+
click.echo(" Configure API keys to enable them:")
|
|
434
|
+
click.echo("")
|
|
435
|
+
click.echo(" GitHub (issues, PRs, repos):")
|
|
436
|
+
click.echo(" export GITHUB_PERSONAL_ACCESS_TOKEN=ghp_...")
|
|
437
|
+
click.echo("")
|
|
438
|
+
click.echo(" Linear (issue tracking):")
|
|
439
|
+
click.echo(" export LINEAR_API_KEY=lin_api_...")
|
|
440
|
+
click.echo("")
|
|
441
|
+
click.echo(" Context7 (library docs, optional for private repos):")
|
|
442
|
+
click.echo(" export CONTEXT7_API_KEY=... # from context7.com/dashboard")
|
|
443
|
+
click.echo("")
|
|
444
|
+
click.echo(" Add these to your shell profile (~/.zshrc, ~/.bashrc) for persistence.")
|
|
445
|
+
click.echo(" Restart the daemon after setting: gobby restart")
|
|
446
|
+
|
|
447
|
+
if not all_success:
|
|
448
|
+
sys.exit(1)
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
@click.command("uninstall")
|
|
452
|
+
@click.option(
|
|
453
|
+
"--claude",
|
|
454
|
+
"claude_flag",
|
|
455
|
+
is_flag=True,
|
|
456
|
+
help="Uninstall Claude Code hooks only",
|
|
457
|
+
)
|
|
458
|
+
@click.option(
|
|
459
|
+
"--gemini",
|
|
460
|
+
"gemini_flag",
|
|
461
|
+
is_flag=True,
|
|
462
|
+
help="Uninstall Gemini CLI hooks only",
|
|
463
|
+
)
|
|
464
|
+
@click.option(
|
|
465
|
+
"--codex",
|
|
466
|
+
"codex_flag",
|
|
467
|
+
is_flag=True,
|
|
468
|
+
help="Uninstall Codex notify integration",
|
|
469
|
+
)
|
|
470
|
+
@click.option(
|
|
471
|
+
"--all",
|
|
472
|
+
"all_flag",
|
|
473
|
+
is_flag=True,
|
|
474
|
+
default=False,
|
|
475
|
+
help="Uninstall hooks from all CLIs (default behavior when no flags specified)",
|
|
476
|
+
)
|
|
477
|
+
@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:
|
|
479
|
+
"""Uninstall Gobby hooks from AI coding CLIs.
|
|
480
|
+
|
|
481
|
+
By default (no flags), uninstalls from all CLIs that have hooks installed.
|
|
482
|
+
Use --claude, --gemini, or --codex to uninstall only from specific CLIs.
|
|
483
|
+
|
|
484
|
+
Uninstalls from project-level directories in current working directory.
|
|
485
|
+
"""
|
|
486
|
+
project_path = Path.cwd()
|
|
487
|
+
|
|
488
|
+
# Determine which CLIs to uninstall
|
|
489
|
+
# If no flags specified, act like --all
|
|
490
|
+
if not claude_flag and not gemini_flag and not codex_flag and not all_flag:
|
|
491
|
+
all_flag = True
|
|
492
|
+
|
|
493
|
+
# Build list of CLIs to uninstall
|
|
494
|
+
clis_to_uninstall = []
|
|
495
|
+
|
|
496
|
+
if all_flag:
|
|
497
|
+
# Check which CLIs have hooks installed
|
|
498
|
+
claude_settings = project_path / ".claude" / "settings.json"
|
|
499
|
+
gemini_settings = project_path / ".gemini" / "settings.json"
|
|
500
|
+
codex_notify = Path.home() / ".gobby" / "hooks" / "codex" / "hook_dispatcher.py"
|
|
501
|
+
|
|
502
|
+
if claude_settings.exists():
|
|
503
|
+
clis_to_uninstall.append("claude")
|
|
504
|
+
if gemini_settings.exists():
|
|
505
|
+
clis_to_uninstall.append("gemini")
|
|
506
|
+
if codex_notify.exists():
|
|
507
|
+
clis_to_uninstall.append("codex")
|
|
508
|
+
|
|
509
|
+
if not clis_to_uninstall:
|
|
510
|
+
click.echo("No Gobby hooks found to uninstall.")
|
|
511
|
+
click.echo(f"\nChecked: {project_path / '.claude'}")
|
|
512
|
+
click.echo(f" {project_path / '.gemini'}")
|
|
513
|
+
click.echo(f" {codex_notify}")
|
|
514
|
+
sys.exit(0)
|
|
515
|
+
else:
|
|
516
|
+
if claude_flag:
|
|
517
|
+
clis_to_uninstall.append("claude")
|
|
518
|
+
if gemini_flag:
|
|
519
|
+
clis_to_uninstall.append("gemini")
|
|
520
|
+
if codex_flag:
|
|
521
|
+
clis_to_uninstall.append("codex")
|
|
522
|
+
|
|
523
|
+
click.echo("=" * 60)
|
|
524
|
+
click.echo(" Gobby Hooks Uninstallation")
|
|
525
|
+
click.echo("=" * 60)
|
|
526
|
+
click.echo(f"\nProject: {project_path}")
|
|
527
|
+
click.echo(f"CLIs to uninstall from: {', '.join(clis_to_uninstall)}")
|
|
528
|
+
click.echo("")
|
|
529
|
+
|
|
530
|
+
# Track results
|
|
531
|
+
results = {}
|
|
532
|
+
|
|
533
|
+
# Uninstall Claude Code hooks
|
|
534
|
+
if "claude" in clis_to_uninstall:
|
|
535
|
+
click.echo("-" * 40)
|
|
536
|
+
click.echo("Claude Code")
|
|
537
|
+
click.echo("-" * 40)
|
|
538
|
+
|
|
539
|
+
result = uninstall_claude(project_path)
|
|
540
|
+
results["claude"] = result
|
|
541
|
+
|
|
542
|
+
if result["success"]:
|
|
543
|
+
if result["hooks_removed"]:
|
|
544
|
+
click.echo(f"Removed {len(result['hooks_removed'])} hooks from settings")
|
|
545
|
+
for hook in result["hooks_removed"]:
|
|
546
|
+
click.echo(f" - {hook}")
|
|
547
|
+
if result["files_removed"]:
|
|
548
|
+
click.echo(f"Removed {len(result['files_removed'])} files")
|
|
549
|
+
|
|
550
|
+
if not result["hooks_removed"] and not result["files_removed"]:
|
|
551
|
+
click.echo(" (no hooks found to remove)")
|
|
552
|
+
else:
|
|
553
|
+
click.echo(f"Failed: {result['error']}", err=True)
|
|
554
|
+
click.echo("")
|
|
555
|
+
|
|
556
|
+
# Uninstall Gemini CLI hooks
|
|
557
|
+
if "gemini" in clis_to_uninstall:
|
|
558
|
+
click.echo("-" * 40)
|
|
559
|
+
click.echo("Gemini CLI")
|
|
560
|
+
click.echo("-" * 40)
|
|
561
|
+
|
|
562
|
+
result = uninstall_gemini(project_path)
|
|
563
|
+
results["gemini"] = result
|
|
564
|
+
|
|
565
|
+
if result["success"]:
|
|
566
|
+
if result["hooks_removed"]:
|
|
567
|
+
click.echo(f"Removed {len(result['hooks_removed'])} hooks from settings")
|
|
568
|
+
for hook in result["hooks_removed"]:
|
|
569
|
+
click.echo(f" - {hook}")
|
|
570
|
+
if result["files_removed"]:
|
|
571
|
+
click.echo(f"Removed {len(result['files_removed'])} files")
|
|
572
|
+
if not result["hooks_removed"] and not result["files_removed"]:
|
|
573
|
+
click.echo(" (no hooks found to remove)")
|
|
574
|
+
else:
|
|
575
|
+
click.echo(f"Failed: {result['error']}", err=True)
|
|
576
|
+
click.echo("")
|
|
577
|
+
|
|
578
|
+
# Uninstall Codex notify integration
|
|
579
|
+
if "codex" in clis_to_uninstall:
|
|
580
|
+
click.echo("-" * 40)
|
|
581
|
+
click.echo("Codex")
|
|
582
|
+
click.echo("-" * 40)
|
|
583
|
+
|
|
584
|
+
result = uninstall_codex_notify()
|
|
585
|
+
results["codex"] = result
|
|
586
|
+
|
|
587
|
+
if result["success"]:
|
|
588
|
+
if result["files_removed"]:
|
|
589
|
+
click.echo(f"Removed {len(result['files_removed'])} files")
|
|
590
|
+
for f in result["files_removed"]:
|
|
591
|
+
click.echo(f" - {f}")
|
|
592
|
+
if result.get("config_updated"):
|
|
593
|
+
click.echo("Updated: ~/.codex/config.toml (removed `notify = ...`)")
|
|
594
|
+
if not result["files_removed"] and not result.get("config_updated"):
|
|
595
|
+
click.echo(" (no codex integration found to remove)")
|
|
596
|
+
else:
|
|
597
|
+
click.echo(f"Failed: {result['error']}", err=True)
|
|
598
|
+
click.echo("")
|
|
599
|
+
|
|
600
|
+
# Summary
|
|
601
|
+
click.echo("=" * 60)
|
|
602
|
+
click.echo(" Summary")
|
|
603
|
+
click.echo("=" * 60)
|
|
604
|
+
|
|
605
|
+
all_success = all(r.get("success", False) for r in results.values())
|
|
606
|
+
|
|
607
|
+
if all_success:
|
|
608
|
+
click.echo("\nUninstallation completed successfully!")
|
|
609
|
+
else:
|
|
610
|
+
failed = [cli for cli, r in results.items() if not r.get("success", False)]
|
|
611
|
+
click.echo(f"\nSome uninstallations failed: {', '.join(failed)}")
|
|
612
|
+
|
|
613
|
+
if not all_success:
|
|
614
|
+
sys.exit(1)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI installation modules for Gobby hooks.
|
|
3
|
+
|
|
4
|
+
This package contains per-CLI installation logic extracted from the main install.py
|
|
5
|
+
using the strangler fig pattern for incremental migration.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .antigravity import install_antigravity
|
|
9
|
+
from .claude import install_claude, uninstall_claude
|
|
10
|
+
from .codex import install_codex_notify, uninstall_codex_notify
|
|
11
|
+
from .gemini import install_gemini, uninstall_gemini
|
|
12
|
+
from .git_hooks import install_git_hooks
|
|
13
|
+
from .shared import (
|
|
14
|
+
install_cli_content,
|
|
15
|
+
install_default_mcp_servers,
|
|
16
|
+
install_shared_content,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
# Shared
|
|
21
|
+
"install_shared_content",
|
|
22
|
+
"install_cli_content",
|
|
23
|
+
"install_default_mcp_servers",
|
|
24
|
+
# Claude
|
|
25
|
+
"install_claude",
|
|
26
|
+
"uninstall_claude",
|
|
27
|
+
# Gemini
|
|
28
|
+
"install_gemini",
|
|
29
|
+
"uninstall_gemini",
|
|
30
|
+
# Codex
|
|
31
|
+
"install_codex_notify",
|
|
32
|
+
"uninstall_codex_notify",
|
|
33
|
+
# Git Hooks
|
|
34
|
+
"install_git_hooks",
|
|
35
|
+
# Antigravity
|
|
36
|
+
"install_antigravity",
|
|
37
|
+
]
|