gobby 0.2.8__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.8 → gobby-0.2.9}/PKG-INFO +1 -1
- {gobby-0.2.8 → gobby-0.2.9}/pyproject.toml +1 -1
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/__init__.py +1 -1
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/adapters/claude_code.py +3 -26
- gobby-0.2.9/src/gobby/app_context.py +59 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/utils.py +5 -17
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/config/features.py +0 -20
- {gobby-0.2.8 → 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.8 → gobby-0.2.9}/src/gobby/hooks/hook_manager.py +2 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/llm/claude.py +377 -42
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/importer.py +4 -41
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/manager.py +13 -3
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/registries.py +14 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/services/recommendation.py +2 -28
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/artifacts.py +3 -3
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/task_readiness.py +27 -4
- 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.9/src/gobby/memory/components/ingestion.py +98 -0
- gobby-0.2.9/src/gobby/memory/components/search.py +108 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/memory/manager.py +16 -25
- gobby-0.2.9/src/gobby/paths.py +51 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/prompts/loader.py +1 -35
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/runner.py +23 -10
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/servers/http.py +186 -149
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/servers/routes/admin.py +12 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/servers/routes/mcp/endpoints/execution.py +15 -7
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/servers/routes/mcp/endpoints/registry.py +8 -8
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/sessions/analyzer.py +2 -2
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/skills/parser.py +23 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/skills/sync.py +5 -4
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/artifacts.py +19 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/migrations.py +25 -2
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/skills.py +47 -7
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/tasks/external_validator.py +4 -17
- {gobby-0.2.8 → 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.8 → gobby-0.2.9}/src/gobby/tools/summarizer.py +18 -51
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/utils/status.py +13 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/actions.py +5 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/context_actions.py +21 -24
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/enforcement/__init__.py +11 -1
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/enforcement/blocking.py +96 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/enforcement/handlers.py +35 -1
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/engine.py +6 -3
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/lifecycle_evaluator.py +2 -1
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby.egg-info/PKG-INFO +1 -1
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby.egg-info/SOURCES.txt +18 -2
- {gobby-0.2.8 → gobby-0.2.9}/tests/test_runner.py +2 -1
- gobby-0.2.8/src/gobby/hooks/event_handlers.py +0 -1008
- gobby-0.2.8/src/gobby/mcp_proxy/tools/workflows.py +0 -1023
- {gobby-0.2.8 → gobby-0.2.9}/LICENSE.md +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/README.md +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/setup.cfg +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/adapters/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/adapters/base.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/adapters/codex_impl/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/adapters/codex_impl/adapter.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/adapters/codex_impl/client.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/adapters/codex_impl/protocol.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/adapters/codex_impl/types.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/adapters/gemini.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/codex_session.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/constants.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/context.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/definitions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/gemini_session.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/isolation.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/registry.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/runner.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/sandbox.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/session.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/spawn.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/spawn_executor.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/spawners/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/spawners/base.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/spawners/command_builder.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/spawners/cross_platform.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/spawners/embedded.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/spawners/headless.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/spawners/linux.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/spawners/macos.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/spawners/prompt_manager.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/spawners/windows.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/agents/tty_config.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/autonomous/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/autonomous/progress_tracker.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/autonomous/stop_registry.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/autonomous/stuck_detector.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/__main__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/agents.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/artifacts.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/clones.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/conductor.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/daemon.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/extensions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/github.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/init.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/install.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/installers/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/installers/antigravity.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/installers/claude.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/installers/codex.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/installers/gemini.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/installers/git_hooks.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/installers/shared.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/linear.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/mcp.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/mcp_proxy.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/memory.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/merge.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/projects.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/sessions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/skills.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/tasks/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/tasks/_utils.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/tasks/ai.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/tasks/commits.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/tasks/crud.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/tasks/deps.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/tasks/labels.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/tasks/main.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/tasks/search.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/workflows.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/cli/worktrees.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/clones/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/clones/git.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/conductor/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/conductor/alerts.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/conductor/loop.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/conductor/monitors/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/conductor/monitors/agents.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/conductor/monitors/tasks.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/conductor/pricing.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/conductor/token_tracker.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/config/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/config/app.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/config/extensions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/config/llm_providers.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/config/logging.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/config/mcp.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/config/persistence.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/config/search.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/config/servers.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/config/sessions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/config/skills.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/hooks/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/hooks/artifact_capture.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/hooks/broadcaster.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/hooks/events.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/hooks/git.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/hooks/health_monitor.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/hooks/hook_types.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/hooks/plugins.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/hooks/session_coordinator.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/hooks/skill_manager.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/hooks/verification_runner.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/hooks/webhooks.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/install/claude/hooks/HOOK_SCHEMAS.md +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/install/claude/hooks/hook_dispatcher.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/install/claude/hooks/validate_settings.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/install/claude/hooks-template.json +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/install/codex/hooks/hook_dispatcher.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/install/gemini/hooks/hook_dispatcher.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/install/gemini/hooks-template.json +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/install/shared/plugins/code_guardian.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/install/shared/plugins/example_notify.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/integrations/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/integrations/github.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/integrations/linear.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/llm/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/llm/base.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/llm/claude_executor.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/llm/codex.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/llm/codex_executor.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/llm/executor.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/llm/factory.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/llm/gemini.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/llm/litellm.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/llm/litellm_executor.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/llm/resolver.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/llm/service.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/actions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/daemon_control.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/instructions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/lazy.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/metrics.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/models.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/schema_hash.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/semantic_search.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/server.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/services/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/services/fallback.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/services/server_mgmt.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/services/system.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/services/tool_filter.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/services/tool_proxy.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/stdio.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/agent_messaging.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/agents.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/clones.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/hub.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/internal.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/memory.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/merge.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/metrics.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/orchestration/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/orchestration/cleanup.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/orchestration/monitor.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/orchestration/orchestrate.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/orchestration/review.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/orchestration/utils.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/orchestration/wait.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/sessions/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/sessions/_commits.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/sessions/_crud.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/sessions/_factory.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/sessions/_handoff.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/sessions/_messages.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/skills/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/spawn_agent.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/task_dependencies.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/task_github.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/task_linear.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/task_orchestration.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/task_sync.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/task_validation.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_context.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_crud.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_expansion.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_factory.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_helpers.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_lifecycle.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_resolution.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_search.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/tasks/_session.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/tools/worktrees.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/transports/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/transports/base.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/transports/factory.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/transports/http.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/transports/stdio.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/mcp_proxy/transports/websocket.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/memory/backends/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/memory/backends/mem0.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/memory/backends/memu.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/memory/backends/null.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/memory/backends/openmemory.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/memory/backends/sqlite.py +0 -0
- {gobby-0.2.8/src/gobby/tools → gobby-0.2.9/src/gobby/memory/components}/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/memory/context.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/memory/extractor.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/memory/ingestion/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/memory/ingestion/multimodal.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/memory/protocol.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/memory/search/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/memory/search/coordinator.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/memory/search/text.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/memory/services/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/memory/services/crossref.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/memory/viz.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/prompts/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/prompts/models.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/py.typed +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/search/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/search/backends/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/search/backends/embedding.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/search/embeddings.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/search/models.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/search/protocol.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/search/tfidf.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/search/unified.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/servers/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/servers/models.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/servers/routes/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/servers/routes/dependencies.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/servers/routes/mcp/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/servers/routes/mcp/endpoints/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/servers/routes/mcp/endpoints/discovery.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/servers/routes/mcp/endpoints/server.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/servers/routes/mcp/hooks.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/servers/routes/mcp/plugins.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/servers/routes/mcp/tools.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/servers/routes/mcp/webhooks.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/servers/routes/sessions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/servers/websocket.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/sessions/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/sessions/lifecycle.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/sessions/manager.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/sessions/processor.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/sessions/summary.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/sessions/transcripts/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/sessions/transcripts/base.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/sessions/transcripts/claude.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/sessions/transcripts/codex.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/sessions/transcripts/gemini.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/skills/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/skills/loader.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/skills/manager.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/skills/search.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/skills/updater.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/skills/validator.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/agents.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/artifact_classifier.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/clones.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/compaction.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/database.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/inter_session_messages.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/mcp.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/memories.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/merge_resolutions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/projects.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/session_messages.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/session_tasks.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/sessions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/task_dependencies.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/tasks/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/tasks/_aggregates.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/tasks/_crud.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/tasks/_id.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/tasks/_lifecycle.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/tasks/_manager.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/tasks/_models.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/tasks/_ordering.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/tasks/_path_cache.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/tasks/_queries.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/tasks/_search.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/workflow_audit.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/storage/worktrees.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/sync/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/sync/github.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/sync/linear.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/sync/memories.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/sync/tasks.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/tasks/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/tasks/build_verification.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/tasks/commits.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/tasks/enhanced_validator.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/tasks/escalation.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/tasks/issue_extraction.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/tasks/tree_builder.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/tasks/validation_history.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/tasks/validation_models.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/utils/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/utils/daemon_client.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/utils/git.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/utils/id.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/utils/json_helpers.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/utils/logging.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/utils/machine_id.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/utils/metrics.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/utils/project_context.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/utils/project_init.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/utils/validation.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/utils/version.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/approval_flow.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/artifact_actions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/audit_helpers.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/autonomous_actions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/definitions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/detection_helpers.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/enforcement/commit_policy.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/enforcement/task_policy.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/evaluator.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/git_utils.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/hooks.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/llm_actions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/loader.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/mcp_actions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/memory_actions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/premature_stop.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/safe_evaluator.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/session_actions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/state_actions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/state_manager.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/stop_signal_actions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/summary_actions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/task_actions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/task_sync_actions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/templates.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/todo_actions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/webhook.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/webhook_actions.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/workflows/webhook_executor.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/worktrees/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/worktrees/git.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/worktrees/merge/__init__.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/worktrees/merge/conflict_parser.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby/worktrees/merge/resolver.py +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby.egg-info/dependency_links.txt +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby.egg-info/entry_points.txt +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby.egg-info/requires.txt +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/src/gobby.egg-info/top_level.txt +0 -0
- {gobby-0.2.8 → gobby-0.2.9}/tests/test_import_pathing_trap.py +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.
|
|
@@ -310,14 +307,6 @@ class ClaudeCodeAdapter(BaseAdapter):
|
|
|
310
307
|
) -> dict[str, Any]:
|
|
311
308
|
"""Main entry point for HTTP endpoint.
|
|
312
309
|
|
|
313
|
-
Strangler fig pattern:
|
|
314
|
-
- Phase 2A-2B: Delegates to existing execute() — validates translation only
|
|
315
|
-
- Phase 2C+: Calls new handle() with HookEvent
|
|
316
|
-
|
|
317
|
-
Note: This method is synchronous for Phase 2A-2B compatibility with
|
|
318
|
-
the existing execute() method. In Phase 2C+, it will become async
|
|
319
|
-
when handle() is implemented as async.
|
|
320
|
-
|
|
321
310
|
Args:
|
|
322
311
|
native_event: Raw payload from Claude Code's hook_dispatcher.py
|
|
323
312
|
hook_manager: HookManager instance for processing.
|
|
@@ -325,22 +314,10 @@ class ClaudeCodeAdapter(BaseAdapter):
|
|
|
325
314
|
Returns:
|
|
326
315
|
Response dict in Claude Code's expected format.
|
|
327
316
|
"""
|
|
328
|
-
#
|
|
317
|
+
# Translate to HookEvent
|
|
329
318
|
hook_event = self.translate_to_hook_event(native_event)
|
|
330
319
|
|
|
331
|
-
#
|
|
332
|
-
# Legacy execute() path removed as HookManager.execute is deprecated/removed.
|
|
320
|
+
# Use HookEvent-based handler
|
|
333
321
|
hook_type = native_event.get("hook_type", "")
|
|
334
322
|
hook_response = hook_manager.handle(hook_event)
|
|
335
323
|
return self.translate_from_hook_response(hook_response, hook_type=hook_type)
|
|
336
|
-
|
|
337
|
-
def set_legacy_mode(self, use_legacy: bool) -> None:
|
|
338
|
-
"""Toggle between legacy and new code paths.
|
|
339
|
-
|
|
340
|
-
This method is used during the strangler fig migration to switch
|
|
341
|
-
between delegating to execute() and calling handle() directly.
|
|
342
|
-
|
|
343
|
-
Args:
|
|
344
|
-
use_legacy: If True, use legacy execute() path. If False, use new handle() path.
|
|
345
|
-
"""
|
|
346
|
-
self._use_legacy = use_legacy
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Service container for dependency injection in Gobby daemon.
|
|
3
|
+
|
|
4
|
+
Holds references to singleton services to avoid prop-drilling in HTTPServer
|
|
5
|
+
and other components.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from gobby.config.app import DaemonConfig
|
|
12
|
+
from gobby.llm import LLMService
|
|
13
|
+
from gobby.memory.manager import MemoryManager
|
|
14
|
+
from gobby.storage.clones import LocalCloneManager
|
|
15
|
+
from gobby.storage.database import DatabaseProtocol
|
|
16
|
+
from gobby.storage.sessions import LocalSessionManager
|
|
17
|
+
from gobby.storage.tasks import LocalTaskManager
|
|
18
|
+
from gobby.storage.worktrees import LocalWorktreeManager
|
|
19
|
+
from gobby.sync.memories import MemorySyncManager
|
|
20
|
+
from gobby.sync.tasks import TaskSyncManager
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class ServiceContainer:
|
|
25
|
+
"""Container for daemon services."""
|
|
26
|
+
|
|
27
|
+
# Core Infrastructure
|
|
28
|
+
config: DaemonConfig
|
|
29
|
+
database: DatabaseProtocol
|
|
30
|
+
|
|
31
|
+
# Core Managers
|
|
32
|
+
session_manager: LocalSessionManager
|
|
33
|
+
task_manager: LocalTaskManager
|
|
34
|
+
|
|
35
|
+
# Sync Managers
|
|
36
|
+
task_sync_manager: TaskSyncManager | None = None
|
|
37
|
+
memory_sync_manager: MemorySyncManager | None = None
|
|
38
|
+
|
|
39
|
+
# Advanced Features
|
|
40
|
+
memory_manager: MemoryManager | None = None
|
|
41
|
+
llm_service: LLMService | None = None
|
|
42
|
+
|
|
43
|
+
# MCP & Agents
|
|
44
|
+
mcp_manager: Any | None = None # MCPClientManager
|
|
45
|
+
mcp_db_manager: Any | None = None # LocalMCPManager
|
|
46
|
+
metrics_manager: Any | None = None # ToolMetricsManager
|
|
47
|
+
agent_runner: Any | None = None # AgentRunner
|
|
48
|
+
message_processor: Any | None = None # SessionMessageProcessor
|
|
49
|
+
message_manager: Any | None = None # LocalSessionMessageManager
|
|
50
|
+
|
|
51
|
+
# Validation & Git
|
|
52
|
+
task_validator: Any | None = None # TaskValidator
|
|
53
|
+
worktree_storage: LocalWorktreeManager | None = None
|
|
54
|
+
clone_storage: LocalCloneManager | None = None
|
|
55
|
+
git_manager: Any | None = None # WorktreeGitManager
|
|
56
|
+
|
|
57
|
+
# Context
|
|
58
|
+
project_id: str | None = None
|
|
59
|
+
websocket_server: Any | None = None
|
|
@@ -394,23 +394,10 @@ def get_install_dir() -> Path:
|
|
|
394
394
|
Returns:
|
|
395
395
|
Path to the install directory
|
|
396
396
|
"""
|
|
397
|
-
|
|
397
|
+
# Import from centralized paths module to avoid duplication
|
|
398
|
+
from gobby.paths import get_install_dir as _get_install_dir
|
|
398
399
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
# Try to find source directory (project root)
|
|
402
|
-
current = Path(gobby.__file__).resolve()
|
|
403
|
-
source_install_dir = None
|
|
404
|
-
|
|
405
|
-
for parent in current.parents:
|
|
406
|
-
potential_source = parent / "src" / "gobby" / "install"
|
|
407
|
-
if potential_source.exists():
|
|
408
|
-
source_install_dir = potential_source
|
|
409
|
-
break
|
|
410
|
-
|
|
411
|
-
if source_install_dir and source_install_dir.exists():
|
|
412
|
-
return source_install_dir
|
|
413
|
-
return package_install_dir
|
|
400
|
+
return _get_install_dir()
|
|
414
401
|
|
|
415
402
|
|
|
416
403
|
def _is_process_alive(pid: int) -> bool:
|
|
@@ -472,7 +459,8 @@ def stop_daemon(quiet: bool = False) -> bool:
|
|
|
472
459
|
click.echo(f"Sent shutdown signal to Gobby daemon (PID {pid})")
|
|
473
460
|
|
|
474
461
|
# Wait for graceful shutdown
|
|
475
|
-
|
|
462
|
+
# Match daemon's uvicorn timeout_graceful_shutdown (15s) + buffer
|
|
463
|
+
max_wait = 20
|
|
476
464
|
for _ in range(max_wait * 10):
|
|
477
465
|
time.sleep(0.1)
|
|
478
466
|
if not _is_process_alive(pid):
|
|
@@ -23,7 +23,6 @@ __all__ = [
|
|
|
23
23
|
"HookStageConfig",
|
|
24
24
|
"HooksConfig",
|
|
25
25
|
"TaskDescriptionConfig",
|
|
26
|
-
"DEFAULT_IMPORT_MCP_SERVER_PROMPT",
|
|
27
26
|
]
|
|
28
27
|
|
|
29
28
|
|
|
@@ -139,25 +138,6 @@ class RecommendToolsConfig(BaseModel):
|
|
|
139
138
|
)
|
|
140
139
|
|
|
141
140
|
|
|
142
|
-
DEFAULT_IMPORT_MCP_SERVER_PROMPT = """You are an MCP server configuration extractor. Given documentation for an MCP server, extract the configuration needed to connect to it.
|
|
143
|
-
|
|
144
|
-
Return ONLY a valid JSON object (no markdown, no code blocks) with these fields:
|
|
145
|
-
- name: Server name (lowercase, no spaces, use hyphens)
|
|
146
|
-
- transport: "http", "stdio", or "websocket"
|
|
147
|
-
- url: Server URL (required for http/websocket transports)
|
|
148
|
-
- command: Command to run (required for stdio, e.g., "npx", "uv", "node")
|
|
149
|
-
- args: Array of command arguments (for stdio)
|
|
150
|
-
- env: Object of environment variables needed (use placeholder "<YOUR_KEY_NAME>" for secrets)
|
|
151
|
-
- headers: Object of HTTP headers needed (use placeholder "<YOUR_KEY_NAME>" for secrets)
|
|
152
|
-
- instructions: How to obtain any required API keys or setup steps
|
|
153
|
-
|
|
154
|
-
Example stdio server:
|
|
155
|
-
{"name": "filesystem", "transport": "stdio", "command": "npx", "args": ["-y", "@anthropic-ai/filesystem-mcp"], "env": {}, "instructions": "No setup required"}
|
|
156
|
-
|
|
157
|
-
Example http server with API key:
|
|
158
|
-
{"name": "exa", "transport": "http", "url": "https://mcp.exa.ai/mcp", "headers": {"EXA_API_KEY": "<YOUR_EXA_API_KEY>"}, "instructions": "Get your API key from https://exa.ai/dashboard"}"""
|
|
159
|
-
|
|
160
|
-
|
|
161
141
|
class ImportMCPServerConfig(BaseModel):
|
|
162
142
|
"""MCP server import configuration."""
|
|
163
143
|
|
|
@@ -669,6 +669,10 @@ class WorkflowConfig(BaseModel):
|
|
|
669
669
|
default_factory=lambda: ["Edit", "Write", "Update", "NotebookEdit"],
|
|
670
670
|
description="Tools that require an active task when require_task_before_edit is enabled",
|
|
671
671
|
)
|
|
672
|
+
debug_echo_context: bool = Field(
|
|
673
|
+
default=False,
|
|
674
|
+
description="Debug: echo additionalContext to system_message for terminal visibility",
|
|
675
|
+
)
|
|
672
676
|
|
|
673
677
|
@field_validator("timeout")
|
|
674
678
|
@classmethod
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Event handlers module for hook event processing.
|
|
3
|
+
|
|
4
|
+
This module is extracted from hook_manager.py using Strangler Fig pattern.
|
|
5
|
+
It provides centralized event handler registration and dispatch.
|
|
6
|
+
|
|
7
|
+
Classes:
|
|
8
|
+
EventHandlers: Manages event handler registration and dispatch.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import logging
|
|
14
|
+
from collections.abc import Callable
|
|
15
|
+
from typing import TYPE_CHECKING, Any
|
|
16
|
+
|
|
17
|
+
from gobby.hooks.event_handlers._agent import AgentEventHandlerMixin
|
|
18
|
+
from gobby.hooks.event_handlers._misc import MiscEventHandlerMixin
|
|
19
|
+
from gobby.hooks.event_handlers._session import SessionEventHandlerMixin
|
|
20
|
+
from gobby.hooks.event_handlers._tool import EDIT_TOOLS, ToolEventHandlerMixin
|
|
21
|
+
from gobby.hooks.events import HookEvent, HookEventType, HookResponse
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from gobby.config.skills import SkillsConfig
|
|
25
|
+
from gobby.config.tasks import WorkflowConfig
|
|
26
|
+
from gobby.hooks.artifact_capture import ArtifactCaptureHook
|
|
27
|
+
from gobby.hooks.session_coordinator import SessionCoordinator
|
|
28
|
+
from gobby.hooks.skill_manager import HookSkillManager
|
|
29
|
+
from gobby.sessions.manager import SessionManager
|
|
30
|
+
from gobby.sessions.summary import SummaryFileGenerator
|
|
31
|
+
from gobby.storage.session_messages import LocalSessionMessageManager
|
|
32
|
+
from gobby.storage.session_tasks import SessionTaskManager
|
|
33
|
+
from gobby.storage.sessions import LocalSessionManager
|
|
34
|
+
from gobby.storage.tasks import LocalTaskManager
|
|
35
|
+
from gobby.workflows.hooks import WorkflowHookHandler
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class EventHandlers(
|
|
39
|
+
SessionEventHandlerMixin,
|
|
40
|
+
AgentEventHandlerMixin,
|
|
41
|
+
ToolEventHandlerMixin,
|
|
42
|
+
MiscEventHandlerMixin,
|
|
43
|
+
):
|
|
44
|
+
"""
|
|
45
|
+
Manages event handler registration and dispatch.
|
|
46
|
+
|
|
47
|
+
Provides handler methods for all HookEventType values and a registration
|
|
48
|
+
mechanism for looking up handlers by event type.
|
|
49
|
+
|
|
50
|
+
Extracted from HookManager to separate event handling concerns.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(
|
|
54
|
+
self,
|
|
55
|
+
session_manager: SessionManager | None = None,
|
|
56
|
+
workflow_handler: WorkflowHookHandler | None = None,
|
|
57
|
+
session_storage: LocalSessionManager | None = None,
|
|
58
|
+
session_task_manager: SessionTaskManager | None = None,
|
|
59
|
+
message_processor: Any | None = None,
|
|
60
|
+
summary_file_generator: SummaryFileGenerator | None = None,
|
|
61
|
+
task_manager: LocalTaskManager | None = None,
|
|
62
|
+
session_coordinator: SessionCoordinator | None = None,
|
|
63
|
+
message_manager: LocalSessionMessageManager | None = None,
|
|
64
|
+
skill_manager: HookSkillManager | None = None,
|
|
65
|
+
skills_config: SkillsConfig | None = None,
|
|
66
|
+
artifact_capture_hook: ArtifactCaptureHook | None = None,
|
|
67
|
+
workflow_config: WorkflowConfig | None = None,
|
|
68
|
+
get_machine_id: Callable[[], str] | None = None,
|
|
69
|
+
resolve_project_id: Callable[[str | None, str | None], str] | None = None,
|
|
70
|
+
logger: logging.Logger | None = None,
|
|
71
|
+
) -> None:
|
|
72
|
+
"""
|
|
73
|
+
Initialize EventHandlers.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
session_manager: SessionManager for session operations
|
|
77
|
+
workflow_handler: WorkflowHookHandler for lifecycle workflows
|
|
78
|
+
session_storage: LocalSessionManager for session storage
|
|
79
|
+
session_task_manager: SessionTaskManager for session-task links
|
|
80
|
+
message_processor: SessionMessageProcessor for message handling
|
|
81
|
+
summary_file_generator: SummaryFileGenerator for summaries
|
|
82
|
+
task_manager: LocalTaskManager for task operations
|
|
83
|
+
session_coordinator: SessionCoordinator for session tracking
|
|
84
|
+
message_manager: LocalSessionMessageManager for messages
|
|
85
|
+
skill_manager: HookSkillManager for skill discovery
|
|
86
|
+
skills_config: SkillsConfig for skill injection settings
|
|
87
|
+
artifact_capture_hook: ArtifactCaptureHook for capturing artifacts
|
|
88
|
+
workflow_config: WorkflowConfig for workflow settings (debug_echo_context)
|
|
89
|
+
get_machine_id: Function to get machine ID
|
|
90
|
+
resolve_project_id: Function to resolve project ID from cwd
|
|
91
|
+
logger: Optional logger instance
|
|
92
|
+
"""
|
|
93
|
+
self._session_manager = session_manager
|
|
94
|
+
self._workflow_handler = workflow_handler
|
|
95
|
+
self._session_storage = session_storage
|
|
96
|
+
self._session_task_manager = session_task_manager
|
|
97
|
+
self._message_processor = message_processor
|
|
98
|
+
self._summary_file_generator = summary_file_generator
|
|
99
|
+
self._task_manager = task_manager
|
|
100
|
+
self._session_coordinator = session_coordinator
|
|
101
|
+
self._message_manager = message_manager
|
|
102
|
+
self._skill_manager = skill_manager
|
|
103
|
+
self._skills_config = skills_config
|
|
104
|
+
self._artifact_capture_hook = artifact_capture_hook
|
|
105
|
+
self._workflow_config = workflow_config
|
|
106
|
+
self._get_machine_id = get_machine_id or (lambda: "unknown-machine")
|
|
107
|
+
self._resolve_project_id = resolve_project_id or (lambda p, c: p or "")
|
|
108
|
+
self.logger = logger or logging.getLogger(__name__)
|
|
109
|
+
|
|
110
|
+
# Build handler map
|
|
111
|
+
self._handler_map: dict[HookEventType, Callable[[HookEvent], HookResponse]] = {
|
|
112
|
+
HookEventType.SESSION_START: self.handle_session_start,
|
|
113
|
+
HookEventType.SESSION_END: self.handle_session_end,
|
|
114
|
+
HookEventType.BEFORE_AGENT: self.handle_before_agent,
|
|
115
|
+
HookEventType.AFTER_AGENT: self.handle_after_agent,
|
|
116
|
+
HookEventType.BEFORE_TOOL: self.handle_before_tool,
|
|
117
|
+
HookEventType.AFTER_TOOL: self.handle_after_tool,
|
|
118
|
+
HookEventType.PRE_COMPACT: self.handle_pre_compact,
|
|
119
|
+
HookEventType.SUBAGENT_START: self.handle_subagent_start,
|
|
120
|
+
HookEventType.SUBAGENT_STOP: self.handle_subagent_stop,
|
|
121
|
+
HookEventType.NOTIFICATION: self.handle_notification,
|
|
122
|
+
HookEventType.BEFORE_TOOL_SELECTION: self.handle_before_tool_selection,
|
|
123
|
+
HookEventType.BEFORE_MODEL: self.handle_before_model,
|
|
124
|
+
HookEventType.AFTER_MODEL: self.handle_after_model,
|
|
125
|
+
HookEventType.PERMISSION_REQUEST: self.handle_permission_request,
|
|
126
|
+
HookEventType.STOP: self.handle_stop,
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
def get_handler(
|
|
130
|
+
self, event_type: HookEventType | str
|
|
131
|
+
) -> Callable[[HookEvent], HookResponse] | None:
|
|
132
|
+
"""
|
|
133
|
+
Get handler for an event type.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
event_type: The event type to get handler for
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
Handler callable or None if not found
|
|
140
|
+
"""
|
|
141
|
+
if isinstance(event_type, str):
|
|
142
|
+
try:
|
|
143
|
+
event_type = HookEventType(event_type)
|
|
144
|
+
except ValueError:
|
|
145
|
+
return None
|
|
146
|
+
return self._handler_map.get(event_type)
|
|
147
|
+
|
|
148
|
+
def get_handler_map(self) -> dict[HookEventType, Callable[[HookEvent], HookResponse]]:
|
|
149
|
+
"""
|
|
150
|
+
Get a copy of the handler map.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Copy of handler map (modifications don't affect internal state)
|
|
154
|
+
"""
|
|
155
|
+
return dict(self._handler_map)
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from gobby.hooks.event_handlers._base import EventHandlersBase
|
|
4
|
+
from gobby.hooks.events import HookEvent, HookResponse, SessionSource
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AgentEventHandlerMixin(EventHandlersBase):
|
|
8
|
+
"""Mixin for handling agent-related events."""
|
|
9
|
+
|
|
10
|
+
def handle_before_agent(self, event: HookEvent) -> HookResponse:
|
|
11
|
+
"""Handle BEFORE_AGENT event (user prompt submit)."""
|
|
12
|
+
input_data = event.data
|
|
13
|
+
prompt = input_data.get("prompt", "")
|
|
14
|
+
transcript_path = input_data.get("transcript_path")
|
|
15
|
+
session_id = event.metadata.get("_platform_session_id")
|
|
16
|
+
|
|
17
|
+
context_parts = []
|
|
18
|
+
|
|
19
|
+
if session_id:
|
|
20
|
+
self.logger.debug(f"BEFORE_AGENT: session {session_id}, prompt_len={len(prompt)}")
|
|
21
|
+
|
|
22
|
+
# Update status to active (unless /clear or /exit)
|
|
23
|
+
prompt_lower = prompt.strip().lower()
|
|
24
|
+
if prompt_lower not in ("/clear", "/exit") and self._session_manager:
|
|
25
|
+
try:
|
|
26
|
+
self._session_manager.update_session_status(session_id, "active")
|
|
27
|
+
if self._session_storage:
|
|
28
|
+
self._session_storage.reset_transcript_processed(session_id)
|
|
29
|
+
except Exception as e:
|
|
30
|
+
self.logger.warning(f"Failed to update session status: {e}")
|
|
31
|
+
|
|
32
|
+
# Handle /clear command - lifecycle workflows handle handoff
|
|
33
|
+
if prompt_lower in ("/clear", "/exit") and transcript_path:
|
|
34
|
+
self.logger.debug(f"Detected {prompt_lower} - lifecycle workflows handle handoff")
|
|
35
|
+
|
|
36
|
+
# Execute lifecycle workflow triggers
|
|
37
|
+
if self._workflow_handler:
|
|
38
|
+
try:
|
|
39
|
+
wf_response = self._workflow_handler.handle_all_lifecycles(event)
|
|
40
|
+
if wf_response.context:
|
|
41
|
+
context_parts.append(wf_response.context)
|
|
42
|
+
if wf_response.decision != "allow":
|
|
43
|
+
return wf_response
|
|
44
|
+
except Exception as e:
|
|
45
|
+
self.logger.error(f"Failed to execute lifecycle workflows: {e}", exc_info=True)
|
|
46
|
+
|
|
47
|
+
return HookResponse(
|
|
48
|
+
decision="allow",
|
|
49
|
+
context="\n\n".join(context_parts) if context_parts else None,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def handle_after_agent(self, event: HookEvent) -> HookResponse:
|
|
53
|
+
"""Handle AFTER_AGENT event."""
|
|
54
|
+
session_id = event.metadata.get("_platform_session_id")
|
|
55
|
+
cli_source = event.source.value
|
|
56
|
+
|
|
57
|
+
context_parts = []
|
|
58
|
+
|
|
59
|
+
if session_id:
|
|
60
|
+
self.logger.debug(f"AFTER_AGENT: session {session_id}, cli={cli_source}")
|
|
61
|
+
if self._session_manager:
|
|
62
|
+
try:
|
|
63
|
+
self._session_manager.update_session_status(session_id, "paused")
|
|
64
|
+
except Exception as e:
|
|
65
|
+
self.logger.warning(f"Failed to update session status: {e}")
|
|
66
|
+
else:
|
|
67
|
+
self.logger.debug(f"AFTER_AGENT: cli={cli_source}")
|
|
68
|
+
|
|
69
|
+
# Execute lifecycle workflow triggers
|
|
70
|
+
if self._workflow_handler:
|
|
71
|
+
try:
|
|
72
|
+
wf_response = self._workflow_handler.handle_all_lifecycles(event)
|
|
73
|
+
if wf_response.context:
|
|
74
|
+
context_parts.append(wf_response.context)
|
|
75
|
+
if wf_response.decision != "allow":
|
|
76
|
+
return wf_response
|
|
77
|
+
except Exception as e:
|
|
78
|
+
self.logger.error(f"Failed to execute lifecycle workflows: {e}", exc_info=True)
|
|
79
|
+
|
|
80
|
+
return HookResponse(
|
|
81
|
+
decision="allow",
|
|
82
|
+
context="\n\n".join(context_parts) if context_parts else None,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def handle_stop(self, event: HookEvent) -> HookResponse:
|
|
86
|
+
"""Handle STOP event (Claude Code only)."""
|
|
87
|
+
session_id = event.metadata.get("_platform_session_id")
|
|
88
|
+
|
|
89
|
+
context_parts = []
|
|
90
|
+
|
|
91
|
+
if session_id:
|
|
92
|
+
self.logger.debug(f"STOP: session {session_id}")
|
|
93
|
+
if self._session_manager:
|
|
94
|
+
try:
|
|
95
|
+
self._session_manager.update_session_status(session_id, "paused")
|
|
96
|
+
except Exception as e:
|
|
97
|
+
self.logger.warning(f"Failed to update session status: {e}")
|
|
98
|
+
else:
|
|
99
|
+
self.logger.debug("STOP")
|
|
100
|
+
|
|
101
|
+
# Execute lifecycle workflow triggers
|
|
102
|
+
if self._workflow_handler:
|
|
103
|
+
try:
|
|
104
|
+
wf_response = self._workflow_handler.handle_all_lifecycles(event)
|
|
105
|
+
if wf_response.context:
|
|
106
|
+
context_parts.append(wf_response.context)
|
|
107
|
+
if wf_response.decision != "allow":
|
|
108
|
+
return wf_response
|
|
109
|
+
except Exception as e:
|
|
110
|
+
self.logger.error(f"Failed to execute lifecycle workflows: {e}", exc_info=True)
|
|
111
|
+
|
|
112
|
+
return HookResponse(
|
|
113
|
+
decision="allow",
|
|
114
|
+
context="\n\n".join(context_parts) if context_parts else None,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
def handle_pre_compact(self, event: HookEvent) -> HookResponse:
|
|
118
|
+
"""Handle PRE_COMPACT event.
|
|
119
|
+
|
|
120
|
+
Note: Gemini fires PreCompress constantly during normal operation,
|
|
121
|
+
unlike Claude which fires it only when approaching context limits.
|
|
122
|
+
We skip handoff logic and workflow execution for Gemini to avoid
|
|
123
|
+
excessive state changes and workflow interruptions.
|
|
124
|
+
"""
|
|
125
|
+
trigger = event.data.get("trigger", "auto")
|
|
126
|
+
session_id = event.metadata.get("_platform_session_id")
|
|
127
|
+
|
|
128
|
+
# Skip handoff logic for Gemini - it fires PreCompress too frequently
|
|
129
|
+
if event.source == SessionSource.GEMINI:
|
|
130
|
+
self.logger.debug(f"PRE_COMPACT ({trigger}): session {session_id} [Gemini - skipped]")
|
|
131
|
+
return HookResponse(decision="allow")
|
|
132
|
+
|
|
133
|
+
if session_id:
|
|
134
|
+
self.logger.debug(f"PRE_COMPACT ({trigger}): session {session_id}")
|
|
135
|
+
# Mark session as handoff_ready so it can be found as parent after compact
|
|
136
|
+
if self._session_manager:
|
|
137
|
+
self._session_manager.update_session_status(session_id, "handoff_ready")
|
|
138
|
+
else:
|
|
139
|
+
self.logger.debug(f"PRE_COMPACT ({trigger})")
|
|
140
|
+
|
|
141
|
+
# Execute lifecycle workflows
|
|
142
|
+
if self._workflow_handler:
|
|
143
|
+
try:
|
|
144
|
+
return self._workflow_handler.handle_all_lifecycles(event)
|
|
145
|
+
except Exception as e:
|
|
146
|
+
self.logger.error(f"Failed to execute lifecycle workflows: {e}", exc_info=True)
|
|
147
|
+
|
|
148
|
+
return HookResponse(decision="allow")
|
|
149
|
+
|
|
150
|
+
def handle_subagent_start(self, event: HookEvent) -> HookResponse:
|
|
151
|
+
"""Handle SUBAGENT_START event."""
|
|
152
|
+
input_data = event.data
|
|
153
|
+
session_id = event.metadata.get("_platform_session_id")
|
|
154
|
+
agent_id = input_data.get("agent_id")
|
|
155
|
+
subagent_id = input_data.get("subagent_id")
|
|
156
|
+
|
|
157
|
+
log_msg = f"SUBAGENT_START: session {session_id}" if session_id else "SUBAGENT_START"
|
|
158
|
+
if agent_id:
|
|
159
|
+
log_msg += f", agent_id={agent_id}"
|
|
160
|
+
if subagent_id:
|
|
161
|
+
log_msg += f", subagent_id={subagent_id}"
|
|
162
|
+
self.logger.debug(log_msg)
|
|
163
|
+
|
|
164
|
+
return HookResponse(decision="allow")
|
|
165
|
+
|
|
166
|
+
def handle_subagent_stop(self, event: HookEvent) -> HookResponse:
|
|
167
|
+
"""Handle SUBAGENT_STOP event."""
|
|
168
|
+
session_id = event.metadata.get("_platform_session_id")
|
|
169
|
+
|
|
170
|
+
if session_id:
|
|
171
|
+
self.logger.debug(f"SUBAGENT_STOP: session {session_id}")
|
|
172
|
+
else:
|
|
173
|
+
self.logger.debug("SUBAGENT_STOP")
|
|
174
|
+
|
|
175
|
+
return HookResponse(decision="allow")
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
from gobby.hooks.events import HookEvent, HookEventType, HookResponse
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from gobby.config.skills import SkillsConfig
|
|
11
|
+
from gobby.config.tasks import WorkflowConfig
|
|
12
|
+
from gobby.hooks.artifact_capture import ArtifactCaptureHook
|
|
13
|
+
from gobby.hooks.session_coordinator import SessionCoordinator
|
|
14
|
+
from gobby.hooks.skill_manager import HookSkillManager
|
|
15
|
+
from gobby.sessions.manager import SessionManager
|
|
16
|
+
from gobby.sessions.summary import SummaryFileGenerator
|
|
17
|
+
from gobby.storage.session_messages import LocalSessionMessageManager
|
|
18
|
+
from gobby.storage.session_tasks import SessionTaskManager
|
|
19
|
+
from gobby.storage.sessions import LocalSessionManager
|
|
20
|
+
from gobby.storage.tasks import LocalTaskManager
|
|
21
|
+
from gobby.workflows.hooks import WorkflowHookHandler
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class EventHandlersBase:
|
|
25
|
+
"""Base class for EventHandlers mixins with type hints for shared state."""
|
|
26
|
+
|
|
27
|
+
_session_manager: SessionManager | None
|
|
28
|
+
_workflow_handler: WorkflowHookHandler | None
|
|
29
|
+
_workflow_config: WorkflowConfig | None
|
|
30
|
+
_session_storage: LocalSessionManager | None
|
|
31
|
+
_session_task_manager: SessionTaskManager | None
|
|
32
|
+
_message_processor: Any | None
|
|
33
|
+
_summary_file_generator: SummaryFileGenerator | None
|
|
34
|
+
_task_manager: LocalTaskManager | None
|
|
35
|
+
_session_coordinator: SessionCoordinator | None
|
|
36
|
+
_message_manager: LocalSessionMessageManager | None
|
|
37
|
+
_skill_manager: HookSkillManager | None
|
|
38
|
+
_skills_config: SkillsConfig | None
|
|
39
|
+
_artifact_capture_hook: ArtifactCaptureHook | None
|
|
40
|
+
_get_machine_id: Callable[[], str]
|
|
41
|
+
_resolve_project_id: Callable[[str | None, str | None], str]
|
|
42
|
+
logger: logging.Logger
|
|
43
|
+
_handler_map: dict[HookEventType, Callable[[HookEvent], HookResponse]]
|
|
44
|
+
|
|
45
|
+
def _auto_activate_workflow(
|
|
46
|
+
self, workflow_name: str, session_id: str, project_path: str | None
|
|
47
|
+
) -> None:
|
|
48
|
+
"""Shared method for auto-activating workflows."""
|
|
49
|
+
if not self._workflow_handler:
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
result = self._workflow_handler.activate_workflow(
|
|
54
|
+
workflow_name=workflow_name,
|
|
55
|
+
session_id=session_id,
|
|
56
|
+
project_path=project_path,
|
|
57
|
+
)
|
|
58
|
+
if result.get("success"):
|
|
59
|
+
self.logger.info(
|
|
60
|
+
"Auto-activated workflow for session",
|
|
61
|
+
extra={
|
|
62
|
+
"workflow_name": workflow_name,
|
|
63
|
+
"session_id": session_id,
|
|
64
|
+
"project_path": project_path,
|
|
65
|
+
},
|
|
66
|
+
)
|
|
67
|
+
else:
|
|
68
|
+
self.logger.warning(
|
|
69
|
+
"Failed to auto-activate workflow",
|
|
70
|
+
extra={
|
|
71
|
+
"workflow_name": workflow_name,
|
|
72
|
+
"session_id": session_id,
|
|
73
|
+
"project_path": project_path,
|
|
74
|
+
"error": result.get("error"),
|
|
75
|
+
},
|
|
76
|
+
)
|
|
77
|
+
except Exception as e:
|
|
78
|
+
self.logger.warning(
|
|
79
|
+
"Failed to auto-activate workflow",
|
|
80
|
+
extra={
|
|
81
|
+
"workflow_name": workflow_name,
|
|
82
|
+
"session_id": session_id,
|
|
83
|
+
"project_path": project_path,
|
|
84
|
+
"error": str(e),
|
|
85
|
+
},
|
|
86
|
+
exc_info=True,
|
|
87
|
+
)
|