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.
- agents/__init__.py +2 -0
- agents/coder/__init__.py +0 -0
- agents/coder/agent.json +4 -0
- agents/coder/api-integration.md +2150 -0
- agents/coder/cli-pretty.md +765 -0
- agents/coder/code-review.md +1092 -0
- agents/coder/database-design.md +1525 -0
- agents/coder/debugging.md +1102 -0
- agents/coder/dependency-management.md +1397 -0
- agents/coder/git-workflow.md +1099 -0
- agents/coder/refactoring.md +1454 -0
- agents/coder/security-hardening.md +1732 -0
- agents/coder/system_prompt.md +1448 -0
- agents/coder/tdd.md +1367 -0
- agents/creative-writer/__init__.py +0 -0
- agents/creative-writer/agent.json +4 -0
- agents/creative-writer/character-development.md +1852 -0
- agents/creative-writer/dialogue-craft.md +1122 -0
- agents/creative-writer/plot-structure.md +1073 -0
- agents/creative-writer/revision-editing.md +1484 -0
- agents/creative-writer/system_prompt.md +690 -0
- agents/creative-writer/worldbuilding.md +2049 -0
- agents/data-analyst/__init__.py +30 -0
- agents/data-analyst/agent.json +4 -0
- agents/data-analyst/data-visualization.md +992 -0
- agents/data-analyst/exploratory-data-analysis.md +1110 -0
- agents/data-analyst/pandas-data-manipulation.md +1081 -0
- agents/data-analyst/sql-query-optimization.md +881 -0
- agents/data-analyst/statistical-analysis.md +1118 -0
- agents/data-analyst/system_prompt.md +928 -0
- agents/default/__init__.py +0 -0
- agents/default/agent.json +4 -0
- agents/default/dead-code.md +794 -0
- agents/default/explore-agent-system.md +585 -0
- agents/default/system_prompt.md +1448 -0
- agents/kollabor/__init__.py +0 -0
- agents/kollabor/analyze-plugin-lifecycle.md +175 -0
- agents/kollabor/analyze-terminal-rendering.md +388 -0
- agents/kollabor/code-review.md +1092 -0
- agents/kollabor/debug-mcp-integration.md +521 -0
- agents/kollabor/debug-plugin-hooks.md +547 -0
- agents/kollabor/debugging.md +1102 -0
- agents/kollabor/dependency-management.md +1397 -0
- agents/kollabor/git-workflow.md +1099 -0
- agents/kollabor/inspect-llm-conversation.md +148 -0
- agents/kollabor/monitor-event-bus.md +558 -0
- agents/kollabor/profile-performance.md +576 -0
- agents/kollabor/refactoring.md +1454 -0
- agents/kollabor/system_prompt copy.md +1448 -0
- agents/kollabor/system_prompt.md +757 -0
- agents/kollabor/trace-command-execution.md +178 -0
- agents/kollabor/validate-config.md +879 -0
- agents/research/__init__.py +0 -0
- agents/research/agent.json +4 -0
- agents/research/architecture-mapping.md +1099 -0
- agents/research/codebase-analysis.md +1077 -0
- agents/research/dependency-audit.md +1027 -0
- agents/research/performance-profiling.md +1047 -0
- agents/research/security-review.md +1359 -0
- agents/research/system_prompt.md +492 -0
- agents/technical-writer/__init__.py +0 -0
- agents/technical-writer/agent.json +4 -0
- agents/technical-writer/api-documentation.md +2328 -0
- agents/technical-writer/changelog-management.md +1181 -0
- agents/technical-writer/readme-writing.md +1360 -0
- agents/technical-writer/style-guide.md +1410 -0
- agents/technical-writer/system_prompt.md +653 -0
- agents/technical-writer/tutorial-creation.md +1448 -0
- core/__init__.py +0 -2
- core/application.py +343 -88
- core/cli.py +229 -10
- core/commands/menu_renderer.py +463 -59
- core/commands/registry.py +14 -9
- core/commands/system_commands.py +2461 -14
- core/config/loader.py +151 -37
- core/config/service.py +18 -6
- core/events/bus.py +29 -9
- core/events/executor.py +205 -75
- core/events/models.py +27 -8
- core/fullscreen/command_integration.py +20 -24
- core/fullscreen/components/__init__.py +10 -1
- core/fullscreen/components/matrix_components.py +1 -2
- core/fullscreen/components/space_shooter_components.py +654 -0
- core/fullscreen/plugin.py +5 -0
- core/fullscreen/renderer.py +52 -13
- core/fullscreen/session.py +52 -15
- core/io/__init__.py +29 -5
- core/io/buffer_manager.py +6 -1
- core/io/config_status_view.py +7 -29
- core/io/core_status_views.py +267 -347
- core/io/input/__init__.py +25 -0
- core/io/input/command_mode_handler.py +711 -0
- core/io/input/display_controller.py +128 -0
- core/io/input/hook_registrar.py +286 -0
- core/io/input/input_loop_manager.py +421 -0
- core/io/input/key_press_handler.py +502 -0
- core/io/input/modal_controller.py +1011 -0
- core/io/input/paste_processor.py +339 -0
- core/io/input/status_modal_renderer.py +184 -0
- core/io/input_errors.py +5 -1
- core/io/input_handler.py +211 -2452
- core/io/key_parser.py +7 -0
- core/io/layout.py +15 -3
- core/io/message_coordinator.py +111 -2
- core/io/message_renderer.py +129 -4
- core/io/status_renderer.py +147 -607
- core/io/terminal_renderer.py +97 -51
- core/io/terminal_state.py +21 -4
- core/io/visual_effects.py +816 -165
- core/llm/agent_manager.py +1063 -0
- core/llm/api_adapters/__init__.py +44 -0
- core/llm/api_adapters/anthropic_adapter.py +432 -0
- core/llm/api_adapters/base.py +241 -0
- core/llm/api_adapters/openai_adapter.py +326 -0
- core/llm/api_communication_service.py +167 -113
- core/llm/conversation_logger.py +322 -16
- core/llm/conversation_manager.py +556 -30
- core/llm/file_operations_executor.py +84 -32
- core/llm/llm_service.py +934 -103
- core/llm/mcp_integration.py +541 -57
- core/llm/message_display_service.py +135 -18
- core/llm/plugin_sdk.py +1 -2
- core/llm/profile_manager.py +1183 -0
- core/llm/response_parser.py +274 -56
- core/llm/response_processor.py +16 -3
- core/llm/tool_executor.py +6 -1
- core/logging/__init__.py +2 -0
- core/logging/setup.py +34 -6
- core/models/resume.py +54 -0
- core/plugins/__init__.py +4 -2
- core/plugins/base.py +127 -0
- core/plugins/collector.py +23 -161
- core/plugins/discovery.py +37 -3
- core/plugins/factory.py +6 -12
- core/plugins/registry.py +5 -17
- core/ui/config_widgets.py +128 -28
- core/ui/live_modal_renderer.py +2 -1
- core/ui/modal_actions.py +5 -0
- core/ui/modal_overlay_renderer.py +0 -60
- core/ui/modal_renderer.py +268 -7
- core/ui/modal_state_manager.py +29 -4
- core/ui/widgets/base_widget.py +7 -0
- core/updates/__init__.py +10 -0
- core/updates/version_check_service.py +348 -0
- core/updates/version_comparator.py +103 -0
- core/utils/config_utils.py +685 -526
- core/utils/plugin_utils.py +1 -1
- core/utils/session_naming.py +111 -0
- fonts/LICENSE +21 -0
- fonts/README.md +46 -0
- fonts/SymbolsNerdFont-Regular.ttf +0 -0
- fonts/SymbolsNerdFontMono-Regular.ttf +0 -0
- fonts/__init__.py +44 -0
- {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/METADATA +54 -4
- kollabor-0.4.15.dist-info/RECORD +228 -0
- {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/top_level.txt +2 -0
- plugins/agent_orchestrator/__init__.py +39 -0
- plugins/agent_orchestrator/activity_monitor.py +181 -0
- plugins/agent_orchestrator/file_attacher.py +77 -0
- plugins/agent_orchestrator/message_injector.py +135 -0
- plugins/agent_orchestrator/models.py +48 -0
- plugins/agent_orchestrator/orchestrator.py +403 -0
- plugins/agent_orchestrator/plugin.py +976 -0
- plugins/agent_orchestrator/xml_parser.py +191 -0
- plugins/agent_orchestrator_plugin.py +9 -0
- plugins/enhanced_input/box_styles.py +1 -0
- plugins/enhanced_input/color_engine.py +19 -4
- plugins/enhanced_input/config.py +2 -2
- plugins/enhanced_input_plugin.py +61 -11
- plugins/fullscreen/__init__.py +6 -2
- plugins/fullscreen/example_plugin.py +1035 -222
- plugins/fullscreen/setup_wizard_plugin.py +592 -0
- plugins/fullscreen/space_shooter_plugin.py +131 -0
- plugins/hook_monitoring_plugin.py +436 -78
- plugins/query_enhancer_plugin.py +66 -30
- plugins/resume_conversation_plugin.py +1494 -0
- plugins/save_conversation_plugin.py +98 -32
- plugins/system_commands_plugin.py +70 -56
- plugins/tmux_plugin.py +154 -78
- plugins/workflow_enforcement_plugin.py +94 -92
- system_prompt/default.md +952 -886
- core/io/input_mode_manager.py +0 -402
- core/io/modal_interaction_handler.py +0 -315
- core/io/raw_input_processor.py +0 -946
- core/storage/__init__.py +0 -5
- core/storage/state_manager.py +0 -84
- core/ui/widget_integration.py +0 -222
- core/utils/key_reader.py +0 -171
- kollabor-0.4.9.dist-info/RECORD +0 -128
- {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/WHEEL +0 -0
- {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/entry_points.txt +0 -0
- {kollabor-0.4.9.dist-info → kollabor-0.4.15.dist-info}/licenses/LICENSE +0 -0
plugins/tmux_plugin.py
CHANGED
|
@@ -15,14 +15,20 @@ from typing import Dict, Any, List, Optional
|
|
|
15
15
|
from dataclasses import dataclass, field
|
|
16
16
|
from datetime import datetime
|
|
17
17
|
|
|
18
|
+
from core.io.visual_effects import AgnosterSegment
|
|
19
|
+
|
|
18
20
|
logger = logging.getLogger(__name__)
|
|
19
21
|
|
|
22
|
+
# Default socket name for kollabor's isolated tmux server
|
|
23
|
+
DEFAULT_KOLLABOR_SOCKET = "kollabor"
|
|
24
|
+
|
|
20
25
|
|
|
21
26
|
@dataclass
|
|
22
27
|
class TmuxSession:
|
|
23
28
|
"""Represents a managed tmux session."""
|
|
24
29
|
name: str
|
|
25
30
|
command: str
|
|
31
|
+
tmux_cmd: callable # Function to build tmux commands with correct socket
|
|
26
32
|
created_at: datetime = field(default_factory=datetime.now)
|
|
27
33
|
pid: Optional[int] = None
|
|
28
34
|
|
|
@@ -30,7 +36,7 @@ class TmuxSession:
|
|
|
30
36
|
"""Check if the tmux session is still running."""
|
|
31
37
|
try:
|
|
32
38
|
result = subprocess.run(
|
|
33
|
-
|
|
39
|
+
self.tmux_cmd("has-session", "-t", self.name),
|
|
34
40
|
capture_output=True
|
|
35
41
|
)
|
|
36
42
|
return result.returncode == 0
|
|
@@ -41,13 +47,12 @@ class TmuxSession:
|
|
|
41
47
|
class TmuxPlugin:
|
|
42
48
|
"""Plugin for tmux session management and live viewing."""
|
|
43
49
|
|
|
44
|
-
def __init__(self, name: str = "tmux",
|
|
50
|
+
def __init__(self, name: str = "tmux", event_bus=None,
|
|
45
51
|
renderer=None, config=None):
|
|
46
52
|
"""Initialize the tmux plugin.
|
|
47
53
|
|
|
48
54
|
Args:
|
|
49
55
|
name: Plugin name.
|
|
50
|
-
state_manager: State management system.
|
|
51
56
|
event_bus: Event bus for hook registration.
|
|
52
57
|
renderer: Terminal renderer.
|
|
53
58
|
config: Configuration manager.
|
|
@@ -58,7 +63,6 @@ class TmuxPlugin:
|
|
|
58
63
|
self.enabled = True
|
|
59
64
|
|
|
60
65
|
self.sessions: Dict[str, TmuxSession] = {}
|
|
61
|
-
self.state_manager = state_manager
|
|
62
66
|
self.event_bus = event_bus
|
|
63
67
|
self.renderer = renderer
|
|
64
68
|
self.config = config
|
|
@@ -71,6 +75,19 @@ class TmuxPlugin:
|
|
|
71
75
|
|
|
72
76
|
self.logger = logger
|
|
73
77
|
|
|
78
|
+
def _tmux_cmd(self, *args: str) -> List[str]:
|
|
79
|
+
"""Build a tmux command with optional socket name.
|
|
80
|
+
|
|
81
|
+
If use_separate_server is enabled (default), uses a dedicated
|
|
82
|
+
kollabor tmux server. Otherwise uses the user's main tmux server.
|
|
83
|
+
"""
|
|
84
|
+
use_separate = self.config.get("plugins.tmux.use_separate_server", True) if self.config else True
|
|
85
|
+
socket_name = self.config.get("plugins.tmux.socket_name", DEFAULT_KOLLABOR_SOCKET) if self.config else DEFAULT_KOLLABOR_SOCKET
|
|
86
|
+
|
|
87
|
+
if use_separate:
|
|
88
|
+
return ["tmux", "-L", socket_name] + list(args)
|
|
89
|
+
return ["tmux"] + list(args)
|
|
90
|
+
|
|
74
91
|
@staticmethod
|
|
75
92
|
def get_default_config() -> Dict[str, Any]:
|
|
76
93
|
"""Get default configuration for tmux plugin."""
|
|
@@ -79,8 +96,10 @@ class TmuxPlugin:
|
|
|
79
96
|
"tmux": {
|
|
80
97
|
"enabled": True,
|
|
81
98
|
"show_status": True,
|
|
82
|
-
"refresh_rate": 0.
|
|
83
|
-
"
|
|
99
|
+
"refresh_rate": 0.5, # Live view refresh rate in seconds
|
|
100
|
+
"capture_lines": 200, # Number of lines to capture from pane history
|
|
101
|
+
"use_separate_server": True, # Use dedicated tmux server
|
|
102
|
+
"socket_name": DEFAULT_KOLLABOR_SOCKET # Socket name for separate server
|
|
84
103
|
}
|
|
85
104
|
}
|
|
86
105
|
}
|
|
@@ -129,19 +148,26 @@ class TmuxPlugin:
|
|
|
129
148
|
def _register_commands(self):
|
|
130
149
|
"""Register tmux commands with the command registry."""
|
|
131
150
|
from core.events.models import (
|
|
132
|
-
CommandDefinition, CommandMode, CommandCategory
|
|
151
|
+
CommandDefinition, CommandMode, CommandCategory, SubcommandInfo
|
|
133
152
|
)
|
|
134
153
|
|
|
135
154
|
# /terminal - manage tmux sessions
|
|
136
155
|
terminal_cmd = CommandDefinition(
|
|
137
156
|
name="terminal",
|
|
138
|
-
description="Manage
|
|
157
|
+
description="Manage terminal sessions (new/view/list/kill)",
|
|
139
158
|
handler=self._handle_tmux_command,
|
|
140
159
|
plugin_name=self.name,
|
|
141
|
-
category=CommandCategory.
|
|
160
|
+
category=CommandCategory.CUSTOM, # Plugin command
|
|
142
161
|
mode=CommandMode.INSTANT,
|
|
143
162
|
aliases=["term", "tmux", "t"],
|
|
144
|
-
icon="[>_]"
|
|
163
|
+
icon="[>_]",
|
|
164
|
+
subcommands=[
|
|
165
|
+
SubcommandInfo("new", "<name> <cmd>", "Create session running command"),
|
|
166
|
+
SubcommandInfo("view", "[name]", "Live view session (default)"),
|
|
167
|
+
SubcommandInfo("list", "", "List all sessions"),
|
|
168
|
+
SubcommandInfo("kill", "<name>", "Kill a session"),
|
|
169
|
+
SubcommandInfo("attach", "<name>", "Attach (exits kollabor)"),
|
|
170
|
+
]
|
|
145
171
|
)
|
|
146
172
|
self.command_registry.register_command(terminal_cmd)
|
|
147
173
|
|
|
@@ -194,6 +220,8 @@ class TmuxPlugin:
|
|
|
194
220
|
"""Get help text for terminal command."""
|
|
195
221
|
return """Terminal Session Manager
|
|
196
222
|
|
|
223
|
+
By default, sessions use a dedicated tmux server (configurable).
|
|
224
|
+
|
|
197
225
|
Usage:
|
|
198
226
|
/terminal new <name> <command> Create new session running command
|
|
199
227
|
/terminal view [name] Live view session (</> to cycle)
|
|
@@ -207,6 +235,11 @@ Examples:
|
|
|
207
235
|
/terminal view
|
|
208
236
|
/terminal kill myserver
|
|
209
237
|
|
|
238
|
+
To attach directly (with default separate server):
|
|
239
|
+
tmux -L kollabor attach -t <session_name>
|
|
240
|
+
|
|
241
|
+
To use your main tmux instead, disable "Use Separate Server" in config.
|
|
242
|
+
|
|
210
243
|
Aliases: /t, /term, /tmux"""
|
|
211
244
|
|
|
212
245
|
async def _handle_new_session(self, args: List[str]) -> "CommandResult":
|
|
@@ -232,9 +265,10 @@ Aliases: /t, /term, /tmux"""
|
|
|
232
265
|
)
|
|
233
266
|
|
|
234
267
|
try:
|
|
235
|
-
# Create detached tmux session with
|
|
268
|
+
# Create detached tmux session with user's default shell
|
|
269
|
+
# (tmux uses $SHELL or user's login shell by default)
|
|
236
270
|
result = subprocess.run(
|
|
237
|
-
|
|
271
|
+
self._tmux_cmd("new-session", "-d", "-s", session_name),
|
|
238
272
|
capture_output=True,
|
|
239
273
|
text=True
|
|
240
274
|
)
|
|
@@ -249,7 +283,7 @@ Aliases: /t, /term, /tmux"""
|
|
|
249
283
|
# Send the command to the new session (if provided)
|
|
250
284
|
if command:
|
|
251
285
|
send_result = subprocess.run(
|
|
252
|
-
|
|
286
|
+
self._tmux_cmd("send-keys", "-t", session_name, command, "Enter"),
|
|
253
287
|
capture_output=True,
|
|
254
288
|
text=True
|
|
255
289
|
)
|
|
@@ -260,7 +294,8 @@ Aliases: /t, /term, /tmux"""
|
|
|
260
294
|
# Track the session
|
|
261
295
|
self.sessions[session_name] = TmuxSession(
|
|
262
296
|
name=session_name,
|
|
263
|
-
command=command or "bash"
|
|
297
|
+
command=command or "bash",
|
|
298
|
+
tmux_cmd=self._tmux_cmd
|
|
264
299
|
)
|
|
265
300
|
|
|
266
301
|
msg = f"Created session '{session_name}'"
|
|
@@ -308,13 +343,23 @@ Aliases: /t, /term, /tmux"""
|
|
|
308
343
|
from core.ui.live_modal_renderer import LiveModalConfig
|
|
309
344
|
from core.events.models import EventType
|
|
310
345
|
|
|
346
|
+
# Get terminal height for viewport calculation
|
|
347
|
+
terminal_height = 40 # Default fallback
|
|
348
|
+
if self.renderer and hasattr(self.renderer, 'terminal_state'):
|
|
349
|
+
_, terminal_height = self.renderer.terminal_state.get_size()
|
|
350
|
+
# Capture enough lines to fill the modal content area
|
|
351
|
+
# Modal height is roughly terminal_height - 4, minus borders/header/footer
|
|
352
|
+
# Use terminal_height - 6 to be safe
|
|
353
|
+
viewport_lines = max(10, terminal_height-8)
|
|
354
|
+
|
|
311
355
|
# Content generator - uses self._current_session for dynamic switching
|
|
312
356
|
async def get_tmux_content() -> List[str]:
|
|
313
357
|
# Show session name as header line
|
|
314
358
|
sessions = self._get_all_tmux_sessions()
|
|
315
359
|
session_idx = sessions.index(self._current_session) + 1 if self._current_session in sessions else 1
|
|
316
360
|
header = f"[Session: {self._current_session}] ({session_idx}/{len(sessions)})"
|
|
317
|
-
|
|
361
|
+
# Capture only the newest lines that fit in viewport
|
|
362
|
+
content = self._capture_tmux_pane(self._current_session, max_lines=viewport_lines)
|
|
318
363
|
return [header, "─" * len(header)] + content
|
|
319
364
|
|
|
320
365
|
# Input callback for passthrough
|
|
@@ -326,7 +371,7 @@ Aliases: /t, /term, /tmux"""
|
|
|
326
371
|
if key_press.code == 8776:
|
|
327
372
|
try:
|
|
328
373
|
subprocess.run(
|
|
329
|
-
|
|
374
|
+
self._tmux_cmd("kill-session", "-t", self._current_session),
|
|
330
375
|
capture_output=True,
|
|
331
376
|
text=True,
|
|
332
377
|
timeout=2
|
|
@@ -433,35 +478,44 @@ Aliases: /t, /term, /tmux"""
|
|
|
433
478
|
display_type="error"
|
|
434
479
|
)
|
|
435
480
|
|
|
436
|
-
def _capture_tmux_pane(self, session_name: str) -> List[str]:
|
|
437
|
-
"""Capture current content of a tmux pane.
|
|
481
|
+
def _capture_tmux_pane(self, session_name: str, max_lines: Optional[int] = None) -> List[str]:
|
|
482
|
+
"""Capture current content of a tmux pane.
|
|
483
|
+
|
|
484
|
+
Args:
|
|
485
|
+
session_name: Name of the tmux session
|
|
486
|
+
max_lines: Maximum lines to return. Returns newest content from bottom.
|
|
487
|
+
|
|
488
|
+
Returns:
|
|
489
|
+
List of cleaned content lines from newest to oldest.
|
|
490
|
+
"""
|
|
438
491
|
try:
|
|
439
|
-
# Capture
|
|
492
|
+
# Capture more lines than we need, then take from the end
|
|
493
|
+
capture_lines = self.config.get("plugins.tmux.capture_lines", 500) if self.config else 500
|
|
494
|
+
# Capture from the end of the pane history
|
|
440
495
|
result = subprocess.run(
|
|
441
|
-
|
|
496
|
+
self._tmux_cmd("capture-pane", "-p", "-e", "-S", f"-{capture_lines}", "-t", session_name),
|
|
442
497
|
capture_output=True,
|
|
443
498
|
text=True,
|
|
444
|
-
timeout=2
|
|
499
|
+
timeout=2
|
|
445
500
|
)
|
|
446
501
|
if result.returncode == 0:
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
# Remove trailing empty lines but keep internal empty lines
|
|
450
|
-
while content.endswith('\n'):
|
|
451
|
-
content = content[:-1]
|
|
502
|
+
import re
|
|
503
|
+
lines = result.stdout.rstrip('\n').split('\n')
|
|
452
504
|
|
|
453
|
-
lines
|
|
454
|
-
|
|
455
|
-
# Clean up captured content for display
|
|
505
|
+
# Strip ANSI codes but keep all lines including empty ones
|
|
456
506
|
cleaned = []
|
|
457
507
|
for line in lines:
|
|
458
|
-
# Strip ANSI escape sequences and trailing whitespace
|
|
459
|
-
import re
|
|
460
508
|
line = re.sub(r'\x1b\[[0-9;]*m', '', line) # Remove ANSI colors
|
|
461
509
|
line = line.rstrip()
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
510
|
+
cleaned.append(line)
|
|
511
|
+
|
|
512
|
+
# Remove trailing empty lines from the END (not beginning)
|
|
513
|
+
while cleaned and not cleaned[-1]:
|
|
514
|
+
cleaned.pop()
|
|
515
|
+
|
|
516
|
+
# Return only the newest lines that fit in viewport (from the end)
|
|
517
|
+
if max_lines and len(cleaned) > max_lines:
|
|
518
|
+
return cleaned[-max_lines:]
|
|
465
519
|
return cleaned
|
|
466
520
|
else:
|
|
467
521
|
return [f"Error capturing pane: {result.stderr}"]
|
|
@@ -474,7 +528,7 @@ Aliases: /t, /term, /tmux"""
|
|
|
474
528
|
"""Send keys to a tmux session."""
|
|
475
529
|
try:
|
|
476
530
|
subprocess.run(
|
|
477
|
-
|
|
531
|
+
self._tmux_cmd("send-keys", "-t", session_name, keys),
|
|
478
532
|
capture_output=True
|
|
479
533
|
)
|
|
480
534
|
except Exception as e:
|
|
@@ -490,7 +544,7 @@ Aliases: /t, /term, /tmux"""
|
|
|
490
544
|
if not all_sessions:
|
|
491
545
|
return CommandResult(
|
|
492
546
|
success=True,
|
|
493
|
-
message="No tmux sessions found. Use '/
|
|
547
|
+
message="No tmux sessions found. Use '/terminal new <name> <command>' to create one.",
|
|
494
548
|
display_type="info"
|
|
495
549
|
)
|
|
496
550
|
|
|
@@ -520,7 +574,7 @@ Aliases: /t, /term, /tmux"""
|
|
|
520
574
|
if not args:
|
|
521
575
|
return CommandResult(
|
|
522
576
|
success=False,
|
|
523
|
-
message="Usage: /
|
|
577
|
+
message="Usage: /terminal kill <session_name>",
|
|
524
578
|
display_type="error"
|
|
525
579
|
)
|
|
526
580
|
|
|
@@ -528,7 +582,7 @@ Aliases: /t, /term, /tmux"""
|
|
|
528
582
|
|
|
529
583
|
try:
|
|
530
584
|
result = subprocess.run(
|
|
531
|
-
|
|
585
|
+
self._tmux_cmd("kill-session", "-t", session_name),
|
|
532
586
|
capture_output=True,
|
|
533
587
|
text=True
|
|
534
588
|
)
|
|
@@ -563,7 +617,7 @@ Aliases: /t, /term, /tmux"""
|
|
|
563
617
|
if not args:
|
|
564
618
|
return CommandResult(
|
|
565
619
|
success=False,
|
|
566
|
-
message="Usage: /
|
|
620
|
+
message="Usage: /terminal attach <session_name>",
|
|
567
621
|
display_type="error"
|
|
568
622
|
)
|
|
569
623
|
|
|
@@ -576,9 +630,18 @@ Aliases: /t, /term, /tmux"""
|
|
|
576
630
|
display_type="error"
|
|
577
631
|
)
|
|
578
632
|
|
|
633
|
+
# Build attach command based on whether using separate server
|
|
634
|
+
use_separate = self.config.get("plugins.tmux.use_separate_server", True) if self.config else True
|
|
635
|
+
socket_name = self.config.get("plugins.tmux.socket_name", DEFAULT_KOLLABOR_SOCKET) if self.config else DEFAULT_KOLLABOR_SOCKET
|
|
636
|
+
|
|
637
|
+
if use_separate:
|
|
638
|
+
attach_cmd = f"tmux -L {socket_name} attach -t {session_name}"
|
|
639
|
+
else:
|
|
640
|
+
attach_cmd = f"tmux attach -t {session_name}"
|
|
641
|
+
|
|
579
642
|
return CommandResult(
|
|
580
643
|
success=True,
|
|
581
|
-
message=f"To attach to '{session_name}', exit kollabor and run:\n
|
|
644
|
+
message=f"To attach to '{session_name}', exit kollabor and run:\n {attach_cmd}",
|
|
582
645
|
display_type="info"
|
|
583
646
|
)
|
|
584
647
|
|
|
@@ -586,7 +649,7 @@ Aliases: /t, /term, /tmux"""
|
|
|
586
649
|
"""Check if a tmux session exists."""
|
|
587
650
|
try:
|
|
588
651
|
result = subprocess.run(
|
|
589
|
-
|
|
652
|
+
self._tmux_cmd("has-session", "-t", session_name),
|
|
590
653
|
capture_output=True
|
|
591
654
|
)
|
|
592
655
|
return result.returncode == 0
|
|
@@ -631,7 +694,7 @@ Aliases: /t, /term, /tmux"""
|
|
|
631
694
|
"""Get list of all existing tmux sessions."""
|
|
632
695
|
try:
|
|
633
696
|
result = subprocess.run(
|
|
634
|
-
|
|
697
|
+
self._tmux_cmd("list-sessions", "-F", "#{session_name}"),
|
|
635
698
|
capture_output=True,
|
|
636
699
|
text=True,
|
|
637
700
|
check=True
|
|
@@ -655,7 +718,7 @@ Aliases: /t, /term, /tmux"""
|
|
|
655
718
|
"""Discover existing tmux sessions (optional bootstrap)."""
|
|
656
719
|
try:
|
|
657
720
|
result = subprocess.run(
|
|
658
|
-
|
|
721
|
+
self._tmux_cmd("list-sessions", "-F", "#{session_name}"),
|
|
659
722
|
capture_output=True,
|
|
660
723
|
text=True
|
|
661
724
|
)
|
|
@@ -702,53 +765,45 @@ Aliases: /t, /term, /tmux"""
|
|
|
702
765
|
logger.error(f"Failed to register tmux status view: {e}")
|
|
703
766
|
|
|
704
767
|
def _get_tmux_sessions_content(self) -> List[str]:
|
|
705
|
-
"""Get tmux sessions content for status view."""
|
|
768
|
+
"""Get tmux sessions content for status view (agnoster style)."""
|
|
706
769
|
try:
|
|
770
|
+
seg = AgnosterSegment()
|
|
771
|
+
|
|
707
772
|
if not self.enabled:
|
|
708
|
-
|
|
773
|
+
seg.add_neutral("Tmux: Disabled", "dark")
|
|
774
|
+
return [seg.render()]
|
|
709
775
|
|
|
710
|
-
# Get all tmux sessions
|
|
776
|
+
# Get all tmux sessions
|
|
711
777
|
all_sessions = self._get_all_tmux_sessions()
|
|
712
|
-
|
|
713
|
-
if not all_sessions:
|
|
714
|
-
return ["Sessions: 0 active"]
|
|
715
|
-
|
|
716
|
-
lines = []
|
|
717
778
|
session_count = len(all_sessions)
|
|
718
779
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
max_display = 9 # Show max 9 sessions (3 rows x 3 columns)
|
|
724
|
-
display_sessions = all_sessions[:max_display]
|
|
725
|
-
|
|
726
|
-
# Group into rows of 3
|
|
727
|
-
rows = []
|
|
728
|
-
for i in range(0, len(display_sessions), 3):
|
|
729
|
-
row = display_sessions[i:i+3]
|
|
730
|
-
rows.append(row)
|
|
780
|
+
if session_count == 0:
|
|
781
|
+
seg.add_lime("Tmux", "dark")
|
|
782
|
+
seg.add_neutral("No sessions", "mid")
|
|
783
|
+
return [seg.render()]
|
|
731
784
|
|
|
732
|
-
#
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
while len(row) < 3:
|
|
736
|
-
row.append("")
|
|
785
|
+
# Build agnoster bar: Tmux | count | session names | hint
|
|
786
|
+
seg.add_lime("Tmux", "dark")
|
|
787
|
+
seg.add_cyan(f"{session_count} active", "dark")
|
|
737
788
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
789
|
+
# Show first few session names
|
|
790
|
+
max_show = 3
|
|
791
|
+
if all_sessions:
|
|
792
|
+
names = all_sessions[:max_show]
|
|
793
|
+
names_str = " | ".join(names)
|
|
794
|
+
if session_count > max_show:
|
|
795
|
+
names_str += f" +{session_count - max_show}"
|
|
796
|
+
seg.add_lime(names_str)
|
|
741
797
|
|
|
742
|
-
|
|
743
|
-
if session_count > max_display:
|
|
744
|
-
overflow = session_count - max_display
|
|
745
|
-
lines.append(f"({overflow} more...)")
|
|
798
|
+
seg.add_neutral("/terminal", "mid")
|
|
746
799
|
|
|
747
|
-
return
|
|
800
|
+
return [seg.render()]
|
|
748
801
|
|
|
749
802
|
except Exception as e:
|
|
750
803
|
logger.error(f"Error getting tmux sessions content: {e}")
|
|
751
|
-
|
|
804
|
+
seg = AgnosterSegment()
|
|
805
|
+
seg.add_neutral("Tmux: Error", "dark")
|
|
806
|
+
return [seg.render()]
|
|
752
807
|
|
|
753
808
|
def get_status_line(self) -> Dict[str, List[str]]:
|
|
754
809
|
"""Get status line (no longer used - using status view instead)."""
|
|
@@ -774,7 +829,7 @@ Aliases: /t, /term, /tmux"""
|
|
|
774
829
|
def get_config_widgets() -> Optional[Dict[str, Any]]:
|
|
775
830
|
"""Get configuration widgets for the config modal."""
|
|
776
831
|
return {
|
|
777
|
-
"title": "
|
|
832
|
+
"title": "Terminal Settings",
|
|
778
833
|
"widgets": [
|
|
779
834
|
{
|
|
780
835
|
"type": "checkbox",
|
|
@@ -790,6 +845,27 @@ Aliases: /t, /term, /tmux"""
|
|
|
790
845
|
"max_value": 1000,
|
|
791
846
|
"step": 50,
|
|
792
847
|
"help": "Live view refresh rate in milliseconds"
|
|
848
|
+
},
|
|
849
|
+
{
|
|
850
|
+
"type": "slider",
|
|
851
|
+
"label": "Capture Lines",
|
|
852
|
+
"config_path": "plugins.tmux.capture_lines",
|
|
853
|
+
"min_value": 10,
|
|
854
|
+
"max_value": 1000,
|
|
855
|
+
"step": 10,
|
|
856
|
+
"help": "Number of lines to capture from pane history"
|
|
857
|
+
},
|
|
858
|
+
{
|
|
859
|
+
"type": "checkbox",
|
|
860
|
+
"label": "Use Separate Server",
|
|
861
|
+
"config_path": "plugins.tmux.use_separate_server",
|
|
862
|
+
"help": "Use dedicated tmux server (isolated from your main tmux)"
|
|
863
|
+
},
|
|
864
|
+
{
|
|
865
|
+
"type": "text_input",
|
|
866
|
+
"label": "Socket Name",
|
|
867
|
+
"config_path": "plugins.tmux.socket_name",
|
|
868
|
+
"help": "Socket name for separate tmux server (default: kollabor)"
|
|
793
869
|
}
|
|
794
870
|
]
|
|
795
871
|
}
|