@adiontaegerron/claude-multi-terminal 1.2.0 → 2.1.0
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.
- package/.claude/settings.local.json +8 -15
- package/CLAUDE.md +75 -42
- package/README.md +42 -7
- package/index.html +0 -7
- package/ipc-handlers.js +303 -0
- package/main.js +441 -25
- package/package.json +6 -3
- package/renderer.js +613 -117
- package/style.css +0 -53
- package/test/test.html +12 -0
- package/test-app.js +0 -16
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"permissions": {
|
|
3
3
|
"allow": [
|
|
4
|
-
"Bash(
|
|
5
|
-
"Bash(
|
|
6
|
-
"Bash(
|
|
7
|
-
"Bash(npm start)",
|
|
8
|
-
"Bash(npx:*)",
|
|
4
|
+
"Bash(create_terminal \"Terminal 3\")",
|
|
5
|
+
"Bash(terminal_help)",
|
|
6
|
+
"Bash(create_terminal \"Terminal 4\")",
|
|
9
7
|
"Bash(rm:*)",
|
|
10
|
-
"Bash(
|
|
11
|
-
"Bash(git init:*)",
|
|
12
|
-
"Bash(git remote add:*)",
|
|
13
|
-
"Bash(git add:*)",
|
|
14
|
-
"Bash(git commit:*)",
|
|
15
|
-
"Bash(git push:*)",
|
|
16
|
-
"Bash(mkdir:*)",
|
|
8
|
+
"Bash(create_terminal \"Terminal 2\")",
|
|
17
9
|
"Bash(chmod:*)",
|
|
18
|
-
"Bash(
|
|
19
|
-
"Bash(
|
|
20
|
-
"
|
|
10
|
+
"Bash(*)",
|
|
11
|
+
"Bash(/tmp/create_terminals.sh:*)",
|
|
12
|
+
"Bash(ls:*)",
|
|
13
|
+
"Write(*)"
|
|
21
14
|
],
|
|
22
15
|
"deny": []
|
|
23
16
|
}
|
package/CLAUDE.md
CHANGED
|
@@ -1,63 +1,96 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Terminal Agent Instructions
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
You are a terminal agent running in a multi-terminal coordination system. Your terminal name will be provided when you start.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## IMPORTANT: Follow Instructions Exactly
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
When given a command:
|
|
8
|
+
- Execute ONLY what is explicitly requested
|
|
9
|
+
- Do NOT add extra actions or create additional items
|
|
10
|
+
- If asked to create 2 terminals, create exactly 2 (not 3, 4, or 6)
|
|
11
|
+
- Wait for further instructions after completing each task
|
|
8
12
|
|
|
9
|
-
##
|
|
13
|
+
## Communication Commands
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
You communicate with other terminals using file-based IPC. Use the Write tool to create command files in `.ipc-messages/`.
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
# Install dependencies
|
|
15
|
-
npm install
|
|
17
|
+
### Command Format
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
Use the Write tool to create files with unique names. Include your name in the filename to ensure uniqueness:
|
|
20
|
+
`.ipc-messages/cmd_YourName_timestamp.txt`
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
npm run build
|
|
22
|
+
Always use the exact terminal name you were given at startup.
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
### Send Message to Another Terminal
|
|
25
|
+
Use Write to create a file `.ipc-messages/msg_YourName_[timestamp].txt` with content:
|
|
26
|
+
```
|
|
27
|
+
YourName:IPC:SEND:TargetTerminal:Your message
|
|
25
28
|
```
|
|
26
29
|
|
|
27
|
-
|
|
30
|
+
### Broadcast to All Terminals
|
|
31
|
+
Use Write to create a file `.ipc-messages/bcast_YourName_[timestamp].txt` with content:
|
|
32
|
+
```
|
|
33
|
+
YourName:IPC:BROADCAST:Your message
|
|
34
|
+
```
|
|
28
35
|
|
|
29
|
-
|
|
36
|
+
### List All Terminals
|
|
37
|
+
Use Write to create a file `.ipc-messages/list_YourName_[timestamp].txt` with content:
|
|
38
|
+
```
|
|
39
|
+
YourName:IPC:LIST
|
|
40
|
+
```
|
|
30
41
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
42
|
+
### Create New Terminal
|
|
43
|
+
Use Write to create a file `.ipc-messages/create_YourName_[timestamp].txt` with content:
|
|
44
|
+
```
|
|
45
|
+
YourName:IPC:CREATE:NewTerminalName
|
|
46
|
+
```
|
|
47
|
+
The name is optional - if not provided, a default name will be assigned.
|
|
34
48
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
49
|
+
### Close Terminal
|
|
50
|
+
Use Write to create a file `.ipc-messages/close_YourName_[timestamp].txt` with content:
|
|
51
|
+
```
|
|
52
|
+
YourName:IPC:CLOSE:TargetTerminal
|
|
53
|
+
```
|
|
54
|
+
Note: You cannot close your own terminal.
|
|
55
|
+
|
|
56
|
+
### Rename Terminal
|
|
57
|
+
Use Write to create a file `.ipc-messages/rename_YourName_[timestamp].txt` with content:
|
|
58
|
+
```
|
|
59
|
+
YourName:IPC:RENAME:TargetTerminal:NewName
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Get Terminal Status
|
|
63
|
+
Use Write to create a file `.ipc-messages/status_YourName_[timestamp].txt` with content:
|
|
64
|
+
```
|
|
65
|
+
YourName:IPC:STATUS:TargetTerminal
|
|
66
|
+
```
|
|
39
67
|
|
|
40
|
-
|
|
68
|
+
**Important**: Replace [timestamp] with a unique number or use the current timestamp when creating files.
|
|
41
69
|
|
|
42
|
-
|
|
43
|
-
- Multiple terminal sessions (2+ terminals)
|
|
44
|
-
- Each terminal displays:
|
|
45
|
-
- xterm.js terminal area for output and keyboard input
|
|
46
|
-
- Text input box for sending commands
|
|
47
|
-
- Send button (Enter key also submits)
|
|
48
|
-
- "New Terminal" button for creating sessions
|
|
49
|
-
- Real-time output display
|
|
50
|
-
- Local execution only (no cloud dependencies)
|
|
70
|
+
## Important Rules
|
|
51
71
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
72
|
+
1. Terminal names are case-sensitive
|
|
73
|
+
2. You cannot send messages to yourself
|
|
74
|
+
3. You cannot close your own terminal
|
|
75
|
+
4. Maximum of 6 terminals can exist
|
|
76
|
+
5. Messages are delivered instantly
|
|
77
|
+
6. When you receive a message, it will appear as:
|
|
78
|
+
- `💬 Message from Terminal X: [message]`
|
|
79
|
+
- `📢 Broadcast from Terminal X: [message]`
|
|
55
80
|
|
|
56
|
-
##
|
|
81
|
+
## Your Role
|
|
57
82
|
|
|
58
|
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
-
|
|
83
|
+
As a terminal agent, you should:
|
|
84
|
+
- Wait for messages and commands from other terminals
|
|
85
|
+
- When you receive a message ending with "[End of message - no response needed]", do NOT respond automatically
|
|
86
|
+
- Only respond to messages when they contain a specific request or question
|
|
87
|
+
- Execute tasks as directed
|
|
88
|
+
- Communicate status updates when appropriate
|
|
89
|
+
- Avoid taking actions unless specifically requested
|
|
62
90
|
|
|
91
|
+
## Important: Message Handling
|
|
63
92
|
|
|
93
|
+
When you receive messages from other terminals:
|
|
94
|
+
- Messages marked with "[End of message - no response needed]" are notifications only
|
|
95
|
+
- Do not echo or respond to these messages unless they contain a specific request
|
|
96
|
+
- Wait for explicit instructions before taking action
|
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Claude Multi-Terminal
|
|
2
2
|
|
|
3
|
+
**Note for Terminal Agents**: If you are a Claude instance running inside this application, you don't need to read this file. Please refer to CLAUDE.md for your instructions.
|
|
4
|
+
|
|
3
5
|
A multi-terminal editor for coordinating multiple Claude Code instances. Run multiple Claude sessions side-by-side with a unified chat interface to send commands to selected terminals.
|
|
4
6
|
|
|
5
7
|

|
|
@@ -7,20 +9,35 @@ A multi-terminal editor for coordinating multiple Claude Code instances. Run mul
|
|
|
7
9
|
## Features
|
|
8
10
|
|
|
9
11
|
- **Multi-Terminal Support**: Run multiple Claude Code instances in a 2x2 grid layout
|
|
10
|
-
- **Unified Chat Interface**: Send commands to
|
|
11
|
-
- **Terminal
|
|
12
|
-
- **Multi-@ Terminal Commands**: Send commands to multiple terminals with various syntaxes:
|
|
12
|
+
- **Unified Chat Interface**: Send commands to terminals using @ syntax
|
|
13
|
+
- **Multi-@ Terminal Commands**: Target terminals with flexible syntax:
|
|
13
14
|
- `@all command` - Send to all terminals
|
|
14
15
|
- `@term1,term2,term3 command` - Comma-separated terminals
|
|
15
16
|
- `@term1 @term2 command` - Space-separated terminals
|
|
16
|
-
- **
|
|
17
|
-
-
|
|
17
|
+
- **Mixed @/ Commands**: Combine terminal targeting with slash commands:
|
|
18
|
+
- `@term1 /clear` - Run slash command on specific terminal
|
|
19
|
+
- `@term1 @term2 /list` - Multiple terminals with slash commands
|
|
20
|
+
- `@all /interrupt` - Slash commands with @all
|
|
21
|
+
- **Smart Autocomplete**: Get suggestions while typing @ or / anywhere in your message
|
|
22
|
+
- **Slash Commands**: Built-in commands for terminal management (works anywhere)
|
|
18
23
|
- **Default Prompt**: Set a default prompt to prepend to all messages
|
|
19
24
|
- **Plan Mode Support**: Automatically enters plan mode with Shift+Tab
|
|
20
25
|
- **Auto-Start Claude**: Each terminal automatically starts Claude Code
|
|
26
|
+
- **Inter-Claude Communication**: Claude instances can chat and coordinate using echo commands
|
|
27
|
+
- **Terminal Management**: Claude can create, close, and rename terminals autonomously
|
|
21
28
|
- **Rename Terminals**: Double-click terminal titles to rename them
|
|
22
29
|
- **Responsive Layout**: Adapts to different screen sizes
|
|
23
30
|
|
|
31
|
+
## Breaking Changes in v2.0
|
|
32
|
+
|
|
33
|
+
- **Removed checkbox selection system** - All commands must now use @ syntax
|
|
34
|
+
- **No default terminal selection** - You must explicitly specify terminals with @
|
|
35
|
+
- **Plain text without @ or / will show an error** - Every command needs a target
|
|
36
|
+
|
|
37
|
+
To migrate from v1.x:
|
|
38
|
+
- Instead of selecting checkboxes and typing a command, use: `@term1 @term2 command`
|
|
39
|
+
- To send to all terminals, use: `@all command`
|
|
40
|
+
|
|
24
41
|
## Installation
|
|
25
42
|
|
|
26
43
|
### From npm
|
|
@@ -52,10 +69,12 @@ This will open the multi-terminal editor with all terminals starting in your cur
|
|
|
52
69
|
|
|
53
70
|
1. **Start the Application**: Run `claude-multi` in your project directory
|
|
54
71
|
2. **Create Terminals**: Click "New Terminal" to add more Claude instances (starts with one)
|
|
55
|
-
3. **
|
|
56
|
-
4. **Send Commands**: Type
|
|
72
|
+
3. **Target Terminals**: Use @ syntax to specify which terminals receive commands
|
|
73
|
+
4. **Send Commands**: Type your command with @ targeting and press Enter
|
|
57
74
|
5. **Use Default Prompt**: Expand the "Default Prompt" section to set a prompt that's prepended to all messages
|
|
58
75
|
|
|
76
|
+
**Important**: Version 2.0 removes checkbox selection. All commands must now use @ syntax to target terminals.
|
|
77
|
+
|
|
59
78
|
### Slash Commands
|
|
60
79
|
|
|
61
80
|
- `/send <terminal> <command>` - Send command to specific terminal
|
|
@@ -79,13 +98,29 @@ Send commands directly to terminals using @ syntax:
|
|
|
79
98
|
|
|
80
99
|
**Autocomplete**: Type `@` anywhere to get terminal suggestions!
|
|
81
100
|
|
|
101
|
+
### Claude-to-Claude Communication
|
|
102
|
+
|
|
103
|
+
Claude instances communicate by creating files in the `.ipc-messages/` directory. Claude will handle the file creation directly when asked to:
|
|
104
|
+
|
|
105
|
+
- **Send message**: Claude creates a file to send `TerminalName:IPC:SEND:Target:Message`
|
|
106
|
+
- **Broadcast**: Claude creates a file to broadcast `TerminalName:IPC:BROADCAST:Message`
|
|
107
|
+
- **Create terminal**: Claude creates a file with `TerminalName:IPC:CREATE:NewName`
|
|
108
|
+
- **Close terminal**: Claude creates a file with `TerminalName:IPC:CLOSE:Target`
|
|
109
|
+
- **Rename terminal**: Claude creates a file with `TerminalName:IPC:RENAME:Target:NewName`
|
|
110
|
+
- **List terminals**: Claude creates a file with `TerminalName:IPC:LIST`
|
|
111
|
+
- **Get status**: Claude creates a file with `TerminalName:IPC:STATUS:Target`
|
|
112
|
+
|
|
113
|
+
Claude knows how to create these files when you ask it to communicate with other terminals. This enables Claude instances to work together as a coordinated team.
|
|
114
|
+
|
|
82
115
|
### Tips
|
|
83
116
|
|
|
84
117
|
- Each terminal automatically starts Claude Code when created
|
|
118
|
+
- Claude receives its identity: "You are a terminal agent. Your name is [Terminal Name]."
|
|
85
119
|
- Plan mode is activated automatically using Shift+Tab
|
|
86
120
|
- Double-click terminal titles to rename them for better organization
|
|
87
121
|
- The application uses your current directory as the working directory for all terminals
|
|
88
122
|
- Use multi-@ commands to coordinate multiple Claude instances efficiently
|
|
123
|
+
- Claude instances communicate instantly using IPC echo commands for seamless teamwork
|
|
89
124
|
|
|
90
125
|
## Development
|
|
91
126
|
|
package/index.html
CHANGED
|
@@ -14,13 +14,6 @@
|
|
|
14
14
|
|
|
15
15
|
<div id="toolbar-extended">
|
|
16
16
|
<div class="controls-row">
|
|
17
|
-
<div id="terminal-selector" class="control-section">
|
|
18
|
-
<label>Send to:</label>
|
|
19
|
-
<div id="terminal-checkboxes">
|
|
20
|
-
<!-- Terminal checkboxes will be added here dynamically -->
|
|
21
|
-
</div>
|
|
22
|
-
</div>
|
|
23
|
-
|
|
24
17
|
<div id="default-prompt-section" class="control-section collapsible">
|
|
25
18
|
<div class="collapsible-header">
|
|
26
19
|
<button class="collapse-toggle">▶</button>
|
package/ipc-handlers.js
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
// IPC Handlers for Direct Terminal Communication
|
|
2
|
+
// Handles IPC calls from injected JavaScript functions
|
|
3
|
+
|
|
4
|
+
const { ipcMain } = require('electron');
|
|
5
|
+
|
|
6
|
+
class DirectIPCHandler {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.terminals = null; // Will be set by main process
|
|
9
|
+
this.mainWindow = null; // Will be set by main process
|
|
10
|
+
this.setupHandlers();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
setTerminals(terminals) {
|
|
14
|
+
this.terminals = terminals;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
setMainWindow(mainWindow) {
|
|
18
|
+
this.mainWindow = mainWindow;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
setupHandlers() {
|
|
22
|
+
// Send message between terminals
|
|
23
|
+
ipcMain.handle('terminal:send-message', async (event, data) => {
|
|
24
|
+
try {
|
|
25
|
+
const { from, to, message, timestamp } = data;
|
|
26
|
+
|
|
27
|
+
const targetTerminal = this.findTerminal(to);
|
|
28
|
+
if (!targetTerminal) {
|
|
29
|
+
return { success: false, error: `Terminal '${to}' not found` };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const chatMessage = `💬 Message from ${from}:\n${message}`;
|
|
33
|
+
this.mainWindow.webContents.send('terminal:system-response', targetTerminal.id, {
|
|
34
|
+
message: chatMessage,
|
|
35
|
+
type: 'chat',
|
|
36
|
+
timestamp: timestamp || Date.now()
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Send return to submit message
|
|
40
|
+
setTimeout(() => {
|
|
41
|
+
targetTerminal.pty.write('\r');
|
|
42
|
+
}, 200);
|
|
43
|
+
|
|
44
|
+
return { success: true, target: to, from };
|
|
45
|
+
} catch (error) {
|
|
46
|
+
return { success: false, error: error.message };
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Create new terminal
|
|
51
|
+
ipcMain.handle('terminal:create-new', async (event, data) => {
|
|
52
|
+
try {
|
|
53
|
+
const { name, requesterId } = data;
|
|
54
|
+
|
|
55
|
+
// Check terminal limit
|
|
56
|
+
if (this.terminals.size >= 6) {
|
|
57
|
+
return { success: false, error: 'Maximum of 6 terminals allowed' };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Rate limiting check
|
|
61
|
+
const requesterTerminal = this.terminals.get(requesterId);
|
|
62
|
+
if (requesterTerminal) {
|
|
63
|
+
const now = Date.now();
|
|
64
|
+
const timeSinceLastCreate = now - (requesterTerminal.lastCreateRequest || 0);
|
|
65
|
+
if (timeSinceLastCreate < 2000) {
|
|
66
|
+
return {
|
|
67
|
+
success: false,
|
|
68
|
+
error: `Please wait ${Math.ceil((2000 - timeSinceLastCreate) / 1000)} seconds before creating another terminal`
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
requesterTerminal.lastCreateRequest = now;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Trigger terminal creation
|
|
75
|
+
this.mainWindow.webContents.send('terminal:create-request', name);
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
success: true,
|
|
79
|
+
terminalId: `terminal-${Date.now()}`, // Temporary ID
|
|
80
|
+
name: name
|
|
81
|
+
};
|
|
82
|
+
} catch (error) {
|
|
83
|
+
return { success: false, error: error.message };
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Close terminal
|
|
88
|
+
ipcMain.handle('terminal:close-target', async (event, data) => {
|
|
89
|
+
try {
|
|
90
|
+
const { target, requesterId } = data;
|
|
91
|
+
|
|
92
|
+
const targetTerminal = this.findTerminal(target);
|
|
93
|
+
if (!targetTerminal) {
|
|
94
|
+
return { success: false, error: `Terminal '${target}' not found` };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (targetTerminal.id === requesterId) {
|
|
98
|
+
return { success: false, error: 'Cannot close your own terminal' };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
targetTerminal.pty.kill();
|
|
102
|
+
this.terminals.delete(targetTerminal.id);
|
|
103
|
+
|
|
104
|
+
this.mainWindow.webContents.send('terminal:closed', targetTerminal.id);
|
|
105
|
+
|
|
106
|
+
return { success: true, target, closed: targetTerminal.name };
|
|
107
|
+
} catch (error) {
|
|
108
|
+
return { success: false, error: error.message };
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Rename terminal
|
|
113
|
+
ipcMain.handle('terminal:rename-target', async (event, data) => {
|
|
114
|
+
try {
|
|
115
|
+
const { target, newName, requesterId } = data;
|
|
116
|
+
|
|
117
|
+
const targetTerminal = this.findTerminal(target);
|
|
118
|
+
if (!targetTerminal) {
|
|
119
|
+
return { success: false, error: `Terminal '${target}' not found` };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const oldName = targetTerminal.name;
|
|
123
|
+
targetTerminal.name = newName;
|
|
124
|
+
|
|
125
|
+
this.mainWindow.webContents.send('terminal:renamed', targetTerminal.id, newName);
|
|
126
|
+
|
|
127
|
+
return { success: true, target, oldName, newName };
|
|
128
|
+
} catch (error) {
|
|
129
|
+
return { success: false, error: error.message };
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// List all terminals
|
|
134
|
+
ipcMain.handle('terminal:list-all', async (event) => {
|
|
135
|
+
try {
|
|
136
|
+
const terminalList = Array.from(this.terminals.entries()).map(([id, terminal]) => ({
|
|
137
|
+
id,
|
|
138
|
+
name: terminal.name,
|
|
139
|
+
status: terminal.status || 'active',
|
|
140
|
+
hasClaudeStarted: terminal.hasClaudeStarted || false
|
|
141
|
+
}));
|
|
142
|
+
|
|
143
|
+
return { success: true, terminals: terminalList };
|
|
144
|
+
} catch (error) {
|
|
145
|
+
return { success: false, error: error.message };
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Get terminal status
|
|
150
|
+
ipcMain.handle('terminal:get-status', async (event, data) => {
|
|
151
|
+
try {
|
|
152
|
+
const { target } = data;
|
|
153
|
+
|
|
154
|
+
const targetTerminal = this.findTerminal(target);
|
|
155
|
+
if (!targetTerminal) {
|
|
156
|
+
return { success: false, error: `Terminal '${target}' not found` };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const status = {
|
|
160
|
+
id: targetTerminal.id,
|
|
161
|
+
name: targetTerminal.name,
|
|
162
|
+
status: targetTerminal.status || 'active',
|
|
163
|
+
hasClaudeStarted: targetTerminal.hasClaudeStarted || false
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
return { success: true, target, status };
|
|
167
|
+
} catch (error) {
|
|
168
|
+
return { success: false, error: error.message };
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Broadcast message to all terminals
|
|
173
|
+
ipcMain.handle('terminal:broadcast', async (event, data) => {
|
|
174
|
+
try {
|
|
175
|
+
const { message, from, excludeId } = data;
|
|
176
|
+
|
|
177
|
+
let count = 0;
|
|
178
|
+
for (const [id, terminal] of this.terminals.entries()) {
|
|
179
|
+
if (excludeId && id === excludeId) continue; // Don't send to sender
|
|
180
|
+
|
|
181
|
+
const chatMessage = `📢 Broadcast from ${from}:\n${message}`;
|
|
182
|
+
this.mainWindow.webContents.send('terminal:system-response', id, {
|
|
183
|
+
message: chatMessage,
|
|
184
|
+
type: 'chat',
|
|
185
|
+
timestamp: Date.now()
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
count++;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return { success: true, count, message };
|
|
192
|
+
} catch (error) {
|
|
193
|
+
return { success: false, error: error.message };
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// Advanced workflow handlers
|
|
198
|
+
ipcMain.handle('terminal:workflow-setup', async (event, data) => {
|
|
199
|
+
try {
|
|
200
|
+
const { type, requesterId } = data;
|
|
201
|
+
|
|
202
|
+
const workflows = {
|
|
203
|
+
'development': [
|
|
204
|
+
'Backend Server',
|
|
205
|
+
'Frontend Dev',
|
|
206
|
+
'Database',
|
|
207
|
+
'Test Runner'
|
|
208
|
+
],
|
|
209
|
+
'review': [
|
|
210
|
+
'Code Reviewer',
|
|
211
|
+
'Test Runner',
|
|
212
|
+
'Build Monitor'
|
|
213
|
+
],
|
|
214
|
+
'debug': [
|
|
215
|
+
'Bug Hunter',
|
|
216
|
+
'Log Monitor',
|
|
217
|
+
'Test Verifier'
|
|
218
|
+
]
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const terminalNames = workflows[type];
|
|
222
|
+
if (!terminalNames) {
|
|
223
|
+
return { success: false, error: `Unknown workflow type: ${type}` };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Check if we can create all terminals
|
|
227
|
+
if (this.terminals.size + terminalNames.length > 6) {
|
|
228
|
+
return {
|
|
229
|
+
success: false,
|
|
230
|
+
error: `Cannot create ${terminalNames.length} terminals. Would exceed limit of 6.`
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Create terminals
|
|
235
|
+
const created = [];
|
|
236
|
+
for (const name of terminalNames) {
|
|
237
|
+
this.mainWindow.webContents.send('terminal:create-request', name);
|
|
238
|
+
created.push(name);
|
|
239
|
+
// Small delay between creations
|
|
240
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return { success: true, type, created };
|
|
244
|
+
} catch (error) {
|
|
245
|
+
return { success: false, error: error.message };
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// Health check
|
|
250
|
+
ipcMain.handle('terminal:health', async (event) => {
|
|
251
|
+
try {
|
|
252
|
+
return {
|
|
253
|
+
success: true,
|
|
254
|
+
terminalCount: this.terminals.size,
|
|
255
|
+
uptime: process.uptime(),
|
|
256
|
+
platform: process.platform,
|
|
257
|
+
memory: process.memoryUsage()
|
|
258
|
+
};
|
|
259
|
+
} catch (error) {
|
|
260
|
+
return { success: false, error: error.message };
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Helper method to find terminals
|
|
266
|
+
findTerminal(identifier) {
|
|
267
|
+
if (!this.terminals) {
|
|
268
|
+
throw new Error('Terminals not initialized');
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Try by ID first
|
|
272
|
+
if (this.terminals.has(identifier)) {
|
|
273
|
+
return this.terminals.get(identifier);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Try by name (exact match)
|
|
277
|
+
for (const [id, terminal] of this.terminals.entries()) {
|
|
278
|
+
if (terminal.name === identifier) {
|
|
279
|
+
return terminal;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Try by name (partial match)
|
|
284
|
+
for (const [id, terminal] of this.terminals.entries()) {
|
|
285
|
+
if (terminal.name.toLowerCase().includes(identifier.toLowerCase())) {
|
|
286
|
+
return terminal;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Try by position
|
|
291
|
+
const position = parseInt(identifier);
|
|
292
|
+
if (!isNaN(position) && position > 0) {
|
|
293
|
+
const terminalArray = Array.from(this.terminals.values());
|
|
294
|
+
if (position <= terminalArray.length) {
|
|
295
|
+
return terminalArray[position - 1];
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
module.exports = DirectIPCHandler;
|