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
@@ -5,6 +5,8 @@ import re
5
5
  from typing import TYPE_CHECKING, Any
6
6
 
7
7
  from gobby.config.app import DaemonConfig
8
+ from gobby.config.features import DEFAULT_IMPORT_MCP_SERVER_PROMPT
9
+ from gobby.prompts import PromptLoader
8
10
  from gobby.storage.database import DatabaseProtocol
9
11
  from gobby.storage.mcp import LocalMCPManager
10
12
  from gobby.storage.projects import LocalProjectManager
@@ -18,6 +20,21 @@ logger = logging.getLogger(__name__)
18
20
  # Pattern to detect placeholder secrets like <YOUR_API_KEY>
19
21
  SECRET_PLACEHOLDER_PATTERN = re.compile(r"<YOUR_[A-Z0-9_]+>")
20
22
 
23
+ DEFAULT_GITHUB_FETCH_PROMPT = """Fetch the README from this GitHub repository and extract MCP server configuration:
24
+
25
+ {github_url}
26
+
27
+ If the URL doesn't point directly to a README, try to find and fetch the README.md file.
28
+
29
+ After reading the documentation, extract the MCP server configuration as a JSON object."""
30
+
31
+ DEFAULT_SEARCH_FETCH_PROMPT = """Search for MCP server: {search_query}
32
+
33
+ Find the official documentation or GitHub repository for this MCP server.
34
+ Then fetch and read the README or installation docs.
35
+
36
+ After reading the documentation, extract the MCP server configuration as a JSON object."""
37
+
21
38
 
22
39
  class MCPServerImporter:
23
40
  """Handles importing MCP servers from various sources."""
@@ -46,6 +63,21 @@ class MCPServerImporter:
46
63
  self.mcp_client_manager = mcp_client_manager
47
64
  self.import_config = config.get_import_mcp_server_config()
48
65
 
