gobby 0.2.5__py3-none-any.whl → 0.2.6__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/adapters/claude_code.py +13 -4
- gobby/adapters/codex.py +43 -3
- gobby/agents/runner.py +8 -0
- gobby/cli/__init__.py +6 -0
- gobby/cli/clones.py +419 -0
- gobby/cli/conductor.py +266 -0
- gobby/cli/installers/antigravity.py +3 -9
- gobby/cli/installers/claude.py +9 -9
- gobby/cli/installers/codex.py +2 -8
- gobby/cli/installers/gemini.py +2 -8
- gobby/cli/installers/shared.py +71 -8
- gobby/cli/skills.py +858 -0
- gobby/cli/tasks/ai.py +0 -440
- gobby/cli/tasks/crud.py +44 -6
- gobby/cli/tasks/main.py +0 -4
- gobby/cli/tui.py +2 -2
- gobby/cli/utils.py +3 -3
- gobby/clones/__init__.py +13 -0
- gobby/clones/git.py +547 -0
- gobby/conductor/__init__.py +16 -0
- gobby/conductor/alerts.py +135 -0
- gobby/conductor/loop.py +164 -0
- gobby/conductor/monitors/__init__.py +11 -0
- gobby/conductor/monitors/agents.py +116 -0
- gobby/conductor/monitors/tasks.py +155 -0
- gobby/conductor/pricing.py +234 -0
- gobby/conductor/token_tracker.py +160 -0
- gobby/config/app.py +63 -1
- gobby/config/search.py +110 -0
- gobby/config/servers.py +1 -1
- gobby/config/skills.py +43 -0
- gobby/config/tasks.py +6 -14
- gobby/hooks/event_handlers.py +145 -2
- gobby/hooks/hook_manager.py +48 -2
- gobby/hooks/skill_manager.py +130 -0
- gobby/install/claude/hooks/hook_dispatcher.py +4 -4
- gobby/install/codex/hooks/hook_dispatcher.py +1 -1
- gobby/install/gemini/hooks/hook_dispatcher.py +87 -12
- gobby/llm/claude.py +22 -34
- gobby/llm/claude_executor.py +46 -256
- gobby/llm/codex_executor.py +59 -291
- gobby/llm/executor.py +21 -0
- gobby/llm/gemini.py +134 -110
- gobby/llm/litellm_executor.py +143 -6
- gobby/llm/resolver.py +95 -33
- gobby/mcp_proxy/instructions.py +54 -0
- gobby/mcp_proxy/models.py +15 -0
- gobby/mcp_proxy/registries.py +68 -5
- gobby/mcp_proxy/server.py +33 -3
- gobby/mcp_proxy/services/tool_proxy.py +81 -1
- gobby/mcp_proxy/stdio.py +2 -1
- gobby/mcp_proxy/tools/__init__.py +0 -2
- gobby/mcp_proxy/tools/agent_messaging.py +317 -0
- gobby/mcp_proxy/tools/clones.py +903 -0
- gobby/mcp_proxy/tools/memory.py +1 -24
- gobby/mcp_proxy/tools/metrics.py +65 -1
- gobby/mcp_proxy/tools/orchestration/__init__.py +3 -0
- gobby/mcp_proxy/tools/orchestration/cleanup.py +151 -0
- gobby/mcp_proxy/tools/orchestration/wait.py +467 -0
- gobby/mcp_proxy/tools/session_messages.py +1 -2
- gobby/mcp_proxy/tools/skills/__init__.py +631 -0
- gobby/mcp_proxy/tools/task_orchestration.py +7 -0
- gobby/mcp_proxy/tools/task_readiness.py +14 -0
- gobby/mcp_proxy/tools/task_sync.py +1 -1
- gobby/mcp_proxy/tools/tasks/_context.py +0 -20
- gobby/mcp_proxy/tools/tasks/_crud.py +91 -4
- gobby/mcp_proxy/tools/tasks/_expansion.py +348 -0
- gobby/mcp_proxy/tools/tasks/_factory.py +6 -16
- gobby/mcp_proxy/tools/tasks/_lifecycle.py +60 -29
- gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +18 -29
- gobby/mcp_proxy/tools/workflows.py +1 -1
- gobby/mcp_proxy/tools/worktrees.py +5 -0
- gobby/memory/backends/__init__.py +6 -1
- gobby/memory/backends/mem0.py +6 -1
- gobby/memory/extractor.py +477 -0
- gobby/memory/manager.py +11 -2
- gobby/prompts/defaults/handoff/compact.md +63 -0
- gobby/prompts/defaults/handoff/session_end.md +57 -0
- gobby/prompts/defaults/memory/extract.md +61 -0
- gobby/runner.py +37 -16
- gobby/search/__init__.py +48 -6
- gobby/search/backends/__init__.py +159 -0
- gobby/search/backends/embedding.py +225 -0
- gobby/search/embeddings.py +238 -0
- gobby/search/models.py +148 -0
- gobby/search/unified.py +496 -0
- gobby/servers/http.py +23 -8
- gobby/servers/routes/admin.py +280 -0
- gobby/servers/routes/mcp/tools.py +241 -52
- gobby/servers/websocket.py +2 -2
- gobby/sessions/analyzer.py +2 -0
- gobby/sessions/transcripts/base.py +1 -0
- gobby/sessions/transcripts/claude.py +64 -5
- gobby/skills/__init__.py +91 -0
- gobby/skills/loader.py +685 -0
- gobby/skills/manager.py +384 -0
- gobby/skills/parser.py +258 -0
- gobby/skills/search.py +463 -0
- gobby/skills/sync.py +119 -0
- gobby/skills/updater.py +385 -0
- gobby/skills/validator.py +368 -0
- gobby/storage/clones.py +378 -0
- gobby/storage/database.py +1 -1
- gobby/storage/memories.py +43 -13
- gobby/storage/migrations.py +180 -6
- gobby/storage/sessions.py +73 -0
- gobby/storage/skills.py +749 -0
- gobby/storage/tasks/_crud.py +4 -4
- gobby/storage/tasks/_lifecycle.py +41 -6
- gobby/storage/tasks/_manager.py +14 -5
- gobby/storage/tasks/_models.py +8 -3
- gobby/sync/memories.py +39 -4
- gobby/sync/tasks.py +83 -6
- gobby/tasks/__init__.py +1 -2
- gobby/tasks/validation.py +24 -15
- gobby/tui/api_client.py +4 -7
- gobby/tui/app.py +5 -3
- gobby/tui/screens/orchestrator.py +1 -2
- gobby/tui/screens/tasks.py +2 -4
- gobby/tui/ws_client.py +1 -1
- gobby/utils/daemon_client.py +2 -2
- gobby/workflows/actions.py +84 -2
- gobby/workflows/context_actions.py +43 -0
- gobby/workflows/detection_helpers.py +115 -31
- gobby/workflows/engine.py +13 -2
- gobby/workflows/lifecycle_evaluator.py +29 -1
- gobby/workflows/loader.py +19 -6
- gobby/workflows/memory_actions.py +74 -0
- gobby/workflows/summary_actions.py +17 -0
- gobby/workflows/task_enforcement_actions.py +448 -6
- {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/METADATA +82 -21
- {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/RECORD +136 -107
- gobby/install/codex/prompts/forget.md +0 -7
- gobby/install/codex/prompts/memories.md +0 -7
- gobby/install/codex/prompts/recall.md +0 -7
- gobby/install/codex/prompts/remember.md +0 -13
- gobby/llm/gemini_executor.py +0 -339
- gobby/mcp_proxy/tools/task_expansion.py +0 -591
- gobby/tasks/context.py +0 -747
- gobby/tasks/criteria.py +0 -342
- gobby/tasks/expansion.py +0 -626
- gobby/tasks/prompts/expand.py +0 -327
- gobby/tasks/research.py +0 -421
- gobby/tasks/tdd.py +0 -352
- {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/WHEEL +0 -0
- {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/entry_points.txt +0 -0
- {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/licenses/LICENSE.md +0 -0
- {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/top_level.txt +0 -0
|
@@ -123,7 +123,11 @@ class ClaudeTranscriptParser:
|
|
|
123
123
|
|
|
124
124
|
# If no /clear found, just take the last max_turns
|
|
125
125
|
if most_recent_clear_idx is None:
|
|
126
|
-
|
|
126
|
+
result = turns[-max_turns:] if len(turns) > max_turns else turns
|
|
127
|
+
result, removed = self._validate_tool_pairing(result)
|
|
128
|
+
if removed:
|
|
129
|
+
self.logger.debug(f"Removed {len(removed)} orphaned tool_results: {removed}")
|
|
130
|
+
return result
|
|
127
131
|
|
|
128
132
|
# Start after this /clear (which is the last in any cluster since we scanned backwards)
|
|
129
133
|
start_idx = most_recent_clear_idx + 1
|
|
@@ -159,7 +163,11 @@ class ClaudeTranscriptParser:
|
|
|
159
163
|
start_idx = max(start_idx, boundary_idx + 1)
|
|
160
164
|
break
|
|
161
165
|
|
|
162
|
-
|
|
166
|
+
result = turns[start_idx:end_idx]
|
|
167
|
+
result, removed = self._validate_tool_pairing(result)
|
|
168
|
+
if removed:
|
|
169
|
+
self.logger.debug(f"Removed {len(removed)} orphaned tool_results: {removed}")
|
|
170
|
+
return result
|
|
163
171
|
|
|
164
172
|
# Segment is > max_turns, so we need to limit it
|
|
165
173
|
# Take the last max_turns from the segment
|
|
@@ -184,7 +192,11 @@ class ClaudeTranscriptParser:
|
|
|
184
192
|
start_idx = boundary_idx + 1
|
|
185
193
|
break
|
|
186
194
|
|
|
187
|
-
|
|
195
|
+
result = turns[start_idx:end_idx]
|
|
196
|
+
result, removed = self._validate_tool_pairing(result)
|
|
197
|
+
if removed:
|
|
198
|
+
self.logger.debug(f"Removed {len(removed)} orphaned tool_results: {removed}")
|
|
199
|
+
return result
|
|
188
200
|
|
|
189
201
|
def is_session_boundary(self, turn: dict[str, Any]) -> bool:
|
|
190
202
|
"""
|
|
@@ -212,6 +224,51 @@ class ClaudeTranscriptParser:
|
|
|
212
224
|
# Check for /clear command marker
|
|
213
225
|
return "<command-name>/clear</command-name>" in str(content)
|
|
214
226
|
|
|
227
|
+
def _validate_tool_pairing(
|
|
228
|
+
self, turns: list[dict[str, Any]]
|
|
229
|
+
) -> tuple[list[dict[str, Any]], list[str]]:
|
|
230
|
+
"""Remove orphaned tool_results that reference missing tool_use blocks.
|
|
231
|
+
|
|
232
|
+
This prevents Claude API validation errors when truncation cuts between
|
|
233
|
+
a tool_use and its corresponding tool_result.
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
turns: List of transcript turns to validate
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
Tuple of (cleaned turns, list of removed tool_use_ids)
|
|
240
|
+
"""
|
|
241
|
+
# Collect valid tool_use_ids from assistant messages
|
|
242
|
+
valid_ids: set[str] = set()
|
|
243
|
+
for turn in turns:
|
|
244
|
+
content = turn.get("message", {}).get("content", [])
|
|
245
|
+
if isinstance(content, list):
|
|
246
|
+
for block in content:
|
|
247
|
+
if isinstance(block, dict) and block.get("type") == "tool_use":
|
|
248
|
+
if tid := block.get("id"):
|
|
249
|
+
valid_ids.add(tid)
|
|
250
|
+
|
|
251
|
+
# Filter orphaned tool_results from user messages
|
|
252
|
+
cleaned: list[dict[str, Any]] = []
|
|
253
|
+
removed: list[str] = []
|
|
254
|
+
for turn in turns:
|
|
255
|
+
msg = turn.get("message", {})
|
|
256
|
+
content = msg.get("content", [])
|
|
257
|
+
if isinstance(content, list):
|
|
258
|
+
new_content: list[Any] = []
|
|
259
|
+
for block in content:
|
|
260
|
+
if isinstance(block, dict) and block.get("type") == "tool_result":
|
|
261
|
+
tid = block.get("tool_use_id")
|
|
262
|
+
if tid and tid not in valid_ids:
|
|
263
|
+
removed.append(tid)
|
|
264
|
+
continue
|
|
265
|
+
new_content.append(block)
|
|
266
|
+
if new_content != content:
|
|
267
|
+
turn = {**turn, "message": {**msg, "content": new_content}}
|
|
268
|
+
cleaned.append(turn)
|
|
269
|
+
|
|
270
|
+
return cleaned, removed
|
|
271
|
+
|
|
215
272
|
def parse_line(self, line: str, index: int) -> ParsedMessage | None:
|
|
216
273
|
"""
|
|
217
274
|
Parse a single line from the transcript JSONL.
|
|
@@ -247,6 +304,7 @@ class ClaudeTranscriptParser:
|
|
|
247
304
|
tool_name = None
|
|
248
305
|
tool_input = None
|
|
249
306
|
tool_result = None
|
|
307
|
+
tool_use_id = None
|
|
250
308
|
|
|
251
309
|
if msg_type == "user":
|
|
252
310
|
role = "user"
|
|
@@ -274,8 +332,7 @@ class ClaudeTranscriptParser:
|
|
|
274
332
|
content_type = "tool_use"
|
|
275
333
|
tool_name = block.get("name")
|
|
276
334
|
tool_input = block.get("input")
|
|
277
|
-
|
|
278
|
-
# but for now we append nothing to text content
|
|
335
|
+
tool_use_id = block.get("id")
|
|
279
336
|
|
|
280
337
|
elif block_type == "tool_result":
|
|
281
338
|
content_type = "tool_result"
|
|
@@ -291,6 +348,7 @@ class ClaudeTranscriptParser:
|
|
|
291
348
|
content_type = "tool_result"
|
|
292
349
|
tool_name = data.get("tool_name")
|
|
293
350
|
tool_result = data.get("result")
|
|
351
|
+
tool_use_id = data.get("tool_use_id")
|
|
294
352
|
content = str(tool_result)
|
|
295
353
|
|
|
296
354
|
else:
|
|
@@ -308,6 +366,7 @@ class ClaudeTranscriptParser:
|
|
|
308
366
|
timestamp=timestamp,
|
|
309
367
|
raw_json=data,
|
|
310
368
|
usage=self._extract_usage(data),
|
|
369
|
+
tool_use_id=tool_use_id,
|
|
311
370
|
)
|
|
312
371
|
|
|
313
372
|
def _extract_usage(self, data: dict[str, Any]) -> TokenUsage | None:
|
gobby/skills/__init__.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Skills module for Agent Skills spec compliant skill management.
|
|
2
|
+
|
|
3
|
+
This module provides:
|
|
4
|
+
- YAML frontmatter parsing for SKILL.md files
|
|
5
|
+
- Validation against Agent Skills specification
|
|
6
|
+
- Search integration (TF-IDF + optional embeddings via UnifiedSearcher)
|
|
7
|
+
- Skill loading from filesystem, GitHub, and ZIP archives
|
|
8
|
+
- Skill updates from source
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
# Embedding utilities are now in gobby.search
|
|
12
|
+
from gobby.search import (
|
|
13
|
+
generate_embedding,
|
|
14
|
+
generate_embeddings,
|
|
15
|
+
is_embedding_available,
|
|
16
|
+
)
|
|
17
|
+
from gobby.skills.loader import (
|
|
18
|
+
GitHubRef,
|
|
19
|
+
SkillLoader,
|
|
20
|
+
SkillLoadError,
|
|
21
|
+
clone_skill_repo,
|
|
22
|
+
extract_zip,
|
|
23
|
+
parse_github_url,
|
|
24
|
+
)
|
|
25
|
+
from gobby.skills.manager import SkillManager
|
|
26
|
+
from gobby.skills.parser import (
|
|
27
|
+
ParsedSkill,
|
|
28
|
+
SkillParseError,
|
|
29
|
+
parse_frontmatter,
|
|
30
|
+
parse_skill_file,
|
|
31
|
+
parse_skill_text,
|
|
32
|
+
)
|
|
33
|
+
from gobby.skills.search import (
|
|
34
|
+
SearchFilters,
|
|
35
|
+
SkillSearch,
|
|
36
|
+
SkillSearchResult,
|
|
37
|
+
)
|
|
38
|
+
from gobby.skills.updater import (
|
|
39
|
+
SkillUpdateError,
|
|
40
|
+
SkillUpdater,
|
|
41
|
+
SkillUpdateResult,
|
|
42
|
+
)
|
|
43
|
+
from gobby.skills.validator import (
|
|
44
|
+
SkillValidator,
|
|
45
|
+
ValidationResult,
|
|
46
|
+
validate_skill_category,
|
|
47
|
+
validate_skill_compatibility,
|
|
48
|
+
validate_skill_description,
|
|
49
|
+
validate_skill_name,
|
|
50
|
+
validate_skill_tags,
|
|
51
|
+
validate_skill_version,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
__all__ = [
|
|
55
|
+
# Embeddings (from gobby.search)
|
|
56
|
+
"generate_embedding",
|
|
57
|
+
"generate_embeddings",
|
|
58
|
+
"is_embedding_available",
|
|
59
|
+
# Loader
|
|
60
|
+
"GitHubRef",
|
|
61
|
+
"SkillLoadError",
|
|
62
|
+
"SkillLoader",
|
|
63
|
+
"clone_skill_repo",
|
|
64
|
+
"extract_zip",
|
|
65
|
+
"parse_github_url",
|
|
66
|
+
# Manager
|
|
67
|
+
"SkillManager",
|
|
68
|
+
# Updater
|
|
69
|
+
"SkillUpdateError",
|
|
70
|
+
"SkillUpdateResult",
|
|
71
|
+
"SkillUpdater",
|
|
72
|
+
# Parser
|
|
73
|
+
"ParsedSkill",
|
|
74
|
+
"SkillParseError",
|
|
75
|
+
"parse_frontmatter",
|
|
76
|
+
"parse_skill_file",
|
|
77
|
+
"parse_skill_text",
|
|
78
|
+
# Search
|
|
79
|
+
"SearchFilters",
|
|
80
|
+
"SkillSearch",
|
|
81
|
+
"SkillSearchResult",
|
|
82
|
+
# Validator
|
|
83
|
+
"SkillValidator",
|
|
84
|
+
"ValidationResult",
|
|
85
|
+
"validate_skill_category",
|
|
86
|
+
"validate_skill_compatibility",
|
|
87
|
+
"validate_skill_description",
|
|
88
|
+
"validate_skill_name",
|
|
89
|
+
"validate_skill_tags",
|
|
90
|
+
"validate_skill_version",
|
|
91
|
+
]
|