workon 3.2.1 → 3.2.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/dist/cli.js +289 -228
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +9 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +9 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -345,18 +345,22 @@ var init_cwd = __esm({
|
|
|
345
345
|
const { project, isShellMode, shellCommands } = context;
|
|
346
346
|
const projectPath = project.path.path;
|
|
347
347
|
if (isShellMode) {
|
|
348
|
-
shellCommands.push(`
|
|
348
|
+
shellCommands.push(`pushd "${projectPath}" > /dev/null`);
|
|
349
349
|
} else {
|
|
350
350
|
const shell = process.env.SHELL || "/bin/bash";
|
|
351
|
-
spawn(shell, [], {
|
|
351
|
+
const child = spawn(shell, ["-i"], {
|
|
352
352
|
cwd: projectPath,
|
|
353
353
|
stdio: "inherit"
|
|
354
354
|
});
|
|
355
|
+
await new Promise((resolve2, reject) => {
|
|
356
|
+
child.on("close", () => resolve2());
|
|
357
|
+
child.on("error", (err) => reject(err));
|
|
358
|
+
});
|
|
355
359
|
}
|
|
356
360
|
},
|
|
357
361
|
generateShellCommand(context) {
|
|
358
362
|
const projectPath = context.project.path.path;
|
|
359
|
-
return [`
|
|
363
|
+
return [`pushd "${projectPath}" > /dev/null`];
|
|
360
364
|
}
|
|
361
365
|
};
|
|
362
366
|
}
|
|
@@ -421,7 +425,7 @@ var init_ide = __esm({
|
|
|
421
425
|
const projectPath = project.path.path;
|
|
422
426
|
const ide = project.ide || "code";
|
|
423
427
|
if (isShellMode) {
|
|
424
|
-
shellCommands.push(
|
|
428
|
+
shellCommands.push(`set +m; ${ide} "${projectPath}" &>/dev/null &`);
|
|
425
429
|
} else {
|
|
426
430
|
spawn2(ide, [projectPath], {
|
|
427
431
|
detached: true,
|
|
@@ -432,7 +436,7 @@ var init_ide = __esm({
|
|
|
432
436
|
generateShellCommand(context) {
|
|
433
437
|
const projectPath = context.project.path.path;
|
|
434
438
|
const ide = context.project.ide || "code";
|
|
435
|
-
return [
|
|
439
|
+
return [`set +m; ${ide} "${projectPath}" &>/dev/null &`];
|
|
436
440
|
}
|
|
437
441
|
};
|
|
438
442
|
}
|
|
@@ -1094,12 +1098,213 @@ var init_registry = __esm({
|
|
|
1094
1098
|
}
|
|
1095
1099
|
});
|
|
1096
1100
|
|
|
1101
|
+
// src/lib/sanitize.ts
|
|
1102
|
+
function sanitizeForShell(input6) {
|
|
1103
|
+
if (!input6) return "";
|
|
1104
|
+
return input6.replace(/[^a-zA-Z0-9_\-.]/g, "_");
|
|
1105
|
+
}
|
|
1106
|
+
function escapeForSingleQuotes(input6) {
|
|
1107
|
+
if (!input6) return "";
|
|
1108
|
+
return input6.replace(/'/g, "'\\''");
|
|
1109
|
+
}
|
|
1110
|
+
var init_sanitize = __esm({
|
|
1111
|
+
"src/lib/sanitize.ts"() {
|
|
1112
|
+
"use strict";
|
|
1113
|
+
}
|
|
1114
|
+
});
|
|
1115
|
+
|
|
1116
|
+
// src/lib/tmux.ts
|
|
1117
|
+
import { exec as execCallback, spawn as spawn7 } from "child_process";
|
|
1118
|
+
import { promisify } from "util";
|
|
1119
|
+
var exec, TmuxManager;
|
|
1120
|
+
var init_tmux = __esm({
|
|
1121
|
+
"src/lib/tmux.ts"() {
|
|
1122
|
+
"use strict";
|
|
1123
|
+
init_sanitize();
|
|
1124
|
+
exec = promisify(execCallback);
|
|
1125
|
+
TmuxManager = class {
|
|
1126
|
+
sessionPrefix = "workon-";
|
|
1127
|
+
async isTmuxAvailable() {
|
|
1128
|
+
try {
|
|
1129
|
+
await exec("which tmux");
|
|
1130
|
+
return true;
|
|
1131
|
+
} catch {
|
|
1132
|
+
return false;
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
async sessionExists(sessionName) {
|
|
1136
|
+
try {
|
|
1137
|
+
await exec(`tmux has-session -t '${escapeForSingleQuotes(sessionName)}'`);
|
|
1138
|
+
return true;
|
|
1139
|
+
} catch {
|
|
1140
|
+
return false;
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
getSessionName(projectName) {
|
|
1144
|
+
return `${this.sessionPrefix}${sanitizeForShell(projectName)}`;
|
|
1145
|
+
}
|
|
1146
|
+
async killSession(sessionName) {
|
|
1147
|
+
try {
|
|
1148
|
+
await exec(`tmux kill-session -t '${escapeForSingleQuotes(sessionName)}'`);
|
|
1149
|
+
return true;
|
|
1150
|
+
} catch {
|
|
1151
|
+
return false;
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
async createSplitSession(projectName, projectPath, claudeArgs = []) {
|
|
1155
|
+
const sessionName = this.getSessionName(projectName);
|
|
1156
|
+
const escapedSession = escapeForSingleQuotes(sessionName);
|
|
1157
|
+
const escapedPath = escapeForSingleQuotes(projectPath);
|
|
1158
|
+
if (await this.sessionExists(sessionName)) {
|
|
1159
|
+
await this.killSession(sessionName);
|
|
1160
|
+
}
|
|
1161
|
+
const claudeCommand = claudeArgs.length > 0 ? `claude ${claudeArgs.join(" ")}` : "claude";
|
|
1162
|
+
const escapedClaudeCmd = escapeForSingleQuotes(claudeCommand);
|
|
1163
|
+
await exec(
|
|
1164
|
+
`tmux new-session -d -s '${escapedSession}' -c '${escapedPath}' '${escapedClaudeCmd}'`
|
|
1165
|
+
);
|
|
1166
|
+
await exec(`tmux split-window -h -t '${escapedSession}' -c '${escapedPath}'`);
|
|
1167
|
+
await exec(`tmux select-pane -t '${escapedSession}:0.0'`);
|
|
1168
|
+
return sessionName;
|
|
1169
|
+
}
|
|
1170
|
+
async createThreePaneSession(projectName, projectPath, claudeArgs = [], npmCommand = "npm run dev") {
|
|
1171
|
+
const sessionName = this.getSessionName(projectName);
|
|
1172
|
+
const escapedSession = escapeForSingleQuotes(sessionName);
|
|
1173
|
+
const escapedPath = escapeForSingleQuotes(projectPath);
|
|
1174
|
+
if (await this.sessionExists(sessionName)) {
|
|
1175
|
+
await this.killSession(sessionName);
|
|
1176
|
+
}
|
|
1177
|
+
const claudeCommand = claudeArgs.length > 0 ? `claude ${claudeArgs.join(" ")}` : "claude";
|
|
1178
|
+
const escapedClaudeCmd = escapeForSingleQuotes(claudeCommand);
|
|
1179
|
+
const escapedNpmCmd = escapeForSingleQuotes(npmCommand);
|
|
1180
|
+
await exec(
|
|
1181
|
+
`tmux new-session -d -s '${escapedSession}' -c '${escapedPath}' '${escapedClaudeCmd}'`
|
|
1182
|
+
);
|
|
1183
|
+
await exec(`tmux split-window -h -t '${escapedSession}' -c '${escapedPath}'`);
|
|
1184
|
+
await exec(
|
|
1185
|
+
`tmux split-window -v -t '${escapedSession}:0.1' -c '${escapedPath}' '${escapedNpmCmd}'`
|
|
1186
|
+
);
|
|
1187
|
+
await exec(`tmux set-option -t '${escapedSession}:0.2' remain-on-exit on`);
|
|
1188
|
+
await exec(`tmux resize-pane -t '${escapedSession}:0.2' -y 10`);
|
|
1189
|
+
await exec(`tmux select-pane -t '${escapedSession}:0.0'`);
|
|
1190
|
+
return sessionName;
|
|
1191
|
+
}
|
|
1192
|
+
async createTwoPaneNpmSession(projectName, projectPath, npmCommand = "npm run dev") {
|
|
1193
|
+
const sessionName = this.getSessionName(projectName);
|
|
1194
|
+
const escapedSession = escapeForSingleQuotes(sessionName);
|
|
1195
|
+
const escapedPath = escapeForSingleQuotes(projectPath);
|
|
1196
|
+
const escapedNpmCmd = escapeForSingleQuotes(npmCommand);
|
|
1197
|
+
if (await this.sessionExists(sessionName)) {
|
|
1198
|
+
await this.killSession(sessionName);
|
|
1199
|
+
}
|
|
1200
|
+
await exec(`tmux new-session -d -s '${escapedSession}' -c '${escapedPath}'`);
|
|
1201
|
+
await exec(
|
|
1202
|
+
`tmux split-window -h -t '${escapedSession}' -c '${escapedPath}' '${escapedNpmCmd}'`
|
|
1203
|
+
);
|
|
1204
|
+
await exec(`tmux set-option -t '${escapedSession}:0.1' remain-on-exit on`);
|
|
1205
|
+
await exec(`tmux select-pane -t '${escapedSession}:0.0'`);
|
|
1206
|
+
return sessionName;
|
|
1207
|
+
}
|
|
1208
|
+
async attachToSession(sessionName) {
|
|
1209
|
+
const escapedSession = escapeForSingleQuotes(sessionName);
|
|
1210
|
+
if (process.env.TMUX) {
|
|
1211
|
+
await exec(`tmux switch-client -t '${escapedSession}'`);
|
|
1212
|
+
} else {
|
|
1213
|
+
const isITerm = process.env.TERM_PROGRAM === "iTerm.app" || process.env.LC_TERMINAL === "iTerm2" || !!process.env.ITERM_SESSION_ID;
|
|
1214
|
+
const useiTermIntegration = isITerm && !process.env.TMUX_CC_NOT_SUPPORTED;
|
|
1215
|
+
if (useiTermIntegration) {
|
|
1216
|
+
spawn7("tmux", ["-CC", "attach-session", "-t", sessionName], {
|
|
1217
|
+
stdio: "inherit",
|
|
1218
|
+
detached: true
|
|
1219
|
+
});
|
|
1220
|
+
} else {
|
|
1221
|
+
spawn7("tmux", ["attach-session", "-t", sessionName], {
|
|
1222
|
+
stdio: "inherit",
|
|
1223
|
+
detached: true
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
buildShellCommands(projectName, projectPath, claudeArgs = []) {
|
|
1229
|
+
const sessionName = this.getSessionName(projectName);
|
|
1230
|
+
const escapedSession = escapeForSingleQuotes(sessionName);
|
|
1231
|
+
const escapedPath = escapeForSingleQuotes(projectPath);
|
|
1232
|
+
const claudeCommand = claudeArgs.length > 0 ? `claude ${claudeArgs.join(" ")}` : "claude";
|
|
1233
|
+
const escapedClaudeCmd = escapeForSingleQuotes(claudeCommand);
|
|
1234
|
+
return [
|
|
1235
|
+
`# Create tmux split session for ${sanitizeForShell(projectName)}`,
|
|
1236
|
+
`tmux has-session -t '${escapedSession}' 2>/dev/null && tmux kill-session -t '${escapedSession}'`,
|
|
1237
|
+
`tmux new-session -d -s '${escapedSession}' -c '${escapedPath}' '${escapedClaudeCmd}'`,
|
|
1238
|
+
`tmux split-window -h -t '${escapedSession}' -c '${escapedPath}'`,
|
|
1239
|
+
`tmux select-pane -t '${escapedSession}:0.0'`,
|
|
1240
|
+
this.getAttachCommand(sessionName)
|
|
1241
|
+
];
|
|
1242
|
+
}
|
|
1243
|
+
buildThreePaneShellCommands(projectName, projectPath, claudeArgs = [], npmCommand = "npm run dev") {
|
|
1244
|
+
const sessionName = this.getSessionName(projectName);
|
|
1245
|
+
const escapedSession = escapeForSingleQuotes(sessionName);
|
|
1246
|
+
const escapedPath = escapeForSingleQuotes(projectPath);
|
|
1247
|
+
const claudeCommand = claudeArgs.length > 0 ? `claude ${claudeArgs.join(" ")}` : "claude";
|
|
1248
|
+
const escapedClaudeCmd = escapeForSingleQuotes(claudeCommand);
|
|
1249
|
+
const escapedNpmCmd = escapeForSingleQuotes(npmCommand);
|
|
1250
|
+
return [
|
|
1251
|
+
`# Create tmux three-pane session for ${sanitizeForShell(projectName)}`,
|
|
1252
|
+
`tmux has-session -t '${escapedSession}' 2>/dev/null && tmux kill-session -t '${escapedSession}'`,
|
|
1253
|
+
`tmux new-session -d -s '${escapedSession}' -c '${escapedPath}' '${escapedClaudeCmd}'`,
|
|
1254
|
+
`tmux split-window -h -t '${escapedSession}' -c '${escapedPath}'`,
|
|
1255
|
+
`tmux split-window -v -t '${escapedSession}:0.1' -c '${escapedPath}' '${escapedNpmCmd}'`,
|
|
1256
|
+
`tmux set-option -t '${escapedSession}:0.2' remain-on-exit on`,
|
|
1257
|
+
`tmux resize-pane -t '${escapedSession}:0.2' -y 10`,
|
|
1258
|
+
`tmux select-pane -t '${escapedSession}:0.0'`,
|
|
1259
|
+
this.getAttachCommand(sessionName)
|
|
1260
|
+
];
|
|
1261
|
+
}
|
|
1262
|
+
buildTwoPaneNpmShellCommands(projectName, projectPath, npmCommand = "npm run dev") {
|
|
1263
|
+
const sessionName = this.getSessionName(projectName);
|
|
1264
|
+
const escapedSession = escapeForSingleQuotes(sessionName);
|
|
1265
|
+
const escapedPath = escapeForSingleQuotes(projectPath);
|
|
1266
|
+
const escapedNpmCmd = escapeForSingleQuotes(npmCommand);
|
|
1267
|
+
return [
|
|
1268
|
+
`# Create tmux two-pane session with npm for ${sanitizeForShell(projectName)}`,
|
|
1269
|
+
`tmux has-session -t '${escapedSession}' 2>/dev/null && tmux kill-session -t '${escapedSession}'`,
|
|
1270
|
+
`tmux new-session -d -s '${escapedSession}' -c '${escapedPath}'`,
|
|
1271
|
+
`tmux split-window -h -t '${escapedSession}' -c '${escapedPath}' '${escapedNpmCmd}'`,
|
|
1272
|
+
`tmux set-option -t '${escapedSession}:0.1' remain-on-exit on`,
|
|
1273
|
+
`tmux select-pane -t '${escapedSession}:0.0'`,
|
|
1274
|
+
this.getAttachCommand(sessionName)
|
|
1275
|
+
];
|
|
1276
|
+
}
|
|
1277
|
+
getAttachCommand(sessionName) {
|
|
1278
|
+
const escapedSession = escapeForSingleQuotes(sessionName);
|
|
1279
|
+
if (process.env.TMUX) {
|
|
1280
|
+
return `tmux switch-client -t '${escapedSession}'`;
|
|
1281
|
+
}
|
|
1282
|
+
const isITerm = process.env.TERM_PROGRAM === "iTerm.app" || process.env.LC_TERMINAL === "iTerm2" || process.env.ITERM_SESSION_ID;
|
|
1283
|
+
const useiTermIntegration = isITerm && !process.env.TMUX_CC_NOT_SUPPORTED;
|
|
1284
|
+
if (useiTermIntegration) {
|
|
1285
|
+
return `tmux -CC attach-session -t '${escapedSession}'`;
|
|
1286
|
+
}
|
|
1287
|
+
return `tmux attach-session -t '${escapedSession}'`;
|
|
1288
|
+
}
|
|
1289
|
+
async listWorkonSessions() {
|
|
1290
|
+
try {
|
|
1291
|
+
const { stdout } = await exec('tmux list-sessions -F "#{session_name}"');
|
|
1292
|
+
return stdout.trim().split("\n").filter((session) => session.startsWith(this.sessionPrefix)).map((session) => session.replace(this.sessionPrefix, ""));
|
|
1293
|
+
} catch {
|
|
1294
|
+
return [];
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
};
|
|
1298
|
+
}
|
|
1299
|
+
});
|
|
1300
|
+
|
|
1097
1301
|
// src/types/constants.ts
|
|
1098
1302
|
var IDE_CHOICES;
|
|
1099
1303
|
var init_constants = __esm({
|
|
1100
1304
|
"src/types/constants.ts"() {
|
|
1101
1305
|
"use strict";
|
|
1102
1306
|
IDE_CHOICES = [
|
|
1307
|
+
{ name: "Cursor", value: "cursor" },
|
|
1103
1308
|
{ name: "Visual Studio Code", value: "vscode" },
|
|
1104
1309
|
{ name: "Visual Studio Code (code)", value: "code" },
|
|
1105
1310
|
{ name: "IntelliJ IDEA", value: "idea" },
|
|
@@ -1732,215 +1937,21 @@ var init_interactive = __esm({
|
|
|
1732
1937
|
}
|
|
1733
1938
|
});
|
|
1734
1939
|
|
|
1735
|
-
// src/commands/index.ts
|
|
1736
|
-
init_config();
|
|
1737
|
-
init_environment();
|
|
1738
|
-
init_registry();
|
|
1739
|
-
import { Command as Command8 } from "commander";
|
|
1740
|
-
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
1741
|
-
import { join, dirname } from "path";
|
|
1742
|
-
import { fileURLToPath } from "url";
|
|
1743
|
-
import loog from "loog";
|
|
1744
|
-
import omelette from "omelette";
|
|
1745
|
-
import File7 from "phylo";
|
|
1746
|
-
|
|
1747
1940
|
// src/commands/open.ts
|
|
1748
|
-
|
|
1941
|
+
var open_exports = {};
|
|
1942
|
+
__export(open_exports, {
|
|
1943
|
+
createOpenCommand: () => createOpenCommand,
|
|
1944
|
+
runOpen: () => runOpen
|
|
1945
|
+
});
|
|
1749
1946
|
import { Command } from "commander";
|
|
1750
1947
|
import File4 from "phylo";
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
// src/lib/sanitize.ts
|
|
1757
|
-
function sanitizeForShell(input6) {
|
|
1758
|
-
if (!input6) return "";
|
|
1759
|
-
return input6.replace(/[^a-zA-Z0-9_\-.]/g, "_");
|
|
1760
|
-
}
|
|
1761
|
-
function escapeForSingleQuotes(input6) {
|
|
1762
|
-
if (!input6) return "";
|
|
1763
|
-
return input6.replace(/'/g, "'\\''");
|
|
1764
|
-
}
|
|
1765
|
-
|
|
1766
|
-
// src/lib/tmux.ts
|
|
1767
|
-
var exec = promisify(execCallback);
|
|
1768
|
-
var TmuxManager = class {
|
|
1769
|
-
sessionPrefix = "workon-";
|
|
1770
|
-
async isTmuxAvailable() {
|
|
1771
|
-
try {
|
|
1772
|
-
await exec("which tmux");
|
|
1773
|
-
return true;
|
|
1774
|
-
} catch {
|
|
1775
|
-
return false;
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
|
-
async sessionExists(sessionName) {
|
|
1779
|
-
try {
|
|
1780
|
-
await exec(`tmux has-session -t '${escapeForSingleQuotes(sessionName)}'`);
|
|
1781
|
-
return true;
|
|
1782
|
-
} catch {
|
|
1783
|
-
return false;
|
|
1784
|
-
}
|
|
1785
|
-
}
|
|
1786
|
-
getSessionName(projectName) {
|
|
1787
|
-
return `${this.sessionPrefix}${sanitizeForShell(projectName)}`;
|
|
1788
|
-
}
|
|
1789
|
-
async killSession(sessionName) {
|
|
1790
|
-
try {
|
|
1791
|
-
await exec(`tmux kill-session -t '${escapeForSingleQuotes(sessionName)}'`);
|
|
1792
|
-
return true;
|
|
1793
|
-
} catch {
|
|
1794
|
-
return false;
|
|
1795
|
-
}
|
|
1796
|
-
}
|
|
1797
|
-
async createSplitSession(projectName, projectPath, claudeArgs = []) {
|
|
1798
|
-
const sessionName = this.getSessionName(projectName);
|
|
1799
|
-
const escapedSession = escapeForSingleQuotes(sessionName);
|
|
1800
|
-
const escapedPath = escapeForSingleQuotes(projectPath);
|
|
1801
|
-
if (await this.sessionExists(sessionName)) {
|
|
1802
|
-
await this.killSession(sessionName);
|
|
1803
|
-
}
|
|
1804
|
-
const claudeCommand = claudeArgs.length > 0 ? `claude ${claudeArgs.join(" ")}` : "claude";
|
|
1805
|
-
const escapedClaudeCmd = escapeForSingleQuotes(claudeCommand);
|
|
1806
|
-
await exec(
|
|
1807
|
-
`tmux new-session -d -s '${escapedSession}' -c '${escapedPath}' '${escapedClaudeCmd}'`
|
|
1808
|
-
);
|
|
1809
|
-
await exec(`tmux split-window -h -t '${escapedSession}' -c '${escapedPath}'`);
|
|
1810
|
-
await exec(`tmux select-pane -t '${escapedSession}:0.0'`);
|
|
1811
|
-
return sessionName;
|
|
1812
|
-
}
|
|
1813
|
-
async createThreePaneSession(projectName, projectPath, claudeArgs = [], npmCommand = "npm run dev") {
|
|
1814
|
-
const sessionName = this.getSessionName(projectName);
|
|
1815
|
-
const escapedSession = escapeForSingleQuotes(sessionName);
|
|
1816
|
-
const escapedPath = escapeForSingleQuotes(projectPath);
|
|
1817
|
-
if (await this.sessionExists(sessionName)) {
|
|
1818
|
-
await this.killSession(sessionName);
|
|
1819
|
-
}
|
|
1820
|
-
const claudeCommand = claudeArgs.length > 0 ? `claude ${claudeArgs.join(" ")}` : "claude";
|
|
1821
|
-
const escapedClaudeCmd = escapeForSingleQuotes(claudeCommand);
|
|
1822
|
-
const escapedNpmCmd = escapeForSingleQuotes(npmCommand);
|
|
1823
|
-
await exec(
|
|
1824
|
-
`tmux new-session -d -s '${escapedSession}' -c '${escapedPath}' '${escapedClaudeCmd}'`
|
|
1825
|
-
);
|
|
1826
|
-
await exec(`tmux split-window -h -t '${escapedSession}' -c '${escapedPath}'`);
|
|
1827
|
-
await exec(
|
|
1828
|
-
`tmux split-window -v -t '${escapedSession}:0.1' -c '${escapedPath}' '${escapedNpmCmd}'`
|
|
1829
|
-
);
|
|
1830
|
-
await exec(`tmux set-option -t '${escapedSession}:0.2' remain-on-exit on`);
|
|
1831
|
-
await exec(`tmux resize-pane -t '${escapedSession}:0.2' -y 10`);
|
|
1832
|
-
await exec(`tmux select-pane -t '${escapedSession}:0.0'`);
|
|
1833
|
-
return sessionName;
|
|
1834
|
-
}
|
|
1835
|
-
async createTwoPaneNpmSession(projectName, projectPath, npmCommand = "npm run dev") {
|
|
1836
|
-
const sessionName = this.getSessionName(projectName);
|
|
1837
|
-
const escapedSession = escapeForSingleQuotes(sessionName);
|
|
1838
|
-
const escapedPath = escapeForSingleQuotes(projectPath);
|
|
1839
|
-
const escapedNpmCmd = escapeForSingleQuotes(npmCommand);
|
|
1840
|
-
if (await this.sessionExists(sessionName)) {
|
|
1841
|
-
await this.killSession(sessionName);
|
|
1842
|
-
}
|
|
1843
|
-
await exec(`tmux new-session -d -s '${escapedSession}' -c '${escapedPath}'`);
|
|
1844
|
-
await exec(
|
|
1845
|
-
`tmux split-window -h -t '${escapedSession}' -c '${escapedPath}' '${escapedNpmCmd}'`
|
|
1846
|
-
);
|
|
1847
|
-
await exec(`tmux set-option -t '${escapedSession}:0.1' remain-on-exit on`);
|
|
1848
|
-
await exec(`tmux select-pane -t '${escapedSession}:0.0'`);
|
|
1849
|
-
return sessionName;
|
|
1850
|
-
}
|
|
1851
|
-
async attachToSession(sessionName) {
|
|
1852
|
-
const escapedSession = escapeForSingleQuotes(sessionName);
|
|
1853
|
-
if (process.env.TMUX) {
|
|
1854
|
-
await exec(`tmux switch-client -t '${escapedSession}'`);
|
|
1855
|
-
} else {
|
|
1856
|
-
const isITerm = process.env.TERM_PROGRAM === "iTerm.app" || process.env.LC_TERMINAL === "iTerm2" || !!process.env.ITERM_SESSION_ID;
|
|
1857
|
-
const useiTermIntegration = isITerm && !process.env.TMUX_CC_NOT_SUPPORTED;
|
|
1858
|
-
if (useiTermIntegration) {
|
|
1859
|
-
spawn7("tmux", ["-CC", "attach-session", "-t", sessionName], {
|
|
1860
|
-
stdio: "inherit",
|
|
1861
|
-
detached: true
|
|
1862
|
-
});
|
|
1863
|
-
} else {
|
|
1864
|
-
spawn7("tmux", ["attach-session", "-t", sessionName], {
|
|
1865
|
-
stdio: "inherit",
|
|
1866
|
-
detached: true
|
|
1867
|
-
});
|
|
1868
|
-
}
|
|
1869
|
-
}
|
|
1870
|
-
}
|
|
1871
|
-
buildShellCommands(projectName, projectPath, claudeArgs = []) {
|
|
1872
|
-
const sessionName = this.getSessionName(projectName);
|
|
1873
|
-
const escapedSession = escapeForSingleQuotes(sessionName);
|
|
1874
|
-
const escapedPath = escapeForSingleQuotes(projectPath);
|
|
1875
|
-
const claudeCommand = claudeArgs.length > 0 ? `claude ${claudeArgs.join(" ")}` : "claude";
|
|
1876
|
-
const escapedClaudeCmd = escapeForSingleQuotes(claudeCommand);
|
|
1877
|
-
return [
|
|
1878
|
-
`# Create tmux split session for ${sanitizeForShell(projectName)}`,
|
|
1879
|
-
`tmux has-session -t '${escapedSession}' 2>/dev/null && tmux kill-session -t '${escapedSession}'`,
|
|
1880
|
-
`tmux new-session -d -s '${escapedSession}' -c '${escapedPath}' '${escapedClaudeCmd}'`,
|
|
1881
|
-
`tmux split-window -h -t '${escapedSession}' -c '${escapedPath}'`,
|
|
1882
|
-
`tmux select-pane -t '${escapedSession}:0.0'`,
|
|
1883
|
-
this.getAttachCommand(sessionName)
|
|
1884
|
-
];
|
|
1885
|
-
}
|
|
1886
|
-
buildThreePaneShellCommands(projectName, projectPath, claudeArgs = [], npmCommand = "npm run dev") {
|
|
1887
|
-
const sessionName = this.getSessionName(projectName);
|
|
1888
|
-
const escapedSession = escapeForSingleQuotes(sessionName);
|
|
1889
|
-
const escapedPath = escapeForSingleQuotes(projectPath);
|
|
1890
|
-
const claudeCommand = claudeArgs.length > 0 ? `claude ${claudeArgs.join(" ")}` : "claude";
|
|
1891
|
-
const escapedClaudeCmd = escapeForSingleQuotes(claudeCommand);
|
|
1892
|
-
const escapedNpmCmd = escapeForSingleQuotes(npmCommand);
|
|
1893
|
-
return [
|
|
1894
|
-
`# Create tmux three-pane session for ${sanitizeForShell(projectName)}`,
|
|
1895
|
-
`tmux has-session -t '${escapedSession}' 2>/dev/null && tmux kill-session -t '${escapedSession}'`,
|
|
1896
|
-
`tmux new-session -d -s '${escapedSession}' -c '${escapedPath}' '${escapedClaudeCmd}'`,
|
|
1897
|
-
`tmux split-window -h -t '${escapedSession}' -c '${escapedPath}'`,
|
|
1898
|
-
`tmux split-window -v -t '${escapedSession}:0.1' -c '${escapedPath}' '${escapedNpmCmd}'`,
|
|
1899
|
-
`tmux set-option -t '${escapedSession}:0.2' remain-on-exit on`,
|
|
1900
|
-
`tmux resize-pane -t '${escapedSession}:0.2' -y 10`,
|
|
1901
|
-
`tmux select-pane -t '${escapedSession}:0.0'`,
|
|
1902
|
-
this.getAttachCommand(sessionName)
|
|
1903
|
-
];
|
|
1904
|
-
}
|
|
1905
|
-
buildTwoPaneNpmShellCommands(projectName, projectPath, npmCommand = "npm run dev") {
|
|
1906
|
-
const sessionName = this.getSessionName(projectName);
|
|
1907
|
-
const escapedSession = escapeForSingleQuotes(sessionName);
|
|
1908
|
-
const escapedPath = escapeForSingleQuotes(projectPath);
|
|
1909
|
-
const escapedNpmCmd = escapeForSingleQuotes(npmCommand);
|
|
1910
|
-
return [
|
|
1911
|
-
`# Create tmux two-pane session with npm for ${sanitizeForShell(projectName)}`,
|
|
1912
|
-
`tmux has-session -t '${escapedSession}' 2>/dev/null && tmux kill-session -t '${escapedSession}'`,
|
|
1913
|
-
`tmux new-session -d -s '${escapedSession}' -c '${escapedPath}'`,
|
|
1914
|
-
`tmux split-window -h -t '${escapedSession}' -c '${escapedPath}' '${escapedNpmCmd}'`,
|
|
1915
|
-
`tmux set-option -t '${escapedSession}:0.1' remain-on-exit on`,
|
|
1916
|
-
`tmux select-pane -t '${escapedSession}:0.0'`,
|
|
1917
|
-
this.getAttachCommand(sessionName)
|
|
1918
|
-
];
|
|
1919
|
-
}
|
|
1920
|
-
getAttachCommand(sessionName) {
|
|
1921
|
-
const escapedSession = escapeForSingleQuotes(sessionName);
|
|
1922
|
-
if (process.env.TMUX) {
|
|
1923
|
-
return `tmux switch-client -t '${escapedSession}'`;
|
|
1924
|
-
}
|
|
1925
|
-
const isITerm = process.env.TERM_PROGRAM === "iTerm.app" || process.env.LC_TERMINAL === "iTerm2" || process.env.ITERM_SESSION_ID;
|
|
1926
|
-
const useiTermIntegration = isITerm && !process.env.TMUX_CC_NOT_SUPPORTED;
|
|
1927
|
-
if (useiTermIntegration) {
|
|
1928
|
-
return `tmux -CC attach-session -t '${escapedSession}'`;
|
|
1929
|
-
}
|
|
1930
|
-
return `tmux attach-session -t '${escapedSession}'`;
|
|
1931
|
-
}
|
|
1932
|
-
async listWorkonSessions() {
|
|
1933
|
-
try {
|
|
1934
|
-
const { stdout } = await exec('tmux list-sessions -F "#{session_name}"');
|
|
1935
|
-
return stdout.trim().split("\n").filter((session) => session.startsWith(this.sessionPrefix)).map((session) => session.replace(this.sessionPrefix, ""));
|
|
1936
|
-
} catch {
|
|
1937
|
-
return [];
|
|
1938
|
-
}
|
|
1948
|
+
async function runOpen(projectArg, options, ctx) {
|
|
1949
|
+
const { log } = ctx;
|
|
1950
|
+
if (options.debug) {
|
|
1951
|
+
log.setLogLevel("debug");
|
|
1939
1952
|
}
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
// src/commands/open.ts
|
|
1943
|
-
init_registry();
|
|
1953
|
+
await processProject(projectArg, options, ctx);
|
|
1954
|
+
}
|
|
1944
1955
|
function createOpenCommand(ctx) {
|
|
1945
1956
|
const { config, log } = ctx;
|
|
1946
1957
|
const command = new Command("open").description("Open a project by passing its project id").argument("[project]", "The id of the project to open (supports project:command syntax)").option("-d, --debug", "Enable debug logging").option("-n, --dry-run", "Show what would happen without executing").option("--shell", "Output shell commands instead of spawning processes").action(async (projectArg, options) => {
|
|
@@ -2069,12 +2080,25 @@ async function handleTmuxLayout(project, layout, options, shellCommands, events,
|
|
|
2069
2080
|
let tmuxHandled = false;
|
|
2070
2081
|
if (isShellMode) {
|
|
2071
2082
|
if (await tmux.isTmuxAvailable()) {
|
|
2083
|
+
const remainingEvents = events.filter((e) => !layout.handledEvents.includes(e));
|
|
2084
|
+
for (const event of remainingEvents) {
|
|
2085
|
+
const eventHandler = EventRegistry.getEventByName(event);
|
|
2086
|
+
if (eventHandler) {
|
|
2087
|
+
const cmds = eventHandler.processing.generateShellCommand({
|
|
2088
|
+
project,
|
|
2089
|
+
isShellMode: true,
|
|
2090
|
+
shellCommands: []
|
|
2091
|
+
});
|
|
2092
|
+
shellCommands.push(...cmds);
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2072
2095
|
const commands = buildLayoutShellCommands(tmux, project, layout);
|
|
2073
2096
|
shellCommands.push(...commands);
|
|
2074
2097
|
tmuxHandled = true;
|
|
2075
2098
|
} else {
|
|
2076
2099
|
log.debug("Tmux not available, falling back to normal mode");
|
|
2077
|
-
|
|
2100
|
+
const remainingEvents = events.filter((e) => !layout.handledEvents.includes(e));
|
|
2101
|
+
buildFallbackCommandsWithEvents(shellCommands, project, layout, remainingEvents, ctx);
|
|
2078
2102
|
tmuxHandled = true;
|
|
2079
2103
|
}
|
|
2080
2104
|
} else if (!dryRun) {
|
|
@@ -2098,7 +2122,7 @@ async function handleTmuxLayout(project, layout, options, shellCommands, events,
|
|
|
2098
2122
|
await processEvent(event, { project, isShellMode, shellCommands }, ctx);
|
|
2099
2123
|
}
|
|
2100
2124
|
}
|
|
2101
|
-
if (!dryRun) {
|
|
2125
|
+
if (!dryRun && !isShellMode) {
|
|
2102
2126
|
for (const event of events.filter((e) => !layout.handledEvents.includes(e))) {
|
|
2103
2127
|
await processEvent(event, { project, isShellMode, shellCommands }, ctx);
|
|
2104
2128
|
}
|
|
@@ -2119,14 +2143,19 @@ function buildLayoutShellCommands(tmux, project, layout) {
|
|
|
2119
2143
|
return tmux.buildTwoPaneNpmShellCommands(project.name, project.path.path, layout.npmCommand);
|
|
2120
2144
|
}
|
|
2121
2145
|
}
|
|
2122
|
-
function
|
|
2123
|
-
shellCommands.push(`
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2146
|
+
function buildFallbackCommandsWithEvents(shellCommands, project, layout, remainingEvents, _ctx) {
|
|
2147
|
+
shellCommands.push(`echo "\u26A0 tmux not available - install with: brew install tmux" >&2`);
|
|
2148
|
+
shellCommands.push(`pushd "${project.path.path}" > /dev/null`);
|
|
2149
|
+
for (const event of remainingEvents) {
|
|
2150
|
+
const eventHandler = EventRegistry.getEventByName(event);
|
|
2151
|
+
if (eventHandler) {
|
|
2152
|
+
const cmds = eventHandler.processing.generateShellCommand({
|
|
2153
|
+
project,
|
|
2154
|
+
isShellMode: true,
|
|
2155
|
+
shellCommands: []
|
|
2156
|
+
});
|
|
2157
|
+
shellCommands.push(...cmds);
|
|
2158
|
+
}
|
|
2130
2159
|
}
|
|
2131
2160
|
}
|
|
2132
2161
|
async function createTmuxSession(tmux, project, layout) {
|
|
@@ -2227,6 +2256,27 @@ Available commands for '${projectName}':`);
|
|
|
2227
2256
|
console.log(` workon ${projectName}:cwd --shell # Output shell commands
|
|
2228
2257
|
`);
|
|
2229
2258
|
}
|
|
2259
|
+
var init_open = __esm({
|
|
2260
|
+
"src/commands/open.ts"() {
|
|
2261
|
+
"use strict";
|
|
2262
|
+
init_environment();
|
|
2263
|
+
init_tmux();
|
|
2264
|
+
init_registry();
|
|
2265
|
+
}
|
|
2266
|
+
});
|
|
2267
|
+
|
|
2268
|
+
// src/commands/index.ts
|
|
2269
|
+
init_config();
|
|
2270
|
+
init_environment();
|
|
2271
|
+
init_registry();
|
|
2272
|
+
init_open();
|
|
2273
|
+
import { Command as Command8 } from "commander";
|
|
2274
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
2275
|
+
import { join, dirname } from "path";
|
|
2276
|
+
import { fileURLToPath } from "url";
|
|
2277
|
+
import loog from "loog";
|
|
2278
|
+
import omelette from "omelette";
|
|
2279
|
+
import File7 from "phylo";
|
|
2230
2280
|
|
|
2231
2281
|
// src/commands/config/index.ts
|
|
2232
2282
|
import { Command as Command5 } from "commander";
|
|
@@ -2644,7 +2694,7 @@ async function addProject(pathArg, options, ctx) {
|
|
|
2644
2694
|
return;
|
|
2645
2695
|
}
|
|
2646
2696
|
}
|
|
2647
|
-
const ide = options.ide || discovery.detectedIde || "
|
|
2697
|
+
const ide = options.ide || discovery.detectedIde || "code";
|
|
2648
2698
|
log.debug(`IDE: ${ide}`);
|
|
2649
2699
|
let relativePath = targetPath;
|
|
2650
2700
|
if (defaults?.base) {
|
|
@@ -2672,6 +2722,9 @@ async function addProject(pathArg, options, ctx) {
|
|
|
2672
2722
|
projectConfig.events.npm = scripts.dev ? "dev" : "start";
|
|
2673
2723
|
}
|
|
2674
2724
|
}
|
|
2725
|
+
if (discovery.hasClaude) {
|
|
2726
|
+
projectConfig.events.claude = true;
|
|
2727
|
+
}
|
|
2675
2728
|
config.setProject(projectName, projectConfig);
|
|
2676
2729
|
log.info(`Added project '${projectName}'`);
|
|
2677
2730
|
log.info(` Path: ${relativePath}`);
|
|
@@ -2686,6 +2739,7 @@ function discoverProject(targetPath, log) {
|
|
|
2686
2739
|
name: dirName,
|
|
2687
2740
|
isNode: false,
|
|
2688
2741
|
isBun: false,
|
|
2742
|
+
hasClaude: false,
|
|
2689
2743
|
detectedIde: null,
|
|
2690
2744
|
packageJson: null
|
|
2691
2745
|
};
|
|
@@ -2711,14 +2765,23 @@ function discoverProject(targetPath, log) {
|
|
|
2711
2765
|
log.debug("Detected Bun project (bun.lockb found)");
|
|
2712
2766
|
}
|
|
2713
2767
|
const vscodeDir = resolve(targetPath, ".vscode");
|
|
2768
|
+
const cursorDir = resolve(targetPath, ".cursor");
|
|
2714
2769
|
const ideaDir = resolve(targetPath, ".idea");
|
|
2715
|
-
if (existsSync(
|
|
2716
|
-
discovery.detectedIde = "
|
|
2770
|
+
if (existsSync(cursorDir)) {
|
|
2771
|
+
discovery.detectedIde = "cursor";
|
|
2772
|
+
log.debug("Detected Cursor (.cursor directory found)");
|
|
2773
|
+
} else if (existsSync(vscodeDir)) {
|
|
2774
|
+
discovery.detectedIde = "code";
|
|
2717
2775
|
log.debug("Detected VS Code (.vscode directory found)");
|
|
2718
2776
|
} else if (existsSync(ideaDir)) {
|
|
2719
2777
|
discovery.detectedIde = "idea";
|
|
2720
2778
|
log.debug("Detected IntelliJ IDEA (.idea directory found)");
|
|
2721
2779
|
}
|
|
2780
|
+
const claudeMdPath = resolve(targetPath, "CLAUDE.md");
|
|
2781
|
+
if (existsSync(claudeMdPath)) {
|
|
2782
|
+
discovery.hasClaude = true;
|
|
2783
|
+
log.debug("Detected Claude Code project (CLAUDE.md found)");
|
|
2784
|
+
}
|
|
2722
2785
|
return discovery;
|
|
2723
2786
|
}
|
|
2724
2787
|
|
|
@@ -2750,7 +2813,7 @@ function createCli() {
|
|
|
2750
2813
|
config.set("pkg", packageJson);
|
|
2751
2814
|
EnvironmentRecognizer.configure(config, log);
|
|
2752
2815
|
const completion = setupCompletion(config);
|
|
2753
|
-
program2.name("workon").description("Work on something great!").version(packageJson.version).argument("[project]", "Project name to open (supports project:command syntax)").option("-d, --debug", "Enable debug logging").option("--completion", "Setup shell tab completion").option("--shell", "Output shell commands for evaluation").option("--init", "Generate shell integration function").hook("preAction", async (thisCommand) => {
|
|
2816
|
+
program2.name("workon").description("Work on something great!").version(packageJson.version).enablePositionalOptions().argument("[project]", "Project name to open (supports project:command syntax)").option("-d, --debug", "Enable debug logging").option("--completion", "Setup shell tab completion").option("--shell", "Output shell commands for evaluation").option("--init", "Generate shell integration function").hook("preAction", async (thisCommand) => {
|
|
2754
2817
|
const opts = thisCommand.opts();
|
|
2755
2818
|
if (opts.debug) {
|
|
2756
2819
|
log.setLogLevel("debug");
|
|
@@ -2772,10 +2835,8 @@ function createCli() {
|
|
|
2772
2835
|
return;
|
|
2773
2836
|
}
|
|
2774
2837
|
if (project) {
|
|
2775
|
-
const
|
|
2776
|
-
|
|
2777
|
-
if (options.debug) args.push("--debug");
|
|
2778
|
-
await program2.parseAsync(["node", "workon", ...args]);
|
|
2838
|
+
const { runOpen: runOpen2 } = await Promise.resolve().then(() => (init_open(), open_exports));
|
|
2839
|
+
await runOpen2(project, { debug: options.debug, shell: options.shell }, { config, log });
|
|
2779
2840
|
return;
|
|
2780
2841
|
}
|
|
2781
2842
|
const environment = await EnvironmentRecognizer.recognize(File7.cwd());
|