workon 2.1.0 → 2.1.2
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 +11 -0
- package/.history/lib/tmux_20250807120751.js +190 -0
- package/.history/lib/tmux_20250807120757.js +190 -0
- package/.history/lib/tmux_20250807120802.js +190 -0
- package/.history/lib/tmux_20250807120808.js +190 -0
- package/CHANGELOG.md +9 -0
- package/README.md +1 -1
- package/cli/index.js +4 -4
- package/lib/tmux.js +40 -9
- package/package.json +1 -1
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
const { spawn } = require('child_process');
|
|
2
|
+
const { promisify } = require('util');
|
|
3
|
+
const exec = promisify(require('child_process').exec);
|
|
4
|
+
|
|
5
|
+
class TmuxManager {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.sessionPrefix = 'workon-';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async isTmuxAvailable() {
|
|
11
|
+
try {
|
|
12
|
+
await exec('which tmux');
|
|
13
|
+
return true;
|
|
14
|
+
} catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async sessionExists(sessionName) {
|
|
20
|
+
try {
|
|
21
|
+
await exec(`tmux has-session -t "${sessionName}"`);
|
|
22
|
+
return true;
|
|
23
|
+
} catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getSessionName(projectName) {
|
|
29
|
+
return `${this.sessionPrefix}${projectName}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async killSession(sessionName) {
|
|
33
|
+
try {
|
|
34
|
+
await exec(`tmux kill-session -t "${sessionName}"`);
|
|
35
|
+
return true;
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async createSplitSession(projectName, projectPath, claudeArgs = []) {
|
|
42
|
+
const sessionName = this.getSessionName(projectName);
|
|
43
|
+
|
|
44
|
+
// Kill existing session if it exists
|
|
45
|
+
if (await this.sessionExists(sessionName)) {
|
|
46
|
+
await this.killSession(sessionName);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const claudeCommand = claudeArgs.length > 0
|
|
50
|
+
? `claude ${claudeArgs.join(' ')}`
|
|
51
|
+
: 'claude';
|
|
52
|
+
|
|
53
|
+
// Create new tmux session with claude in the first pane
|
|
54
|
+
await exec(`tmux new-session -d -s "${sessionName}" -c "${projectPath}" '${claudeCommand}'`);
|
|
55
|
+
|
|
56
|
+
// Split window horizontally and run shell in second pane
|
|
57
|
+
await exec(`tmux split-window -h -t "${sessionName}" -c "${projectPath}"`);
|
|
58
|
+
|
|
59
|
+
// Set focus on claude pane (left pane)
|
|
60
|
+
await exec(`tmux select-pane -t "${sessionName}:0.0"`);
|
|
61
|
+
|
|
62
|
+
return sessionName;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async createThreePaneSession(projectName, projectPath, claudeArgs = [], npmCommand = 'npm run dev') {
|
|
66
|
+
const sessionName = this.getSessionName(projectName);
|
|
67
|
+
|
|
68
|
+
// Kill existing session if it exists
|
|
69
|
+
if (await this.sessionExists(sessionName)) {
|
|
70
|
+
await this.killSession(sessionName);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const claudeCommand = claudeArgs.length > 0
|
|
74
|
+
? `claude ${claudeArgs.join(' ')}`
|
|
75
|
+
: 'claude';
|
|
76
|
+
|
|
77
|
+
// Create new tmux session with claude in the first pane (left side)
|
|
78
|
+
await exec(`tmux new-session -d -s "${sessionName}" -c "${projectPath}" '${claudeCommand}'`);
|
|
79
|
+
|
|
80
|
+
// Split window vertically - creates right side
|
|
81
|
+
await exec(`tmux split-window -h -t "${sessionName}" -c "${projectPath}"`);
|
|
82
|
+
|
|
83
|
+
// Split the right pane horizontally - creates top-right and bottom-right
|
|
84
|
+
await exec(`tmux split-window -v -t "${sessionName}:0.1" -c "${projectPath}" '${npmCommand}'`);
|
|
85
|
+
|
|
86
|
+
// Set focus on claude pane (left pane)
|
|
87
|
+
await exec(`tmux select-pane -t "${sessionName}:0.0"`);
|
|
88
|
+
|
|
89
|
+
return sessionName;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async createTwoPaneNpmSession(projectName, projectPath, npmCommand = 'npm run dev') {
|
|
93
|
+
const sessionName = this.getSessionName(projectName);
|
|
94
|
+
|
|
95
|
+
// Kill existing session if it exists
|
|
96
|
+
if (await this.sessionExists(sessionName)) {
|
|
97
|
+
await this.killSession(sessionName);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Create new tmux session with shell in the first pane (left side)
|
|
101
|
+
await exec(`tmux new-session -d -s "${sessionName}" -c "${projectPath}"`);
|
|
102
|
+
|
|
103
|
+
// Split window vertically and run npm command in right pane
|
|
104
|
+
await exec(`tmux split-window -h -t "${sessionName}" -c "${projectPath}" '${npmCommand}'`);
|
|
105
|
+
|
|
106
|
+
// Set focus on terminal pane (left pane)
|
|
107
|
+
await exec(`tmux select-pane -t "${sessionName}:0.0"`);
|
|
108
|
+
|
|
109
|
+
return sessionName;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async attachToSession(sessionName) {
|
|
113
|
+
// Check if we're already in a tmux session
|
|
114
|
+
if (process.env.TMUX) {
|
|
115
|
+
// If we're already in tmux, switch to the session
|
|
116
|
+
await exec(`tmux switch-client -t "${sessionName}"`);
|
|
117
|
+
} else {
|
|
118
|
+
// If not in tmux, attach to the session
|
|
119
|
+
spawn('tmux', ['-CC', 'attach-session', '-t', sessionName], {
|
|
120
|
+
stdio: 'inherit'
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
buildShellCommands(projectName, projectPath, claudeArgs = []) {
|
|
126
|
+
const sessionName = this.getSessionName(projectName);
|
|
127
|
+
const claudeCommand = claudeArgs.length > 0
|
|
128
|
+
? `claude ${claudeArgs.join(' ')}`
|
|
129
|
+
: 'claude';
|
|
130
|
+
|
|
131
|
+
return [
|
|
132
|
+
`# Create tmux split session for ${projectName}`,
|
|
133
|
+
`tmux has-session -t "${sessionName}" 2>/dev/null && tmux kill-session -t "${sessionName}"`,
|
|
134
|
+
`tmux new-session -d -s "${sessionName}" -c "${projectPath}" '${claudeCommand}'`,
|
|
135
|
+
`tmux split-window -h -t "${sessionName}" -c "${projectPath}"`,
|
|
136
|
+
`tmux select-pane -t "${sessionName}:0.0"`,
|
|
137
|
+
process.env.TMUX
|
|
138
|
+
? `tmux switch-client -t "${sessionName}"`
|
|
139
|
+
: `tmux -CC attach-session -t "${sessionName}"`
|
|
140
|
+
];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
buildThreePaneShellCommands(projectName, projectPath, claudeArgs = [], npmCommand = 'npm run dev') {
|
|
144
|
+
const sessionName = this.getSessionName(projectName);
|
|
145
|
+
const claudeCommand = claudeArgs.length > 0
|
|
146
|
+
? `claude ${claudeArgs.join(' ')}`
|
|
147
|
+
: 'claude';
|
|
148
|
+
|
|
149
|
+
return [
|
|
150
|
+
`# Create tmux three-pane session for ${projectName}`,
|
|
151
|
+
`tmux has-session -t "${sessionName}" 2>/dev/null && tmux kill-session -t "${sessionName}"`,
|
|
152
|
+
`tmux new-session -d -s "${sessionName}" -c "${projectPath}" '${claudeCommand}'`,
|
|
153
|
+
`tmux split-window -h -t "${sessionName}" -c "${projectPath}"`,
|
|
154
|
+
`tmux split-window -v -t "${sessionName}:0.1" -c "${projectPath}" '${npmCommand}'`,
|
|
155
|
+
`tmux select-pane -t "${sessionName}:0.0"`,
|
|
156
|
+
process.env.TMUX
|
|
157
|
+
? `tmux switch-client -t "${sessionName}"`
|
|
158
|
+
: `tmux attach-session -t "${sessionName}"`
|
|
159
|
+
];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
buildTwoPaneNpmShellCommands(projectName, projectPath, npmCommand = 'npm run dev') {
|
|
163
|
+
const sessionName = this.getSessionName(projectName);
|
|
164
|
+
|
|
165
|
+
return [
|
|
166
|
+
`# Create tmux two-pane session with npm for ${projectName}`,
|
|
167
|
+
`tmux has-session -t "${sessionName}" 2>/dev/null && tmux kill-session -t "${sessionName}"`,
|
|
168
|
+
`tmux new-session -d -s "${sessionName}" -c "${projectPath}"`,
|
|
169
|
+
`tmux split-window -h -t "${sessionName}" -c "${projectPath}" '${npmCommand}'`,
|
|
170
|
+
`tmux select-pane -t "${sessionName}:0.0"`,
|
|
171
|
+
process.env.TMUX
|
|
172
|
+
? `tmux switch-client -t "${sessionName}"`
|
|
173
|
+
: `tmux attach-session -t "${sessionName}"`
|
|
174
|
+
];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async listWorkonSessions() {
|
|
178
|
+
try {
|
|
179
|
+
const { stdout } = await exec('tmux list-sessions -F "#{session_name}"');
|
|
180
|
+
return stdout.trim()
|
|
181
|
+
.split('\n')
|
|
182
|
+
.filter(session => session.startsWith(this.sessionPrefix))
|
|
183
|
+
.map(session => session.replace(this.sessionPrefix, ''));
|
|
184
|
+
} catch {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
module.exports = TmuxManager;
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
const { spawn } = require('child_process');
|
|
2
|
+
const { promisify } = require('util');
|
|
3
|
+
const exec = promisify(require('child_process').exec);
|
|
4
|
+
|
|
5
|
+
class TmuxManager {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.sessionPrefix = 'workon-';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async isTmuxAvailable() {
|
|
11
|
+
try {
|
|
12
|
+
await exec('which tmux');
|
|
13
|
+
return true;
|
|
14
|
+
} catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async sessionExists(sessionName) {
|
|
20
|
+
try {
|
|
21
|
+
await exec(`tmux has-session -t "${sessionName}"`);
|
|
22
|
+
return true;
|
|
23
|
+
} catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getSessionName(projectName) {
|
|
29
|
+
return `${this.sessionPrefix}${projectName}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async killSession(sessionName) {
|
|
33
|
+
try {
|
|
34
|
+
await exec(`tmux kill-session -t "${sessionName}"`);
|
|
35
|
+
return true;
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async createSplitSession(projectName, projectPath, claudeArgs = []) {
|
|
42
|
+
const sessionName = this.getSessionName(projectName);
|
|
43
|
+
|
|
44
|
+
// Kill existing session if it exists
|
|
45
|
+
if (await this.sessionExists(sessionName)) {
|
|
46
|
+
await this.killSession(sessionName);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const claudeCommand = claudeArgs.length > 0
|
|
50
|
+
? `claude ${claudeArgs.join(' ')}`
|
|
51
|
+
: 'claude';
|
|
52
|
+
|
|
53
|
+
// Create new tmux session with claude in the first pane
|
|
54
|
+
await exec(`tmux new-session -d -s "${sessionName}" -c "${projectPath}" '${claudeCommand}'`);
|
|
55
|
+
|
|
56
|
+
// Split window horizontally and run shell in second pane
|
|
57
|
+
await exec(`tmux split-window -h -t "${sessionName}" -c "${projectPath}"`);
|
|
58
|
+
|
|
59
|
+
// Set focus on claude pane (left pane)
|
|
60
|
+
await exec(`tmux select-pane -t "${sessionName}:0.0"`);
|
|
61
|
+
|
|
62
|
+
return sessionName;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async createThreePaneSession(projectName, projectPath, claudeArgs = [], npmCommand = 'npm run dev') {
|
|
66
|
+
const sessionName = this.getSessionName(projectName);
|
|
67
|
+
|
|
68
|
+
// Kill existing session if it exists
|
|
69
|
+
if (await this.sessionExists(sessionName)) {
|
|
70
|
+
await this.killSession(sessionName);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const claudeCommand = claudeArgs.length > 0
|
|
74
|
+
? `claude ${claudeArgs.join(' ')}`
|
|
75
|
+
: 'claude';
|
|
76
|
+
|
|
77
|
+
// Create new tmux session with claude in the first pane (left side)
|
|
78
|
+
await exec(`tmux new-session -d -s "${sessionName}" -c "${projectPath}" '${claudeCommand}'`);
|
|
79
|
+
|
|
80
|
+
// Split window vertically - creates right side
|
|
81
|
+
await exec(`tmux split-window -h -t "${sessionName}" -c "${projectPath}"`);
|
|
82
|
+
|
|
83
|
+
// Split the right pane horizontally - creates top-right and bottom-right
|
|
84
|
+
await exec(`tmux split-window -v -t "${sessionName}:0.1" -c "${projectPath}" '${npmCommand}'`);
|
|
85
|
+
|
|
86
|
+
// Set focus on claude pane (left pane)
|
|
87
|
+
await exec(`tmux select-pane -t "${sessionName}:0.0"`);
|
|
88
|
+
|
|
89
|
+
return sessionName;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async createTwoPaneNpmSession(projectName, projectPath, npmCommand = 'npm run dev') {
|
|
93
|
+
const sessionName = this.getSessionName(projectName);
|
|
94
|
+
|
|
95
|
+
// Kill existing session if it exists
|
|
96
|
+
if (await this.sessionExists(sessionName)) {
|
|
97
|
+
await this.killSession(sessionName);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Create new tmux session with shell in the first pane (left side)
|
|
101
|
+
await exec(`tmux new-session -d -s "${sessionName}" -c "${projectPath}"`);
|
|
102
|
+
|
|
103
|
+
// Split window vertically and run npm command in right pane
|
|
104
|
+
await exec(`tmux split-window -h -t "${sessionName}" -c "${projectPath}" '${npmCommand}'`);
|
|
105
|
+
|
|
106
|
+
// Set focus on terminal pane (left pane)
|
|
107
|
+
await exec(`tmux select-pane -t "${sessionName}:0.0"`);
|
|
108
|
+
|
|
109
|
+
return sessionName;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async attachToSession(sessionName) {
|
|
113
|
+
// Check if we're already in a tmux session
|
|
114
|
+
if (process.env.TMUX) {
|
|
115
|
+
// If we're already in tmux, switch to the session
|
|
116
|
+
await exec(`tmux switch-client -t "${sessionName}"`);
|
|
117
|
+
} else {
|
|
118
|
+
// If not in tmux, attach to the session
|
|
119
|
+
spawn('tmux', ['-CC', 'attach-session', '-t', sessionName], {
|
|
120
|
+
stdio: 'inherit'
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
buildShellCommands(projectName, projectPath, claudeArgs = []) {
|
|
126
|
+
const sessionName = this.getSessionName(projectName);
|
|
127
|
+
const claudeCommand = claudeArgs.length > 0
|
|
128
|
+
? `claude ${claudeArgs.join(' ')}`
|
|
129
|
+
: 'claude';
|
|
130
|
+
|
|
131
|
+
return [
|
|
132
|
+
`# Create tmux split session for ${projectName}`,
|
|
133
|
+
`tmux has-session -t "${sessionName}" 2>/dev/null && tmux kill-session -t "${sessionName}"`,
|
|
134
|
+
`tmux new-session -d -s "${sessionName}" -c "${projectPath}" '${claudeCommand}'`,
|
|
135
|
+
`tmux split-window -h -t "${sessionName}" -c "${projectPath}"`,
|
|
136
|
+
`tmux select-pane -t "${sessionName}:0.0"`,
|
|
137
|
+
process.env.TMUX
|
|
138
|
+
? `tmux switch-client -t "${sessionName}"`
|
|
139
|
+
: `tmux -CC attach-session -t "${sessionName}"`
|
|
140
|
+
];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
buildThreePaneShellCommands(projectName, projectPath, claudeArgs = [], npmCommand = 'npm run dev') {
|
|
144
|
+
const sessionName = this.getSessionName(projectName);
|
|
145
|
+
const claudeCommand = claudeArgs.length > 0
|
|
146
|
+
? `claude ${claudeArgs.join(' ')}`
|
|
147
|
+
: 'claude';
|
|
148
|
+
|
|
149
|
+
return [
|
|
150
|
+
`# Create tmux three-pane session for ${projectName}`,
|
|
151
|
+
`tmux has-session -t "${sessionName}" 2>/dev/null && tmux kill-session -t "${sessionName}"`,
|
|
152
|
+
`tmux new-session -d -s "${sessionName}" -c "${projectPath}" '${claudeCommand}'`,
|
|
153
|
+
`tmux split-window -h -t "${sessionName}" -c "${projectPath}"`,
|
|
154
|
+
`tmux split-window -v -t "${sessionName}:0.1" -c "${projectPath}" '${npmCommand}'`,
|
|
155
|
+
`tmux select-pane -t "${sessionName}:0.0"`,
|
|
156
|
+
process.env.TMUX
|
|
157
|
+
? `tmux switch-client -t "${sessionName}"`
|
|
158
|
+
: `tmux -CC attach-session -t "${sessionName}"`
|
|
159
|
+
];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
buildTwoPaneNpmShellCommands(projectName, projectPath, npmCommand = 'npm run dev') {
|
|
163
|
+
const sessionName = this.getSessionName(projectName);
|
|
164
|
+
|
|
165
|
+
return [
|
|
166
|
+
`# Create tmux two-pane session with npm for ${projectName}`,
|
|
167
|
+
`tmux has-session -t "${sessionName}" 2>/dev/null && tmux kill-session -t "${sessionName}"`,
|
|
168
|
+
`tmux new-session -d -s "${sessionName}" -c "${projectPath}"`,
|
|
169
|
+
`tmux split-window -h -t "${sessionName}" -c "${projectPath}" '${npmCommand}'`,
|
|
170
|
+
`tmux select-pane -t "${sessionName}:0.0"`,
|
|
171
|
+
process.env.TMUX
|
|
172
|
+
? `tmux switch-client -t "${sessionName}"`
|
|
173
|
+
: `tmux attach-session -t "${sessionName}"`
|
|
174
|
+
];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async listWorkonSessions() {
|
|
178
|
+
try {
|
|
179
|
+
const { stdout } = await exec('tmux list-sessions -F "#{session_name}"');
|
|
180
|
+
return stdout.trim()
|
|
181
|
+
.split('\n')
|
|
182
|
+
.filter(session => session.startsWith(this.sessionPrefix))
|
|
183
|
+
.map(session => session.replace(this.sessionPrefix, ''));
|
|
184
|
+
} catch {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
module.exports = TmuxManager;
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
const { spawn } = require('child_process');
|
|
2
|
+
const { promisify } = require('util');
|
|
3
|
+
const exec = promisify(require('child_process').exec);
|
|
4
|
+
|
|
5
|
+
class TmuxManager {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.sessionPrefix = 'workon-';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async isTmuxAvailable() {
|
|
11
|
+
try {
|
|
12
|
+
await exec('which tmux');
|
|
13
|
+
return true;
|
|
14
|
+
} catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async sessionExists(sessionName) {
|
|
20
|
+
try {
|
|
21
|
+
await exec(`tmux has-session -t "${sessionName}"`);
|
|
22
|
+
return true;
|
|
23
|
+
} catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getSessionName(projectName) {
|
|
29
|
+
return `${this.sessionPrefix}${projectName}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async killSession(sessionName) {
|
|
33
|
+
try {
|
|
34
|
+
await exec(`tmux kill-session -t "${sessionName}"`);
|
|
35
|
+
return true;
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async createSplitSession(projectName, projectPath, claudeArgs = []) {
|
|
42
|
+
const sessionName = this.getSessionName(projectName);
|
|
43
|
+
|
|
44
|
+
// Kill existing session if it exists
|
|
45
|
+
if (await this.sessionExists(sessionName)) {
|
|
46
|
+
await this.killSession(sessionName);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const claudeCommand = claudeArgs.length > 0
|
|
50
|
+
? `claude ${claudeArgs.join(' ')}`
|
|
51
|
+
: 'claude';
|
|
52
|
+
|
|
53
|
+
// Create new tmux session with claude in the first pane
|
|
54
|
+
await exec(`tmux new-session -d -s "${sessionName}" -c "${projectPath}" '${claudeCommand}'`);
|
|
55
|
+
|
|
56
|
+
// Split window horizontally and run shell in second pane
|
|
57
|
+
await exec(`tmux split-window -h -t "${sessionName}" -c "${projectPath}"`);
|
|
58
|
+
|
|
59
|
+
// Set focus on claude pane (left pane)
|
|
60
|
+
await exec(`tmux select-pane -t "${sessionName}:0.0"`);
|
|
61
|
+
|
|
62
|
+
return sessionName;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async createThreePaneSession(projectName, projectPath, claudeArgs = [], npmCommand = 'npm run dev') {
|
|
66
|
+
const sessionName = this.getSessionName(projectName);
|
|
67
|
+
|
|
68
|
+
// Kill existing session if it exists
|
|
69
|
+
if (await this.sessionExists(sessionName)) {
|
|
70
|
+
await this.killSession(sessionName);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const claudeCommand = claudeArgs.length > 0
|
|
74
|
+
? `claude ${claudeArgs.join(' ')}`
|
|
75
|
+
: 'claude';
|
|
76
|
+
|
|
77
|
+
// Create new tmux session with claude in the first pane (left side)
|
|
78
|
+
await exec(`tmux new-session -d -s "${sessionName}" -c "${projectPath}" '${claudeCommand}'`);
|
|
79
|
+
|
|
80
|
+
// Split window vertically - creates right side
|
|
81
|
+
await exec(`tmux split-window -h -t "${sessionName}" -c "${projectPath}"`);
|
|
82
|
+
|
|
83
|
+
// Split the right pane horizontally - creates top-right and bottom-right
|
|
84
|
+
await exec(`tmux split-window -v -t "${sessionName}:0.1" -c "${projectPath}" '${npmCommand}'`);
|
|
85
|
+
|
|
86
|
+
// Set focus on claude pane (left pane)
|
|
87
|
+
await exec(`tmux select-pane -t "${sessionName}:0.0"`);
|
|
88
|
+
|
|
89
|
+
return sessionName;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async createTwoPaneNpmSession(projectName, projectPath, npmCommand = 'npm run dev') {
|
|
93
|
+
const sessionName = this.getSessionName(projectName);
|
|
94
|
+
|
|
95
|
+
// Kill existing session if it exists
|
|
96
|
+
if (await this.sessionExists(sessionName)) {
|
|
97
|
+
await this.killSession(sessionName);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Create new tmux session with shell in the first pane (left side)
|
|
101
|
+
await exec(`tmux new-session -d -s "${sessionName}" -c "${projectPath}"`);
|
|
102
|
+
|
|
103
|
+
// Split window vertically and run npm command in right pane
|
|
104
|
+
await exec(`tmux split-window -h -t "${sessionName}" -c "${projectPath}" '${npmCommand}'`);
|
|
105
|
+
|
|
106
|
+
// Set focus on terminal pane (left pane)
|
|
107
|
+
await exec(`tmux select-pane -t "${sessionName}:0.0"`);
|
|
108
|
+
|
|
109
|
+
return sessionName;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async attachToSession(sessionName) {
|
|
113
|
+
// Check if we're already in a tmux session
|
|
114
|
+
if (process.env.TMUX) {
|
|
115
|
+
// If we're already in tmux, switch to the session
|
|
116
|
+
await exec(`tmux switch-client -t "${sessionName}"`);
|
|
117
|
+
} else {
|
|
118
|
+
// If not in tmux, attach to the session
|
|
119
|
+
spawn('tmux', ['-CC', 'attach-session', '-t', sessionName], {
|
|
120
|
+
stdio: 'inherit'
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
buildShellCommands(projectName, projectPath, claudeArgs = []) {
|
|
126
|
+
const sessionName = this.getSessionName(projectName);
|
|
127
|
+
const claudeCommand = claudeArgs.length > 0
|
|
128
|
+
? `claude ${claudeArgs.join(' ')}`
|
|
129
|
+
: 'claude';
|
|
130
|
+
|
|
131
|
+
return [
|
|
132
|
+
`# Create tmux split session for ${projectName}`,
|
|
133
|
+
`tmux has-session -t "${sessionName}" 2>/dev/null && tmux kill-session -t "${sessionName}"`,
|
|
134
|
+
`tmux new-session -d -s "${sessionName}" -c "${projectPath}" '${claudeCommand}'`,
|
|
135
|
+
`tmux split-window -h -t "${sessionName}" -c "${projectPath}"`,
|
|
136
|
+
`tmux select-pane -t "${sessionName}:0.0"`,
|
|
137
|
+
process.env.TMUX
|
|
138
|
+
? `tmux switch-client -t "${sessionName}"`
|
|
139
|
+
: `tmux -CC attach-session -t "${sessionName}"`
|
|
140
|
+
];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
buildThreePaneShellCommands(projectName, projectPath, claudeArgs = [], npmCommand = 'npm run dev') {
|
|
144
|
+
const sessionName = this.getSessionName(projectName);
|
|
145
|
+
const claudeCommand = claudeArgs.length > 0
|
|
146
|
+
? `claude ${claudeArgs.join(' ')}`
|
|
147
|
+
: 'claude';
|
|
148
|
+
|
|
149
|
+
return [
|
|
150
|
+
`# Create tmux three-pane session for ${projectName}`,
|
|
151
|
+
`tmux has-session -t "${sessionName}" 2>/dev/null && tmux kill-session -t "${sessionName}"`,
|
|
152
|
+
`tmux new-session -d -s "${sessionName}" -c "${projectPath}" '${claudeCommand}'`,
|
|
153
|
+
`tmux split-window -h -t "${sessionName}" -c "${projectPath}"`,
|
|
154
|
+
`tmux split-window -v -t "${sessionName}:0.1" -c "${projectPath}" '${npmCommand}'`,
|
|
155
|
+
`tmux select-pane -t "${sessionName}:0.0"`,
|
|
156
|
+
process.env.TMUX
|
|
157
|
+
? `tmux switch-client -t "${sessionName}"`
|
|
158
|
+
: `tmux -CC attach-session -t "${sessionName}"`
|
|
159
|
+
];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
buildTwoPaneNpmShellCommands(projectName, projectPath, npmCommand = 'npm run dev') {
|
|
163
|
+
const sessionName = this.getSessionName(projectName);
|
|
164
|
+
|
|
165
|
+
return [
|
|
166
|
+
`# Create tmux two-pane session with npm for ${projectName}`,
|
|
167
|
+
`tmux has-session -t "${sessionName}" 2>/dev/null && tmux kill-session -t "${sessionName}"`,
|
|
168
|
+
`tmux new-session -d -s "${sessionName}" -c "${projectPath}"`,
|
|
169
|
+
`tmux split-window -h -t "${sessionName}" -c "${projectPath}" '${npmCommand}'`,
|
|
170
|
+
`tmux select-pane -t "${sessionName}:0.0"`,
|
|
171
|
+
process.env.TMUX
|
|
172
|
+
? `tmux switch-client -t "${sessionName}"`
|
|
173
|
+
: `tmux -CC attach-session -t "${sessionName}"`
|
|
174
|
+
];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async listWorkonSessions() {
|
|
178
|
+
try {
|
|
179
|
+
const { stdout } = await exec('tmux list-sessions -F "#{session_name}"');
|
|
180
|
+
return stdout.trim()
|
|
181
|
+
.split('\n')
|
|
182
|
+
.filter(session => session.startsWith(this.sessionPrefix))
|
|
183
|
+
.map(session => session.replace(this.sessionPrefix, ''));
|
|
184
|
+
} catch {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
module.exports = TmuxManager;
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
const { spawn } = require('child_process');
|
|
2
|
+
const { promisify } = require('util');
|
|
3
|
+
const exec = promisify(require('child_process').exec);
|
|
4
|
+
|
|
5
|
+
class TmuxManager {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.sessionPrefix = 'workon-';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async isTmuxAvailable() {
|
|
11
|
+
try {
|
|
12
|
+
await exec('which tmux');
|
|
13
|
+
return true;
|
|
14
|
+
} catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async sessionExists(sessionName) {
|
|
20
|
+
try {
|
|
21
|
+
await exec(`tmux has-session -t "${sessionName}"`);
|
|
22
|
+
return true;
|
|
23
|
+
} catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getSessionName(projectName) {
|
|
29
|
+
return `${this.sessionPrefix}${projectName}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async killSession(sessionName) {
|
|
33
|
+
try {
|
|
34
|
+
await exec(`tmux kill-session -t "${sessionName}"`);
|
|
35
|
+
return true;
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async createSplitSession(projectName, projectPath, claudeArgs = []) {
|
|
42
|
+
const sessionName = this.getSessionName(projectName);
|
|
43
|
+
|
|
44
|
+
// Kill existing session if it exists
|
|
45
|
+
if (await this.sessionExists(sessionName)) {
|
|
46
|
+
await this.killSession(sessionName);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const claudeCommand = claudeArgs.length > 0
|
|
50
|
+
? `claude ${claudeArgs.join(' ')}`
|
|
51
|
+
: 'claude';
|
|
52
|
+
|
|
53
|
+
// Create new tmux session with claude in the first pane
|
|
54
|
+
await exec(`tmux new-session -d -s "${sessionName}" -c "${projectPath}" '${claudeCommand}'`);
|
|
55
|
+
|
|
56
|
+
// Split window horizontally and run shell in second pane
|
|
57
|
+
await exec(`tmux split-window -h -t "${sessionName}" -c "${projectPath}"`);
|
|
58
|
+
|
|
59
|
+
// Set focus on claude pane (left pane)
|
|
60
|
+
await exec(`tmux select-pane -t "${sessionName}:0.0"`);
|
|
61
|
+
|
|
62
|
+
return sessionName;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async createThreePaneSession(projectName, projectPath, claudeArgs = [], npmCommand = 'npm run dev') {
|
|
66
|
+
const sessionName = this.getSessionName(projectName);
|
|
67
|
+
|
|
68
|
+
// Kill existing session if it exists
|
|
69
|
+
if (await this.sessionExists(sessionName)) {
|
|
70
|
+
await this.killSession(sessionName);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const claudeCommand = claudeArgs.length > 0
|
|
74
|
+
? `claude ${claudeArgs.join(' ')}`
|
|
75
|
+
: 'claude';
|
|
76
|
+
|
|
77
|
+
// Create new tmux session with claude in the first pane (left side)
|
|
78
|
+
await exec(`tmux new-session -d -s "${sessionName}" -c "${projectPath}" '${claudeCommand}'`);
|
|
79
|
+
|
|
80
|
+
// Split window vertically - creates right side
|
|
81
|
+
await exec(`tmux split-window -h -t "${sessionName}" -c "${projectPath}"`);
|
|
82
|
+
|
|
83
|
+
// Split the right pane horizontally - creates top-right and bottom-right
|
|
84
|
+
await exec(`tmux split-window -v -t "${sessionName}:0.1" -c "${projectPath}" '${npmCommand}'`);
|
|
85
|
+
|
|
86
|
+
// Set focus on claude pane (left pane)
|
|
87
|
+
await exec(`tmux select-pane -t "${sessionName}:0.0"`);
|
|
88
|
+
|
|
89
|
+
return sessionName;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async createTwoPaneNpmSession(projectName, projectPath, npmCommand = 'npm run dev') {
|
|
93
|
+
const sessionName = this.getSessionName(projectName);
|
|
94
|
+
|
|
95
|
+
// Kill existing session if it exists
|
|
96
|
+
if (await this.sessionExists(sessionName)) {
|
|
97
|
+
await this.killSession(sessionName);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Create new tmux session with shell in the first pane (left side)
|
|
101
|
+
await exec(`tmux new-session -d -s "${sessionName}" -c "${projectPath}"`);
|
|
102
|
+
|
|
103
|
+
// Split window vertically and run npm command in right pane
|
|
104
|
+
await exec(`tmux split-window -h -t "${sessionName}" -c "${projectPath}" '${npmCommand}'`);
|
|
105
|
+
|
|
106
|
+
// Set focus on terminal pane (left pane)
|
|
107
|
+
await exec(`tmux select-pane -t "${sessionName}:0.0"`);
|
|
108
|
+
|
|
109
|
+
return sessionName;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async attachToSession(sessionName) {
|
|
113
|
+
// Check if we're already in a tmux session
|
|
114
|
+
if (process.env.TMUX) {
|
|
115
|
+
// If we're already in tmux, switch to the session
|
|
116
|
+
await exec(`tmux switch-client -t "${sessionName}"`);
|
|
117
|
+
} else {
|
|
118
|
+
// If not in tmux, attach to the session
|
|
119
|
+
spawn('tmux', ['-CC', 'attach-session', '-t', sessionName], {
|
|
120
|
+
stdio: 'inherit'
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
buildShellCommands(projectName, projectPath, claudeArgs = []) {
|
|
126
|
+
const sessionName = this.getSessionName(projectName);
|
|
127
|
+
const claudeCommand = claudeArgs.length > 0
|
|
128
|
+
? `claude ${claudeArgs.join(' ')}`
|
|
129
|
+
: 'claude';
|
|
130
|
+
|
|
131
|
+
return [
|
|
132
|
+
`# Create tmux split session for ${projectName}`,
|
|
133
|
+
`tmux has-session -t "${sessionName}" 2>/dev/null && tmux kill-session -t "${sessionName}"`,
|
|
134
|
+
`tmux new-session -d -s "${sessionName}" -c "${projectPath}" '${claudeCommand}'`,
|
|
135
|
+
`tmux split-window -h -t "${sessionName}" -c "${projectPath}"`,
|
|
136
|
+
`tmux select-pane -t "${sessionName}:0.0"`,
|
|
137
|
+
process.env.TMUX
|
|
138
|
+
? `tmux switch-client -t "${sessionName}"`
|
|
139
|
+
: `tmux -CC attach-session -t "${sessionName}"`
|
|
140
|
+
];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
buildThreePaneShellCommands(projectName, projectPath, claudeArgs = [], npmCommand = 'npm run dev') {
|
|
144
|
+
const sessionName = this.getSessionName(projectName);
|
|
145
|
+
const claudeCommand = claudeArgs.length > 0
|
|
146
|
+
? `claude ${claudeArgs.join(' ')}`
|
|
147
|
+
: 'claude';
|
|
148
|
+
|
|
149
|
+
return [
|
|
150
|
+
`# Create tmux three-pane session for ${projectName}`,
|
|
151
|
+
`tmux has-session -t "${sessionName}" 2>/dev/null && tmux kill-session -t "${sessionName}"`,
|
|
152
|
+
`tmux new-session -d -s "${sessionName}" -c "${projectPath}" '${claudeCommand}'`,
|
|
153
|
+
`tmux split-window -h -t "${sessionName}" -c "${projectPath}"`,
|
|
154
|
+
`tmux split-window -v -t "${sessionName}:0.1" -c "${projectPath}" '${npmCommand}'`,
|
|
155
|
+
`tmux select-pane -t "${sessionName}:0.0"`,
|
|
156
|
+
process.env.TMUX
|
|
157
|
+
? `tmux switch-client -t "${sessionName}"`
|
|
158
|
+
: `tmux -CC attach-session -t "${sessionName}"`
|
|
159
|
+
];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
buildTwoPaneNpmShellCommands(projectName, projectPath, npmCommand = 'npm run dev') {
|
|
163
|
+
const sessionName = this.getSessionName(projectName);
|
|
164
|
+
|
|
165
|
+
return [
|
|
166
|
+
`# Create tmux two-pane session with npm for ${projectName}`,
|
|
167
|
+
`tmux has-session -t "${sessionName}" 2>/dev/null && tmux kill-session -t "${sessionName}"`,
|
|
168
|
+
`tmux new-session -d -s "${sessionName}" -c "${projectPath}"`,
|
|
169
|
+
`tmux split-window -h -t "${sessionName}" -c "${projectPath}" '${npmCommand}'`,
|
|
170
|
+
`tmux select-pane -t "${sessionName}:0.0"`,
|
|
171
|
+
process.env.TMUX
|
|
172
|
+
? `tmux switch-client -t "${sessionName}"`
|
|
173
|
+
: `tmux -CC attach-session -t "${sessionName}"`
|
|
174
|
+
];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async listWorkonSessions() {
|
|
178
|
+
try {
|
|
179
|
+
const { stdout } = await exec('tmux list-sessions -F "#{session_name}"');
|
|
180
|
+
return stdout.trim()
|
|
181
|
+
.split('\n')
|
|
182
|
+
.filter(session => session.startsWith(this.sessionPrefix))
|
|
183
|
+
.map(session => session.replace(this.sessionPrefix, ''));
|
|
184
|
+
} catch {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
module.exports = TmuxManager;
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [2.1.2](https://github.com/israelroldan/workon/compare/v2.1.1...v2.1.2) (2025-08-07)
|
|
6
|
+
|
|
7
|
+
### [2.1.1](https://github.com/israelroldan/workon/compare/v2.1.0...v2.1.1) (2025-08-07)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Bug Fixes
|
|
11
|
+
|
|
12
|
+
* Update tmux attach command to use -CC flag for compatibility with iTerm ([2d3d7c7](https://github.com/israelroldan/workon/commit/2d3d7c73fb164f1718908d276f91b8fba004f42d))
|
|
13
|
+
|
|
5
14
|
## [2.1.0](https://github.com/israelroldan/workon/compare/v2.0.0...v2.1.0) (2025-08-07)
|
|
6
15
|
|
|
7
16
|
|
package/README.md
CHANGED
package/cli/index.js
CHANGED
|
@@ -62,8 +62,8 @@ class workon extends container {
|
|
|
62
62
|
me.log.setLogLevel('debug');
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
if (params
|
|
66
|
-
me.log.debug('
|
|
65
|
+
if (params.completion) {
|
|
66
|
+
me.log.debug('Setting up command-line completion');
|
|
67
67
|
me.completion.setupShellInitFile();
|
|
68
68
|
return true;
|
|
69
69
|
} else if (params.init) {
|
|
@@ -164,11 +164,11 @@ workon.define({
|
|
|
164
164
|
help: {
|
|
165
165
|
'': 'Work on something great!',
|
|
166
166
|
debug: 'Provide debug logging output',
|
|
167
|
-
|
|
167
|
+
completion: 'Configure and generate shell tab completion',
|
|
168
168
|
shell: 'Output shell commands instead of spawning processes',
|
|
169
169
|
init: 'Generate shell integration function for seamless directory switching'
|
|
170
170
|
},
|
|
171
|
-
switches: '[d#debug:boolean=false] [
|
|
171
|
+
switches: '[d#debug:boolean=false] [completion:boolean=false] [shell:boolean=false] [init:boolean=false]',
|
|
172
172
|
commands: {
|
|
173
173
|
'': 'open',
|
|
174
174
|
interactive: {
|
package/lib/tmux.js
CHANGED
|
@@ -77,12 +77,18 @@ class TmuxManager {
|
|
|
77
77
|
// Create new tmux session with claude in the first pane (left side)
|
|
78
78
|
await exec(`tmux new-session -d -s "${sessionName}" -c "${projectPath}" '${claudeCommand}'`);
|
|
79
79
|
|
|
80
|
-
// Split window vertically - creates right side
|
|
80
|
+
// Split window vertically - creates right side (50/50 split)
|
|
81
81
|
await exec(`tmux split-window -h -t "${sessionName}" -c "${projectPath}"`);
|
|
82
82
|
|
|
83
|
-
// Split the right pane horizontally - creates top-right and bottom-right
|
|
83
|
+
// Split the right pane horizontally - creates top-right and bottom-right (50/50 split)
|
|
84
84
|
await exec(`tmux split-window -v -t "${sessionName}:0.1" -c "${projectPath}" '${npmCommand}'`);
|
|
85
85
|
|
|
86
|
+
// Set remain-on-exit to keep pane open if command fails
|
|
87
|
+
await exec(`tmux set-option -t "${sessionName}:0.2" remain-on-exit on`);
|
|
88
|
+
|
|
89
|
+
// Resize panes to ensure npm pane is visible (give it at least 10 lines)
|
|
90
|
+
await exec(`tmux resize-pane -t "${sessionName}:0.2" -y 10`);
|
|
91
|
+
|
|
86
92
|
// Set focus on claude pane (left pane)
|
|
87
93
|
await exec(`tmux select-pane -t "${sessionName}:0.0"`);
|
|
88
94
|
|
|
@@ -103,6 +109,9 @@ class TmuxManager {
|
|
|
103
109
|
// Split window vertically and run npm command in right pane
|
|
104
110
|
await exec(`tmux split-window -h -t "${sessionName}" -c "${projectPath}" '${npmCommand}'`);
|
|
105
111
|
|
|
112
|
+
// Set remain-on-exit to keep pane open if command fails
|
|
113
|
+
await exec(`tmux set-option -t "${sessionName}:0.1" remain-on-exit on`);
|
|
114
|
+
|
|
106
115
|
// Set focus on terminal pane (left pane)
|
|
107
116
|
await exec(`tmux select-pane -t "${sessionName}:0.0"`);
|
|
108
117
|
|
|
@@ -115,10 +124,23 @@ class TmuxManager {
|
|
|
115
124
|
// If we're already in tmux, switch to the session
|
|
116
125
|
await exec(`tmux switch-client -t "${sessionName}"`);
|
|
117
126
|
} else {
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
127
|
+
// Check if iTerm2 integration is available
|
|
128
|
+
const isITerm = process.env.TERM_PROGRAM === 'iTerm.app' ||
|
|
129
|
+
process.env.LC_TERMINAL === 'iTerm2' ||
|
|
130
|
+
!!process.env.ITERM_SESSION_ID;
|
|
131
|
+
const useiTermIntegration = isITerm && !process.env.TMUX_CC_NOT_SUPPORTED;
|
|
132
|
+
|
|
133
|
+
if (useiTermIntegration) {
|
|
134
|
+
// Use iTerm2 tmux integration
|
|
135
|
+
spawn('tmux', ['-CC', 'attach-session', '-t', sessionName], {
|
|
136
|
+
stdio: 'inherit'
|
|
137
|
+
});
|
|
138
|
+
} else {
|
|
139
|
+
// Use regular tmux
|
|
140
|
+
spawn('tmux', ['attach-session', '-t', sessionName], {
|
|
141
|
+
stdio: 'inherit'
|
|
142
|
+
});
|
|
143
|
+
}
|
|
122
144
|
}
|
|
123
145
|
}
|
|
124
146
|
|
|
@@ -136,7 +158,9 @@ class TmuxManager {
|
|
|
136
158
|
`tmux select-pane -t "${sessionName}:0.0"`,
|
|
137
159
|
process.env.TMUX
|
|
138
160
|
? `tmux switch-client -t "${sessionName}"`
|
|
139
|
-
:
|
|
161
|
+
: ((process.env.TERM_PROGRAM === 'iTerm.app' || process.env.LC_TERMINAL === 'iTerm2' || process.env.ITERM_SESSION_ID) && !process.env.TMUX_CC_NOT_SUPPORTED)
|
|
162
|
+
? `tmux -CC attach-session -t "${sessionName}"`
|
|
163
|
+
: `tmux attach-session -t "${sessionName}"`
|
|
140
164
|
];
|
|
141
165
|
}
|
|
142
166
|
|
|
@@ -152,10 +176,14 @@ class TmuxManager {
|
|
|
152
176
|
`tmux new-session -d -s "${sessionName}" -c "${projectPath}" '${claudeCommand}'`,
|
|
153
177
|
`tmux split-window -h -t "${sessionName}" -c "${projectPath}"`,
|
|
154
178
|
`tmux split-window -v -t "${sessionName}:0.1" -c "${projectPath}" '${npmCommand}'`,
|
|
179
|
+
`tmux set-option -t "${sessionName}:0.2" remain-on-exit on`,
|
|
180
|
+
`tmux resize-pane -t "${sessionName}:0.2" -y 10`,
|
|
155
181
|
`tmux select-pane -t "${sessionName}:0.0"`,
|
|
156
182
|
process.env.TMUX
|
|
157
183
|
? `tmux switch-client -t "${sessionName}"`
|
|
158
|
-
:
|
|
184
|
+
: ((process.env.TERM_PROGRAM === 'iTerm.app' || process.env.LC_TERMINAL === 'iTerm2' || process.env.ITERM_SESSION_ID) && !process.env.TMUX_CC_NOT_SUPPORTED)
|
|
185
|
+
? `tmux -CC attach-session -t "${sessionName}"`
|
|
186
|
+
: `tmux attach-session -t "${sessionName}"`
|
|
159
187
|
];
|
|
160
188
|
}
|
|
161
189
|
|
|
@@ -167,10 +195,13 @@ class TmuxManager {
|
|
|
167
195
|
`tmux has-session -t "${sessionName}" 2>/dev/null && tmux kill-session -t "${sessionName}"`,
|
|
168
196
|
`tmux new-session -d -s "${sessionName}" -c "${projectPath}"`,
|
|
169
197
|
`tmux split-window -h -t "${sessionName}" -c "${projectPath}" '${npmCommand}'`,
|
|
198
|
+
`tmux set-option -t "${sessionName}:0.1" remain-on-exit on`,
|
|
170
199
|
`tmux select-pane -t "${sessionName}:0.0"`,
|
|
171
200
|
process.env.TMUX
|
|
172
201
|
? `tmux switch-client -t "${sessionName}"`
|
|
173
|
-
:
|
|
202
|
+
: ((process.env.TERM_PROGRAM === 'iTerm.app' || process.env.LC_TERMINAL === 'iTerm2' || process.env.ITERM_SESSION_ID) && !process.env.TMUX_CC_NOT_SUPPORTED)
|
|
203
|
+
? `tmux -CC attach-session -t "${sessionName}"`
|
|
204
|
+
: `tmux attach-session -t "${sessionName}"`
|
|
174
205
|
];
|
|
175
206
|
}
|
|
176
207
|
|