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.
Files changed (146) hide show
  1. gobby/__init__.py +1 -1
  2. gobby/adapters/__init__.py +2 -1
  3. gobby/adapters/codex_impl/__init__.py +28 -0
  4. gobby/adapters/codex_impl/adapter.py +722 -0
  5. gobby/adapters/codex_impl/client.py +679 -0
  6. gobby/adapters/codex_impl/protocol.py +20 -0
  7. gobby/adapters/codex_impl/types.py +68 -0
  8. gobby/agents/definitions.py +11 -1
  9. gobby/agents/isolation.py +395 -0
  10. gobby/agents/sandbox.py +261 -0
  11. gobby/agents/spawn.py +42 -287
  12. gobby/agents/spawn_executor.py +385 -0
  13. gobby/agents/spawners/__init__.py +24 -0
  14. gobby/agents/spawners/command_builder.py +189 -0
  15. gobby/agents/spawners/embedded.py +21 -2
  16. gobby/agents/spawners/headless.py +21 -2
  17. gobby/agents/spawners/prompt_manager.py +125 -0
  18. gobby/cli/install.py +4 -4
  19. gobby/cli/installers/claude.py +6 -0
  20. gobby/cli/installers/gemini.py +6 -0
  21. gobby/cli/installers/shared.py +103 -4
  22. gobby/cli/sessions.py +1 -1
  23. gobby/cli/utils.py +9 -2
  24. gobby/config/__init__.py +12 -97
  25. gobby/config/app.py +10 -94
  26. gobby/config/extensions.py +2 -2
  27. gobby/config/features.py +7 -130
  28. gobby/config/tasks.py +4 -28
  29. gobby/hooks/__init__.py +0 -13
  30. gobby/hooks/event_handlers.py +45 -2
  31. gobby/hooks/hook_manager.py +2 -2
  32. gobby/hooks/plugins.py +1 -1
  33. gobby/hooks/webhooks.py +1 -1
  34. gobby/llm/resolver.py +3 -2
  35. gobby/mcp_proxy/importer.py +62 -4
  36. gobby/mcp_proxy/instructions.py +2 -0
  37. gobby/mcp_proxy/registries.py +1 -4
  38. gobby/mcp_proxy/services/recommendation.py +43 -11
  39. gobby/mcp_proxy/tools/agents.py +31 -731
  40. gobby/mcp_proxy/tools/clones.py +0 -385
  41. gobby/mcp_proxy/tools/memory.py +2 -2
  42. gobby/mcp_proxy/tools/sessions/__init__.py +14 -0
  43. gobby/mcp_proxy/tools/sessions/_commits.py +232 -0
  44. gobby/mcp_proxy/tools/sessions/_crud.py +253 -0
  45. gobby/mcp_proxy/tools/sessions/_factory.py +63 -0
  46. gobby/mcp_proxy/tools/sessions/_handoff.py +499 -0
  47. gobby/mcp_proxy/tools/sessions/_messages.py +138 -0
  48. gobby/mcp_proxy/tools/skills/__init__.py +14 -29
  49. gobby/mcp_proxy/tools/spawn_agent.py +417 -0
  50. gobby/mcp_proxy/tools/tasks/_lifecycle.py +52 -18
  51. gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +1 -1
  52. gobby/mcp_proxy/tools/worktrees.py +0 -343
  53. gobby/memory/ingestion/__init__.py +5 -0
  54. gobby/memory/ingestion/multimodal.py +221 -0
  55. gobby/memory/manager.py +62 -283
  56. gobby/memory/search/__init__.py +10 -0
  57. gobby/memory/search/coordinator.py +248 -0
  58. gobby/memory/services/__init__.py +5 -0
  59. gobby/memory/services/crossref.py +142 -0
  60. gobby/prompts/loader.py +5 -2
  61. gobby/servers/http.py +1 -4
  62. gobby/servers/routes/admin.py +14 -0
  63. gobby/servers/routes/mcp/endpoints/__init__.py +61 -0
  64. gobby/servers/routes/mcp/endpoints/discovery.py +405 -0
  65. gobby/servers/routes/mcp/endpoints/execution.py +568 -0
  66. gobby/servers/routes/mcp/endpoints/registry.py +378 -0
  67. gobby/servers/routes/mcp/endpoints/server.py +304 -0
  68. gobby/servers/routes/mcp/hooks.py +1 -1
  69. gobby/servers/routes/mcp/tools.py +48 -1506
  70. gobby/sessions/lifecycle.py +1 -1
  71. gobby/sessions/processor.py +10 -0
  72. gobby/sessions/transcripts/base.py +1 -0
  73. gobby/sessions/transcripts/claude.py +15 -5
  74. gobby/skills/parser.py +30 -2
  75. gobby/storage/migrations.py +159 -372
  76. gobby/storage/sessions.py +43 -7
  77. gobby/storage/skills.py +37 -4
  78. gobby/storage/tasks/_lifecycle.py +18 -3
  79. gobby/sync/memories.py +1 -1
  80. gobby/tasks/external_validator.py +1 -1
  81. gobby/tasks/validation.py +22 -20
  82. gobby/tools/summarizer.py +91 -10
  83. gobby/utils/project_context.py +2 -3
  84. gobby/utils/status.py +13 -0
  85. gobby/workflows/actions.py +221 -1217
  86. gobby/workflows/artifact_actions.py +31 -0
  87. gobby/workflows/autonomous_actions.py +11 -0
  88. gobby/workflows/context_actions.py +50 -1
  89. gobby/workflows/enforcement/__init__.py +47 -0
  90. gobby/workflows/enforcement/blocking.py +269 -0
  91. gobby/workflows/enforcement/commit_policy.py +283 -0
  92. gobby/workflows/enforcement/handlers.py +269 -0
  93. gobby/workflows/enforcement/task_policy.py +542 -0
  94. gobby/workflows/git_utils.py +106 -0
  95. gobby/workflows/llm_actions.py +30 -0
  96. gobby/workflows/mcp_actions.py +20 -1
  97. gobby/workflows/memory_actions.py +80 -0
  98. gobby/workflows/safe_evaluator.py +183 -0
  99. gobby/workflows/session_actions.py +44 -0
  100. gobby/workflows/state_actions.py +60 -1
  101. gobby/workflows/stop_signal_actions.py +55 -0
  102. gobby/workflows/summary_actions.py +94 -1
  103. gobby/workflows/task_sync_actions.py +347 -0
  104. gobby/workflows/todo_actions.py +34 -1
  105. gobby/workflows/webhook_actions.py +185 -0
  106. {gobby-0.2.6.dist-info → gobby-0.2.7.dist-info}/METADATA +6 -1
  107. {gobby-0.2.6.dist-info → gobby-0.2.7.dist-info}/RECORD +111 -111
  108. {gobby-0.2.6.dist-info → gobby-0.2.7.dist-info}/WHEEL +1 -1
  109. gobby/adapters/codex.py +0 -1332
  110. gobby/install/claude/commands/gobby/bug.md +0 -51
  111. gobby/install/claude/commands/gobby/chore.md +0 -51
  112. gobby/install/claude/commands/gobby/epic.md +0 -52
  113. gobby/install/claude/commands/gobby/eval.md +0 -235
  114. gobby/install/claude/commands/gobby/feat.md +0 -49
  115. gobby/install/claude/commands/gobby/nit.md +0 -52
  116. gobby/install/claude/commands/gobby/ref.md +0 -52
  117. gobby/mcp_proxy/tools/session_messages.py +0 -1055
  118. gobby/prompts/defaults/expansion/system.md +0 -119
  119. gobby/prompts/defaults/expansion/user.md +0 -48
  120. gobby/prompts/defaults/external_validation/agent.md +0 -72
  121. gobby/prompts/defaults/external_validation/external.md +0 -63
  122. gobby/prompts/defaults/external_validation/spawn.md +0 -83
  123. gobby/prompts/defaults/external_validation/system.md +0 -6
  124. gobby/prompts/defaults/features/import_mcp.md +0 -22
  125. gobby/prompts/defaults/features/import_mcp_github.md +0 -17
  126. gobby/prompts/defaults/features/import_mcp_search.md +0 -16
  127. gobby/prompts/defaults/features/recommend_tools.md +0 -32
  128. gobby/prompts/defaults/features/recommend_tools_hybrid.md +0 -35
  129. gobby/prompts/defaults/features/recommend_tools_llm.md +0 -30
  130. gobby/prompts/defaults/features/server_description.md +0 -20
  131. gobby/prompts/defaults/features/server_description_system.md +0 -6
  132. gobby/prompts/defaults/features/task_description.md +0 -31
  133. gobby/prompts/defaults/features/task_description_system.md +0 -6
  134. gobby/prompts/defaults/features/tool_summary.md +0 -17
  135. gobby/prompts/defaults/features/tool_summary_system.md +0 -6
  136. gobby/prompts/defaults/handoff/compact.md +0 -63
  137. gobby/prompts/defaults/handoff/session_end.md +0 -57
  138. gobby/prompts/defaults/memory/extract.md +0 -61
  139. gobby/prompts/defaults/research/step.md +0 -58
  140. gobby/prompts/defaults/validation/criteria.md +0 -47
  141. gobby/prompts/defaults/validation/validate.md +0 -38
  142. gobby/storage/migrations_legacy.py +0 -1359
  143. gobby/workflows/task_enforcement_actions.py +0 -1343
  144. {gobby-0.2.6.dist-info → gobby-0.2.7.dist-info}/entry_points.txt +0 -0
  145. {gobby-0.2.6.dist-info → gobby-0.2.7.dist-info}/licenses/LICENSE.md +0 -0
  146. {gobby-0.2.6.dist-info → gobby-0.2.7.dist-info}/top_level.txt +0 -0
@@ -11,7 +11,7 @@ import logging
11
11
  import os
12
12
  from typing import Any
13
13
 
14
- from gobby.config.app import SessionLifecycleConfig
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
@@ -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:
@@ -40,6 +40,7 @@ class ParsedMessage:
40
40
  raw_json: dict[str, Any]
41
41
  usage: TokenUsage | None = None
42
42
  tool_use_id: str | None = None
43
+ model: str | None = None
43
44
 
44
45
 
45
46
  @runtime_checkable
@@ -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=self._extract_usage(data),
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):