gobby 0.2.7__tar.gz → 0.2.9__tar.gz
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-0.2.7 → gobby-0.2.9}/PKG-INFO +1 -1
- {gobby-0.2.7 → gobby-0.2.9}/pyproject.toml +1 -1
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/__init__.py +1 -1
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/adapters/claude_code.py +99 -61
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/adapters/gemini.py +140 -38
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/isolation.py +130 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/registry.py +11 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/session.py +1 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawn_executor.py +43 -13
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawners/macos.py +26 -1
- gobby-0.2.9/src/gobby/app_context.py +59 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/__init__.py +0 -2
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/memory.py +185 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/utils.py +5 -17
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/clones/git.py +177 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/features.py +0 -20
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/skills.py +31 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/tasks.py +4 -0
- gobby-0.2.9/src/gobby/hooks/event_handlers/__init__.py +155 -0
- gobby-0.2.9/src/gobby/hooks/event_handlers/_agent.py +175 -0
- gobby-0.2.9/src/gobby/hooks/event_handlers/_base.py +87 -0
- gobby-0.2.9/src/gobby/hooks/event_handlers/_misc.py +66 -0
- gobby-0.2.9/src/gobby/hooks/event_handlers/_session.py +573 -0
- gobby-0.2.9/src/gobby/hooks/event_handlers/_tool.py +196 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/hook_manager.py +21 -1
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/install/gemini/hooks/hook_dispatcher.py +74 -15
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/claude.py +377 -42
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/importer.py +4 -41
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/instructions.py +2 -2
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/manager.py +13 -3
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/registries.py +35 -4
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/services/recommendation.py +2 -28
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/agent_messaging.py +93 -44
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/agents.py +45 -9
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/artifacts.py +46 -12
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/sessions/_commits.py +31 -24
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/sessions/_crud.py +5 -5
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/sessions/_handoff.py +45 -41
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/sessions/_messages.py +35 -7
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/spawn_agent.py +44 -6
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/task_readiness.py +27 -4
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_context.py +18 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_crud.py +13 -6
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_lifecycle.py +29 -14
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_session.py +22 -7
- gobby-0.2.9/src/gobby/mcp_proxy/tools/workflows/__init__.py +266 -0
- gobby-0.2.9/src/gobby/mcp_proxy/tools/workflows/_artifacts.py +225 -0
- gobby-0.2.9/src/gobby/mcp_proxy/tools/workflows/_import.py +112 -0
- gobby-0.2.9/src/gobby/mcp_proxy/tools/workflows/_lifecycle.py +321 -0
- gobby-0.2.9/src/gobby/mcp_proxy/tools/workflows/_query.py +207 -0
- gobby-0.2.9/src/gobby/mcp_proxy/tools/workflows/_resolution.py +78 -0
- gobby-0.2.9/src/gobby/mcp_proxy/tools/workflows/_terminal.py +139 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/worktrees.py +32 -7
- gobby-0.2.9/src/gobby/memory/components/ingestion.py +98 -0
- gobby-0.2.9/src/gobby/memory/components/search.py +108 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/extractor.py +15 -1
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/manager.py +16 -25
- gobby-0.2.9/src/gobby/paths.py +51 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/prompts/loader.py +1 -35
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/runner.py +36 -10
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/http.py +186 -149
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/admin.py +12 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/mcp/endpoints/execution.py +15 -7
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/mcp/endpoints/registry.py +8 -8
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/mcp/hooks.py +50 -3
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/websocket.py +57 -1
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sessions/analyzer.py +4 -4
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sessions/manager.py +9 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sessions/transcripts/gemini.py +100 -34
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/skills/parser.py +23 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/skills/sync.py +5 -4
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/artifacts.py +19 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/database.py +9 -2
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/memories.py +32 -21
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/migrations.py +46 -4
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/sessions.py +4 -2
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/skills.py +87 -7
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tasks/external_validator.py +4 -17
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tasks/validation.py +13 -87
- gobby-0.2.9/src/gobby/tools/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tools/summarizer.py +18 -51
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/status.py +13 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/actions.py +5 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/context_actions.py +21 -24
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/detection_helpers.py +38 -24
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/enforcement/__init__.py +11 -1
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/enforcement/blocking.py +109 -1
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/enforcement/handlers.py +35 -1
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/engine.py +96 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/evaluator.py +110 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/hooks.py +41 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/lifecycle_evaluator.py +2 -1
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/memory_actions.py +11 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/safe_evaluator.py +8 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/summary_actions.py +123 -50
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby.egg-info/PKG-INFO +1 -1
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby.egg-info/SOURCES.txt +18 -26
- {gobby-0.2.7 → gobby-0.2.9}/tests/test_import_pathing_trap.py +5 -2
- {gobby-0.2.7 → gobby-0.2.9}/tests/test_runner.py +24 -22
- gobby-0.2.7/src/gobby/cli/tui.py +0 -34
- gobby-0.2.7/src/gobby/hooks/event_handlers.py +0 -909
- gobby-0.2.7/src/gobby/mcp_proxy/tools/workflows.py +0 -973
- gobby-0.2.7/src/gobby/tui/__init__.py +0 -5
- gobby-0.2.7/src/gobby/tui/api_client.py +0 -278
- gobby-0.2.7/src/gobby/tui/app.py +0 -329
- gobby-0.2.7/src/gobby/tui/screens/__init__.py +0 -25
- gobby-0.2.7/src/gobby/tui/screens/agents.py +0 -333
- gobby-0.2.7/src/gobby/tui/screens/chat.py +0 -450
- gobby-0.2.7/src/gobby/tui/screens/dashboard.py +0 -377
- gobby-0.2.7/src/gobby/tui/screens/memory.py +0 -305
- gobby-0.2.7/src/gobby/tui/screens/metrics.py +0 -231
- gobby-0.2.7/src/gobby/tui/screens/orchestrator.py +0 -903
- gobby-0.2.7/src/gobby/tui/screens/sessions.py +0 -412
- gobby-0.2.7/src/gobby/tui/screens/tasks.py +0 -440
- gobby-0.2.7/src/gobby/tui/screens/workflows.py +0 -289
- gobby-0.2.7/src/gobby/tui/screens/worktrees.py +0 -174
- gobby-0.2.7/src/gobby/tui/widgets/__init__.py +0 -21
- gobby-0.2.7/src/gobby/tui/widgets/chat.py +0 -210
- gobby-0.2.7/src/gobby/tui/widgets/conductor.py +0 -104
- gobby-0.2.7/src/gobby/tui/widgets/menu.py +0 -132
- gobby-0.2.7/src/gobby/tui/widgets/message_panel.py +0 -160
- gobby-0.2.7/src/gobby/tui/widgets/review_gate.py +0 -224
- gobby-0.2.7/src/gobby/tui/widgets/task_tree.py +0 -99
- gobby-0.2.7/src/gobby/tui/widgets/token_budget.py +0 -166
- gobby-0.2.7/src/gobby/tui/ws_client.py +0 -258
- {gobby-0.2.7 → gobby-0.2.9}/LICENSE.md +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/README.md +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/setup.cfg +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/adapters/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/adapters/base.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/adapters/codex_impl/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/adapters/codex_impl/adapter.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/adapters/codex_impl/client.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/adapters/codex_impl/protocol.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/adapters/codex_impl/types.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/codex_session.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/constants.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/context.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/definitions.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/gemini_session.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/runner.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/sandbox.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawn.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawners/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawners/base.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawners/command_builder.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawners/cross_platform.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawners/embedded.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawners/headless.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawners/linux.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawners/prompt_manager.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/spawners/windows.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/agents/tty_config.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/autonomous/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/autonomous/progress_tracker.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/autonomous/stop_registry.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/autonomous/stuck_detector.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/__main__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/agents.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/artifacts.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/clones.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/conductor.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/daemon.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/extensions.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/github.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/init.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/install.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/installers/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/installers/antigravity.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/installers/claude.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/installers/codex.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/installers/gemini.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/installers/git_hooks.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/installers/shared.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/linear.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/mcp.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/mcp_proxy.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/merge.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/projects.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/sessions.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/skills.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/tasks/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/tasks/_utils.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/tasks/ai.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/tasks/commits.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/tasks/crud.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/tasks/deps.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/tasks/labels.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/tasks/main.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/tasks/search.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/workflows.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/cli/worktrees.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/clones/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/conductor/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/conductor/alerts.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/conductor/loop.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/conductor/monitors/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/conductor/monitors/agents.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/conductor/monitors/tasks.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/conductor/pricing.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/conductor/token_tracker.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/app.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/extensions.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/llm_providers.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/logging.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/mcp.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/persistence.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/search.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/servers.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/config/sessions.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/artifact_capture.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/broadcaster.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/events.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/git.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/health_monitor.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/hook_types.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/plugins.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/session_coordinator.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/skill_manager.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/verification_runner.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/hooks/webhooks.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/install/claude/hooks/HOOK_SCHEMAS.md +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/install/claude/hooks/hook_dispatcher.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/install/claude/hooks/validate_settings.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/install/claude/hooks-template.json +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/install/codex/hooks/hook_dispatcher.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/install/gemini/hooks-template.json +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/install/shared/plugins/code_guardian.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/install/shared/plugins/example_notify.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/integrations/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/integrations/github.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/integrations/linear.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/base.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/claude_executor.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/codex.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/codex_executor.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/executor.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/factory.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/gemini.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/litellm.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/litellm_executor.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/resolver.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/llm/service.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/actions.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/daemon_control.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/lazy.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/metrics.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/models.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/schema_hash.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/semantic_search.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/server.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/services/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/services/fallback.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/services/server_mgmt.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/services/system.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/services/tool_filter.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/services/tool_proxy.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/stdio.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/clones.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/hub.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/internal.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/memory.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/merge.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/metrics.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/orchestration/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/orchestration/cleanup.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/orchestration/monitor.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/orchestration/orchestrate.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/orchestration/review.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/orchestration/utils.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/orchestration/wait.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/sessions/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/sessions/_factory.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/skills/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/task_dependencies.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/task_github.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/task_linear.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/task_orchestration.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/task_sync.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/task_validation.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_expansion.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_factory.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_helpers.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_resolution.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_search.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/transports/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/transports/base.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/transports/factory.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/transports/http.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/transports/stdio.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/mcp_proxy/transports/websocket.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/backends/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/backends/mem0.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/backends/memu.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/backends/null.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/backends/openmemory.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/backends/sqlite.py +0 -0
- {gobby-0.2.7/src/gobby/tools → gobby-0.2.9/src/gobby/memory/components}/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/context.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/ingestion/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/ingestion/multimodal.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/protocol.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/search/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/search/coordinator.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/search/text.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/services/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/services/crossref.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/memory/viz.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/prompts/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/prompts/models.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/py.typed +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/search/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/search/backends/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/search/backends/embedding.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/search/embeddings.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/search/models.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/search/protocol.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/search/tfidf.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/search/unified.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/models.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/dependencies.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/mcp/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/mcp/endpoints/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/mcp/endpoints/discovery.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/mcp/endpoints/server.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/mcp/plugins.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/mcp/tools.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/mcp/webhooks.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/servers/routes/sessions.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sessions/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sessions/lifecycle.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sessions/processor.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sessions/summary.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sessions/transcripts/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sessions/transcripts/base.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sessions/transcripts/claude.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sessions/transcripts/codex.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/skills/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/skills/loader.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/skills/manager.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/skills/search.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/skills/updater.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/skills/validator.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/agents.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/artifact_classifier.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/clones.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/compaction.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/inter_session_messages.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/mcp.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/merge_resolutions.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/projects.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/session_messages.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/session_tasks.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/task_dependencies.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/tasks/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/tasks/_aggregates.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/tasks/_crud.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/tasks/_id.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/tasks/_lifecycle.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/tasks/_manager.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/tasks/_models.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/tasks/_ordering.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/tasks/_path_cache.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/tasks/_queries.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/tasks/_search.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/workflow_audit.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/storage/worktrees.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sync/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sync/github.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sync/linear.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sync/memories.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/sync/tasks.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tasks/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tasks/build_verification.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tasks/commits.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tasks/enhanced_validator.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tasks/escalation.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tasks/issue_extraction.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tasks/tree_builder.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tasks/validation_history.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/tasks/validation_models.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/daemon_client.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/git.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/id.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/json_helpers.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/logging.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/machine_id.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/metrics.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/project_context.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/project_init.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/validation.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/utils/version.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/approval_flow.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/artifact_actions.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/audit_helpers.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/autonomous_actions.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/definitions.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/enforcement/commit_policy.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/enforcement/task_policy.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/git_utils.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/llm_actions.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/loader.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/mcp_actions.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/premature_stop.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/session_actions.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/state_actions.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/state_manager.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/stop_signal_actions.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/task_actions.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/task_sync_actions.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/templates.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/todo_actions.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/webhook.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/webhook_actions.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/workflows/webhook_executor.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/worktrees/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/worktrees/git.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/worktrees/merge/__init__.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/worktrees/merge/conflict_parser.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby/worktrees/merge/resolver.py +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby.egg-info/dependency_links.txt +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby.egg-info/entry_points.txt +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby.egg-info/requires.txt +0 -0
- {gobby-0.2.7 → gobby-0.2.9}/src/gobby.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gobby
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.9
|
|
4
4
|
Summary: A local-first daemon to unify your AI coding tools. Session tracking and handoffs across Claude Code, Gemini CLI, and Codex. An MCP proxy that discovers tools without flooding context. Task management with dependencies, validation, and TDD expansion. Agent spawning and worktree orchestration. Persistent memory, extensible workflows, and hooks.
|
|
5
5
|
Author-email: Josh Wilhelmi <josh@gobby.ai>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "gobby"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.9"
|
|
4
4
|
description = "A local-first daemon to unify your AI coding tools. Session tracking and handoffs across Claude Code, Gemini CLI, and Codex. An MCP proxy that discovers tools without flooding context. Task management with dependencies, validation, and TDD expansion. Agent spawning and worktree orchestration. Persistent memory, extensible workflows, and hooks."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -61,13 +61,10 @@ class ClaudeCodeAdapter(BaseAdapter):
|
|
|
61
61
|
"""Initialize the Claude Code adapter.
|
|
62
62
|
|
|
63
63
|
Args:
|
|
64
|
-
hook_manager: Reference to HookManager for
|
|
64
|
+
hook_manager: Reference to HookManager for delegation.
|
|
65
65
|
If None, the adapter can only translate (not handle events).
|
|
66
66
|
"""
|
|
67
67
|
self._hook_manager = hook_manager
|
|
68
|
-
# Phase 2C: Use new handle() path with unified HookEvent model
|
|
69
|
-
# Note: systemMessage handoff notification bug exists in both paths (see plan-multi-cli.md)
|
|
70
|
-
self._use_legacy = False
|
|
71
68
|
|
|
72
69
|
def translate_to_hook_event(self, native_event: dict[str, Any]) -> HookEvent:
|
|
73
70
|
"""Convert Claude Code native event to unified HookEvent.
|
|
@@ -104,6 +101,10 @@ class ClaudeCodeAdapter(BaseAdapter):
|
|
|
104
101
|
is_failure = hook_type == "post-tool-use-failure"
|
|
105
102
|
metadata = {"is_failure": is_failure} if is_failure else {}
|
|
106
103
|
|
|
104
|
+
# Normalize event data for CLI-agnostic processing
|
|
105
|
+
# This allows downstream code to use consistent field names
|
|
106
|
+
normalized_data = self._normalize_event_data(input_data)
|
|
107
|
+
|
|
107
108
|
return HookEvent(
|
|
108
109
|
event_type=event_type,
|
|
109
110
|
session_id=session_id,
|
|
@@ -111,10 +112,46 @@ class ClaudeCodeAdapter(BaseAdapter):
|
|
|
111
112
|
timestamp=datetime.now(UTC),
|
|
112
113
|
machine_id=input_data.get("machine_id"),
|
|
113
114
|
cwd=input_data.get("cwd"),
|
|
114
|
-
data=
|
|
115
|
+
data=normalized_data,
|
|
115
116
|
metadata=metadata,
|
|
116
117
|
)
|
|
117
118
|
|
|
119
|
+
def _normalize_event_data(self, input_data: dict[str, Any]) -> dict[str, Any]:
|
|
120
|
+
"""Normalize Claude Code event data for CLI-agnostic processing.
|
|
121
|
+
|
|
122
|
+
This method enriches the input_data with normalized fields so downstream
|
|
123
|
+
code doesn't need to handle Claude-specific formats.
|
|
124
|
+
|
|
125
|
+
Normalizations performed:
|
|
126
|
+
1. tool_input.server_name/tool_name → mcp_server/mcp_tool (for MCP calls)
|
|
127
|
+
2. tool_result → tool_output
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
input_data: Raw input data from Claude Code
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Enriched data dict with normalized fields added
|
|
134
|
+
"""
|
|
135
|
+
# Start with a copy to avoid mutating original
|
|
136
|
+
data = dict(input_data)
|
|
137
|
+
|
|
138
|
+
# Get tool info
|
|
139
|
+
tool_name = data.get("tool_name", "")
|
|
140
|
+
tool_input = data.get("tool_input", {}) or {}
|
|
141
|
+
|
|
142
|
+
# 1. Extract MCP info from nested tool_input for call_tool calls
|
|
143
|
+
if tool_name in ("call_tool", "mcp__gobby__call_tool"):
|
|
144
|
+
if "mcp_server" not in data:
|
|
145
|
+
data["mcp_server"] = tool_input.get("server_name")
|
|
146
|
+
if "mcp_tool" not in data:
|
|
147
|
+
data["mcp_tool"] = tool_input.get("tool_name")
|
|
148
|
+
|
|
149
|
+
# 2. Normalize tool_result → tool_output
|
|
150
|
+
if "tool_result" in data and "tool_output" not in data:
|
|
151
|
+
data["tool_output"] = data["tool_result"]
|
|
152
|
+
|
|
153
|
+
return data
|
|
154
|
+
|
|
118
155
|
# Map Claude Code hook types to hookEventName for hookSpecificOutput
|
|
119
156
|
HOOK_EVENT_NAME_MAP: dict[str, str] = {
|
|
120
157
|
"session-start": "SessionStart",
|
|
@@ -193,44 +230,65 @@ class ClaudeCodeAdapter(BaseAdapter):
|
|
|
193
230
|
# Add session identifiers from metadata
|
|
194
231
|
# Note: "session_id" in metadata is Gobby's internal platform session ID
|
|
195
232
|
# "external_id" in metadata is the CLI's session UUID
|
|
233
|
+
# "session_ref" is the short #N format for easier reference
|
|
234
|
+
# Token optimization: Only inject full metadata on first hook per session
|
|
196
235
|
if response.metadata:
|
|
197
236
|
gobby_session_id = response.metadata.get("session_id")
|
|
237
|
+
session_ref = response.metadata.get("session_ref")
|
|
198
238
|
external_id = response.metadata.get("external_id")
|
|
239
|
+
is_first_hook = response.metadata.get("_first_hook_for_session", False)
|
|
240
|
+
|
|
199
241
|
if gobby_session_id:
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
"
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
"
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
242
|
+
if is_first_hook:
|
|
243
|
+
# First hook: inject full metadata (~60-100 tokens)
|
|
244
|
+
context_lines = []
|
|
245
|
+
if session_ref:
|
|
246
|
+
context_lines.append(
|
|
247
|
+
f"Gobby Session ID: {session_ref} (or {gobby_session_id})"
|
|
248
|
+
)
|
|
249
|
+
else:
|
|
250
|
+
context_lines.append(f"Gobby Session ID: {gobby_session_id}")
|
|
251
|
+
if external_id:
|
|
252
|
+
context_lines.append(
|
|
253
|
+
f"CLI-Specific Session ID (external_id): {external_id}"
|
|
254
|
+
)
|
|
255
|
+
if response.metadata.get("parent_session_id"):
|
|
256
|
+
context_lines.append(
|
|
257
|
+
f"parent_session_id: {response.metadata['parent_session_id']}"
|
|
258
|
+
)
|
|
259
|
+
if response.metadata.get("machine_id"):
|
|
260
|
+
context_lines.append(f"machine_id: {response.metadata['machine_id']}")
|
|
261
|
+
if response.metadata.get("project_id"):
|
|
262
|
+
context_lines.append(f"project_id: {response.metadata['project_id']}")
|
|
263
|
+
# Add terminal context (non-null values only)
|
|
264
|
+
if response.metadata.get("terminal_term_program"):
|
|
265
|
+
context_lines.append(
|
|
266
|
+
f"terminal: {response.metadata['terminal_term_program']}"
|
|
267
|
+
)
|
|
268
|
+
if response.metadata.get("terminal_tty"):
|
|
269
|
+
context_lines.append(f"tty: {response.metadata['terminal_tty']}")
|
|
270
|
+
if response.metadata.get("terminal_parent_pid"):
|
|
271
|
+
context_lines.append(
|
|
272
|
+
f"parent_pid: {response.metadata['terminal_parent_pid']}"
|
|
273
|
+
)
|
|
274
|
+
# Add terminal-specific session IDs (only one will be present)
|
|
275
|
+
for key in [
|
|
276
|
+
"terminal_iterm_session_id",
|
|
277
|
+
"terminal_term_session_id",
|
|
278
|
+
"terminal_kitty_window_id",
|
|
279
|
+
"terminal_tmux_pane",
|
|
280
|
+
"terminal_vscode_terminal_id",
|
|
281
|
+
"terminal_alacritty_socket",
|
|
282
|
+
]:
|
|
283
|
+
if response.metadata.get(key):
|
|
284
|
+
# Use friendlier names in output
|
|
285
|
+
friendly_name = key.replace("terminal_", "").replace("_", " ")
|
|
286
|
+
context_lines.append(f"{friendly_name}: {response.metadata[key]}")
|
|
287
|
+
additional_context_parts.append("\n".join(context_lines))
|
|
288
|
+
else:
|
|
289
|
+
# Subsequent hooks: inject minimal session ref only (~8 tokens)
|
|
290
|
+
if session_ref:
|
|
291
|
+
additional_context_parts.append(f"Gobby Session ID: {session_ref}")
|
|
234
292
|
|
|
235
293
|
# Build hookSpecificOutput if we have any context to inject
|
|
236
294
|
# Only include hookSpecificOutput for hook types that Claude Code's schema accepts
|
|
@@ -249,14 +307,6 @@ class ClaudeCodeAdapter(BaseAdapter):
|
|
|
249
307
|
) -> dict[str, Any]:
|
|
250
308
|
"""Main entry point for HTTP endpoint.
|
|
251
309
|
|
|
252
|
-
Strangler fig pattern:
|
|
253
|
-
- Phase 2A-2B: Delegates to existing execute() — validates translation only
|
|
254
|
-
- Phase 2C+: Calls new handle() with HookEvent
|
|
255
|
-
|
|
256
|
-
Note: This method is synchronous for Phase 2A-2B compatibility with
|
|
257
|
-
the existing execute() method. In Phase 2C+, it will become async
|
|
258
|
-
when handle() is implemented as async.
|
|
259
|
-
|
|
260
310
|
Args:
|
|
261
311
|
native_event: Raw payload from Claude Code's hook_dispatcher.py
|
|
262
312
|
hook_manager: HookManager instance for processing.
|
|
@@ -264,22 +314,10 @@ class ClaudeCodeAdapter(BaseAdapter):
|
|
|
264
314
|
Returns:
|
|
265
315
|
Response dict in Claude Code's expected format.
|
|
266
316
|
"""
|
|
267
|
-
#
|
|
317
|
+
# Translate to HookEvent
|
|
268
318
|
hook_event = self.translate_to_hook_event(native_event)
|
|
269
319
|
|
|
270
|
-
#
|
|
271
|
-
# Legacy execute() path removed as HookManager.execute is deprecated/removed.
|
|
320
|
+
# Use HookEvent-based handler
|
|
272
321
|
hook_type = native_event.get("hook_type", "")
|
|
273
322
|
hook_response = hook_manager.handle(hook_event)
|
|
274
323
|
return self.translate_from_hook_response(hook_response, hook_type=hook_type)
|
|
275
|
-
|
|
276
|
-
def set_legacy_mode(self, use_legacy: bool) -> None:
|
|
277
|
-
"""Toggle between legacy and new code paths.
|
|
278
|
-
|
|
279
|
-
This method is used during the strangler fig migration to switch
|
|
280
|
-
between delegating to execute() and calling handle() directly.
|
|
281
|
-
|
|
282
|
-
Args:
|
|
283
|
-
use_legacy: If True, use legacy execute() path. If False, use new handle() path.
|
|
284
|
-
"""
|
|
285
|
-
self._use_legacy = use_legacy
|
|
@@ -76,21 +76,39 @@ class GeminiAdapter(BaseAdapter):
|
|
|
76
76
|
|
|
77
77
|
# Tool name mapping: Gemini tool names -> normalized names
|
|
78
78
|
# Gemini uses different tool names than Claude Code
|
|
79
|
+
# This enables workflows to use Claude Code naming conventions
|
|
79
80
|
TOOL_MAP: dict[str, str] = {
|
|
81
|
+
# Shell/Bash
|
|
80
82
|
"run_shell_command": "Bash",
|
|
81
83
|
"RunShellCommand": "Bash",
|
|
84
|
+
"ShellTool": "Bash",
|
|
85
|
+
# File read
|
|
82
86
|
"read_file": "Read",
|
|
83
87
|
"ReadFile": "Read",
|
|
84
88
|
"ReadFileTool": "Read",
|
|
89
|
+
# File write
|
|
85
90
|
"write_file": "Write",
|
|
86
91
|
"WriteFile": "Write",
|
|
87
92
|
"WriteFileTool": "Write",
|
|
93
|
+
# File edit
|
|
88
94
|
"edit_file": "Edit",
|
|
89
95
|
"EditFile": "Edit",
|
|
90
96
|
"EditFileTool": "Edit",
|
|
97
|
+
# Search/Glob/Grep
|
|
91
98
|
"GlobTool": "Glob",
|
|
92
99
|
"GrepTool": "Grep",
|
|
93
|
-
"
|
|
100
|
+
"search_file_content": "Grep",
|
|
101
|
+
"SearchText": "Grep",
|
|
102
|
+
# MCP tools (Gobby MCP server)
|
|
103
|
+
"call_tool": "mcp__gobby__call_tool",
|
|
104
|
+
"list_mcp_servers": "mcp__gobby__list_mcp_servers",
|
|
105
|
+
"list_tools": "mcp__gobby__list_tools",
|
|
106
|
+
"get_tool_schema": "mcp__gobby__get_tool_schema",
|
|
107
|
+
"search_tools": "mcp__gobby__search_tools",
|
|
108
|
+
"recommend_tools": "mcp__gobby__recommend_tools",
|
|
109
|
+
# Skill and agent tools
|
|
110
|
+
"activate_skill": "Skill",
|
|
111
|
+
"delegate_to_agent": "Task",
|
|
94
112
|
}
|
|
95
113
|
|
|
96
114
|
def __init__(self, hook_manager: "HookManager | None" = None):
|
|
@@ -135,6 +153,55 @@ class GeminiAdapter(BaseAdapter):
|
|
|
135
153
|
"""
|
|
136
154
|
return self.TOOL_MAP.get(gemini_tool_name, gemini_tool_name)
|
|
137
155
|
|
|
156
|
+
def _normalize_event_data(self, input_data: dict[str, Any]) -> dict[str, Any]:
|
|
157
|
+
"""Normalize Gemini event data for CLI-agnostic processing.
|
|
158
|
+
|
|
159
|
+
This method enriches the input_data with normalized fields so downstream
|
|
160
|
+
code doesn't need to handle Gemini-specific formats.
|
|
161
|
+
|
|
162
|
+
Normalizations performed:
|
|
163
|
+
1. mcp_context.server_name/tool_name → mcp_server/mcp_tool (top-level)
|
|
164
|
+
2. tool_response → tool_output
|
|
165
|
+
3. function_name → tool_name (if not already present)
|
|
166
|
+
4. parameters/args → tool_input (if not already present)
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
input_data: Raw input data from Gemini CLI
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
Enriched data dict with normalized fields added
|
|
173
|
+
"""
|
|
174
|
+
# Start with a copy to avoid mutating original
|
|
175
|
+
data = dict(input_data)
|
|
176
|
+
|
|
177
|
+
# 1. Flatten mcp_context to top-level mcp_server/mcp_tool
|
|
178
|
+
mcp_context = data.get("mcp_context")
|
|
179
|
+
if mcp_context and isinstance(mcp_context, dict):
|
|
180
|
+
if "mcp_server" not in data:
|
|
181
|
+
data["mcp_server"] = mcp_context.get("server_name")
|
|
182
|
+
if "mcp_tool" not in data:
|
|
183
|
+
data["mcp_tool"] = mcp_context.get("tool_name")
|
|
184
|
+
|
|
185
|
+
# 2. Normalize tool_response → tool_output
|
|
186
|
+
if "tool_response" in data and "tool_output" not in data:
|
|
187
|
+
data["tool_output"] = data["tool_response"]
|
|
188
|
+
|
|
189
|
+
# 3. Normalize function_name → tool_name
|
|
190
|
+
if "function_name" in data and "tool_name" not in data:
|
|
191
|
+
data["tool_name"] = self.normalize_tool_name(data["function_name"])
|
|
192
|
+
elif "tool_name" in data:
|
|
193
|
+
# Normalize existing tool_name
|
|
194
|
+
data["tool_name"] = self.normalize_tool_name(data["tool_name"])
|
|
195
|
+
|
|
196
|
+
# 4. Normalize parameters/args → tool_input
|
|
197
|
+
if "tool_input" not in data:
|
|
198
|
+
if "parameters" in data:
|
|
199
|
+
data["tool_input"] = data["parameters"]
|
|
200
|
+
elif "args" in data:
|
|
201
|
+
data["tool_input"] = data["args"]
|
|
202
|
+
|
|
203
|
+
return data
|
|
204
|
+
|
|
138
205
|
def translate_to_hook_event(self, native_event: dict[str, Any]) -> HookEvent:
|
|
139
206
|
"""Convert Gemini CLI native event to unified HookEvent.
|
|
140
207
|
|
|
@@ -202,6 +269,10 @@ class GeminiAdapter(BaseAdapter):
|
|
|
202
269
|
else:
|
|
203
270
|
metadata = {}
|
|
204
271
|
|
|
272
|
+
# Normalize event data for CLI-agnostic processing
|
|
273
|
+
# This allows downstream code to use consistent field names
|
|
274
|
+
normalized_data = self._normalize_event_data(input_data)
|
|
275
|
+
|
|
205
276
|
return HookEvent(
|
|
206
277
|
event_type=event_type,
|
|
207
278
|
session_id=session_id,
|
|
@@ -209,7 +280,7 @@ class GeminiAdapter(BaseAdapter):
|
|
|
209
280
|
timestamp=timestamp,
|
|
210
281
|
machine_id=machine_id,
|
|
211
282
|
cwd=input_data.get("cwd"),
|
|
212
|
-
data=
|
|
283
|
+
data=normalized_data,
|
|
213
284
|
metadata=metadata,
|
|
214
285
|
)
|
|
215
286
|
|
|
@@ -254,46 +325,77 @@ class GeminiAdapter(BaseAdapter):
|
|
|
254
325
|
if response.context:
|
|
255
326
|
hook_specific["additionalContext"] = response.context
|
|
256
327
|
|
|
257
|
-
# Add session/terminal context for
|
|
258
|
-
|
|
328
|
+
# Add session/terminal context for hooks that support additionalContext
|
|
329
|
+
# Parity with Claude Code: inject on SessionStart, BeforeAgent, BeforeTool, AfterTool
|
|
330
|
+
hooks_with_context = {"SessionStart", "BeforeAgent", "BeforeTool", "AfterTool"}
|
|
331
|
+
if hook_type in hooks_with_context and response.metadata:
|
|
259
332
|
session_id = response.metadata.get("session_id")
|
|
333
|
+
session_ref = response.metadata.get("session_ref")
|
|
334
|
+
external_id = response.metadata.get("external_id")
|
|
335
|
+
is_first_hook = response.metadata.get("_first_hook_for_session", False)
|
|
336
|
+
|
|
260
337
|
if session_id:
|
|
261
338
|
hook_event_name = self.HOOK_EVENT_NAME_MAP.get(hook_type, "Unknown")
|
|
262
|
-
|
|
263
|
-
if
|
|
264
|
-
|
|
265
|
-
|
|
339
|
+
|
|
340
|
+
if is_first_hook:
|
|
341
|
+
# First hook: inject full metadata (~60-100 tokens)
|
|
342
|
+
context_lines = []
|
|
343
|
+
if session_ref:
|
|
344
|
+
context_lines.append(f"Gobby Session ID: {session_ref} (or {session_id})")
|
|
345
|
+
else:
|
|
346
|
+
context_lines.append(f"Gobby Session ID: {session_id}")
|
|
347
|
+
if external_id:
|
|
348
|
+
context_lines.append(
|
|
349
|
+
f"CLI-Specific Session ID (external_id): {external_id}"
|
|
350
|
+
)
|
|
351
|
+
if response.metadata.get("parent_session_id"):
|
|
352
|
+
context_lines.append(
|
|
353
|
+
f"parent_session_id: {response.metadata['parent_session_id']}"
|
|
354
|
+
)
|
|
355
|
+
if response.metadata.get("machine_id"):
|
|
356
|
+
context_lines.append(f"machine_id: {response.metadata['machine_id']}")
|
|
357
|
+
if response.metadata.get("project_id"):
|
|
358
|
+
context_lines.append(f"project_id: {response.metadata['project_id']}")
|
|
359
|
+
# Add terminal context (non-null values only)
|
|
360
|
+
if response.metadata.get("terminal_term_program"):
|
|
361
|
+
context_lines.append(
|
|
362
|
+
f"terminal: {response.metadata['terminal_term_program']}"
|
|
363
|
+
)
|
|
364
|
+
if response.metadata.get("terminal_tty"):
|
|
365
|
+
context_lines.append(f"tty: {response.metadata['terminal_tty']}")
|
|
366
|
+
if response.metadata.get("terminal_parent_pid"):
|
|
367
|
+
context_lines.append(
|
|
368
|
+
f"parent_pid: {response.metadata['terminal_parent_pid']}"
|
|
369
|
+
)
|
|
370
|
+
# Add terminal-specific session IDs
|
|
371
|
+
for key in [
|
|
372
|
+
"terminal_iterm_session_id",
|
|
373
|
+
"terminal_term_session_id",
|
|
374
|
+
"terminal_kitty_window_id",
|
|
375
|
+
"terminal_tmux_pane",
|
|
376
|
+
"terminal_vscode_terminal_id",
|
|
377
|
+
"terminal_alacritty_socket",
|
|
378
|
+
]:
|
|
379
|
+
if response.metadata.get(key):
|
|
380
|
+
friendly_name = key.replace("terminal_", "").replace("_", " ")
|
|
381
|
+
context_lines.append(f"{friendly_name}: {response.metadata[key]}")
|
|
382
|
+
|
|
383
|
+
hook_specific["hookEventName"] = hook_event_name
|
|
384
|
+
# Append to existing additionalContext if present
|
|
385
|
+
existing = hook_specific.get("additionalContext", "")
|
|
386
|
+
new_context = "\n".join(context_lines)
|
|
387
|
+
hook_specific["additionalContext"] = (
|
|
388
|
+
f"{existing}\n{new_context}" if existing else new_context
|
|
266
389
|
)
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if response.metadata.get("terminal_parent_pid"):
|
|
277
|
-
context_lines.append(f"parent_pid: {response.metadata['terminal_parent_pid']}")
|
|
278
|
-
# Add terminal-specific session IDs
|
|
279
|
-
for key in [
|
|
280
|
-
"terminal_iterm_session_id",
|
|
281
|
-
"terminal_term_session_id",
|
|
282
|
-
"terminal_kitty_window_id",
|
|
283
|
-
"terminal_tmux_pane",
|
|
284
|
-
"terminal_vscode_terminal_id",
|
|
285
|
-
"terminal_alacritty_socket",
|
|
286
|
-
]:
|
|
287
|
-
if response.metadata.get(key):
|
|
288
|
-
friendly_name = key.replace("terminal_", "").replace("_", " ")
|
|
289
|
-
context_lines.append(f"{friendly_name}: {response.metadata[key]}")
|
|
290
|
-
hook_specific["hookEventName"] = hook_event_name
|
|
291
|
-
# Append to existing additionalContext if present
|
|
292
|
-
existing = hook_specific.get("additionalContext", "")
|
|
293
|
-
new_context = "\n".join(context_lines)
|
|
294
|
-
hook_specific["additionalContext"] = (
|
|
295
|
-
f"{existing}\n{new_context}" if existing else new_context
|
|
296
|
-
)
|
|
390
|
+
else:
|
|
391
|
+
# Subsequent hooks: inject minimal session ref only (~8 tokens)
|
|
392
|
+
if session_ref:
|
|
393
|
+
hook_specific["hookEventName"] = hook_event_name
|
|
394
|
+
existing = hook_specific.get("additionalContext", "")
|
|
395
|
+
minimal_context = f"Gobby Session ID: {session_ref}"
|
|
396
|
+
hook_specific["additionalContext"] = (
|
|
397
|
+
f"{existing}\n{minimal_context}" if existing else minimal_context
|
|
398
|
+
)
|
|
297
399
|
|
|
298
400
|
# Handle BeforeModel-specific output (llm_request modification)
|
|
299
401
|
if hook_type == "BeforeModel" and response.modify_args:
|
|
@@ -197,6 +197,13 @@ class WorktreeIsolationHandler(IsolationHandler):
|
|
|
197
197
|
task_id=config.task_id,
|
|
198
198
|
)
|
|
199
199
|
|
|
200
|
+
# Copy CLI hooks to worktree so hooks fire correctly
|
|
201
|
+
await self._copy_cli_hooks(
|
|
202
|
+
main_repo_path=self._git_manager.repo_path,
|
|
203
|
+
worktree_path=worktree_path,
|
|
204
|
+
provider=config.provider,
|
|
205
|
+
)
|
|
206
|
+
|
|
200
207
|
return IsolationContext(
|
|
201
208
|
cwd=worktree.worktree_path,
|
|
202
209
|
branch_name=worktree.branch_name,
|
|
@@ -235,6 +242,64 @@ Commit your changes to the worktree branch when done.
|
|
|
235
242
|
worktree_dir = tempfile.gettempdir()
|
|
236
243
|
return f"{worktree_dir}/gobby-worktrees/{project_name}/{safe_branch}"
|
|
237
244
|
|
|
245
|
+
async def _copy_cli_hooks(
|
|
246
|
+
self,
|
|
247
|
+
main_repo_path: str,
|
|
248
|
+
worktree_path: str,
|
|
249
|
+
provider: str,
|
|
250
|
+
) -> None:
|
|
251
|
+
"""
|
|
252
|
+
Copy CLI-specific hooks to the worktree.
|
|
253
|
+
|
|
254
|
+
Without these hooks, the spawned agent won't trigger SessionStart
|
|
255
|
+
and other lifecycle hooks, breaking Gobby integration.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
main_repo_path: Path to the main repository
|
|
259
|
+
worktree_path: Path to the newly created worktree
|
|
260
|
+
provider: CLI provider (gemini, claude, codex)
|
|
261
|
+
"""
|
|
262
|
+
import asyncio
|
|
263
|
+
import logging
|
|
264
|
+
import shutil
|
|
265
|
+
from pathlib import Path
|
|
266
|
+
|
|
267
|
+
logger = logging.getLogger(__name__)
|
|
268
|
+
|
|
269
|
+
# Map provider to CLI hook directory
|
|
270
|
+
cli_dirs = {
|
|
271
|
+
"gemini": ".gemini",
|
|
272
|
+
"claude": ".claude",
|
|
273
|
+
"codex": ".codex",
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
cli_dir = cli_dirs.get(provider)
|
|
277
|
+
if not cli_dir:
|
|
278
|
+
logger.debug(f"No CLI hooks directory defined for provider: {provider}")
|
|
279
|
+
return
|
|
280
|
+
|
|
281
|
+
src_path = Path(main_repo_path) / cli_dir
|
|
282
|
+
dst_path = Path(worktree_path) / cli_dir
|
|
283
|
+
|
|
284
|
+
if not src_path.exists():
|
|
285
|
+
logger.debug(f"CLI hooks directory not found in main repo: {src_path}")
|
|
286
|
+
return
|
|
287
|
+
|
|
288
|
+
try:
|
|
289
|
+
# Copy entire CLI hooks directory (non-blocking)
|
|
290
|
+
await asyncio.to_thread(shutil.copytree, src_path, dst_path, dirs_exist_ok=True)
|
|
291
|
+
logger.info(f"Copied CLI hooks from {src_path} to {dst_path}")
|
|
292
|
+
except shutil.Error:
|
|
293
|
+
logger.warning(
|
|
294
|
+
f"Failed to copy CLI hooks: provider={provider}, src={src_path}, dst={dst_path}",
|
|
295
|
+
exc_info=True,
|
|
296
|
+
)
|
|
297
|
+
except OSError:
|
|
298
|
+
logger.warning(
|
|
299
|
+
f"Filesystem error copying CLI hooks: provider={provider}, src={src_path}, dst={dst_path}",
|
|
300
|
+
exc_info=True,
|
|
301
|
+
)
|
|
302
|
+
|
|
238
303
|
|
|
239
304
|
class CloneIsolationHandler(IsolationHandler):
|
|
240
305
|
"""
|
|
@@ -310,6 +375,13 @@ class CloneIsolationHandler(IsolationHandler):
|
|
|
310
375
|
task_id=config.task_id,
|
|
311
376
|
)
|
|
312
377
|
|
|
378
|
+
# Copy CLI hooks to clone so hooks fire correctly
|
|
379
|
+
await self._copy_cli_hooks(
|
|
380
|
+
source_repo_path=config.project_path,
|
|
381
|
+
clone_path=clone_path,
|
|
382
|
+
provider=config.provider,
|
|
383
|
+
)
|
|
384
|
+
|
|
313
385
|
return IsolationContext(
|
|
314
386
|
cwd=clone.clone_path,
|
|
315
387
|
branch_name=clone.branch_name,
|
|
@@ -348,6 +420,64 @@ Push your changes when ready to share with the original.
|
|
|
348
420
|
clone_dir = tempfile.gettempdir()
|
|
349
421
|
return f"{clone_dir}/gobby-clones/{project_name}/{safe_branch}"
|
|
350
422
|
|
|
423
|
+
async def _copy_cli_hooks(
|
|
424
|
+
self,
|
|
425
|
+
source_repo_path: str,
|
|
426
|
+
clone_path: str,
|
|
427
|
+
provider: str,
|
|
428
|
+
) -> None:
|
|
429
|
+
"""
|
|
430
|
+
Copy CLI-specific hooks to the clone.
|
|
431
|
+
|
|
432
|
+
Without these hooks, the spawned agent won't trigger SessionStart
|
|
433
|
+
and other lifecycle hooks, breaking Gobby integration.
|
|
434
|
+
|
|
435
|
+
Args:
|
|
436
|
+
source_repo_path: Path to the source repository
|
|
437
|
+
clone_path: Path to the newly created clone
|
|
438
|
+
provider: CLI provider (gemini, claude, codex)
|
|
439
|
+
"""
|
|
440
|
+
import asyncio
|
|
441
|
+
import logging
|
|
442
|
+
import shutil
|
|
443
|
+
from pathlib import Path
|
|
444
|
+
|
|
445
|
+
logger = logging.getLogger(__name__)
|
|
446
|
+
|
|
447
|
+
# Map provider to CLI hook directory
|
|
448
|
+
cli_dirs = {
|
|
449
|
+
"gemini": ".gemini",
|
|
450
|
+
"claude": ".claude",
|
|
451
|
+
"codex": ".codex",
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
cli_dir = cli_dirs.get(provider)
|
|
455
|
+
if not cli_dir:
|
|
456
|
+
logger.debug(f"No CLI hooks directory defined for provider: {provider}")
|
|
457
|
+
return
|
|
458
|
+
|
|
459
|
+
src_path = Path(source_repo_path) / cli_dir
|
|
460
|
+
dst_path = Path(clone_path) / cli_dir
|
|
461
|
+
|
|
462
|
+
if not src_path.exists():
|
|
463
|
+
logger.debug(f"CLI hooks directory not found in source repo: {src_path}")
|
|
464
|
+
return
|
|
465
|
+
|
|
466
|
+
try:
|
|
467
|
+
# Copy entire CLI hooks directory (non-blocking)
|
|
468
|
+
await asyncio.to_thread(shutil.copytree, src_path, dst_path, dirs_exist_ok=True)
|
|
469
|
+
logger.info(f"Copied CLI hooks from {src_path} to {dst_path}")
|
|
470
|
+
except shutil.Error:
|
|
471
|
+
logger.warning(
|
|
472
|
+
f"Failed to copy CLI hooks: provider={provider}, src={src_path}, dst={dst_path}",
|
|
473
|
+
exc_info=True,
|
|
474
|
+
)
|
|
475
|
+
except OSError:
|
|
476
|
+
logger.warning(
|
|
477
|
+
f"Filesystem error copying CLI hooks: provider={provider}, src={src_path}, dst={dst_path}",
|
|
478
|
+
exc_info=True,
|
|
479
|
+
)
|
|
480
|
+
|
|
351
481
|
|
|
352
482
|
def get_isolation_handler(
|
|
353
483
|
mode: Literal["current", "worktree", "clone"],
|
|
@@ -137,6 +137,17 @@ class RunningAgentRegistry:
|
|
|
137
137
|
with self._event_callbacks_lock:
|
|
138
138
|
self._event_callbacks.append(callback)
|
|
139
139
|
|
|
140
|
+
def emit_event(self, event_type: str, run_id: str, data: dict[str, Any]) -> None:
|
|
141
|
+
"""
|
|
142
|
+
Emit a custom event to all registered callbacks.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
event_type: Type of event (e.g., terminal_output)
|
|
146
|
+
run_id: Agent run ID
|
|
147
|
+
data: Additional event data
|
|
148
|
+
"""
|
|
149
|
+
self._emit_event(event_type, run_id, data)
|
|
150
|
+
|
|
140
151
|
def _emit_event(self, event_type: str, run_id: str, data: dict[str, Any]) -> None:
|
|
141
152
|
"""
|
|
142
153
|
Emit an event to all registered callbacks.
|