gobby 0.2.8__py3-none-any.whl → 0.2.11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gobby/__init__.py +1 -1
- gobby/adapters/__init__.py +6 -0
- gobby/adapters/base.py +11 -2
- gobby/adapters/claude_code.py +5 -28
- gobby/adapters/codex_impl/adapter.py +38 -43
- gobby/adapters/copilot.py +324 -0
- gobby/adapters/cursor.py +373 -0
- gobby/adapters/gemini.py +2 -26
- gobby/adapters/windsurf.py +359 -0
- gobby/agents/definitions.py +162 -2
- gobby/agents/isolation.py +33 -1
- gobby/agents/pty_reader.py +192 -0
- gobby/agents/registry.py +10 -1
- gobby/agents/runner.py +24 -8
- gobby/agents/sandbox.py +8 -3
- gobby/agents/session.py +4 -0
- gobby/agents/spawn.py +9 -2
- gobby/agents/spawn_executor.py +49 -61
- gobby/agents/spawners/command_builder.py +4 -4
- gobby/app_context.py +64 -0
- gobby/cli/__init__.py +4 -0
- gobby/cli/install.py +259 -4
- gobby/cli/installers/__init__.py +12 -0
- gobby/cli/installers/copilot.py +242 -0
- gobby/cli/installers/cursor.py +244 -0
- gobby/cli/installers/shared.py +3 -0
- gobby/cli/installers/windsurf.py +242 -0
- gobby/cli/pipelines.py +639 -0
- gobby/cli/sessions.py +3 -1
- gobby/cli/skills.py +209 -0
- gobby/cli/tasks/crud.py +6 -5
- gobby/cli/tasks/search.py +1 -1
- gobby/cli/ui.py +116 -0
- gobby/cli/utils.py +5 -17
- gobby/cli/workflows.py +38 -17
- gobby/config/app.py +5 -0
- gobby/config/features.py +0 -20
- gobby/config/skills.py +23 -2
- gobby/config/tasks.py +4 -0
- gobby/hooks/broadcaster.py +9 -0
- gobby/hooks/event_handlers/__init__.py +155 -0
- gobby/hooks/event_handlers/_agent.py +175 -0
- gobby/hooks/event_handlers/_base.py +92 -0
- gobby/hooks/event_handlers/_misc.py +66 -0
- gobby/hooks/event_handlers/_session.py +487 -0
- gobby/hooks/event_handlers/_tool.py +196 -0
- gobby/hooks/events.py +48 -0
- gobby/hooks/hook_manager.py +27 -3
- gobby/install/copilot/hooks/hook_dispatcher.py +203 -0
- gobby/install/cursor/hooks/hook_dispatcher.py +203 -0
- gobby/install/gemini/hooks/hook_dispatcher.py +8 -0
- gobby/install/windsurf/hooks/hook_dispatcher.py +205 -0
- gobby/llm/__init__.py +14 -1
- gobby/llm/claude.py +594 -43
- gobby/llm/service.py +149 -0
- gobby/mcp_proxy/importer.py +4 -41
- gobby/mcp_proxy/instructions.py +9 -27
- gobby/mcp_proxy/manager.py +13 -3
- gobby/mcp_proxy/models.py +1 -0
- gobby/mcp_proxy/registries.py +66 -5
- gobby/mcp_proxy/server.py +6 -2
- gobby/mcp_proxy/services/recommendation.py +2 -28
- gobby/mcp_proxy/services/tool_filter.py +7 -0
- gobby/mcp_proxy/services/tool_proxy.py +19 -1
- gobby/mcp_proxy/stdio.py +37 -21
- gobby/mcp_proxy/tools/agents.py +7 -0
- gobby/mcp_proxy/tools/artifacts.py +3 -3
- gobby/mcp_proxy/tools/hub.py +30 -1
- gobby/mcp_proxy/tools/orchestration/cleanup.py +5 -5
- gobby/mcp_proxy/tools/orchestration/monitor.py +1 -1
- gobby/mcp_proxy/tools/orchestration/orchestrate.py +8 -3
- gobby/mcp_proxy/tools/orchestration/review.py +17 -4
- gobby/mcp_proxy/tools/orchestration/wait.py +7 -7
- gobby/mcp_proxy/tools/pipelines/__init__.py +254 -0
- gobby/mcp_proxy/tools/pipelines/_discovery.py +67 -0
- gobby/mcp_proxy/tools/pipelines/_execution.py +281 -0
- gobby/mcp_proxy/tools/sessions/_crud.py +4 -4
- gobby/mcp_proxy/tools/sessions/_handoff.py +1 -1
- gobby/mcp_proxy/tools/skills/__init__.py +184 -30
- gobby/mcp_proxy/tools/spawn_agent.py +229 -14
- gobby/mcp_proxy/tools/task_readiness.py +27 -4
- gobby/mcp_proxy/tools/tasks/_context.py +8 -0
- gobby/mcp_proxy/tools/tasks/_crud.py +27 -1
- gobby/mcp_proxy/tools/tasks/_helpers.py +1 -1
- gobby/mcp_proxy/tools/tasks/_lifecycle.py +125 -8
- gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +2 -1
- gobby/mcp_proxy/tools/tasks/_search.py +1 -1
- gobby/mcp_proxy/tools/workflows/__init__.py +273 -0
- gobby/mcp_proxy/tools/workflows/_artifacts.py +225 -0
- gobby/mcp_proxy/tools/workflows/_import.py +112 -0
- gobby/mcp_proxy/tools/workflows/_lifecycle.py +332 -0
- gobby/mcp_proxy/tools/workflows/_query.py +226 -0
- gobby/mcp_proxy/tools/workflows/_resolution.py +78 -0
- gobby/mcp_proxy/tools/workflows/_terminal.py +175 -0
- gobby/mcp_proxy/tools/worktrees.py +54 -15
- gobby/memory/components/__init__.py +0 -0
- gobby/memory/components/ingestion.py +98 -0
- gobby/memory/components/search.py +108 -0
- gobby/memory/context.py +5 -5
- gobby/memory/manager.py +16 -25
- gobby/paths.py +51 -0
- gobby/prompts/loader.py +1 -35
- gobby/runner.py +131 -16
- gobby/servers/http.py +193 -150
- gobby/servers/routes/__init__.py +2 -0
- gobby/servers/routes/admin.py +56 -0
- gobby/servers/routes/mcp/endpoints/execution.py +33 -32
- gobby/servers/routes/mcp/endpoints/registry.py +8 -8
- gobby/servers/routes/mcp/hooks.py +10 -1
- gobby/servers/routes/pipelines.py +227 -0
- gobby/servers/websocket.py +314 -1
- gobby/sessions/analyzer.py +89 -3
- gobby/sessions/manager.py +5 -5
- gobby/sessions/transcripts/__init__.py +3 -0
- gobby/sessions/transcripts/claude.py +5 -0
- gobby/sessions/transcripts/codex.py +5 -0
- gobby/sessions/transcripts/gemini.py +5 -0
- gobby/skills/hubs/__init__.py +25 -0
- gobby/skills/hubs/base.py +234 -0
- gobby/skills/hubs/claude_plugins.py +328 -0
- gobby/skills/hubs/clawdhub.py +289 -0
- gobby/skills/hubs/github_collection.py +465 -0
- gobby/skills/hubs/manager.py +263 -0
- gobby/skills/hubs/skillhub.py +342 -0
- gobby/skills/parser.py +23 -0
- gobby/skills/sync.py +5 -4
- gobby/storage/artifacts.py +19 -0
- gobby/storage/memories.py +4 -4
- gobby/storage/migrations.py +118 -3
- gobby/storage/pipelines.py +367 -0
- gobby/storage/sessions.py +23 -4
- gobby/storage/skills.py +48 -8
- gobby/storage/tasks/_aggregates.py +2 -2
- gobby/storage/tasks/_lifecycle.py +4 -4
- gobby/storage/tasks/_models.py +7 -1
- gobby/storage/tasks/_queries.py +3 -3
- gobby/sync/memories.py +4 -3
- gobby/tasks/commits.py +48 -17
- gobby/tasks/external_validator.py +4 -17
- gobby/tasks/validation.py +13 -87
- gobby/tools/summarizer.py +18 -51
- gobby/utils/status.py +13 -0
- gobby/workflows/actions.py +80 -0
- gobby/workflows/context_actions.py +265 -27
- gobby/workflows/definitions.py +119 -1
- gobby/workflows/detection_helpers.py +23 -11
- gobby/workflows/enforcement/__init__.py +11 -1
- gobby/workflows/enforcement/blocking.py +96 -0
- gobby/workflows/enforcement/handlers.py +35 -1
- gobby/workflows/enforcement/task_policy.py +18 -0
- gobby/workflows/engine.py +26 -4
- gobby/workflows/evaluator.py +8 -5
- gobby/workflows/lifecycle_evaluator.py +59 -27
- gobby/workflows/loader.py +567 -30
- gobby/workflows/lobster_compat.py +147 -0
- gobby/workflows/pipeline_executor.py +801 -0
- gobby/workflows/pipeline_state.py +172 -0
- gobby/workflows/pipeline_webhooks.py +206 -0
- gobby/workflows/premature_stop.py +5 -0
- gobby/worktrees/git.py +135 -20
- {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/METADATA +56 -22
- {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/RECORD +166 -122
- gobby/hooks/event_handlers.py +0 -1008
- gobby/mcp_proxy/tools/workflows.py +0 -1023
- {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/WHEEL +0 -0
- {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/entry_points.txt +0 -0
- {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/licenses/LICENSE.md +0 -0
- {gobby-0.2.8.dist-info → gobby-0.2.11.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Hook Dispatcher - Routes Windsurf (Cascade) hooks to HookManager.
|
|
3
|
+
|
|
4
|
+
This is a thin wrapper script that receives hook calls from Windsurf
|
|
5
|
+
and routes them to the appropriate handler via HookManager.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
hook_dispatcher.py --type pre_user_prompt < input.json > output.json
|
|
9
|
+
hook_dispatcher.py --type pre_run_command --debug < input.json > output.json
|
|
10
|
+
|
|
11
|
+
Exit Codes:
|
|
12
|
+
0 - Success
|
|
13
|
+
1 - General error (logged, continues)
|
|
14
|
+
2 - Block action (Windsurf interprets as deny)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import sys
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
# Default daemon configuration
|
|
24
|
+
DEFAULT_DAEMON_PORT = 60887
|
|
25
|
+
DEFAULT_CONFIG_PATH = "~/.gobby/config.yaml"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_daemon_url() -> str:
|
|
29
|
+
"""Get the daemon HTTP URL from config file."""
|
|
30
|
+
config_path = Path(DEFAULT_CONFIG_PATH).expanduser()
|
|
31
|
+
|
|
32
|
+
if config_path.exists():
|
|
33
|
+
try:
|
|
34
|
+
import yaml
|
|
35
|
+
|
|
36
|
+
with open(config_path) as f:
|
|
37
|
+
config = yaml.safe_load(f) or {}
|
|
38
|
+
port = config.get("daemon_port", DEFAULT_DAEMON_PORT)
|
|
39
|
+
except Exception:
|
|
40
|
+
port = DEFAULT_DAEMON_PORT
|
|
41
|
+
else:
|
|
42
|
+
port = DEFAULT_DAEMON_PORT
|
|
43
|
+
|
|
44
|
+
return f"http://localhost:{port}"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_terminal_context() -> dict[str, str | int | None]:
|
|
48
|
+
"""Capture terminal/process context for session correlation."""
|
|
49
|
+
context: dict[str, str | int | None] = {}
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
context["parent_pid"] = os.getppid()
|
|
53
|
+
except Exception:
|
|
54
|
+
context["parent_pid"] = None
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
context["tty"] = os.ttyname(0)
|
|
58
|
+
except Exception:
|
|
59
|
+
context["tty"] = None
|
|
60
|
+
|
|
61
|
+
context["term_session_id"] = os.environ.get("TERM_SESSION_ID")
|
|
62
|
+
context["iterm_session_id"] = os.environ.get("ITERM_SESSION_ID")
|
|
63
|
+
context["vscode_terminal_id"] = os.environ.get("VSCODE_GIT_ASKPASS_NODE")
|
|
64
|
+
context["tmux_pane"] = os.environ.get("TMUX_PANE")
|
|
65
|
+
context["kitty_window_id"] = os.environ.get("KITTY_WINDOW_ID")
|
|
66
|
+
context["alacritty_socket"] = os.environ.get("ALACRITTY_SOCKET")
|
|
67
|
+
context["term_program"] = os.environ.get("TERM_PROGRAM")
|
|
68
|
+
|
|
69
|
+
return context
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def parse_arguments() -> argparse.Namespace:
|
|
73
|
+
"""Parse command line arguments."""
|
|
74
|
+
parser = argparse.ArgumentParser(description="Windsurf Hook Dispatcher")
|
|
75
|
+
parser.add_argument(
|
|
76
|
+
"--type",
|
|
77
|
+
required=True,
|
|
78
|
+
help="Hook type (e.g., pre_user_prompt, pre_run_command)",
|
|
79
|
+
)
|
|
80
|
+
parser.add_argument(
|
|
81
|
+
"--debug",
|
|
82
|
+
action="store_true",
|
|
83
|
+
help="Enable debug logging",
|
|
84
|
+
)
|
|
85
|
+
return parser.parse_args()
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def check_daemon_running(timeout: float = 0.5) -> bool:
|
|
89
|
+
"""Check if gobby daemon is active and responding."""
|
|
90
|
+
try:
|
|
91
|
+
import httpx
|
|
92
|
+
|
|
93
|
+
daemon_url = get_daemon_url()
|
|
94
|
+
response = httpx.get(
|
|
95
|
+
f"{daemon_url}/admin/status",
|
|
96
|
+
timeout=timeout,
|
|
97
|
+
follow_redirects=False,
|
|
98
|
+
)
|
|
99
|
+
return response.status_code == 200
|
|
100
|
+
except Exception:
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def main() -> int:
|
|
105
|
+
"""Main dispatcher execution."""
|
|
106
|
+
try:
|
|
107
|
+
args = parse_arguments()
|
|
108
|
+
except (argparse.ArgumentError, SystemExit):
|
|
109
|
+
print(json.dumps({}))
|
|
110
|
+
return 2
|
|
111
|
+
|
|
112
|
+
hook_type = args.type
|
|
113
|
+
debug_mode = args.debug
|
|
114
|
+
|
|
115
|
+
# Check if daemon is running
|
|
116
|
+
# Note: Windsurf doesn't have explicit session start/end, so pre_user_prompt is critical
|
|
117
|
+
if not check_daemon_running():
|
|
118
|
+
critical_hooks = {"pre_user_prompt"}
|
|
119
|
+
if hook_type in critical_hooks:
|
|
120
|
+
print(
|
|
121
|
+
f"Gobby daemon is not running. Start with 'gobby start' before continuing. "
|
|
122
|
+
f"({hook_type} requires daemon for session state management)",
|
|
123
|
+
file=sys.stderr,
|
|
124
|
+
)
|
|
125
|
+
return 2
|
|
126
|
+
else:
|
|
127
|
+
print(
|
|
128
|
+
json.dumps(
|
|
129
|
+
{"status": "daemon_not_running", "message": "gobby daemon is not running"}
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
return 0
|
|
133
|
+
|
|
134
|
+
import logging
|
|
135
|
+
|
|
136
|
+
logger = logging.getLogger("gobby.hooks.dispatcher.windsurf")
|
|
137
|
+
if debug_mode:
|
|
138
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
139
|
+
else:
|
|
140
|
+
logging.basicConfig(level=logging.WARNING, handlers=[])
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
input_data = json.load(sys.stdin)
|
|
144
|
+
|
|
145
|
+
# Inject terminal context for first prompt (acts as session start)
|
|
146
|
+
if hook_type == "pre_user_prompt":
|
|
147
|
+
input_data["terminal_context"] = get_terminal_context()
|
|
148
|
+
|
|
149
|
+
logger.info(f"[{hook_type}] Received input keys: {list(input_data.keys())}")
|
|
150
|
+
|
|
151
|
+
if debug_mode:
|
|
152
|
+
logger.debug(f"Input data: {input_data}")
|
|
153
|
+
|
|
154
|
+
except json.JSONDecodeError as e:
|
|
155
|
+
if debug_mode:
|
|
156
|
+
logger.error(f"JSON decode error: {e}")
|
|
157
|
+
print(json.dumps({}))
|
|
158
|
+
return 2
|
|
159
|
+
|
|
160
|
+
import httpx
|
|
161
|
+
|
|
162
|
+
daemon_url = get_daemon_url()
|
|
163
|
+
try:
|
|
164
|
+
response = httpx.post(
|
|
165
|
+
f"{daemon_url}/hooks/execute",
|
|
166
|
+
json={
|
|
167
|
+
"hook_type": hook_type,
|
|
168
|
+
"input_data": input_data,
|
|
169
|
+
"source": "windsurf",
|
|
170
|
+
},
|
|
171
|
+
timeout=90.0,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
if response.status_code == 200:
|
|
175
|
+
result = response.json()
|
|
176
|
+
|
|
177
|
+
if debug_mode:
|
|
178
|
+
logger.debug(f"Output data: {result}")
|
|
179
|
+
|
|
180
|
+
# Check for block decision
|
|
181
|
+
if result.get("decision") == "deny":
|
|
182
|
+
reason = result.get("reason") or "Blocked by hook"
|
|
183
|
+
print(reason, file=sys.stderr)
|
|
184
|
+
return 2
|
|
185
|
+
|
|
186
|
+
if result and result != {}:
|
|
187
|
+
print(json.dumps(result))
|
|
188
|
+
|
|
189
|
+
return 0
|
|
190
|
+
else:
|
|
191
|
+
error_detail = response.text
|
|
192
|
+
logger.error(
|
|
193
|
+
f"Daemon returned error: status={response.status_code}, detail={error_detail}"
|
|
194
|
+
)
|
|
195
|
+
print(json.dumps({"status": "error", "message": f"Daemon error: {error_detail}"}))
|
|
196
|
+
return 1
|
|
197
|
+
|
|
198
|
+
except Exception as e:
|
|
199
|
+
logger.error(f"Hook execution failed: {e}", exc_info=True)
|
|
200
|
+
print(json.dumps({"status": "error", "message": str(e)}))
|
|
201
|
+
return 1
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
if __name__ == "__main__":
|
|
205
|
+
sys.exit(main())
|
gobby/llm/__init__.py
CHANGED
|
@@ -10,7 +10,15 @@ Usage:
|
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
from gobby.llm.base import AuthMode, LLMProvider
|
|
13
|
-
from gobby.llm.claude import
|
|
13
|
+
from gobby.llm.claude import (
|
|
14
|
+
ChatEvent,
|
|
15
|
+
DoneEvent,
|
|
16
|
+
MCPToolResult,
|
|
17
|
+
TextChunk,
|
|
18
|
+
ToolCall,
|
|
19
|
+
ToolCallEvent,
|
|
20
|
+
ToolResultEvent,
|
|
21
|
+
)
|
|
14
22
|
from gobby.llm.claude_executor import ClaudeExecutor
|
|
15
23
|
from gobby.llm.executor import (
|
|
16
24
|
AgentExecutor,
|
|
@@ -27,14 +35,19 @@ __all__ = [
|
|
|
27
35
|
"AgentExecutor",
|
|
28
36
|
"AgentResult",
|
|
29
37
|
"AuthMode",
|
|
38
|
+
"ChatEvent",
|
|
30
39
|
"ClaudeExecutor",
|
|
40
|
+
"DoneEvent",
|
|
31
41
|
"LLMProvider",
|
|
32
42
|
"LLMService",
|
|
33
43
|
"MCPToolResult",
|
|
44
|
+
"TextChunk",
|
|
34
45
|
"ToolCall",
|
|
46
|
+
"ToolCallEvent",
|
|
35
47
|
"ToolCallRecord",
|
|
36
48
|
"ToolHandler",
|
|
37
49
|
"ToolResult",
|
|
50
|
+
"ToolResultEvent",
|
|
38
51
|
"ToolSchema",
|
|
39
52
|
"create_llm_service",
|
|
40
53
|
]
|