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
@@ -0,0 +1,253 @@
1
+ """Session CRUD tools for session management.
2
+
3
+ This module contains MCP tools for:
4
+ - Getting session details (get_session)
5
+ - Getting current session (get_current)
6
+ - Listing sessions (list_sessions)
7
+ - Session statistics (session_stats)
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import TYPE_CHECKING, Any
13
+
14
+ if TYPE_CHECKING:
15
+ from gobby.mcp_proxy.tools.internal import InternalToolRegistry
16
+ from gobby.storage.sessions import LocalSessionManager
17
+
18
+
19
+ def register_crud_tools(
20
+ registry: InternalToolRegistry,
21
+ session_manager: LocalSessionManager,
22
+ ) -> None:
23
+ """
24
+ Register session CRUD tools with a registry.
25
+
26
+ Args:
27
+ registry: The InternalToolRegistry to register tools with
28
+ session_manager: LocalSessionManager instance for session operations
29
+ """
30
+
31
+ @registry.tool(
32
+ name="get_session",
33
+ description="Get session details by ID. Accepts #N (project-scoped ref), UUID, or prefix. Use the session_id from your injected context.",
34
+ )
35
+ def get_session(session_id: str) -> dict[str, Any]:
36
+ """
37
+ Get session details by session reference.
38
+
39
+ Your session_id is injected into your context at session start.
40
+ Look for 'Session Ref: #N' or 'session_id: xxx' in your system reminders.
41
+
42
+ Args:
43
+ session_id: Session reference - supports #N (project-scoped), UUID, or prefix
44
+
45
+ Returns:
46
+ Session dict with all fields, or error if not found
47
+ """
48
+ from gobby.utils.project_context import get_project_context
49
+
50
+ # Support #N format, UUID, and prefix matching
51
+ if session_manager is None:
52
+ return {"error": "Session manager not available"}
53
+
54
+ # Get project_id for project-scoped resolution
55
+ project_ctx = get_project_context()
56
+ project_id = project_ctx.get("id") if project_ctx else None
57
+
58
+ # Try to resolve session reference (#N, UUID, or prefix)
59
+ try:
60
+ resolved_id = session_manager.resolve_session_reference(session_id, project_id)
61
+ session = session_manager.get(resolved_id)
62
+ except ValueError:
63
+ session = None
64
+
65
+ if not session:
66
+ return {"error": f"Session {session_id} not found", "found": False}
67
+
68
+ return {
69
+ "found": True,
70
+ **session.to_dict(),
71
+ }
72
+
73
+ @registry.tool(
74
+ name="get_current",
75
+ description="""Get YOUR current session ID - the CORRECT way to look up your session.
76
+
77
+ Use this when session_id wasn't in your injected context. Pass your external_id
78
+ (from transcript path or GOBBY_SESSION_ID env) and source (claude, gemini, codex).
79
+
80
+ DO NOT use list_sessions to find your session - it won't work with multiple active sessions.""",
81
+ )
82
+ def get_current(
83
+ external_id: str,
84
+ source: str,
85
+ ) -> dict[str, Any]:
86
+ """
87
+ Look up your internal session_id from external_id and source.
88
+
89
+ The agent passes external_id (from injected context or GOBBY_SESSION_ID env var)
90
+ and source (claude, gemini, codex). project_id and machine_id are
91
+ auto-resolved from config files.
92
+
93
+ Args:
94
+ external_id: Your CLI's session ID (from context or GOBBY_SESSION_ID env)
95
+ source: CLI source - "claude", "gemini", or "codex"
96
+
97
+ Returns:
98
+ session_id: Internal Gobby session ID (use for parent_session_id, etc.)
99
+ Plus basic session metadata
100
+ """
101
+ from gobby.utils.machine_id import get_machine_id
102
+ from gobby.utils.project_context import get_project_context
103
+
104
+ if session_manager is None:
105
+ return {"error": "Session manager not available"}
106
+
107
+ # Auto-resolve context
108
+ machine_id = get_machine_id()
109
+ project_ctx = get_project_context()
110
+ project_id = project_ctx.get("id") if project_ctx else None
111
+
112
+ if not machine_id:
113
+ return {"error": "Could not determine machine_id"}
114
+ if not project_id:
115
+ return {"error": "Could not determine project_id (not in a gobby project?)"}
116
+
117
+ # Use find_by_external_id with full composite key (safe lookup)
118
+ session = session_manager.find_by_external_id(
119
+ external_id=external_id,
120
+ machine_id=machine_id,
121
+ project_id=project_id,
122
+ source=source,
123
+ )
124
+
125
+ if not session:
126
+ return {
127
+ "found": False,
128
+ "error": "Session not found",
129
+ "lookup": {
130
+ "external_id": external_id,
131
+ "source": source,
132
+ "project_id": project_id,
133
+ },
134
+ }
135
+
136
+ return {
137
+ "found": True,
138
+ "session_id": session.id,
139
+ "project_id": session.project_id,
140
+ "status": session.status,
141
+ "agent_run_id": session.agent_run_id,
142
+ }
143
+
144
+ @registry.tool(
145
+ name="list_sessions",
146
+ description="""List sessions with optional filtering.
147
+
148
+ WARNING: Do NOT use this to find your own session_id!
149
+ - `list_sessions(status="active", limit=1)` will NOT reliably return YOUR session
150
+ - Multiple sessions can be active simultaneously (parallel agents, multiple terminals)
151
+ - Use `get_current(external_id, source)` instead - it uses your unique session key
152
+
153
+ This tool is for browsing/listing sessions, not for self-identification.""",
154
+ )
155
+ def list_sessions(
156
+ project_id: str | None = None,
157
+ status: str | None = None,
158
+ source: str | None = None,
159
+ limit: int = 20,
160
+ ) -> dict[str, Any]:
161
+ """
162
+ List sessions with filters.
163
+
164
+ Args:
165
+ project_id: Filter by project ID
166
+ status: Filter by status (active, paused, expired, archived, handoff_ready)
167
+ source: Filter by CLI source (claude, gemini, codex)
168
+ limit: Max results (default 20)
169
+
170
+ Returns:
171
+ List of sessions and count
172
+ """
173
+ if session_manager is None:
174
+ return {"error": "Session manager not available"}
175
+
176
+ sessions = session_manager.list(
177
+ project_id=project_id,
178
+ status=status,
179
+ source=source,
180
+ limit=limit,
181
+ )
182
+
183
+ total = session_manager.count(
184
+ project_id=project_id,
185
+ status=status,
186
+ source=source,
187
+ )
188
+
189
+ # Detect likely misuse pattern: trying to find own session
190
+ if status == "active" and limit == 1:
191
+ return {
192
+ "warning": (
193
+ "list_sessions(status='active', limit=1) will NOT reliably get YOUR session_id! "
194
+ "Multiple sessions can be active simultaneously. "
195
+ "Use get_current(external_id='<your-external-id>', source='claude') instead."
196
+ ),
197
+ "hint": "Your external_id is in your transcript path: /path/to/<external_id>.jsonl",
198
+ "sessions": [s.to_dict() for s in sessions],
199
+ "count": len(sessions),
200
+ "total": total,
201
+ "limit": limit,
202
+ "filters": {
203
+ "project_id": project_id,
204
+ "status": status,
205
+ "source": source,
206
+ },
207
+ }
208
+
209
+ return {
210
+ "sessions": [s.to_dict() for s in sessions],
211
+ "count": len(sessions),
212
+ "total": total,
213
+ "limit": limit,
214
+ "filters": {
215
+ "project_id": project_id,
216
+ "status": status,
217
+ "source": source,
218
+ },
219
+ }
220
+
221
+ @registry.tool(
222
+ name="session_stats",
223
+ description="Get session statistics for a project.",
224
+ )
225
+ def session_stats(project_id: str | None = None) -> dict[str, Any]:
226
+ """
227
+ Get session statistics.
228
+
229
+ Args:
230
+ project_id: Filter by project ID (optional)
231
+
232
+ Returns:
233
+ Statistics including total, by_status, by_source
234
+ """
235
+ if session_manager is None:
236
+ return {"error": "Session manager not available"}
237
+
238
+ total = session_manager.count(project_id=project_id)
239
+ by_status = session_manager.count_by_status()
240
+
241
+ # Count by source
242
+ by_source: dict[str, int] = {}
243
+ for src in ["claude_code", "gemini", "codex"]:
244
+ count = session_manager.count(project_id=project_id, source=src)
245
+ if count > 0:
246
+ by_source[src] = count
247
+
248
+ return {
249
+ "total": total,
250
+ "by_status": by_status,
251
+ "by_source": by_source,
252
+ "project_id": project_id,
253
+ }
@@ -0,0 +1,63 @@
1
+ """Factory function for creating the session messages tool registry.
2
+
3
+ Orchestrates the creation of all session tool sub-registries and merges them
4
+ into a unified registry.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import TYPE_CHECKING
10
+
11
+ from gobby.mcp_proxy.tools.internal import InternalToolRegistry
12
+ from gobby.mcp_proxy.tools.sessions._commits import register_commits_tools
13
+ from gobby.mcp_proxy.tools.sessions._crud import register_crud_tools
14
+ from gobby.mcp_proxy.tools.sessions._handoff import register_handoff_tools
15
+ from gobby.mcp_proxy.tools.sessions._messages import register_message_tools
16
+
17
+ if TYPE_CHECKING:
18
+ from gobby.storage.session_messages import LocalSessionMessageManager
19
+ from gobby.storage.sessions import LocalSessionManager
20
+
21
+ __all__ = ["create_session_messages_registry"]
22
+
23
+
24
+ def create_session_messages_registry(
25
+ message_manager: LocalSessionMessageManager | None = None,
26
+ session_manager: LocalSessionManager | None = None,
27
+ ) -> InternalToolRegistry:
28
+ """
29
+ Create a sessions tool registry with session and message tools.
30
+
31
+ Args:
32
+ message_manager: LocalSessionMessageManager instance for message operations
33
+ session_manager: LocalSessionManager instance for session CRUD
34
+
35
+ Returns:
36
+ InternalToolRegistry with all session tools registered
37
+ """
38
+ registry = InternalToolRegistry(
39
+ name="gobby-sessions",
40
+ description="Session management and message querying - CRUD, retrieval, search",
41
+ )
42
+
43
+ # --- Message Tools ---
44
+ # Only register if message_manager is available
45
+ if message_manager is not None:
46
+ register_message_tools(registry, message_manager)
47
+
48
+ # --- Handoff Tools ---
49
+ # Only register if session_manager is available
50
+ if session_manager is not None:
51
+ register_handoff_tools(registry, session_manager)
52
+
53
+ # --- Session CRUD Tools ---
54
+ # Only register if session_manager is available
55
+ if session_manager is not None:
56
+ register_crud_tools(registry, session_manager)
57
+
58
+ # --- Commits Tools ---
59
+ # Only register if session_manager is available
60
+ if session_manager is not None:
61
+ register_commits_tools(registry, session_manager)
62
+
63
+ return registry