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.
Files changed (148) hide show
  1. gobby/adapters/claude_code.py +13 -4
  2. gobby/adapters/codex.py +43 -3
  3. gobby/agents/runner.py +8 -0
  4. gobby/cli/__init__.py +6 -0
  5. gobby/cli/clones.py +419 -0
  6. gobby/cli/conductor.py +266 -0
  7. gobby/cli/installers/antigravity.py +3 -9
  8. gobby/cli/installers/claude.py +9 -9
  9. gobby/cli/installers/codex.py +2 -8
  10. gobby/cli/installers/gemini.py +2 -8
  11. gobby/cli/installers/shared.py +71 -8
  12. gobby/cli/skills.py +858 -0
  13. gobby/cli/tasks/ai.py +0 -440
  14. gobby/cli/tasks/crud.py +44 -6
  15. gobby/cli/tasks/main.py +0 -4
  16. gobby/cli/tui.py +2 -2
  17. gobby/cli/utils.py +3 -3
  18. gobby/clones/__init__.py +13 -0
  19. gobby/clones/git.py +547 -0
  20. gobby/conductor/__init__.py +16 -0
  21. gobby/conductor/alerts.py +135 -0
  22. gobby/conductor/loop.py +164 -0
  23. gobby/conductor/monitors/__init__.py +11 -0
  24. gobby/conductor/monitors/agents.py +116 -0
  25. gobby/conductor/monitors/tasks.py +155 -0
  26. gobby/conductor/pricing.py +234 -0
  27. gobby/conductor/token_tracker.py +160 -0
  28. gobby/config/app.py +63 -1
  29. gobby/config/search.py +110 -0
  30. gobby/config/servers.py +1 -1
  31. gobby/config/skills.py +43 -0
  32. gobby/config/tasks.py +6 -14
  33. gobby/hooks/event_handlers.py +145 -2
  34. gobby/hooks/hook_manager.py +48 -2
  35. gobby/hooks/skill_manager.py +130 -0
  36. gobby/install/claude/hooks/hook_dispatcher.py +4 -4
  37. gobby/install/codex/hooks/hook_dispatcher.py +1 -1
  38. gobby/install/gemini/hooks/hook_dispatcher.py +87 -12
  39. gobby/llm/claude.py +22 -34
  40. gobby/llm/claude_executor.py +46 -256
  41. gobby/llm/codex_executor.py +59 -291
  42. gobby/llm/executor.py +21 -0
  43. gobby/llm/gemini.py +134 -110
  44. gobby/llm/litellm_executor.py +143 -6
  45. gobby/llm/resolver.py +95 -33
  46. gobby/mcp_proxy/instructions.py +54 -0
  47. gobby/mcp_proxy/models.py +15 -0
  48. gobby/mcp_proxy/registries.py +68 -5
  49. gobby/mcp_proxy/server.py +33 -3
  50. gobby/mcp_proxy/services/tool_proxy.py +81 -1
  51. gobby/mcp_proxy/stdio.py +2 -1
  52. gobby/mcp_proxy/tools/__init__.py +0 -2
  53. gobby/mcp_proxy/tools/agent_messaging.py +317 -0
  54. gobby/mcp_proxy/tools/clones.py +903 -0
  55. gobby/mcp_proxy/tools/memory.py +1 -24
  56. gobby/mcp_proxy/tools/metrics.py +65 -1
  57. gobby/mcp_proxy/tools/orchestration/__init__.py +3 -0
  58. gobby/mcp_proxy/tools/orchestration/cleanup.py +151 -0
  59. gobby/mcp_proxy/tools/orchestration/wait.py +467 -0
  60. gobby/mcp_proxy/tools/session_messages.py +1 -2
  61. gobby/mcp_proxy/tools/skills/__init__.py +631 -0
  62. gobby/mcp_proxy/tools/task_orchestration.py +7 -0
  63. gobby/mcp_proxy/tools/task_readiness.py +14 -0
  64. gobby/mcp_proxy/tools/task_sync.py +1 -1
  65. gobby/mcp_proxy/tools/tasks/_context.py +0 -20
  66. gobby/mcp_proxy/tools/tasks/_crud.py +91 -4
  67. gobby/mcp_proxy/tools/tasks/_expansion.py +348 -0
  68. gobby/mcp_proxy/tools/tasks/_factory.py +6 -16
  69. gobby/mcp_proxy/tools/tasks/_lifecycle.py +60 -29
  70. gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +18 -29
  71. gobby/mcp_proxy/tools/workflows.py +1 -1
  72. gobby/mcp_proxy/tools/worktrees.py +5 -0
  73. gobby/memory/backends/__init__.py +6 -1
  74. gobby/memory/backends/mem0.py +6 -1
  75. gobby/memory/extractor.py +477 -0
  76. gobby/memory/manager.py +11 -2
  77. gobby/prompts/defaults/handoff/compact.md +63 -0
  78. gobby/prompts/defaults/handoff/session_end.md +57 -0
  79. gobby/prompts/defaults/memory/extract.md +61 -0
  80. gobby/runner.py +37 -16
  81. gobby/search/__init__.py +48 -6
  82. gobby/search/backends/__init__.py +159 -0
  83. gobby/search/backends/embedding.py +225 -0
  84. gobby/search/embeddings.py +238 -0
  85. gobby/search/models.py +148 -0
  86. gobby/search/unified.py +496 -0
  87. gobby/servers/http.py +23 -8
  88. gobby/servers/routes/admin.py +280 -0
  89. gobby/servers/routes/mcp/tools.py +241 -52
  90. gobby/servers/websocket.py +2 -2
  91. gobby/sessions/analyzer.py +2 -0
  92. gobby/sessions/transcripts/base.py +1 -0
  93. gobby/sessions/transcripts/claude.py +64 -5
  94. gobby/skills/__init__.py +91 -0
  95. gobby/skills/loader.py +685 -0
  96. gobby/skills/manager.py +384 -0
  97. gobby/skills/parser.py +258 -0
  98. gobby/skills/search.py +463 -0
  99. gobby/skills/sync.py +119 -0
  100. gobby/skills/updater.py +385 -0
  101. gobby/skills/validator.py +368 -0
  102. gobby/storage/clones.py +378 -0
  103. gobby/storage/database.py +1 -1
  104. gobby/storage/memories.py +43 -13
  105. gobby/storage/migrations.py +180 -6
  106. gobby/storage/sessions.py +73 -0
  107. gobby/storage/skills.py +749 -0
  108. gobby/storage/tasks/_crud.py +4 -4
  109. gobby/storage/tasks/_lifecycle.py +41 -6
  110. gobby/storage/tasks/_manager.py +14 -5
  111. gobby/storage/tasks/_models.py +8 -3
  112. gobby/sync/memories.py +39 -4
  113. gobby/sync/tasks.py +83 -6
  114. gobby/tasks/__init__.py +1 -2
  115. gobby/tasks/validation.py +24 -15
  116. gobby/tui/api_client.py +4 -7
  117. gobby/tui/app.py +5 -3
  118. gobby/tui/screens/orchestrator.py +1 -2
  119. gobby/tui/screens/tasks.py +2 -4
  120. gobby/tui/ws_client.py +1 -1
  121. gobby/utils/daemon_client.py +2 -2
  122. gobby/workflows/actions.py +84 -2
  123. gobby/workflows/context_actions.py +43 -0
  124. gobby/workflows/detection_helpers.py +115 -31
  125. gobby/workflows/engine.py +13 -2
  126. gobby/workflows/lifecycle_evaluator.py +29 -1
  127. gobby/workflows/loader.py +19 -6
  128. gobby/workflows/memory_actions.py +74 -0
  129. gobby/workflows/summary_actions.py +17 -0
  130. gobby/workflows/task_enforcement_actions.py +448 -6
  131. {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/METADATA +82 -21
  132. {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/RECORD +136 -107
  133. gobby/install/codex/prompts/forget.md +0 -7
  134. gobby/install/codex/prompts/memories.md +0 -7
  135. gobby/install/codex/prompts/recall.md +0 -7
  136. gobby/install/codex/prompts/remember.md +0 -13
  137. gobby/llm/gemini_executor.py +0 -339
  138. gobby/mcp_proxy/tools/task_expansion.py +0 -591
  139. gobby/tasks/context.py +0 -747
  140. gobby/tasks/criteria.py +0 -342
  141. gobby/tasks/expansion.py +0 -626
  142. gobby/tasks/prompts/expand.py +0 -327
  143. gobby/tasks/research.py +0 -421
  144. gobby/tasks/tdd.py +0 -352
  145. {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/WHEEL +0 -0
  146. {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/entry_points.txt +0 -0
  147. {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/licenses/LICENSE.md +0 -0
  148. {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/top_level.txt +0 -0
gobby/tasks/criteria.py DELETED
@@ -1,342 +0,0 @@
1
- """
2
- Criteria generation for task expansion.
3
-
4
- This module provides:
5
- - PatternCriteriaInjector: Detects patterns from labels/descriptions and injects criteria
6
- - CriteriaGenerator: Shared criteria generator for both structured and LLM expansion
7
- """
8
-
9
- from __future__ import annotations
10
-
11
- import logging
12
- import re
13
- from typing import TYPE_CHECKING
14
-
15
- if TYPE_CHECKING:
16
- from gobby.config.app import PatternCriteriaConfig, ProjectVerificationConfig
17
- from gobby.storage.tasks import Task
18
- from gobby.tasks.context import ExpansionContext
19
-
20
- logger = logging.getLogger(__name__)
21
-
22
-
23
- class PatternCriteriaInjector:
24
- """Injects pattern-specific validation criteria into task expansion.
25
-
26
- Detects patterns from:
27
- - Task labels (e.g., 'strangler-fig', 'tdd')
28
- - Description keywords (e.g., 'using strangler fig pattern')
29
-
30
- Then injects appropriate criteria templates with placeholders replaced
31
- by actual verification commands from project config.
32
- """
33
-
34
- def __init__(
35
- self,
36
- pattern_config: PatternCriteriaConfig,
37
- verification_config: ProjectVerificationConfig | None = None,
38
- ):
39
- """Initialize the injector.
40
-
41
- Args:
42
- pattern_config: Pattern criteria configuration with templates
43
- verification_config: Project verification commands for placeholder substitution
44
- """
45
- self.pattern_config = pattern_config
46
- self.verification_config = verification_config
47
-
48
- def detect_patterns(
49
- self,
50
- task: Task,
51
- labels: list[str] | None = None,
52
- ) -> list[str]:
53
- """Detect which patterns apply to a task.
54
-
55
- Args:
56
- task: The task to analyze
57
- labels: Optional explicit labels (overrides task.labels)
58
-
59
- Returns:
60
- List of detected pattern names
61
- """
62
- detected: list[str] = []
63
- task_labels = labels if labels is not None else (task.labels or [])
64
-
65
- # Normalize labels to lowercase for matching
66
- normalized_labels = [label.lower() for label in task_labels]
67
-
68
- # Check labels first (direct match)
69
- for pattern_name in self.pattern_config.patterns.keys():
70
- if pattern_name.lower() in normalized_labels:
71
- if pattern_name not in detected:
72
- detected.append(pattern_name)
73
- logger.debug(f"Detected pattern '{pattern_name}' from task labels")
74
-
75
- # Check description keywords
76
- description = (task.description or "").lower()
77
- title = (task.title or "").lower()
78
- text_to_search = f"{title} {description}"
79
-
80
- for pattern_name, keywords in self.pattern_config.detection_keywords.items():
81
- if pattern_name in detected:
82
- continue # Already detected from labels
83
-
84
- for keyword in keywords:
85
- if keyword.lower() in text_to_search:
86
- detected.append(pattern_name)
87
- logger.debug(
88
- f"Detected pattern '{pattern_name}' from keyword '{keyword}' in description"
89
- )
90
- break
91
-
92
- return detected
93
-
94
- def _get_placeholder_values(self) -> dict[str, str]:
95
- """Get placeholder values from verification config.
96
-
97
- Returns:
98
- Dict mapping placeholder names to their values
99
- """
100
- values: dict[str, str] = {}
101
-
102
- if self.verification_config:
103
- if self.verification_config.unit_tests:
104
- values["unit_tests"] = self.verification_config.unit_tests
105
- if self.verification_config.type_check:
106
- values["type_check"] = self.verification_config.type_check
107
- if self.verification_config.lint:
108
- values["lint"] = self.verification_config.lint
109
- if self.verification_config.integration:
110
- values["integration"] = self.verification_config.integration
111
-
112
- # Add any custom verification commands
113
- for name, cmd in self.verification_config.custom.items():
114
- values[name] = cmd
115
-
116
- return values
117
-
118
- def _substitute_placeholders(
119
- self,
120
- template: str,
121
- extra_values: dict[str, str] | None = None,
122
- ) -> str:
123
- """Substitute placeholders in a template string.
124
-
125
- Args:
126
- template: Template string with {placeholder} syntax
127
- extra_values: Additional values to substitute
128
-
129
- Returns:
130
- String with placeholders replaced
131
- """
132
- values = self._get_placeholder_values()
133
- if extra_values:
134
- values.update(extra_values)
135
-
136
- # Use a regex to find placeholders and replace only those we have values for
137
- def replace_placeholder(match: re.Match[str]) -> str:
138
- key = match.group(1)
139
- return values.get(key, match.group(0)) # Keep original if no value
140
-
141
- return re.sub(r"\{(\w+)\}", replace_placeholder, template)
142
-
143
- def inject(
144
- self,
145
- task: Task,
146
- context: ExpansionContext | None = None,
147
- labels: list[str] | None = None,
148
- extra_placeholders: dict[str, str] | None = None,
149
- ) -> str:
150
- """Generate pattern-specific criteria markdown for a task.
151
-
152
- Args:
153
- task: The task to generate criteria for
154
- context: Optional expansion context (unused currently, for future extensibility)
155
- labels: Optional explicit labels (overrides task.labels)
156
- extra_placeholders: Additional placeholder values for substitution
157
-
158
- Returns:
159
- Markdown-formatted criteria string, empty if no patterns detected
160
- """
161
- detected_patterns = self.detect_patterns(task, labels)
162
-
163
- if not detected_patterns:
164
- return ""
165
-
166
- sections: list[str] = []
167
-
168
- for pattern_name in detected_patterns:
169
- templates = self.pattern_config.patterns.get(pattern_name, [])
170
- if not templates:
171
- continue
172
-
173
- criteria_lines: list[str] = []
174
- for template in templates:
175
- substituted = self._substitute_placeholders(template, extra_placeholders)
176
- criteria_lines.append(f"- [ ] {substituted}")
177
-
178
- if criteria_lines:
179
- # Format pattern name nicely (e.g., "strangler-fig" -> "Strangler-Fig Pattern")
180
- pattern_title = pattern_name.replace("-", " ").title()
181
- section = f"## {pattern_title} Pattern Criteria\n\n" + "\n".join(criteria_lines)
182
- sections.append(section)
183
-
184
- return "\n\n".join(sections)
185
-
186
- def inject_for_labels(
187
- self,
188
- labels: list[str],
189
- task: Task | None = None,
190
- extra_placeholders: dict[str, str] | None = None,
191
- ) -> str:
192
- """Generate criteria based on explicit labels without a full task.
193
-
194
- This is a convenience method when you only have labels and optionally a task.
195
-
196
- Args:
197
- labels: List of pattern labels
198
- task: Optional task for additional context
199
- extra_placeholders: Additional placeholder values
200
-
201
- Returns:
202
- Markdown-formatted criteria string
203
- """
204
- if task:
205
- return self.inject(task, labels=labels, extra_placeholders=extra_placeholders)
206
-
207
- # Create minimal criteria from labels only
208
- sections: list[str] = []
209
- normalized_labels = [label.lower() for label in labels]
210
-
211
- for pattern_name in self.pattern_config.patterns.keys():
212
- if pattern_name.lower() not in normalized_labels:
213
- continue
214
-
215
- templates = self.pattern_config.patterns.get(pattern_name, [])
216
- if not templates:
217
- continue
218
-
219
- criteria_lines: list[str] = []
220
- for template in templates:
221
- substituted = self._substitute_placeholders(template, extra_placeholders)
222
- criteria_lines.append(f"- [ ] {substituted}")
223
-
224
- if criteria_lines:
225
- pattern_title = pattern_name.replace("-", " ").title()
226
- section = f"## {pattern_title} Pattern Criteria\n\n" + "\n".join(criteria_lines)
227
- sections.append(section)
228
-
229
- return "\n\n".join(sections)
230
-
231
-
232
- class CriteriaGenerator:
233
- """Shared criteria generator for both structured and LLM expansion.
234
-
235
- Generates validation criteria by combining:
236
- - Pattern-specific criteria (from labels/description)
237
- - Verification command criteria (from project config)
238
- - File-specific criteria (when relevant files provided)
239
-
240
- Can be used by:
241
- - TaskExpander (LLM expansion)
242
- - TaskHierarchyBuilder (structured expansion)
243
- """
244
-
245
- def __init__(
246
- self,
247
- pattern_config: PatternCriteriaConfig,
248
- verification_config: ProjectVerificationConfig | None = None,
249
- ):
250
- """Initialize the generator.
251
-
252
- Args:
253
- pattern_config: Pattern criteria configuration with templates
254
- verification_config: Project verification commands configuration
255
- """
256
- self.pattern_injector = PatternCriteriaInjector(
257
- pattern_config=pattern_config,
258
- verification_config=verification_config,
259
- )
260
- self.verification_config = verification_config
261
-
262
- def generate(
263
- self,
264
- title: str,
265
- description: str | None = None,
266
- labels: list[str] | None = None,
267
- relevant_files: list[str] | None = None,
268
- verification_commands: dict[str, str] | None = None,
269
- ) -> str:
270
- """Generate validation criteria markdown.
271
-
272
- Args:
273
- title: Task title
274
- description: Task description
275
- labels: Optional labels for pattern detection
276
- relevant_files: Optional list of relevant file paths
277
- verification_commands: Optional verification commands override
278
-
279
- Returns:
280
- Markdown-formatted validation criteria string
281
- """
282
- criteria_parts: list[str] = []
283
-
284
- # 1. Pattern-specific criteria from labels
285
- if labels:
286
- pattern_criteria = self.pattern_injector.inject_for_labels(
287
- labels=labels,
288
- extra_placeholders=verification_commands,
289
- )
290
- if pattern_criteria:
291
- criteria_parts.append(pattern_criteria)
292
-
293
- # 2. File-specific criteria
294
- if relevant_files and description:
295
- text_to_check = (title + " " + (description or "")).lower()
296
- matching_files = [f for f in relevant_files if f.lower() in text_to_check]
297
- if matching_files:
298
- file_criteria = ["## File Requirements", ""]
299
- for f in matching_files:
300
- file_criteria.append(f"- [ ] `{f}` is correctly modified/created")
301
- criteria_parts.append("\n".join(file_criteria))
302
-
303
- # 3. Verification command criteria
304
- verification = self._get_verification_commands(verification_commands)
305
- if verification:
306
- verification_criteria = ["## Verification", ""]
307
- for name, cmd in verification.items():
308
- if name in ["unit_tests", "type_check", "lint"]:
309
- verification_criteria.append(f"- [ ] `{cmd}` passes")
310
- if len(verification_criteria) > 2: # Has items beyond header
311
- criteria_parts.append("\n".join(verification_criteria))
312
-
313
- return "\n\n".join(criteria_parts) if criteria_parts else ""
314
-
315
- def _get_verification_commands(
316
- self,
317
- override: dict[str, str] | None = None,
318
- ) -> dict[str, str]:
319
- """Get verification commands from config or override.
320
-
321
- Args:
322
- override: Optional override commands
323
-
324
- Returns:
325
- Dict of verification command name -> command
326
- """
327
- if override:
328
- return override
329
-
330
- commands: dict[str, str] = {}
331
- if self.verification_config:
332
- if self.verification_config.unit_tests:
333
- commands["unit_tests"] = self.verification_config.unit_tests
334
- if self.verification_config.type_check:
335
- commands["type_check"] = self.verification_config.type_check
336
- if self.verification_config.lint:
337
- commands["lint"] = self.verification_config.lint
338
- if self.verification_config.integration:
339
- commands["integration"] = self.verification_config.integration
340
- for name, cmd in self.verification_config.custom.items():
341
- commands[name] = cmd
342
- return commands