66
+ # Initialize prompt loader
67
+ project_path = None
68
+ if current_project_id:
69
+ if project := self.project_manager.get(current_project_id):
70
+ project_path = project.repo_path
71
+
72
+ from pathlib import Path
73
+
74
+ self._loader = PromptLoader(project_dir=Path(project_path) if project_path else None)
75
+
76
+ # Register fallbacks
77
+ self._loader.register_fallback("import/github_fetch", lambda: DEFAULT_GITHUB_FETCH_PROMPT)
78
+ self._loader.register_fallback("import/search_fetch", lambda: DEFAULT_SEARCH_FETCH_PROMPT)
79
+ self._loader.register_fallback("import/system", lambda: DEFAULT_IMPORT_MCP_SERVER_PROMPT)
80
+
49
81
  async def import_from_project(
50
82
  self,
51
83
  source_project: str,
@@ -171,10 +203,23 @@ class MCPServerImporter:
171
203
  from claude_agent_sdk import AssistantMessage, ClaudeAgentOptions, TextBlock, query
172
204
 
173
205
  # Build prompt to fetch and extract config
174
- prompt = self.import_config.github_fetch_prompt.format(github_url=github_url)
206
+ prompt_path = self.import_config.github_fetch_prompt_path or "import/github_fetch"
207
+ try:
208
+ prompt = self._loader.render(prompt_path, {"github_url": github_url})
209
+ except Exception as e:
210
+ logger.warning(f"Failed to load Github fetch prompt: {e}")
211
+ prompt = DEFAULT_GITHUB_FETCH_PROMPT.format(github_url=github_url)
212
+
213
+ # Get system prompt
214
+ sys_prompt_path = self.import_config.prompt_path or "import/system"
215
+ try:
216
+ system_prompt = self._loader.render(sys_prompt_path, {})
217
+ except Exception as e:
218
+ logger.warning(f"Failed to load import system prompt: {e}")
219
+ system_prompt = DEFAULT_IMPORT_MCP_SERVER_PROMPT
175
220
 
176
221
  options = ClaudeAgentOptions(
177
- system_prompt=self.import_config.prompt,
222
+ system_prompt=system_prompt,
178
223
  max_turns=3,
179
224
  model=self.import_config.model,
180
225
  allowed_tools=["WebFetch"],
@@ -222,10 +267,23 @@ class MCPServerImporter:
222
267
  from claude_agent_sdk import AssistantMessage, ClaudeAgentOptions, TextBlock, query
223
268
 
224
269
  # Build prompt to search and extract config
225
- prompt = self.import_config.search_fetch_prompt.format(search_query=search_query)
270
+ prompt_path = self.import_config.search_fetch_prompt_path or "import/search_fetch"
271
+ try:
272
+ prompt = self._loader.render(prompt_path, {"search_query": search_query})
273
+ except Exception as e:
274
+ logger.warning(f"Failed to load search fetch prompt: {e}")
275
+ prompt = DEFAULT_SEARCH_FETCH_PROMPT.format(search_query=search_query)
276
+
277
+ # Get system prompt
278
+ sys_prompt_path = self.import_config.prompt_path or "import/system"
279
+ try:
280
+ system_prompt = self._loader.render(sys_prompt_path, {})
281
+ except Exception as e:
282
+ logger.warning(f"Failed to load import system prompt: {e}")
283
+ system_prompt = DEFAULT_IMPORT_MCP_SERVER_PROMPT
226
284
 
227
285
  options = ClaudeAgentOptions(
228
- system_prompt=self.import_config.prompt,
286
+ system_prompt=system_prompt,
229
287
  max_turns=5, # More turns for search + fetch
230
288
  model=self.import_config.model,
231
289
  allowed_tools=["WebSearch", "WebFetch"],
@@ -29,6 +29,8 @@ At the start of EVERY session:
29
29
  3. Session ID: Look for `session_id: <uuid>` in your context.
30
30
  If missing, call:
31
31
  `call_tool("gobby-sessions", "get_current", {"external_id": "<your-session-id>", "source": "claude"})`
32
+
33
+ Session and task references use `#N` format (e.g., `#1`, `#42`) which is project-scoped.
32
34
  </startup>
33
35
 
34
36
  <tool_discovery>
@@ -114,7 +114,7 @@ def setup_internal_registries(
114
114
  # Initialize sessions registry (messages + session CRUD)
115
115
  # Register if either message_manager or local_session_manager is available
116
116
  if message_manager is not None or local_session_manager is not None:
117
- from gobby.mcp_proxy.tools.session_messages import create_session_messages_registry
117
+ from gobby.mcp_proxy.tools.sessions import create_session_messages_registry
118
118
 
119
119
  session_messages_registry = create_session_messages_registry(
120
120
  message_manager=message_manager,
@@ -173,7 +173,6 @@ def setup_internal_registries(
173
173
 
174
174
  agents_registry = create_agents_registry(
175
175
  runner=agent_runner,
176
- tool_proxy_getter=tool_proxy_getter,
177
176
  )
178
177
 
179
178
  # Add inter-agent messaging tools if message manager is available
@@ -198,7 +197,6 @@ def setup_internal_registries(
198
197
  worktree_storage=worktree_storage,
199
198
  git_manager=git_manager,
200
199
  project_id=project_id,
201
- agent_runner=agent_runner,
202
200
  )
203
201
  manager.add_registry(worktrees_registry)
204
202
  logger.debug("Worktrees registry initialized")
@@ -222,7 +220,6 @@ def setup_internal_registries(
222
220
  clone_storage=clone_storage,
223
221
  git_manager=clone_git_manager,
224
222
  project_id=project_id or "",
225
- agent_runner=agent_runner,
226
223
  )
227
224
  manager.add_registry(clones_registry)
228
225
  logger.debug("Clones registry initialized")
@@ -7,13 +7,30 @@ import logging
7
7
  from typing import TYPE_CHECKING, Any, Literal
8
8
 
9
9
  if TYPE_CHECKING:
10
- from gobby.config.app import RecommendToolsConfig
10
+ from gobby.config.features import RecommendToolsConfig
11
+ from gobby.prompts import PromptLoader
11
12
 
12
13
  logger = logging.getLogger("gobby.mcp.server")
13
14
 
14
15
  # Search mode type
15
16
  SearchMode = Literal["llm", "semantic", "hybrid"]
16
17
 
18
+ DEFAULT_HYBRID_RERANK_PROMPT = """Re-rank the following tools for the task: "{task_description}"
19
+
20
+ Candidates:
21
+ {candidate_list}
22
+
23
+ Select the best {top_k} tools. Return JSON:
24
+ {{"recommendations": [{{"server": "...", "tool": "...", "reason": "..."}}]}}"""
25
+
26
+ DEFAULT_LLM_PROMPT = """Recommend tools for the task: "{task_description}"
27
+
28
+ Available Servers:
29
+ {available_servers}
30
+
31
+ Return JSON:
32
+ {{"recommendations": [{{"server": "...", "tool": "...", "reason": "..."}}]}}"""
33
+
17
34
 
18
35
  class RecommendationService:
19
36
  """Service for recommending tools."""
@@ -31,12 +48,17 @@ class RecommendationService:
31
48
  self._semantic_search = semantic_search
32
49
  self._project_id = project_id
33
50
  self._config = config
51
+ self._loader = PromptLoader()
52
+ self._loader.register_fallback(
53
+ "features/recommend_hybrid", lambda: DEFAULT_HYBRID_RERANK_PROMPT
54
+ )
55
+ self._loader.register_fallback("features/recommend_llm", lambda: DEFAULT_LLM_PROMPT)
34
56
 
35
57
  def _get_config(self) -> RecommendToolsConfig:
36
58
  """Get config with fallback to defaults."""
37
59
  if self._config is not None:
38
60
  return self._config
39
- from gobby.config.app import RecommendToolsConfig
61
+ from gobby.config.features import RecommendToolsConfig
40
62
 
41
63
  return RecommendToolsConfig()
42
64
 
@@ -153,11 +175,16 @@ class RecommendationService:
153
175
  for c in candidates
154
176
  )
155
177
 
156
- prompt = config.hybrid_rerank_prompt.format(
157
- task_description=task_description,
158
- candidate_list=candidate_list,
159
- top_k=top_k,
160
- )
178
+ prompt_path = config.hybrid_rerank_prompt_path or "features/recommend_hybrid"
179
+ context = {
180
+ "task_description": task_description,
181
+ "candidate_list": candidate_list,
182
+ "top_k": top_k,
183
+ }
184
+ try:
185
+ prompt = self._loader.render(prompt_path, context)
186
+ except Exception:
187
+ prompt = DEFAULT_HYBRID_RERANK_PROMPT.format(**context)
161
188
 
162
189
  provider = self._llm_service.get_default_provider()
163
190
  response = await provider.generate_text(prompt)
@@ -191,10 +218,15 @@ class RecommendationService:
191
218
  config = self._get_config()
192
219
  available_servers = self._mcp_manager.get_available_servers()
193
220
 
194
- prompt = config.llm_prompt.format(
195
- task_description=task_description,
196
- available_servers=", ".join(available_servers),
197
- )
221
+ prompt_path = config.llm_prompt_path or "features/recommend_llm"
222
+ context = {
223
+ "task_description": task_description,
224
+ "available_servers": ", ".join(available_servers),
225
+ }
226
+ try:
227
+ prompt = self._loader.render(prompt_path, context)
228
+ except Exception:
229
+ prompt = DEFAULT_LLM_PROMPT.format(**context)
198
230
 
199
231
  provider = self._llm_service.get_default_provider()
200
232
  response = await provider.generate_text(prompt)