workon 3.2.0 → 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 +431 -364
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +114 -73
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -6
- package/dist/index.d.ts +19 -6
- package/dist/index.js +114 -73
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -236,7 +236,8 @@ var init_environment = __esm({
|
|
|
236
236
|
const git = simpleGit(gitDir.path);
|
|
237
237
|
const branchSummary = await git.branchLocal();
|
|
238
238
|
base.branch = branchSummary.current;
|
|
239
|
-
} catch {
|
|
239
|
+
} catch (error) {
|
|
240
|
+
this.log.debug(`Git branch detection failed: ${error.message}`);
|
|
240
241
|
}
|
|
241
242
|
}
|
|
242
243
|
return this.getProjectEnvironment(base, matching);
|
|
@@ -302,7 +303,7 @@ var init_environment = __esm({
|
|
|
302
303
|
});
|
|
303
304
|
|
|
304
305
|
// src/events/core/cwd.ts
|
|
305
|
-
import { spawn
|
|
306
|
+
import { spawn } from "child_process";
|
|
306
307
|
var CwdEvent;
|
|
307
308
|
var init_cwd = __esm({
|
|
308
309
|
"src/events/core/cwd.ts"() {
|
|
@@ -344,18 +345,22 @@ var init_cwd = __esm({
|
|
|
344
345
|
const { project, isShellMode, shellCommands } = context;
|
|
345
346
|
const projectPath = project.path.path;
|
|
346
347
|
if (isShellMode) {
|
|
347
|
-
shellCommands.push(`
|
|
348
|
+
shellCommands.push(`pushd "${projectPath}" > /dev/null`);
|
|
348
349
|
} else {
|
|
349
350
|
const shell = process.env.SHELL || "/bin/bash";
|
|
350
|
-
|
|
351
|
+
const child = spawn(shell, ["-i"], {
|
|
351
352
|
cwd: projectPath,
|
|
352
353
|
stdio: "inherit"
|
|
353
354
|
});
|
|
355
|
+
await new Promise((resolve2, reject) => {
|
|
356
|
+
child.on("close", () => resolve2());
|
|
357
|
+
child.on("error", (err) => reject(err));
|
|
358
|
+
});
|
|
354
359
|
}
|
|
355
360
|
},
|
|
356
361
|
generateShellCommand(context) {
|
|
357
362
|
const projectPath = context.project.path.path;
|
|
358
|
-
return [`
|
|
363
|
+
return [`pushd "${projectPath}" > /dev/null`];
|
|
359
364
|
}
|
|
360
365
|
};
|
|
361
366
|
}
|
|
@@ -377,7 +382,7 @@ var init_cwd = __esm({
|
|
|
377
382
|
});
|
|
378
383
|
|
|
379
384
|
// src/events/core/ide.ts
|
|
380
|
-
import { spawn as
|
|
385
|
+
import { spawn as spawn2 } from "child_process";
|
|
381
386
|
var IdeEvent;
|
|
382
387
|
var init_ide = __esm({
|
|
383
388
|
"src/events/core/ide.ts"() {
|
|
@@ -420,9 +425,9 @@ var init_ide = __esm({
|
|
|
420
425
|
const projectPath = project.path.path;
|
|
421
426
|
const ide = project.ide || "code";
|
|
422
427
|
if (isShellMode) {
|
|
423
|
-
shellCommands.push(
|
|
428
|
+
shellCommands.push(`set +m; ${ide} "${projectPath}" &>/dev/null &`);
|
|
424
429
|
} else {
|
|
425
|
-
|
|
430
|
+
spawn2(ide, [projectPath], {
|
|
426
431
|
detached: true,
|
|
427
432
|
stdio: "ignore"
|
|
428
433
|
}).unref();
|
|
@@ -431,7 +436,7 @@ var init_ide = __esm({
|
|
|
431
436
|
generateShellCommand(context) {
|
|
432
437
|
const projectPath = context.project.path.path;
|
|
433
438
|
const ide = context.project.ide || "code";
|
|
434
|
-
return [
|
|
439
|
+
return [`set +m; ${ide} "${projectPath}" &>/dev/null &`];
|
|
435
440
|
}
|
|
436
441
|
};
|
|
437
442
|
}
|
|
@@ -453,7 +458,7 @@ var init_ide = __esm({
|
|
|
453
458
|
});
|
|
454
459
|
|
|
455
460
|
// src/events/core/web.ts
|
|
456
|
-
import { spawn as
|
|
461
|
+
import { spawn as spawn3 } from "child_process";
|
|
457
462
|
import { platform } from "os";
|
|
458
463
|
var WebEvent;
|
|
459
464
|
var init_web = __esm({
|
|
@@ -514,7 +519,7 @@ var init_web = __esm({
|
|
|
514
519
|
if (isShellMode) {
|
|
515
520
|
shellCommands.push(`${openCmd} "${homepage}" &`);
|
|
516
521
|
} else {
|
|
517
|
-
|
|
522
|
+
spawn3(openCmd, [homepage], {
|
|
518
523
|
detached: true,
|
|
519
524
|
stdio: "ignore"
|
|
520
525
|
}).unref();
|
|
@@ -546,7 +551,7 @@ var init_web = __esm({
|
|
|
546
551
|
});
|
|
547
552
|
|
|
548
553
|
// src/events/extensions/claude.ts
|
|
549
|
-
import { spawn as
|
|
554
|
+
import { spawn as spawn4 } from "child_process";
|
|
550
555
|
import { input, confirm } from "@inquirer/prompts";
|
|
551
556
|
var ClaudeEvent;
|
|
552
557
|
var init_claude = __esm({
|
|
@@ -642,7 +647,7 @@ var init_claude = __esm({
|
|
|
642
647
|
shellCommands.push(claudeCommand);
|
|
643
648
|
} else {
|
|
644
649
|
const args = claudeCommand.split(" ").slice(1);
|
|
645
|
-
|
|
650
|
+
spawn4("claude", args, {
|
|
646
651
|
cwd: project.path.path,
|
|
647
652
|
stdio: "inherit"
|
|
648
653
|
});
|
|
@@ -686,7 +691,7 @@ var init_claude = __esm({
|
|
|
686
691
|
});
|
|
687
692
|
|
|
688
693
|
// src/events/extensions/docker.ts
|
|
689
|
-
import { spawn as
|
|
694
|
+
import { spawn as spawn5 } from "child_process";
|
|
690
695
|
import { input as input2 } from "@inquirer/prompts";
|
|
691
696
|
var DockerEvent;
|
|
692
697
|
var init_docker = __esm({
|
|
@@ -784,7 +789,7 @@ var init_docker = __esm({
|
|
|
784
789
|
shellCommands.push(dockerCommand);
|
|
785
790
|
} else {
|
|
786
791
|
const [cmd, ...args] = dockerCommand.split(" ");
|
|
787
|
-
|
|
792
|
+
spawn5(cmd, args, {
|
|
788
793
|
cwd: project.path.path,
|
|
789
794
|
stdio: "inherit"
|
|
790
795
|
});
|
|
@@ -823,7 +828,7 @@ __export(npm_exports, {
|
|
|
823
828
|
NpmEvent: () => NpmEvent,
|
|
824
829
|
default: () => npm_default
|
|
825
830
|
});
|
|
826
|
-
import { spawn as
|
|
831
|
+
import { spawn as spawn6 } from "child_process";
|
|
827
832
|
import { input as input3, confirm as confirm2 } from "@inquirer/prompts";
|
|
828
833
|
var NpmEvent, npm_default;
|
|
829
834
|
var init_npm = __esm({
|
|
@@ -924,7 +929,7 @@ var init_npm = __esm({
|
|
|
924
929
|
shellCommands.push(npmCommand);
|
|
925
930
|
} else {
|
|
926
931
|
const [cmd, ...args] = npmCommand.split(" ");
|
|
927
|
-
|
|
932
|
+
spawn6(cmd, args, {
|
|
928
933
|
cwd: project.path.path,
|
|
929
934
|
stdio: "inherit"
|
|
930
935
|
});
|
|
@@ -993,23 +998,19 @@ var init_registry = __esm({
|
|
|
993
998
|
*/
|
|
994
999
|
registerEvents() {
|
|
995
1000
|
for (const EventClass of ALL_EVENTS) {
|
|
996
|
-
if (this.
|
|
997
|
-
|
|
998
|
-
this._events.set(metadata.name, EventClass);
|
|
1001
|
+
if (this.isValidEventClass(EventClass)) {
|
|
1002
|
+
this._events.set(EventClass.metadata.name, EventClass);
|
|
999
1003
|
}
|
|
1000
1004
|
}
|
|
1001
1005
|
}
|
|
1002
1006
|
/**
|
|
1003
|
-
*
|
|
1007
|
+
* Type guard to check if an object is a valid EventHandlerClass
|
|
1004
1008
|
*/
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
} catch {
|
|
1011
|
-
return false;
|
|
1012
|
-
}
|
|
1009
|
+
isValidEventClass(obj) {
|
|
1010
|
+
if (typeof obj !== "function" && typeof obj !== "object") return false;
|
|
1011
|
+
if (obj === null) return false;
|
|
1012
|
+
const candidate = obj;
|
|
1013
|
+
return candidate.metadata !== void 0 && typeof candidate.metadata.name === "string" && typeof candidate.metadata.displayName === "string" && candidate.validation !== void 0 && typeof candidate.validation.validateConfig === "function" && candidate.configuration !== void 0 && typeof candidate.configuration.configureInteractive === "function" && candidate.processing !== void 0 && typeof candidate.processing.processEvent === "function";
|
|
1013
1014
|
}
|
|
1014
1015
|
/**
|
|
1015
1016
|
* Get all valid event names from registered events
|
|
@@ -1031,12 +1032,11 @@ var init_registry = __esm({
|
|
|
1031
1032
|
getEventsForManageUI() {
|
|
1032
1033
|
this.ensureInitialized();
|
|
1033
1034
|
const events = [];
|
|
1034
|
-
for (const [name,
|
|
1035
|
-
const metadata = EventClass.metadata;
|
|
1035
|
+
for (const [name, eventClass] of this._events) {
|
|
1036
1036
|
events.push({
|
|
1037
|
-
name: metadata.displayName,
|
|
1037
|
+
name: eventClass.metadata.displayName,
|
|
1038
1038
|
value: name,
|
|
1039
|
-
description: metadata.description
|
|
1039
|
+
description: eventClass.metadata.description
|
|
1040
1040
|
});
|
|
1041
1041
|
}
|
|
1042
1042
|
return events.sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -1047,13 +1047,13 @@ var init_registry = __esm({
|
|
|
1047
1047
|
getTmuxEnabledEvents() {
|
|
1048
1048
|
this.ensureInitialized();
|
|
1049
1049
|
const tmuxEvents = [];
|
|
1050
|
-
for (const [name,
|
|
1051
|
-
const tmux =
|
|
1050
|
+
for (const [name, eventClass] of this._events) {
|
|
1051
|
+
const tmux = eventClass.tmux;
|
|
1052
1052
|
if (tmux) {
|
|
1053
1053
|
tmuxEvents.push({
|
|
1054
1054
|
name,
|
|
1055
|
-
event:
|
|
1056
|
-
priority: tmux.getLayoutPriority
|
|
1055
|
+
event: eventClass,
|
|
1056
|
+
priority: tmux.getLayoutPriority()
|
|
1057
1057
|
});
|
|
1058
1058
|
}
|
|
1059
1059
|
}
|
|
@@ -1065,16 +1065,15 @@ var init_registry = __esm({
|
|
|
1065
1065
|
getAllEvents() {
|
|
1066
1066
|
this.ensureInitialized();
|
|
1067
1067
|
const events = [];
|
|
1068
|
-
for (const [name,
|
|
1069
|
-
const typedClass = EventClass;
|
|
1068
|
+
for (const [name, eventClass] of this._events) {
|
|
1070
1069
|
events.push({
|
|
1071
1070
|
name,
|
|
1072
|
-
metadata:
|
|
1073
|
-
hasValidation: !!
|
|
1074
|
-
hasConfiguration: !!
|
|
1075
|
-
hasProcessing: !!
|
|
1076
|
-
hasTmux: !!
|
|
1077
|
-
hasHelp: !!
|
|
1071
|
+
metadata: eventClass.metadata,
|
|
1072
|
+
hasValidation: !!eventClass.validation,
|
|
1073
|
+
hasConfiguration: !!eventClass.configuration,
|
|
1074
|
+
hasProcessing: !!eventClass.processing,
|
|
1075
|
+
hasTmux: !!eventClass.tmux,
|
|
1076
|
+
hasHelp: !!eventClass.help
|
|
1078
1077
|
});
|
|
1079
1078
|
}
|
|
1080
1079
|
return events;
|
|
@@ -1099,6 +1098,224 @@ var init_registry = __esm({
|
|
|
1099
1098
|
}
|
|
1100
1099
|
});
|
|
1101
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
|
+
|
|
1301
|
+
// src/types/constants.ts
|
|
1302
|
+
var IDE_CHOICES;
|
|
1303
|
+
var init_constants = __esm({
|
|
1304
|
+
"src/types/constants.ts"() {
|
|
1305
|
+
"use strict";
|
|
1306
|
+
IDE_CHOICES = [
|
|
1307
|
+
{ name: "Cursor", value: "cursor" },
|
|
1308
|
+
{ name: "Visual Studio Code", value: "vscode" },
|
|
1309
|
+
{ name: "Visual Studio Code (code)", value: "code" },
|
|
1310
|
+
{ name: "IntelliJ IDEA", value: "idea" },
|
|
1311
|
+
{ name: "Atom", value: "atom" },
|
|
1312
|
+
{ name: "Sublime Text", value: "subl" },
|
|
1313
|
+
{ name: "Vim", value: "vim" },
|
|
1314
|
+
{ name: "Emacs", value: "emacs" }
|
|
1315
|
+
];
|
|
1316
|
+
}
|
|
1317
|
+
});
|
|
1318
|
+
|
|
1102
1319
|
// src/commands/interactive.ts
|
|
1103
1320
|
var interactive_exports = {};
|
|
1104
1321
|
__export(interactive_exports, {
|
|
@@ -1270,8 +1487,10 @@ async function initBranch(defaultName, ctx) {
|
|
|
1270
1487
|
});
|
|
1271
1488
|
const branchName = `${defaultName}#${branch}`;
|
|
1272
1489
|
const baseProject = projects[defaultName];
|
|
1273
|
-
const
|
|
1274
|
-
|
|
1490
|
+
const { name: _excludedName, ...mergedConfig } = deepAssign2({}, baseProject, {
|
|
1491
|
+
branch
|
|
1492
|
+
});
|
|
1493
|
+
const branchConfig = mergedConfig;
|
|
1275
1494
|
projects[branchName] = branchConfig;
|
|
1276
1495
|
config.set("projects", projects);
|
|
1277
1496
|
log.info("Your branch configuration has been initialized.");
|
|
@@ -1315,7 +1534,6 @@ async function switchBranch(projectName, ctx) {
|
|
|
1315
1534
|
}
|
|
1316
1535
|
async function manageProjects(ctx) {
|
|
1317
1536
|
const { config } = ctx;
|
|
1318
|
-
await EventRegistry.initialize();
|
|
1319
1537
|
const projects = config.getProjects();
|
|
1320
1538
|
const hasProjects = Object.keys(projects).length > 0;
|
|
1321
1539
|
const choices = [
|
|
@@ -1392,7 +1610,6 @@ async function openProject(projectName, ctx) {
|
|
|
1392
1610
|
log.error(`Project '${projectName}' not found.`);
|
|
1393
1611
|
return;
|
|
1394
1612
|
}
|
|
1395
|
-
await EventRegistry.initialize();
|
|
1396
1613
|
const projectConfig = projects[projectName];
|
|
1397
1614
|
const projectCfg = { ...projectConfig, name: projectName };
|
|
1398
1615
|
const projectEnv = ProjectEnvironment.load(projectCfg, config.getDefaults());
|
|
@@ -1402,7 +1619,7 @@ async function openProject(projectName, ctx) {
|
|
|
1402
1619
|
);
|
|
1403
1620
|
for (const event of events) {
|
|
1404
1621
|
const eventHandler = EventRegistry.getEventByName(event);
|
|
1405
|
-
if (eventHandler
|
|
1622
|
+
if (eventHandler) {
|
|
1406
1623
|
await eventHandler.processing.processEvent({
|
|
1407
1624
|
project: projectEnv.project,
|
|
1408
1625
|
isShellMode: false,
|
|
@@ -1711,193 +1928,36 @@ Branch configurations for '${projectName}':
|
|
|
1711
1928
|
console.log();
|
|
1712
1929
|
}
|
|
1713
1930
|
}
|
|
1714
|
-
var IDE_CHOICES;
|
|
1715
1931
|
var init_interactive = __esm({
|
|
1716
1932
|
"src/commands/interactive.ts"() {
|
|
1717
1933
|
"use strict";
|
|
1718
1934
|
init_environment();
|
|
1719
1935
|
init_registry();
|
|
1720
|
-
|
|
1721
|
-
{ name: "Visual Studio Code", value: "vscode" },
|
|
1722
|
-
{ name: "IntelliJ IDEA", value: "idea" },
|
|
1723
|
-
{ name: "Atom", value: "atom" }
|
|
1724
|
-
];
|
|
1936
|
+
init_constants();
|
|
1725
1937
|
}
|
|
1726
1938
|
});
|
|
1727
1939
|
|
|
1728
|
-
// src/commands/index.ts
|
|
1729
|
-
init_config();
|
|
1730
|
-
init_environment();
|
|
1731
|
-
import { Command as Command8 } from "commander";
|
|
1732
|
-
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
1733
|
-
import { join, dirname } from "path";
|
|
1734
|
-
import { fileURLToPath } from "url";
|
|
1735
|
-
import loog from "loog";
|
|
1736
|
-
import omelette from "omelette";
|
|
1737
|
-
import File7 from "phylo";
|
|
1738
|
-
|
|
1739
1940
|
// src/commands/open.ts
|
|
1740
|
-
|
|
1941
|
+
var open_exports = {};
|
|
1942
|
+
__export(open_exports, {
|
|
1943
|
+
createOpenCommand: () => createOpenCommand,
|
|
1944
|
+
runOpen: () => runOpen
|
|
1945
|
+
});
|
|
1741
1946
|
import { Command } from "commander";
|
|
1742
1947
|
import File4 from "phylo";
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
var exec = promisify(execCallback);
|
|
1748
|
-
var TmuxManager = class {
|
|
1749
|
-
sessionPrefix = "workon-";
|
|
1750
|
-
async isTmuxAvailable() {
|
|
1751
|
-
try {
|
|
1752
|
-
await exec("which tmux");
|
|
1753
|
-
return true;
|
|
1754
|
-
} catch {
|
|
1755
|
-
return false;
|
|
1756
|
-
}
|
|
1757
|
-
}
|
|
1758
|
-
async sessionExists(sessionName) {
|
|
1759
|
-
try {
|
|
1760
|
-
await exec(`tmux has-session -t "${sessionName}"`);
|
|
1761
|
-
return true;
|
|
1762
|
-
} catch {
|
|
1763
|
-
return false;
|
|
1764
|
-
}
|
|
1765
|
-
}
|
|
1766
|
-
getSessionName(projectName) {
|
|
1767
|
-
return `${this.sessionPrefix}${projectName}`;
|
|
1768
|
-
}
|
|
1769
|
-
async killSession(sessionName) {
|
|
1770
|
-
try {
|
|
1771
|
-
await exec(`tmux kill-session -t "${sessionName}"`);
|
|
1772
|
-
return true;
|
|
1773
|
-
} catch {
|
|
1774
|
-
return false;
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
async createSplitSession(projectName, projectPath, claudeArgs = []) {
|
|
1778
|
-
const sessionName = this.getSessionName(projectName);
|
|
1779
|
-
if (await this.sessionExists(sessionName)) {
|
|
1780
|
-
await this.killSession(sessionName);
|
|
1781
|
-
}
|
|
1782
|
-
const claudeCommand = claudeArgs.length > 0 ? `claude ${claudeArgs.join(" ")}` : "claude";
|
|
1783
|
-
await exec(`tmux new-session -d -s "${sessionName}" -c "${projectPath}" '${claudeCommand}'`);
|
|
1784
|
-
await exec(`tmux split-window -h -t "${sessionName}" -c "${projectPath}"`);
|
|
1785
|
-
await exec(`tmux select-pane -t "${sessionName}:0.0"`);
|
|
1786
|
-
return sessionName;
|
|
1787
|
-
}
|
|
1788
|
-
async createThreePaneSession(projectName, projectPath, claudeArgs = [], npmCommand = "npm run dev") {
|
|
1789
|
-
const sessionName = this.getSessionName(projectName);
|
|
1790
|
-
if (await this.sessionExists(sessionName)) {
|
|
1791
|
-
await this.killSession(sessionName);
|
|
1792
|
-
}
|
|
1793
|
-
const claudeCommand = claudeArgs.length > 0 ? `claude ${claudeArgs.join(" ")}` : "claude";
|
|
1794
|
-
await exec(`tmux new-session -d -s "${sessionName}" -c "${projectPath}" '${claudeCommand}'`);
|
|
1795
|
-
await exec(`tmux split-window -h -t "${sessionName}" -c "${projectPath}"`);
|
|
1796
|
-
await exec(`tmux split-window -v -t "${sessionName}:0.1" -c "${projectPath}" '${npmCommand}'`);
|
|
1797
|
-
await exec(`tmux set-option -t "${sessionName}:0.2" remain-on-exit on`);
|
|
1798
|
-
await exec(`tmux resize-pane -t "${sessionName}:0.2" -y 10`);
|
|
1799
|
-
await exec(`tmux select-pane -t "${sessionName}:0.0"`);
|
|
1800
|
-
return sessionName;
|
|
1801
|
-
}
|
|
1802
|
-
async createTwoPaneNpmSession(projectName, projectPath, npmCommand = "npm run dev") {
|
|
1803
|
-
const sessionName = this.getSessionName(projectName);
|
|
1804
|
-
if (await this.sessionExists(sessionName)) {
|
|
1805
|
-
await this.killSession(sessionName);
|
|
1806
|
-
}
|
|
1807
|
-
await exec(`tmux new-session -d -s "${sessionName}" -c "${projectPath}"`);
|
|
1808
|
-
await exec(`tmux split-window -h -t "${sessionName}" -c "${projectPath}" '${npmCommand}'`);
|
|
1809
|
-
await exec(`tmux set-option -t "${sessionName}:0.1" remain-on-exit on`);
|
|
1810
|
-
await exec(`tmux select-pane -t "${sessionName}:0.0"`);
|
|
1811
|
-
return sessionName;
|
|
1812
|
-
}
|
|
1813
|
-
async attachToSession(sessionName) {
|
|
1814
|
-
if (process.env.TMUX) {
|
|
1815
|
-
await exec(`tmux switch-client -t "${sessionName}"`);
|
|
1816
|
-
} else {
|
|
1817
|
-
const isITerm = process.env.TERM_PROGRAM === "iTerm.app" || process.env.LC_TERMINAL === "iTerm2" || !!process.env.ITERM_SESSION_ID;
|
|
1818
|
-
const useiTermIntegration = isITerm && !process.env.TMUX_CC_NOT_SUPPORTED;
|
|
1819
|
-
if (useiTermIntegration) {
|
|
1820
|
-
spawn("tmux", ["-CC", "attach-session", "-t", sessionName], {
|
|
1821
|
-
stdio: "inherit",
|
|
1822
|
-
detached: true
|
|
1823
|
-
});
|
|
1824
|
-
} else {
|
|
1825
|
-
spawn("tmux", ["attach-session", "-t", sessionName], {
|
|
1826
|
-
stdio: "inherit",
|
|
1827
|
-
detached: true
|
|
1828
|
-
});
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1831
|
-
}
|
|
1832
|
-
buildShellCommands(projectName, projectPath, claudeArgs = []) {
|
|
1833
|
-
const sessionName = this.getSessionName(projectName);
|
|
1834
|
-
const claudeCommand = claudeArgs.length > 0 ? `claude ${claudeArgs.join(" ")}` : "claude";
|
|
1835
|
-
return [
|
|
1836
|
-
`# Create tmux split session for ${projectName}`,
|
|
1837
|
-
`tmux has-session -t "${sessionName}" 2>/dev/null && tmux kill-session -t "${sessionName}"`,
|
|
1838
|
-
`tmux new-session -d -s "${sessionName}" -c "${projectPath}" '${claudeCommand}'`,
|
|
1839
|
-
`tmux split-window -h -t "${sessionName}" -c "${projectPath}"`,
|
|
1840
|
-
`tmux select-pane -t "${sessionName}:0.0"`,
|
|
1841
|
-
this.getAttachCommand(sessionName)
|
|
1842
|
-
];
|
|
1843
|
-
}
|
|
1844
|
-
buildThreePaneShellCommands(projectName, projectPath, claudeArgs = [], npmCommand = "npm run dev") {
|
|
1845
|
-
const sessionName = this.getSessionName(projectName);
|
|
1846
|
-
const claudeCommand = claudeArgs.length > 0 ? `claude ${claudeArgs.join(" ")}` : "claude";
|
|
1847
|
-
return [
|
|
1848
|
-
`# Create tmux three-pane session for ${projectName}`,
|
|
1849
|
-
`tmux has-session -t "${sessionName}" 2>/dev/null && tmux kill-session -t "${sessionName}"`,
|
|
1850
|
-
`tmux new-session -d -s "${sessionName}" -c "${projectPath}" '${claudeCommand}'`,
|
|
1851
|
-
`tmux split-window -h -t "${sessionName}" -c "${projectPath}"`,
|
|
1852
|
-
`tmux split-window -v -t "${sessionName}:0.1" -c "${projectPath}" '${npmCommand}'`,
|
|
1853
|
-
`tmux set-option -t "${sessionName}:0.2" remain-on-exit on`,
|
|
1854
|
-
`tmux resize-pane -t "${sessionName}:0.2" -y 10`,
|
|
1855
|
-
`tmux select-pane -t "${sessionName}:0.0"`,
|
|
1856
|
-
this.getAttachCommand(sessionName)
|
|
1857
|
-
];
|
|
1858
|
-
}
|
|
1859
|
-
buildTwoPaneNpmShellCommands(projectName, projectPath, npmCommand = "npm run dev") {
|
|
1860
|
-
const sessionName = this.getSessionName(projectName);
|
|
1861
|
-
return [
|
|
1862
|
-
`# Create tmux two-pane session with npm for ${projectName}`,
|
|
1863
|
-
`tmux has-session -t "${sessionName}" 2>/dev/null && tmux kill-session -t "${sessionName}"`,
|
|
1864
|
-
`tmux new-session -d -s "${sessionName}" -c "${projectPath}"`,
|
|
1865
|
-
`tmux split-window -h -t "${sessionName}" -c "${projectPath}" '${npmCommand}'`,
|
|
1866
|
-
`tmux set-option -t "${sessionName}:0.1" remain-on-exit on`,
|
|
1867
|
-
`tmux select-pane -t "${sessionName}:0.0"`,
|
|
1868
|
-
this.getAttachCommand(sessionName)
|
|
1869
|
-
];
|
|
1870
|
-
}
|
|
1871
|
-
getAttachCommand(sessionName) {
|
|
1872
|
-
if (process.env.TMUX) {
|
|
1873
|
-
return `tmux switch-client -t "${sessionName}"`;
|
|
1874
|
-
}
|
|
1875
|
-
const isITerm = process.env.TERM_PROGRAM === "iTerm.app" || process.env.LC_TERMINAL === "iTerm2" || process.env.ITERM_SESSION_ID;
|
|
1876
|
-
const useiTermIntegration = isITerm && !process.env.TMUX_CC_NOT_SUPPORTED;
|
|
1877
|
-
if (useiTermIntegration) {
|
|
1878
|
-
return `tmux -CC attach-session -t "${sessionName}"`;
|
|
1879
|
-
}
|
|
1880
|
-
return `tmux attach-session -t "${sessionName}"`;
|
|
1881
|
-
}
|
|
1882
|
-
async listWorkonSessions() {
|
|
1883
|
-
try {
|
|
1884
|
-
const { stdout } = await exec('tmux list-sessions -F "#{session_name}"');
|
|
1885
|
-
return stdout.trim().split("\n").filter((session) => session.startsWith(this.sessionPrefix)).map((session) => session.replace(this.sessionPrefix, ""));
|
|
1886
|
-
} catch {
|
|
1887
|
-
return [];
|
|
1888
|
-
}
|
|
1948
|
+
async function runOpen(projectArg, options, ctx) {
|
|
1949
|
+
const { log } = ctx;
|
|
1950
|
+
if (options.debug) {
|
|
1951
|
+
log.setLogLevel("debug");
|
|
1889
1952
|
}
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
// src/commands/open.ts
|
|
1893
|
-
init_registry();
|
|
1953
|
+
await processProject(projectArg, options, ctx);
|
|
1954
|
+
}
|
|
1894
1955
|
function createOpenCommand(ctx) {
|
|
1895
1956
|
const { config, log } = ctx;
|
|
1896
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) => {
|
|
1897
1958
|
if (options.debug) {
|
|
1898
1959
|
log.setLogLevel("debug");
|
|
1899
1960
|
}
|
|
1900
|
-
await EventRegistry.initialize();
|
|
1901
1961
|
if (projectArg) {
|
|
1902
1962
|
await processProject(projectArg, options, ctx);
|
|
1903
1963
|
} else {
|
|
@@ -2004,32 +2064,47 @@ function resolveCommandDependencies(requestedCommands, project) {
|
|
|
2004
2064
|
}
|
|
2005
2065
|
return [...new Set(resolved)];
|
|
2006
2066
|
}
|
|
2007
|
-
|
|
2067
|
+
function getClaudeArgs(project) {
|
|
2068
|
+
const claudeConfig = project.events.claude;
|
|
2069
|
+
return typeof claudeConfig === "object" && claudeConfig.flags ? claudeConfig.flags : [];
|
|
2070
|
+
}
|
|
2071
|
+
async function getNpmCommand(project) {
|
|
2072
|
+
const npmConfig = project.events.npm;
|
|
2073
|
+
const { NpmEvent: NpmEvent2 } = await Promise.resolve().then(() => (init_npm(), npm_exports));
|
|
2074
|
+
return NpmEvent2.getNpmCommand(npmConfig);
|
|
2075
|
+
}
|
|
2076
|
+
async function handleTmuxLayout(project, layout, options, shellCommands, events, ctx) {
|
|
2008
2077
|
const { log } = ctx;
|
|
2009
2078
|
const tmux = new TmuxManager();
|
|
2010
|
-
const
|
|
2011
|
-
const claudeArgs = typeof claudeConfig === "object" && claudeConfig.flags ? claudeConfig.flags : [];
|
|
2079
|
+
const { isShellMode, dryRun } = options;
|
|
2012
2080
|
let tmuxHandled = false;
|
|
2013
2081
|
if (isShellMode) {
|
|
2014
2082
|
if (await tmux.isTmuxAvailable()) {
|
|
2015
|
-
const
|
|
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
|
+
}
|
|
2095
|
+
const commands = buildLayoutShellCommands(tmux, project, layout);
|
|
2016
2096
|
shellCommands.push(...commands);
|
|
2017
2097
|
tmuxHandled = true;
|
|
2018
2098
|
} else {
|
|
2019
2099
|
log.debug("Tmux not available, falling back to normal mode");
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
shellCommands.push(claudeCommand);
|
|
2100
|
+
const remainingEvents = events.filter((e) => !layout.handledEvents.includes(e));
|
|
2101
|
+
buildFallbackCommandsWithEvents(shellCommands, project, layout, remainingEvents, ctx);
|
|
2023
2102
|
tmuxHandled = true;
|
|
2024
2103
|
}
|
|
2025
2104
|
} else if (!dryRun) {
|
|
2026
2105
|
if (await tmux.isTmuxAvailable()) {
|
|
2027
2106
|
try {
|
|
2028
|
-
const sessionName = await tmux
|
|
2029
|
-
project.name,
|
|
2030
|
-
project.path.path,
|
|
2031
|
-
claudeArgs
|
|
2032
|
-
);
|
|
2107
|
+
const sessionName = await createTmuxSession(tmux, project, layout);
|
|
2033
2108
|
await tmux.attachToSession(sessionName);
|
|
2034
2109
|
tmuxHandled = true;
|
|
2035
2110
|
} catch (error) {
|
|
@@ -2039,139 +2114,108 @@ async function handleSplitTerminal(project, isShellMode, dryRun, shellCommands,
|
|
|
2039
2114
|
log.debug("Tmux not available, falling back to normal event processing");
|
|
2040
2115
|
}
|
|
2041
2116
|
} else {
|
|
2042
|
-
log.info(
|
|
2117
|
+
log.info(layout.dryRunMessage);
|
|
2043
2118
|
tmuxHandled = true;
|
|
2044
2119
|
}
|
|
2045
2120
|
if (!tmuxHandled && !dryRun) {
|
|
2046
|
-
for (const event of events.filter((e) =>
|
|
2121
|
+
for (const event of events.filter((e) => layout.handledEvents.includes(e))) {
|
|
2047
2122
|
await processEvent(event, { project, isShellMode, shellCommands }, ctx);
|
|
2048
2123
|
}
|
|
2049
2124
|
}
|
|
2050
|
-
if (!dryRun) {
|
|
2051
|
-
for (const event of events.filter((e) => !
|
|
2125
|
+
if (!dryRun && !isShellMode) {
|
|
2126
|
+
for (const event of events.filter((e) => !layout.handledEvents.includes(e))) {
|
|
2052
2127
|
await processEvent(event, { project, isShellMode, shellCommands }, ctx);
|
|
2053
2128
|
}
|
|
2054
2129
|
}
|
|
2055
2130
|
}
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
const { NpmEvent: NpmEvent2 } = await Promise.resolve().then(() => (init_npm(), npm_exports));
|
|
2063
|
-
const npmCommand = NpmEvent2.getNpmCommand(npmConfig);
|
|
2064
|
-
let tmuxHandled = false;
|
|
2065
|
-
if (isShellMode) {
|
|
2066
|
-
if (await tmux.isTmuxAvailable()) {
|
|
2067
|
-
const commands = tmux.buildThreePaneShellCommands(
|
|
2131
|
+
function buildLayoutShellCommands(tmux, project, layout) {
|
|
2132
|
+
switch (layout.type) {
|
|
2133
|
+
case "split-claude":
|
|
2134
|
+
return tmux.buildShellCommands(project.name, project.path.path, layout.claudeArgs);
|
|
2135
|
+
case "three-pane":
|
|
2136
|
+
return tmux.buildThreePaneShellCommands(
|
|
2068
2137
|
project.name,
|
|
2069
2138
|
project.path.path,
|
|
2070
|
-
claudeArgs,
|
|
2071
|
-
npmCommand
|
|
2139
|
+
layout.claudeArgs,
|
|
2140
|
+
layout.npmCommand
|
|
2072
2141
|
);
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
} else {
|
|
2076
|
-
log.debug("Tmux not available, falling back to normal mode");
|
|
2077
|
-
shellCommands.push(`cd "${project.path.path}"`);
|
|
2078
|
-
shellCommands.push(claudeArgs.length > 0 ? `claude ${claudeArgs.join(" ")}` : "claude");
|
|
2079
|
-
shellCommands.push(npmCommand);
|
|
2080
|
-
tmuxHandled = true;
|
|
2081
|
-
}
|
|
2082
|
-
} else if (!dryRun) {
|
|
2083
|
-
if (await tmux.isTmuxAvailable()) {
|
|
2084
|
-
try {
|
|
2085
|
-
const sessionName = await tmux.createThreePaneSession(
|
|
2086
|
-
project.name,
|
|
2087
|
-
project.path.path,
|
|
2088
|
-
claudeArgs,
|
|
2089
|
-
npmCommand
|
|
2090
|
-
);
|
|
2091
|
-
await tmux.attachToSession(sessionName);
|
|
2092
|
-
tmuxHandled = true;
|
|
2093
|
-
} catch (error) {
|
|
2094
|
-
log.debug(`Failed to create tmux session: ${error.message}`);
|
|
2095
|
-
}
|
|
2096
|
-
} else {
|
|
2097
|
-
log.debug("Tmux not available, falling back to normal event processing");
|
|
2098
|
-
}
|
|
2099
|
-
} else {
|
|
2100
|
-
log.info(`Would create three-pane tmux session '${project.name}' with Claude and NPM`);
|
|
2101
|
-
tmuxHandled = true;
|
|
2102
|
-
}
|
|
2103
|
-
if (!tmuxHandled && !dryRun) {
|
|
2104
|
-
for (const event of events.filter((e) => ["cwd", "claude", "npm"].includes(e))) {
|
|
2105
|
-
await processEvent(event, { project, isShellMode, shellCommands }, ctx);
|
|
2106
|
-
}
|
|
2142
|
+
case "two-pane-npm":
|
|
2143
|
+
return tmux.buildTwoPaneNpmShellCommands(project.name, project.path.path, layout.npmCommand);
|
|
2107
2144
|
}
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2145
|
+
}
|
|
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);
|
|
2111
2158
|
}
|
|
2112
2159
|
}
|
|
2113
2160
|
}
|
|
2114
|
-
async function
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
let tmuxHandled = false;
|
|
2121
|
-
if (isShellMode) {
|
|
2122
|
-
if (await tmux.isTmuxAvailable()) {
|
|
2123
|
-
const commands = tmux.buildTwoPaneNpmShellCommands(
|
|
2161
|
+
async function createTmuxSession(tmux, project, layout) {
|
|
2162
|
+
switch (layout.type) {
|
|
2163
|
+
case "split-claude":
|
|
2164
|
+
return tmux.createSplitSession(project.name, project.path.path, layout.claudeArgs);
|
|
2165
|
+
case "three-pane":
|
|
2166
|
+
return tmux.createThreePaneSession(
|
|
2124
2167
|
project.name,
|
|
2125
2168
|
project.path.path,
|
|
2126
|
-
|
|
2169
|
+
layout.claudeArgs,
|
|
2170
|
+
layout.npmCommand
|
|
2127
2171
|
);
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
} else {
|
|
2131
|
-
log.debug("Tmux not available, falling back to normal mode");
|
|
2132
|
-
shellCommands.push(`cd "${project.path.path}"`);
|
|
2133
|
-
shellCommands.push(npmCommand);
|
|
2134
|
-
tmuxHandled = true;
|
|
2135
|
-
}
|
|
2136
|
-
} else if (!dryRun) {
|
|
2137
|
-
if (await tmux.isTmuxAvailable()) {
|
|
2138
|
-
try {
|
|
2139
|
-
const sessionName = await tmux.createTwoPaneNpmSession(
|
|
2140
|
-
project.name,
|
|
2141
|
-
project.path.path,
|
|
2142
|
-
npmCommand
|
|
2143
|
-
);
|
|
2144
|
-
await tmux.attachToSession(sessionName);
|
|
2145
|
-
tmuxHandled = true;
|
|
2146
|
-
} catch (error) {
|
|
2147
|
-
log.debug(`Failed to create tmux session: ${error.message}`);
|
|
2148
|
-
}
|
|
2149
|
-
} else {
|
|
2150
|
-
log.debug("Tmux not available, falling back to normal event processing");
|
|
2151
|
-
}
|
|
2152
|
-
} else {
|
|
2153
|
-
log.info(`Would create two-pane tmux session '${project.name}' with NPM`);
|
|
2154
|
-
tmuxHandled = true;
|
|
2155
|
-
}
|
|
2156
|
-
if (!tmuxHandled && !dryRun) {
|
|
2157
|
-
for (const event of events.filter((e) => ["cwd", "npm"].includes(e))) {
|
|
2158
|
-
await processEvent(event, { project, isShellMode, shellCommands }, ctx);
|
|
2159
|
-
}
|
|
2160
|
-
}
|
|
2161
|
-
if (!dryRun) {
|
|
2162
|
-
for (const event of events.filter((e) => !["cwd", "npm"].includes(e))) {
|
|
2163
|
-
await processEvent(event, { project, isShellMode, shellCommands }, ctx);
|
|
2164
|
-
}
|
|
2172
|
+
case "two-pane-npm":
|
|
2173
|
+
return tmux.createTwoPaneNpmSession(project.name, project.path.path, layout.npmCommand);
|
|
2165
2174
|
}
|
|
2166
2175
|
}
|
|
2176
|
+
async function handleSplitTerminal(project, isShellMode, dryRun, shellCommands, events, ctx) {
|
|
2177
|
+
const layout = {
|
|
2178
|
+
type: "split-claude",
|
|
2179
|
+
handledEvents: ["cwd", "claude"],
|
|
2180
|
+
dryRunMessage: `Would create split tmux session '${project.name}' with Claude`,
|
|
2181
|
+
claudeArgs: getClaudeArgs(project),
|
|
2182
|
+
npmCommand: null
|
|
2183
|
+
};
|
|
2184
|
+
await handleTmuxLayout(project, layout, { isShellMode, dryRun }, shellCommands, events, ctx);
|
|
2185
|
+
}
|
|
2186
|
+
async function handleThreePaneLayout(project, isShellMode, dryRun, shellCommands, events, ctx) {
|
|
2187
|
+
const layout = {
|
|
2188
|
+
type: "three-pane",
|
|
2189
|
+
handledEvents: ["cwd", "claude", "npm"],
|
|
2190
|
+
dryRunMessage: `Would create three-pane tmux session '${project.name}' with Claude and NPM`,
|
|
2191
|
+
claudeArgs: getClaudeArgs(project),
|
|
2192
|
+
npmCommand: await getNpmCommand(project)
|
|
2193
|
+
};
|
|
2194
|
+
await handleTmuxLayout(project, layout, { isShellMode, dryRun }, shellCommands, events, ctx);
|
|
2195
|
+
}
|
|
2196
|
+
async function handleTwoPaneNpmLayout(project, isShellMode, dryRun, shellCommands, events, ctx) {
|
|
2197
|
+
const layout = {
|
|
2198
|
+
type: "two-pane-npm",
|
|
2199
|
+
handledEvents: ["cwd", "npm"],
|
|
2200
|
+
dryRunMessage: `Would create two-pane tmux session '${project.name}' with NPM`,
|
|
2201
|
+
claudeArgs: [],
|
|
2202
|
+
npmCommand: await getNpmCommand(project)
|
|
2203
|
+
};
|
|
2204
|
+
await handleTmuxLayout(project, layout, { isShellMode, dryRun }, shellCommands, events, ctx);
|
|
2205
|
+
}
|
|
2167
2206
|
async function processEvent(event, context, ctx) {
|
|
2168
2207
|
const { log } = ctx;
|
|
2169
2208
|
log.debug(`Processing event ${event}`);
|
|
2170
2209
|
const eventHandler = EventRegistry.getEventByName(event);
|
|
2171
|
-
if (eventHandler
|
|
2172
|
-
await eventHandler.processing.processEvent(context);
|
|
2173
|
-
} else {
|
|
2210
|
+
if (!eventHandler) {
|
|
2174
2211
|
log.debug(`No event handler found for: ${event}`);
|
|
2212
|
+
return;
|
|
2213
|
+
}
|
|
2214
|
+
try {
|
|
2215
|
+
await eventHandler.processing.processEvent(context);
|
|
2216
|
+
} catch (error) {
|
|
2217
|
+
log.error(`Failed to process event '${event}': ${error.message}`);
|
|
2218
|
+
log.debug(`Event error stack: ${error.stack}`);
|
|
2175
2219
|
}
|
|
2176
2220
|
}
|
|
2177
2221
|
async function showProjectHelp(projectName, ctx) {
|
|
@@ -2189,17 +2233,16 @@ Available commands for '${projectName}':`);
|
|
|
2189
2233
|
for (const eventName of configuredEvents) {
|
|
2190
2234
|
const eventHandler = EventRegistry.getEventByName(eventName);
|
|
2191
2235
|
if (eventHandler) {
|
|
2192
|
-
const
|
|
2193
|
-
const config2 = projectConfig.events[eventName];
|
|
2236
|
+
const eventConfig = projectConfig.events[eventName];
|
|
2194
2237
|
let configDesc = "";
|
|
2195
|
-
if (
|
|
2196
|
-
if (typeof
|
|
2197
|
-
configDesc = ` (${JSON.stringify(
|
|
2238
|
+
if (eventConfig !== true && eventConfig !== "true") {
|
|
2239
|
+
if (typeof eventConfig === "object") {
|
|
2240
|
+
configDesc = ` (${JSON.stringify(eventConfig)})`;
|
|
2198
2241
|
} else {
|
|
2199
|
-
configDesc = ` (${
|
|
2242
|
+
configDesc = ` (${eventConfig})`;
|
|
2200
2243
|
}
|
|
2201
2244
|
}
|
|
2202
|
-
console.log(` ${eventName.padEnd(8)} - ${metadata.description}${configDesc}`);
|
|
2245
|
+
console.log(` ${eventName.padEnd(8)} - ${eventHandler.metadata.description}${configDesc}`);
|
|
2203
2246
|
}
|
|
2204
2247
|
}
|
|
2205
2248
|
console.log("\nUsage examples:");
|
|
@@ -2213,6 +2256,27 @@ Available commands for '${projectName}':`);
|
|
|
2213
2256
|
console.log(` workon ${projectName}:cwd --shell # Output shell commands
|
|
2214
2257
|
`);
|
|
2215
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";
|
|
2216
2280
|
|
|
2217
2281
|
// src/commands/config/index.ts
|
|
2218
2282
|
import { Command as Command5 } from "commander";
|
|
@@ -2306,25 +2370,16 @@ function createConfigCommand(ctx) {
|
|
|
2306
2370
|
|
|
2307
2371
|
// src/commands/manage.ts
|
|
2308
2372
|
init_registry();
|
|
2373
|
+
init_constants();
|
|
2309
2374
|
import { Command as Command6 } from "commander";
|
|
2310
2375
|
import { select as select2, input as input5, confirm as confirm4, checkbox as checkbox2 } from "@inquirer/prompts";
|
|
2311
2376
|
import File5 from "phylo";
|
|
2312
|
-
var IDE_CHOICES2 = [
|
|
2313
|
-
{ name: "Visual Studio Code", value: "vscode" },
|
|
2314
|
-
{ name: "Visual Studio Code (code)", value: "code" },
|
|
2315
|
-
{ name: "IntelliJ IDEA", value: "idea" },
|
|
2316
|
-
{ name: "Atom", value: "atom" },
|
|
2317
|
-
{ name: "Sublime Text", value: "subl" },
|
|
2318
|
-
{ name: "Vim", value: "vim" },
|
|
2319
|
-
{ name: "Emacs", value: "emacs" }
|
|
2320
|
-
];
|
|
2321
2377
|
function createManageCommand(ctx) {
|
|
2322
2378
|
const { log } = ctx;
|
|
2323
2379
|
return new Command6("manage").description("Interactive project management").option("-d, --debug", "Enable debug logging").action(async (options) => {
|
|
2324
2380
|
if (options.debug) {
|
|
2325
2381
|
log.setLogLevel("debug");
|
|
2326
2382
|
}
|
|
2327
|
-
await EventRegistry.initialize();
|
|
2328
2383
|
await mainMenu(ctx);
|
|
2329
2384
|
});
|
|
2330
2385
|
}
|
|
@@ -2406,7 +2461,7 @@ async function createProject(ctx) {
|
|
|
2406
2461
|
}
|
|
2407
2462
|
const ide = await select2({
|
|
2408
2463
|
message: "Select IDE:",
|
|
2409
|
-
choices:
|
|
2464
|
+
choices: IDE_CHOICES
|
|
2410
2465
|
});
|
|
2411
2466
|
const homepage = await input5({
|
|
2412
2467
|
message: "Project homepage URL (optional):",
|
|
@@ -2483,7 +2538,7 @@ async function editProject(ctx) {
|
|
|
2483
2538
|
}
|
|
2484
2539
|
const ide = await select2({
|
|
2485
2540
|
message: "Select IDE:",
|
|
2486
|
-
choices:
|
|
2541
|
+
choices: IDE_CHOICES,
|
|
2487
2542
|
default: project.ide || "vscode"
|
|
2488
2543
|
});
|
|
2489
2544
|
const homepage = await input5({
|
|
@@ -2639,7 +2694,7 @@ async function addProject(pathArg, options, ctx) {
|
|
|
2639
2694
|
return;
|
|
2640
2695
|
}
|
|
2641
2696
|
}
|
|
2642
|
-
const ide = options.ide || discovery.detectedIde || "
|
|
2697
|
+
const ide = options.ide || discovery.detectedIde || "code";
|
|
2643
2698
|
log.debug(`IDE: ${ide}`);
|
|
2644
2699
|
let relativePath = targetPath;
|
|
2645
2700
|
if (defaults?.base) {
|
|
@@ -2667,6 +2722,9 @@ async function addProject(pathArg, options, ctx) {
|
|
|
2667
2722
|
projectConfig.events.npm = scripts.dev ? "dev" : "start";
|
|
2668
2723
|
}
|
|
2669
2724
|
}
|
|
2725
|
+
if (discovery.hasClaude) {
|
|
2726
|
+
projectConfig.events.claude = true;
|
|
2727
|
+
}
|
|
2670
2728
|
config.setProject(projectName, projectConfig);
|
|
2671
2729
|
log.info(`Added project '${projectName}'`);
|
|
2672
2730
|
log.info(` Path: ${relativePath}`);
|
|
@@ -2681,6 +2739,7 @@ function discoverProject(targetPath, log) {
|
|
|
2681
2739
|
name: dirName,
|
|
2682
2740
|
isNode: false,
|
|
2683
2741
|
isBun: false,
|
|
2742
|
+
hasClaude: false,
|
|
2684
2743
|
detectedIde: null,
|
|
2685
2744
|
packageJson: null
|
|
2686
2745
|
};
|
|
@@ -2706,14 +2765,23 @@ function discoverProject(targetPath, log) {
|
|
|
2706
2765
|
log.debug("Detected Bun project (bun.lockb found)");
|
|
2707
2766
|
}
|
|
2708
2767
|
const vscodeDir = resolve(targetPath, ".vscode");
|
|
2768
|
+
const cursorDir = resolve(targetPath, ".cursor");
|
|
2709
2769
|
const ideaDir = resolve(targetPath, ".idea");
|
|
2710
|
-
if (existsSync(
|
|
2711
|
-
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";
|
|
2712
2775
|
log.debug("Detected VS Code (.vscode directory found)");
|
|
2713
2776
|
} else if (existsSync(ideaDir)) {
|
|
2714
2777
|
discovery.detectedIde = "idea";
|
|
2715
2778
|
log.debug("Detected IntelliJ IDEA (.idea directory found)");
|
|
2716
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
|
+
}
|
|
2717
2785
|
return discovery;
|
|
2718
2786
|
}
|
|
2719
2787
|
|
|
@@ -2745,11 +2813,12 @@ function createCli() {
|
|
|
2745
2813
|
config.set("pkg", packageJson);
|
|
2746
2814
|
EnvironmentRecognizer.configure(config, log);
|
|
2747
2815
|
const completion = setupCompletion(config);
|
|
2748
|
-
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", (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) => {
|
|
2749
2817
|
const opts = thisCommand.opts();
|
|
2750
2818
|
if (opts.debug) {
|
|
2751
2819
|
log.setLogLevel("debug");
|
|
2752
2820
|
}
|
|
2821
|
+
await EventRegistry.initialize();
|
|
2753
2822
|
}).action(
|
|
2754
2823
|
async (project, options) => {
|
|
2755
2824
|
if (options.debug) {
|
|
@@ -2766,10 +2835,8 @@ function createCli() {
|
|
|
2766
2835
|
return;
|
|
2767
2836
|
}
|
|
2768
2837
|
if (project) {
|
|
2769
|
-
const
|
|
2770
|
-
|
|
2771
|
-
if (options.debug) args.push("--debug");
|
|
2772
|
-
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 });
|
|
2773
2840
|
return;
|
|
2774
2841
|
}
|
|
2775
2842
|
const environment = await EnvironmentRecognizer.recognize(File7.cwd());
|