kollabor 0.4.9__py3-none-any.whl → 0.4.15__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 (192) hide show
  1. agents/__init__.py +2 -0
  2. agents/coder/__init__.py +0 -0
  3. agents/coder/agent.json +4 -0
  4. agents/coder/api-integration.md +2150 -0
  5. agents/coder/cli-pretty.md +765 -0
  6. agents/coder/code-review.md +1092 -0
  7. agents/coder/database-design.md +1525 -0
  8. agents/coder/debugging.md +1102 -0
  9. agents/coder/dependency-management.md +1397 -0
  10. agents/coder/git-workflow.md +1099 -0
  11. agents/coder/refactoring.md +1454 -0
  12. agents/coder/security-hardening.md +1732 -0
  13. agents/coder/system_prompt.md +1448 -0
  14. agents/coder/tdd.md +1367 -0
  15. agents/creative-writer/__init__.py +0 -0
  16. agents/creative-writer/agent.json +4 -0
  17. agents/creative-writer/character-development.md +1852 -0
  18. agents/creative-writer/dialogue-craft.md +1122 -0
  19. agents/creative-writer/plot-structure.md +1073 -0
  20. agents/creative-writer/revision-editing.md +1484 -0
  21. agents/creative-writer/system_prompt.md +690 -0
  22. agents/creative-writer/worldbuilding.md +2049 -0
  23. agents/data-analyst/__init__.py +30 -0
  24. agents/data-analyst/agent.json +4 -0
  25. agents/data-analyst/data-visualization.md +992 -0
  26. agents/data-analyst/exploratory-data-analysis.md +1110 -0
  27. agents/data-analyst/pandas-data-manipulation.md +1081 -0
  28. agents/data-analyst/sql-query-optimization.md +881 -0
  29. agents/data-analyst/statistical-analysis.md +1118 -0
  30. agents/data-analyst/system_prompt.md +928 -0
  31. agents/default/__init__.py +0 -0
  32. agents/default/agent.json +4 -0
  33. agents/default/dead-code.md +794 -0
  34. agents/default/explore-agent-system.md +585 -0
  35. agents/default/system_prompt.md +1448 -0
  36. agents/kollabor/__init__.py +0 -0
  37. agents/kollabor/analyze-plugin-lifecycle.md +175 -0
  38. agents/kollabor/analyze-terminal-rendering.md +388 -0
  39. agents/kollabor/code-review.md +1092 -0
  40. agents/kollabor/debug-mcp-integration.md +521 -0
  41. agents/kollabor/debug-plugin-hooks.md +547 -0
  42. agents/kollabor/debugging.md +1102 -0
  43. agents/kollabor/dependency-management.md +1397 -0
  44. agents/kollabor/git-workflow.md +1099 -0
  45. agents/kollabor/inspect-llm-conversation.md +148 -0
  46. agents/kollabor/monitor-event-bus.md +558 -0
  47. agents/kollabor/profile-performance.md +576 -0
  48. agents/kollabor/refactoring.md +1454 -0
  49. agents/kollabor/system_prompt copy.md +1448 -0
  50. agents/kollabor/system_prompt.md +757 -0
  51. agents/kollabor/trace-command-execution.md +178 -0
  52. agents/kollabor/validate-config.md +879 -0
  53. agents/research/__init__.py +0 -0
  54. agents/research/agent.json +4 -0
  55. agents/research/architecture-mapping.md +1099 -0
  56. agents/research/codebase-analysis.md +1077 -0
  57. agents/research/dependency-audit.md +1027 -0
  58. agents/research/performance-profiling.md +1047 -0
  59. agents/research/security-review.md +1359 -0
  60. agents/research/system_prompt.md +492 -0
  61. agents/technical-writer/__init__.py +0 -0
  62. agents/technical-writer/agent.json +4 -0
  63. agents/technical-writer/api-documentation.md +2328 -0
  64. agents/technical-writer/changelog-management.md +1181 -0
  65. agents/technical-writer/readme-writing.md +1360 -0
  66. agents/technical-writer/style-guide.md +1410 -0
  67. agents/technical-writer/system_prompt.md +653 -0
  68. agents/technical-writer/tutorial-creation.md +1448 -0
  69. core/__init__.py +0 -2
  70. core/application.py +343 -88
  71. core/cli.py +229 -10
  72. core/commands/menu_renderer.py +463 -59
  73. core/commands/registry.py +14 -9
  74. core/commands/system_commands.py +2461 -14
  75. core/config/loader.py +151 -37
  76. core/config/service.py +18 -6
  77. core/events/bus.py +29 -9
  78. core/events/executor.py +205 -75
  79. core/events/models.py +27 -8
  80. core/fullscreen/command_integration.py +20 -24
  81. core/fullscreen/components/__init__.py +10 -1
  82. core/fullscreen/components/matrix_components.py +1 -2
  83. core/fullscreen/components/space_shooter_components.py +654 -0
  84. core/fullscreen/plugin.py +5 -0
  85. core/fullscreen/renderer.py +52 -13
  86. core/fullscreen/session.py +52 -15
  87. core/io/__init__.py +29 -5
  88. core/io/buffer_manager.py +6 -1
  89. core/io/config_status_view.py +7 -29
  90. core/io/core_status_views.py +267 -347
  91. core/io/input/__init__.py +25 -0
  92. core/io/input/command_mode_handler.py +711 -0
  93. core/io/input/display_controller.py +128 -0
  94. core/io/input/hook_registrar.py +286 -0
  95. core/io/input/input_loop_manager.py +421 -0
  96. core/io/input/key_press_handler.py +502 -0
  97. core/io/input/modal_controller.py +1011 -0
  98. core/io/input/paste_processor.py +339 -0
  99. core/io/input/status_modal_renderer.py +184 -0
  100. core/io/input_errors.py +5 -1
  101. core/io/input_handler.py +211 -2452
  102. core/io/key_parser.py +7 -0
  103. core/io/layout.py +15 -3
  104. core/io/message_coordinator.py +111 -2
  105. core/io/message_renderer.py +129 -4
  106. core/io/status_renderer.py +147 -607
  107. core/io/terminal_renderer.py +97 -51
  108. core/io/terminal_state.py +21 -4
  109. core/io/visual_effects.py +816 -165
  110. core/llm/agent_manager.py +1063 -0
  111. core/llm/api_adapters/__init__.py +44 -0
  112. core/llm/api_adapters/anthropic_adapter.py +432 -0
  113. core/llm/api_adapters/base.py +241 -0
  114. core/llm/api_adapters/openai_adapter.py +326 -0
  115. core/llm/api_communication_service.py +167 -113
  116. core/llm/conversation_logger.py +322 -16
  117. core/llm/conversation_manager.py +556 -30
  118. core/llm/file_operations_executor.py +84 -32
  119. core/llm/llm_service.py +934 -103
  120. core/llm/mcp_integration.py +541 -57
  121. core/llm/message_display_service.py +135 -18
  122. core/llm/plugin_sdk.py +1 -2
  123. core/llm/profile_manager.py +1183 -0
  124. core/llm/response_parser.py +274 -56
  125. core/llm/response_processor.py +16 -3
  126. core/llm/tool_executor.py +6 -1
  127. core/logging/__init__.py +2 -0
  128. core/logging/setup.py +34 -6
  129. core/models/resume.py +54 -0
  130. core/plugins/__init__.py +4 -2
  131. core/plugins/base.py +127 -0
  132. core/plugins/collector.py +23 -161
  133. core/plugins/discovery.py +37 -3
  134. core/plugins/factory.py +6 -12
  135. core/plugins/registry.py +5 -17
  136. core/ui/config_widgets.py +128 -28
  137. core/ui/live_modal_renderer.py +2 -1
  138. core/ui/modal_actions.py +5 -0
  139. core/ui/modal_overlay_renderer.py +0 -60
  140. core/ui/modal_renderer.py +268 -7
  141. core/ui/modal_state_manager.py +29 -4
  142. core/ui/widgets/base_widget.py +7 -0
  143. core/updates/__init__.py +10 -0
  144. core/updates/version_check_service.py +348 -0
  145. core/updates/version_comparator.py +103 -0
  146. core/utils/config_utils.py +685 -526
  147. core/utils/plugin_utils.py +1 -1
  148. core/utils/session_naming.py +111 -0
  149. fonts/LICENSE +21 -0
  150. fonts/README.md +46 -0
  151. fonts/SymbolsNerdFont-Regular.ttf +0 -0
  152. fonts/SymbolsNerdFontMono-Regular.ttf +0 -0
  153. fonts/__init__.py +44 -0
  154. {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/METADATA +54 -4
  155. kollabor-0.4.15.dist-info/RECORD +228 -0
  156. {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/top_level.txt +2 -0
  157. plugins/agent_orchestrator/__init__.py +39 -0
  158. plugins/agent_orchestrator/activity_monitor.py +181 -0
  159. plugins/agent_orchestrator/file_attacher.py +77 -0
  160. plugins/agent_orchestrator/message_injector.py +135 -0
  161. plugins/agent_orchestrator/models.py +48 -0
  162. plugins/agent_orchestrator/orchestrator.py +403 -0
  163. plugins/agent_orchestrator/plugin.py +976 -0
  164. plugins/agent_orchestrator/xml_parser.py +191 -0
  165. plugins/agent_orchestrator_plugin.py +9 -0
  166. plugins/enhanced_input/box_styles.py +1 -0
  167. plugins/enhanced_input/color_engine.py +19 -4
  168. plugins/enhanced_input/config.py +2 -2
  169. plugins/enhanced_input_plugin.py +61 -11
  170. plugins/fullscreen/__init__.py +6 -2
  171. plugins/fullscreen/example_plugin.py +1035 -222
  172. plugins/fullscreen/setup_wizard_plugin.py +592 -0
  173. plugins/fullscreen/space_shooter_plugin.py +131 -0
  174. plugins/hook_monitoring_plugin.py +436 -78
  175. plugins/query_enhancer_plugin.py +66 -30
  176. plugins/resume_conversation_plugin.py +1494 -0
  177. plugins/save_conversation_plugin.py +98 -32
  178. plugins/system_commands_plugin.py +70 -56
  179. plugins/tmux_plugin.py +154 -78
  180. plugins/workflow_enforcement_plugin.py +94 -92
  181. system_prompt/default.md +952 -886
  182. core/io/input_mode_manager.py +0 -402
  183. core/io/modal_interaction_handler.py +0 -315
  184. core/io/raw_input_processor.py +0 -946
  185. core/storage/__init__.py +0 -5
  186. core/storage/state_manager.py +0 -84
  187. core/ui/widget_integration.py +0 -222
  188. core/utils/key_reader.py +0 -171
  189. kollabor-0.4.9.dist-info/RECORD +0 -128
  190. {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/WHEEL +0 -0
  191. {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/entry_points.txt +0 -0
  192. {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,403 @@
1
+ """Manages tmux sessions for agent sub-processes."""
2
+
3
+ import subprocess
4
+ import time
5
+ import asyncio
6
+ import fnmatch
7
+ import logging
8
+ from pathlib import Path
9
+ from typing import List, Optional, Tuple, Dict
10
+
11
+ from .models import AgentSession, AgentTask
12
+ from .file_attacher import FileAttacher
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class AgentOrchestrator:
18
+ """Manages tmux sessions for agent sub-processes."""
19
+
20
+ def __init__(self, project_name: str = None):
21
+ """Initialize orchestrator.
22
+
23
+ Args:
24
+ project_name: Project name for session naming. Defaults to cwd name.
25
+ """
26
+ self.project_name = project_name or Path.cwd().name
27
+ self.agents: Dict[str, AgentSession] = {}
28
+ self.file_attacher = FileAttacher()
29
+
30
+ # Timing configuration
31
+ self.session_init_delay = 1
32
+ self.kollab_init_delay = 7 # Allow time for kollab to fully initialize
33
+ self.message_delay = 2
34
+
35
+ # -------------------------------------------------------------------------
36
+ # Spawn
37
+ # -------------------------------------------------------------------------
38
+
39
+ async def spawn(self, name: str, task: str, files: List[str] = None) -> bool:
40
+ """Spawn a new agent session.
41
+
42
+ Args:
43
+ name: Agent name
44
+ task: Task description
45
+ files: Optional list of files to attach
46
+
47
+ Returns:
48
+ True if successful
49
+ """
50
+ full_name = f"{self.project_name}-{name}"
51
+
52
+ # Check if already exists
53
+ if self._session_exists(full_name):
54
+ logger.warning(f"Session already exists: {full_name}")
55
+ return False
56
+
57
+ # Create session
58
+ if not self._create_session(full_name):
59
+ logger.error(f"Failed to create session: {full_name}")
60
+ return False
61
+
62
+ # Start kollab
63
+ logger.info(f"[spawn] Sending 'kollab' command to {full_name}")
64
+ self._send_keys(full_name, "kollab")
65
+ logger.info(f"[spawn] Waiting {self.kollab_init_delay}s for kollab to initialize")
66
+ await asyncio.sleep(self.kollab_init_delay)
67
+
68
+ # Prepare message with attached files
69
+ message = ""
70
+ if files:
71
+ message = self.file_attacher.attach(files)
72
+ message += "\n\n"
73
+ message += task
74
+
75
+ logger.info(f"[spawn] Sending task to {full_name}: {message[:100]}...")
76
+
77
+ # Send task
78
+ result = self._send_keys(full_name, message)
79
+ logger.info(f"[spawn] Task send result: {result}")
80
+
81
+ # Track agent
82
+ self.agents[name] = AgentSession(
83
+ name=name,
84
+ full_name=full_name,
85
+ status="running",
86
+ start_time=time.time(),
87
+ )
88
+
89
+ logger.info(f"Spawned agent: {name}")
90
+ return True
91
+
92
+ async def spawn_clone(
93
+ self,
94
+ name: str,
95
+ task: str,
96
+ files: List[str],
97
+ conversation_file: str,
98
+ ) -> bool:
99
+ """Spawn agent with conversation context.
100
+
101
+ Args:
102
+ name: Agent name
103
+ task: Task description
104
+ files: Files to attach
105
+ conversation_file: Path to exported conversation JSON
106
+
107
+ Returns:
108
+ True if successful
109
+ """
110
+ full_name = f"{self.project_name}-{name}"
111
+
112
+ if self._session_exists(full_name):
113
+ logger.warning(f"Session already exists: {full_name}")
114
+ return False
115
+
116
+ if not self._create_session(full_name):
117
+ logger.error(f"Failed to create session: {full_name}")
118
+ return False
119
+
120
+ # Start kollab with resume
121
+ self._send_keys(full_name, f"kollab --resume {conversation_file}")
122
+ await asyncio.sleep(self.kollab_init_delay)
123
+
124
+ # Send task with files
125
+ message = ""
126
+ if files:
127
+ message = self.file_attacher.attach(files) + "\n\n"
128
+ message += task
129
+
130
+ self._send_keys(full_name, message)
131
+
132
+ self.agents[name] = AgentSession(
133
+ name=name,
134
+ full_name=full_name,
135
+ status="running",
136
+ start_time=time.time(),
137
+ )
138
+
139
+ logger.info(f"Spawned clone agent: {name} with conversation context")
140
+ return True
141
+
142
+ async def spawn_team_lead(
143
+ self,
144
+ lead_name: str,
145
+ max_workers: int,
146
+ task: AgentTask,
147
+ ) -> bool:
148
+ """Spawn a team lead agent that can spawn workers.
149
+
150
+ Args:
151
+ lead_name: Lead agent name
152
+ max_workers: Maximum number of workers the lead can spawn
153
+ task: Task for the lead
154
+
155
+ Returns:
156
+ True if successful
157
+ """
158
+ full_name = f"{self.project_name}-{lead_name}"
159
+
160
+ if self._session_exists(full_name):
161
+ logger.warning(f"Session already exists: {full_name}")
162
+ return False
163
+
164
+ if not self._create_session(full_name):
165
+ logger.error(f"Failed to create session: {full_name}")
166
+ return False
167
+
168
+ self._send_keys(full_name, "kollab")
169
+ await asyncio.sleep(self.kollab_init_delay)
170
+
171
+ # Inject team lead prompt
172
+ lead_prompt = f"""You are a team lead agent.
173
+ You can spawn up to {max_workers} worker agents using <agent> tags.
174
+ Coordinate their work and integrate results.
175
+ Use <status /> to check on workers.
176
+ Use <capture>worker-name 100</capture> to see their progress.
177
+
178
+ """
179
+ message = lead_prompt
180
+ if task.files:
181
+ message += self.file_attacher.attach(task.files) + "\n\n"
182
+ message += task.task
183
+
184
+ self._send_keys(full_name, message)
185
+
186
+ self.agents[lead_name] = AgentSession(
187
+ name=lead_name,
188
+ full_name=full_name,
189
+ status="running",
190
+ start_time=time.time(),
191
+ )
192
+
193
+ logger.info(f"Spawned team lead: {lead_name} with {max_workers} max workers")
194
+ return True
195
+
196
+ # -------------------------------------------------------------------------
197
+ # Message
198
+ # -------------------------------------------------------------------------
199
+
200
+ async def message(self, name: str, content: str) -> bool:
201
+ """Send message to agent.
202
+
203
+ Args:
204
+ name: Agent name
205
+ content: Message content
206
+
207
+ Returns:
208
+ True if successful
209
+ """
210
+ full_name = f"{self.project_name}-{name}"
211
+
212
+ if not self._session_exists(full_name):
213
+ logger.warning(f"Session does not exist: {full_name}")
214
+ return False
215
+
216
+ self._send_keys(full_name, content)
217
+ logger.info(f"Sent message to agent: {name}")
218
+ return True
219
+
220
+ # -------------------------------------------------------------------------
221
+ # Stop
222
+ # -------------------------------------------------------------------------
223
+
224
+ async def stop(self, name: str) -> Tuple[str, str]:
225
+ """Stop agent and return final output + duration.
226
+
227
+ Args:
228
+ name: Agent name
229
+
230
+ Returns:
231
+ Tuple of (output, duration)
232
+ """
233
+ full_name = f"{self.project_name}-{name}"
234
+
235
+ # Capture final output
236
+ output = self.capture_output(name, 100)
237
+
238
+ # Get duration
239
+ agent = self.agents.get(name)
240
+ duration = agent.duration if agent else "?"
241
+
242
+ # Kill session
243
+ self._kill_session(full_name)
244
+
245
+ # Remove from tracking
246
+ if name in self.agents:
247
+ del self.agents[name]
248
+
249
+ logger.info(f"Stopped agent: {name} @ {duration}")
250
+ return output, duration
251
+
252
+ # -------------------------------------------------------------------------
253
+ # Status / Capture
254
+ # -------------------------------------------------------------------------
255
+
256
+ def list_agents(self) -> List[AgentSession]:
257
+ """List all active agents.
258
+
259
+ Returns:
260
+ List of agent sessions
261
+ """
262
+ # Refresh status from tmux
263
+ self._refresh_agents()
264
+ return list(self.agents.values())
265
+
266
+ def get_agent(self, name: str) -> Optional[AgentSession]:
267
+ """Get specific agent.
268
+
269
+ Args:
270
+ name: Agent name
271
+
272
+ Returns:
273
+ Agent session or None
274
+ """
275
+ return self.agents.get(name)
276
+
277
+ def find_agents(self, pattern: str) -> List[str]:
278
+ """Find agents matching glob pattern.
279
+
280
+ Args:
281
+ pattern: Glob pattern (e.g., "lint-*")
282
+
283
+ Returns:
284
+ List of matching agent names
285
+ """
286
+ return [
287
+ name for name in self.agents.keys() if fnmatch.fnmatch(name, pattern)
288
+ ]
289
+
290
+ def capture_output(self, name: str, lines: int = 50) -> str:
291
+ """Capture last N lines from agent.
292
+
293
+ Args:
294
+ name: Agent name
295
+ lines: Number of lines to capture
296
+
297
+ Returns:
298
+ Captured output
299
+ """
300
+ full_name = f"{self.project_name}-{name}"
301
+
302
+ result = subprocess.run(
303
+ ["tmux", "capture-pane", "-t", full_name, "-p", "-S", f"-{lines}"],
304
+ capture_output=True,
305
+ text=True,
306
+ )
307
+ return result.stdout
308
+
309
+ # -------------------------------------------------------------------------
310
+ # Private Helpers
311
+ # -------------------------------------------------------------------------
312
+
313
+ def _session_exists(self, full_name: str) -> bool:
314
+ """Check if tmux session exists."""
315
+ result = subprocess.run(
316
+ ["tmux", "has-session", "-t", full_name], capture_output=True
317
+ )
318
+ return result.returncode == 0
319
+
320
+ def _create_session(self, full_name: str) -> bool:
321
+ """Create new tmux session in current working directory."""
322
+ cwd = str(Path.cwd())
323
+ result = subprocess.run(
324
+ ["tmux", "new-session", "-d", "-s", full_name, "-c", cwd], capture_output=True
325
+ )
326
+ if result.returncode != 0:
327
+ logger.error(f"Failed to create tmux session: {result.stderr}")
328
+ return False
329
+
330
+ time.sleep(self.session_init_delay)
331
+ return True
332
+
333
+ def _send_keys(self, full_name: str, content: str) -> bool:
334
+ """Send keys to tmux session."""
335
+ logger.debug(f"[send_keys] Sending to {full_name}: {len(content)} chars")
336
+
337
+ # Send content first
338
+ result = subprocess.run(
339
+ ["tmux", "send-keys", "-t", full_name, content],
340
+ capture_output=True,
341
+ )
342
+ if result.returncode != 0:
343
+ logger.error(f"[send_keys] Content send failed: {result.stderr}")
344
+ return False
345
+
346
+ logger.debug(f"[send_keys] Content sent, waiting 1s before Enter")
347
+ # Wait for paste to be processed
348
+ time.sleep(1)
349
+
350
+ # Then send Enter to submit
351
+ logger.debug(f"[send_keys] Sending Enter")
352
+ result = subprocess.run(
353
+ ["tmux", "send-keys", "-t", full_name, "Enter"],
354
+ capture_output=True,
355
+ )
356
+ if result.returncode != 0:
357
+ logger.error(f"[send_keys] Enter send failed: {result.stderr}")
358
+
359
+ logger.debug(f"[send_keys] Waiting {self.message_delay}s message delay")
360
+ time.sleep(self.message_delay)
361
+ return result.returncode == 0
362
+
363
+ def _kill_session(self, full_name: str) -> bool:
364
+ """Kill tmux session."""
365
+ result = subprocess.run(
366
+ ["tmux", "kill-session", "-t", full_name], capture_output=True
367
+ )
368
+ return result.returncode == 0
369
+
370
+ def _refresh_agents(self) -> None:
371
+ """Refresh agent status from tmux."""
372
+ result = subprocess.run(
373
+ ["tmux", "list-sessions", "-F", "#{session_name}"],
374
+ capture_output=True,
375
+ text=True,
376
+ )
377
+
378
+ if result.returncode != 0:
379
+ return
380
+
381
+ active_sessions = set(result.stdout.strip().split("\n"))
382
+ prefix = f"{self.project_name}-"
383
+
384
+ # Remove dead agents
385
+ dead = [
386
+ name
387
+ for name in self.agents
388
+ if f"{prefix}{name}" not in active_sessions
389
+ ]
390
+ for name in dead:
391
+ del self.agents[name]
392
+
393
+ # Discover new agents (created externally)
394
+ for session in active_sessions:
395
+ if session.startswith(prefix):
396
+ agent_name = session[len(prefix):]
397
+ if agent_name and agent_name not in self.agents:
398
+ self.agents[agent_name] = AgentSession(
399
+ name=agent_name,
400
+ full_name=session,
401
+ status="running",
402
+ start_time=time.time(), # approximate
403
+ )