gobby 0.2.6__py3-none-any.whl → 0.2.7__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 +2 -1
- gobby/adapters/codex_impl/__init__.py +28 -0
- gobby/adapters/codex_impl/adapter.py +722 -0
- gobby/adapters/codex_impl/client.py +679 -0
- gobby/adapters/codex_impl/protocol.py +20 -0
- gobby/adapters/codex_impl/types.py +68 -0
- gobby/agents/definitions.py +11 -1
- gobby/agents/isolation.py +395 -0
- gobby/agents/sandbox.py +261 -0
- gobby/agents/spawn.py +42 -287
- gobby/agents/spawn_executor.py +385 -0
- gobby/agents/spawners/__init__.py +24 -0
- gobby/agents/spawners/command_builder.py +189 -0
- gobby/agents/spawners/embedded.py +21 -2
- gobby/agents/spawners/headless.py +21 -2
- gobby/agents/spawners/prompt_manager.py +125 -0
- gobby/cli/install.py +4 -4
- gobby/cli/installers/claude.py +6 -0
- gobby/cli/installers/gemini.py +6 -0
- gobby/cli/installers/shared.py +103 -4
- gobby/cli/sessions.py +1 -1
- gobby/cli/utils.py +9 -2
- gobby/config/__init__.py +12 -97
- gobby/config/app.py +10 -94
- gobby/config/extensions.py +2 -2
- gobby/config/features.py +7 -130
- gobby/config/tasks.py +4 -28
- gobby/hooks/__init__.py +0 -13
- gobby/hooks/event_handlers.py +45 -2
- gobby/hooks/hook_manager.py +2 -2
- gobby/hooks/plugins.py +1 -1
- gobby/hooks/webhooks.py +1 -1
- gobby/llm/resolver.py +3 -2
- gobby/mcp_proxy/importer.py +62 -4
- gobby/mcp_proxy/instructions.py +2 -0
- gobby/mcp_proxy/registries.py +1 -4
- gobby/mcp_proxy/services/recommendation.py +43 -11
- gobby/mcp_proxy/tools/agents.py +31 -731
- gobby/mcp_proxy/tools/clones.py +0 -385
- gobby/mcp_proxy/tools/memory.py +2 -2
- gobby/mcp_proxy/tools/sessions/__init__.py +14 -0
- gobby/mcp_proxy/tools/sessions/_commits.py +232 -0
- gobby/mcp_proxy/tools/sessions/_crud.py +253 -0
- gobby/mcp_proxy/tools/sessions/_factory.py +63 -0
- gobby/mcp_proxy/tools/sessions/_handoff.py +499 -0
- gobby/mcp_proxy/tools/sessions/_messages.py +138 -0
- gobby/mcp_proxy/tools/skills/__init__.py +14 -29
- gobby/mcp_proxy/tools/spawn_agent.py +417 -0
- gobby/mcp_proxy/tools/tasks/_lifecycle.py +52 -18
- gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +1 -1
- gobby/mcp_proxy/tools/worktrees.py +0 -343
- gobby/memory/ingestion/__init__.py +5 -0
- gobby/memory/ingestion/multimodal.py +221 -0
- gobby/memory/manager.py +62 -283
- gobby/memory/search/__init__.py +10 -0
- gobby/memory/search/coordinator.py +248 -0
- gobby/memory/services/__init__.py +5 -0
- gobby/memory/services/crossref.py +142 -0
- gobby/prompts/loader.py +5 -2
- gobby/servers/http.py +1 -4
- gobby/servers/routes/admin.py +14 -0
- gobby/servers/routes/mcp/endpoints/__init__.py +61 -0
- gobby/servers/routes/mcp/endpoints/discovery.py +405 -0
- gobby/servers/routes/mcp/endpoints/execution.py +568 -0
- gobby/servers/routes/mcp/endpoints/registry.py +378 -0
- gobby/servers/routes/mcp/endpoints/server.py +304 -0
- gobby/servers/routes/mcp/hooks.py +1 -1
- gobby/servers/routes/mcp/tools.py +48 -1506
- gobby/sessions/lifecycle.py +1 -1
- gobby/sessions/processor.py +10 -0
- gobby/sessions/transcripts/base.py +1 -0
- gobby/sessions/transcripts/claude.py +15 -5
- gobby/skills/parser.py +30 -2
- gobby/storage/migrations.py +159 -372
- gobby/storage/sessions.py +43 -7
- gobby/storage/skills.py +37 -4
- gobby/storage/tasks/_lifecycle.py +18 -3
- gobby/sync/memories.py +1 -1
- gobby/tasks/external_validator.py +1 -1
- gobby/tasks/validation.py +22 -20
- gobby/tools/summarizer.py +91 -10
- gobby/utils/project_context.py +2 -3
- gobby/utils/status.py +13 -0
- gobby/workflows/actions.py +221 -1217
- gobby/workflows/artifact_actions.py +31 -0
- gobby/workflows/autonomous_actions.py +11 -0
- gobby/workflows/context_actions.py +50 -1
- gobby/workflows/enforcement/__init__.py +47 -0
- gobby/workflows/enforcement/blocking.py +269 -0
- gobby/workflows/enforcement/commit_policy.py +283 -0
- gobby/workflows/enforcement/handlers.py +269 -0
- gobby/workflows/enforcement/task_policy.py +542 -0
- gobby/workflows/git_utils.py +106 -0
- gobby/workflows/llm_actions.py +30 -0
- gobby/workflows/mcp_actions.py +20 -1
- gobby/workflows/memory_actions.py +80 -0
- gobby/workflows/safe_evaluator.py +183 -0
- gobby/workflows/session_actions.py +44 -0
- gobby/workflows/state_actions.py +60 -1
- gobby/workflows/stop_signal_actions.py +55 -0
- gobby/workflows/summary_actions.py +94 -1
- gobby/workflows/task_sync_actions.py +347 -0
- gobby/workflows/todo_actions.py +34 -1
- gobby/workflows/webhook_actions.py +185 -0
- {gobby-0.2.6.dist-info → gobby-0.2.7.dist-info}/METADATA +6 -1
- {gobby-0.2.6.dist-info → gobby-0.2.7.dist-info}/RECORD +111 -111
- {gobby-0.2.6.dist-info → gobby-0.2.7.dist-info}/WHEEL +1 -1
- gobby/adapters/codex.py +0 -1332
- gobby/install/claude/commands/gobby/bug.md +0 -51
- gobby/install/claude/commands/gobby/chore.md +0 -51
- gobby/install/claude/commands/gobby/epic.md +0 -52
- gobby/install/claude/commands/gobby/eval.md +0 -235
- gobby/install/claude/commands/gobby/feat.md +0 -49
- gobby/install/claude/commands/gobby/nit.md +0 -52
- gobby/install/claude/commands/gobby/ref.md +0 -52
- gobby/mcp_proxy/tools/session_messages.py +0 -1055
- gobby/prompts/defaults/expansion/system.md +0 -119
- gobby/prompts/defaults/expansion/user.md +0 -48
- gobby/prompts/defaults/external_validation/agent.md +0 -72
- gobby/prompts/defaults/external_validation/external.md +0 -63
- gobby/prompts/defaults/external_validation/spawn.md +0 -83
- gobby/prompts/defaults/external_validation/system.md +0 -6
- gobby/prompts/defaults/features/import_mcp.md +0 -22
- gobby/prompts/defaults/features/import_mcp_github.md +0 -17
- gobby/prompts/defaults/features/import_mcp_search.md +0 -16
- gobby/prompts/defaults/features/recommend_tools.md +0 -32
- gobby/prompts/defaults/features/recommend_tools_hybrid.md +0 -35
- gobby/prompts/defaults/features/recommend_tools_llm.md +0 -30
- gobby/prompts/defaults/features/server_description.md +0 -20
- gobby/prompts/defaults/features/server_description_system.md +0 -6
- gobby/prompts/defaults/features/task_description.md +0 -31
- gobby/prompts/defaults/features/task_description_system.md +0 -6
- gobby/prompts/defaults/features/tool_summary.md +0 -17
- gobby/prompts/defaults/features/tool_summary_system.md +0 -6
- gobby/prompts/defaults/handoff/compact.md +0 -63
- gobby/prompts/defaults/handoff/session_end.md +0 -57
- gobby/prompts/defaults/memory/extract.md +0 -61
- gobby/prompts/defaults/research/step.md +0 -58
- gobby/prompts/defaults/validation/criteria.md +0 -47
- gobby/prompts/defaults/validation/validate.md +0 -38
- gobby/storage/migrations_legacy.py +0 -1359
- gobby/workflows/task_enforcement_actions.py +0 -1343
- {gobby-0.2.6.dist-info → gobby-0.2.7.dist-info}/entry_points.txt +0 -0
- {gobby-0.2.6.dist-info → gobby-0.2.7.dist-info}/licenses/LICENSE.md +0 -0
- {gobby-0.2.6.dist-info → gobby-0.2.7.dist-info}/top_level.txt +0 -0
gobby/sessions/lifecycle.py
CHANGED
|
@@ -11,7 +11,7 @@ import logging
|
|
|
11
11
|
import os
|
|
12
12
|
from typing import Any
|
|
13
13
|
|
|
14
|
-
from gobby.config.
|
|
14
|
+
from gobby.config.sessions import SessionLifecycleConfig
|
|
15
15
|
from gobby.sessions.transcripts.claude import ClaudeTranscriptParser
|
|
16
16
|
from gobby.sessions.transcripts.codex import CodexTranscriptParser
|
|
17
17
|
from gobby.sessions.transcripts.gemini import GeminiTranscriptParser
|
gobby/sessions/processor.py
CHANGED
|
@@ -12,6 +12,7 @@ from typing import TYPE_CHECKING
|
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
14
|
from gobby.servers.websocket import WebSocketServer
|
|
15
|
+
from gobby.storage.sessions import LocalSessionManager
|
|
15
16
|
|
|
16
17
|
from gobby.sessions.transcripts import get_parser
|
|
17
18
|
from gobby.sessions.transcripts.base import TranscriptParser
|
|
@@ -36,11 +37,13 @@ class SessionMessageProcessor:
|
|
|
36
37
|
db: DatabaseProtocol,
|
|
37
38
|
poll_interval: float = 2.0,
|
|
38
39
|
websocket_server: "WebSocketServer | None" = None,
|
|
40
|
+
session_manager: "LocalSessionManager | None" = None,
|
|
39
41
|
):
|
|
40
42
|
self.db = db
|
|
41
43
|
self.message_manager = LocalSessionMessageManager(db)
|
|
42
44
|
self.poll_interval = poll_interval
|
|
43
45
|
self.websocket_server: WebSocketServer | None = websocket_server
|
|
46
|
+
self.session_manager: LocalSessionManager | None = session_manager
|
|
44
47
|
|
|
45
48
|
# Track active sessions: session_id -> transcript_path
|
|
46
49
|
self._active_sessions: dict[str, str] = {}
|
|
@@ -196,6 +199,13 @@ class SessionMessageProcessor:
|
|
|
196
199
|
# Store messages
|
|
197
200
|
await self.message_manager.store_messages(session_id, parsed_messages)
|
|
198
201
|
|
|
202
|
+
# Extract and store model from parsed messages (if present)
|
|
203
|
+
if self.session_manager:
|
|
204
|
+
for msg in parsed_messages:
|
|
205
|
+
if msg.model:
|
|
206
|
+
self.session_manager.update_model(session_id, msg.model)
|
|
207
|
+
break # Only need the first model found
|
|
208
|
+
|
|
199
209
|
# Broadcast new messages
|
|
200
210
|
if self.websocket_server:
|
|
201
211
|
for msg in parsed_messages:
|
|
@@ -355,6 +355,8 @@ class ClaudeTranscriptParser:
|
|
|
355
355
|
# Skip unknown message types (e.g., 'progress', 'error' internal events)
|
|
356
356
|
return None
|
|
357
357
|
|
|
358
|
+
usage, model = self._extract_usage(data)
|
|
359
|
+
|
|
358
360
|
return ParsedMessage(
|
|
359
361
|
index=index,
|
|
360
362
|
role=role,
|
|
@@ -365,12 +367,20 @@ class ClaudeTranscriptParser:
|
|
|
365
367
|
tool_result=tool_result,
|
|
366
368
|
timestamp=timestamp,
|
|
367
369
|
raw_json=data,
|
|
368
|
-
usage=
|
|
370
|
+
usage=usage,
|
|
369
371
|
tool_use_id=tool_use_id,
|
|
372
|
+
model=model,
|
|
370
373
|
)
|
|
371
374
|
|
|
372
|
-
def _extract_usage(self, data: dict[str, Any]) -> TokenUsage | None:
|
|
373
|
-
"""Extract token usage from message data.
|
|
375
|
+
def _extract_usage(self, data: dict[str, Any]) -> tuple[TokenUsage | None, str | None]:
|
|
376
|
+
"""Extract token usage and model from message data.
|
|
377
|
+
|
|
378
|
+
Returns:
|
|
379
|
+
Tuple of (TokenUsage | None, model string | None)
|
|
380
|
+
"""
|
|
381
|
+
# Extract model from message object
|
|
382
|
+
model = data.get("message", {}).get("model")
|
|
383
|
+
|
|
374
384
|
# Check for top-level usage field (some formats)
|
|
375
385
|
usage_data = data.get("usage")
|
|
376
386
|
|
|
@@ -379,7 +389,7 @@ class ClaudeTranscriptParser:
|
|
|
379
389
|
usage_data = data.get("message", {}).get("usage")
|
|
380
390
|
|
|
381
391
|
if not usage_data:
|
|
382
|
-
return None
|
|
392
|
+
return None, model
|
|
383
393
|
|
|
384
394
|
# Use explicit presence checks to handle 0 correctly
|
|
385
395
|
input_tokens = (
|
|
@@ -413,7 +423,7 @@ class ClaudeTranscriptParser:
|
|
|
413
423
|
cache_creation_tokens=cache_creation_tokens,
|
|
414
424
|
cache_read_tokens=cache_read_tokens,
|
|
415
425
|
total_cost_usd=total_cost_usd,
|
|
416
|
-
)
|
|
426
|
+
), model
|
|
417
427
|
|
|
418
428
|
def parse_lines(self, lines: list[str], start_index: int = 0) -> list[ParsedMessage]:
|
|
419
429
|
"""
|
gobby/skills/parser.py
CHANGED
|
@@ -82,9 +82,14 @@ class ParsedSkill:
|
|
|
82
82
|
assets: list[str] | None = None
|
|
83
83
|
|
|
84
84
|
def get_category(self) -> str | None:
|
|
85
|
-
"""Get category from metadata.skillport.category."""
|
|
85
|
+
"""Get category from top-level or metadata.skillport.category."""
|
|
86
86
|
if not self.metadata:
|
|
87
87
|
return None
|
|
88
|
+
# Check top-level first (from frontmatter)
|
|
89
|
+
result = self.metadata.get("category")
|
|
90
|
+
if result is not None:
|
|
91
|
+
return str(result)
|
|
92
|
+
# Fall back to nested skillport.category
|
|
88
93
|
skillport = self.metadata.get("skillport", {})
|
|
89
94
|
result = skillport.get("category")
|
|
90
95
|
return str(result) if result is not None else None
|
|
@@ -102,9 +107,18 @@ class ParsedSkill:
|
|
|
102
107
|
return []
|
|
103
108
|
|
|
104
109
|
def is_always_apply(self) -> bool:
|
|
105
|
-
"""Check if this is a core skill (alwaysApply=true).
|
|
110
|
+
"""Check if this is a core skill (alwaysApply=true).
|
|
111
|
+
|
|
112
|
+
Supports both top-level alwaysApply and nested metadata.skillport.alwaysApply.
|
|
113
|
+
Top-level takes precedence.
|
|
114
|
+
"""
|
|
106
115
|
if not self.metadata:
|
|
107
116
|
return False
|
|
117
|
+
# Check top-level first (from frontmatter)
|
|
118
|
+
top_level = self.metadata.get("alwaysApply")
|
|
119
|
+
if top_level is not None:
|
|
120
|
+
return bool(top_level)
|
|
121
|
+
# Fall back to nested skillport.alwaysApply
|
|
108
122
|
skillport = self.metadata.get("skillport", {})
|
|
109
123
|
return bool(skillport.get("alwaysApply", False))
|
|
110
124
|
|
|
@@ -214,6 +228,20 @@ def parse_skill_text(text: str, source_path: str | None = None) -> ParsedSkill:
|
|
|
214
228
|
# Extract metadata (may contain version, skillport, gobby namespaces)
|
|
215
229
|
metadata = frontmatter.get("metadata")
|
|
216
230
|
|
|
231
|
+
# Handle top-level alwaysApply and category by including them in metadata
|
|
232
|
+
# This allows both top-level and nested formats to work
|
|
233
|
+
top_level_always_apply = frontmatter.get("alwaysApply")
|
|
234
|
+
top_level_category = frontmatter.get("category")
|
|
235
|
+
|
|
236
|
+
if top_level_always_apply is not None or top_level_category is not None:
|
|
237
|
+
if metadata is None:
|
|
238
|
+
metadata = {}
|
|
239
|
+
# Store at top level of metadata (not nested in skillport)
|
|
240
|
+
if top_level_always_apply is not None:
|
|
241
|
+
metadata["alwaysApply"] = top_level_always_apply
|
|
242
|
+
if top_level_category is not None:
|
|
243
|
+
metadata["category"] = top_level_category
|
|
244
|
+
|
|
217
245
|
# Version can be at top level or in metadata
|
|
218
246
|
version = frontmatter.get("version")
|
|
219
247
|
if version is None and metadata and isinstance(metadata, dict):
|