workon 1.2.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.history/.gitignore_20250806202718 +30 -0
- package/.history/.gitignore_20250806231444 +32 -0
- package/.history/.gitignore_20250806231450 +32 -0
- package/.history/lib/tmux_20250806233103.js +109 -0
- package/.history/lib/tmux_20250806233219.js +109 -0
- package/.history/lib/tmux_20250806233223.js +109 -0
- package/.history/lib/tmux_20250806233230.js +109 -0
- package/.history/lib/tmux_20250806233231.js +109 -0
- package/CHANGELOG.md +21 -0
- package/cli/manage.js +179 -10
- package/cli/open.js +217 -6
- package/docs/ideas.md +79 -35
- package/lib/tmux.js +190 -0
- package/lib/validation.js +71 -1
- package/package.json +1 -1
- package/.history/package_20250806214300.json +0 -42
- package/.history/package_20250806215528.json +0 -43
package/cli/manage.js
CHANGED
|
@@ -127,18 +127,29 @@ class manage extends command {
|
|
|
127
127
|
{ name: 'Change directory (cwd)', value: 'cwd', checked: true },
|
|
128
128
|
{ name: 'Open in IDE', value: 'ide', checked: true },
|
|
129
129
|
{ name: 'Open homepage in browser', value: 'web' },
|
|
130
|
-
{ name: 'Launch Claude Code', value: 'claude' }
|
|
130
|
+
{ name: 'Launch Claude Code', value: 'claude' },
|
|
131
|
+
{ name: 'Run NPM command', value: 'npm' }
|
|
131
132
|
]
|
|
132
133
|
}
|
|
133
134
|
];
|
|
134
135
|
|
|
135
136
|
const answers = await inquirer.prompt(questions);
|
|
136
137
|
|
|
137
|
-
// Convert events array to object
|
|
138
|
+
// Convert events array to object and configure advanced options
|
|
138
139
|
const events = {};
|
|
139
|
-
answers.events
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
for (const event of answers.events) {
|
|
141
|
+
if (event === 'claude') {
|
|
142
|
+
// Ask for Claude-specific configuration
|
|
143
|
+
const claudeConfig = await me.configureClaudeEvent();
|
|
144
|
+
events[event] = claudeConfig;
|
|
145
|
+
} else if (event === 'npm') {
|
|
146
|
+
// Ask for NPM-specific configuration
|
|
147
|
+
const npmConfig = await me.configureNpmEvent();
|
|
148
|
+
events[event] = npmConfig;
|
|
149
|
+
} else {
|
|
150
|
+
events[event] = 'true';
|
|
151
|
+
}
|
|
152
|
+
}
|
|
142
153
|
|
|
143
154
|
const projectConfig = {
|
|
144
155
|
path: answers.path,
|
|
@@ -241,18 +252,59 @@ class manage extends command {
|
|
|
241
252
|
{ name: 'Change directory (cwd)', value: 'cwd', checked: currentEvents.includes('cwd') },
|
|
242
253
|
{ name: 'Open in IDE', value: 'ide', checked: currentEvents.includes('ide') },
|
|
243
254
|
{ name: 'Open homepage in browser', value: 'web', checked: currentEvents.includes('web') },
|
|
244
|
-
{ name: 'Launch Claude Code', value: 'claude', checked: currentEvents.includes('claude') }
|
|
255
|
+
{ name: 'Launch Claude Code', value: 'claude', checked: currentEvents.includes('claude') },
|
|
256
|
+
{ name: 'Run NPM command', value: 'npm', checked: currentEvents.includes('npm') }
|
|
245
257
|
]
|
|
246
258
|
}
|
|
247
259
|
];
|
|
248
260
|
|
|
249
261
|
const answers = await inquirer.prompt(questions);
|
|
250
262
|
|
|
251
|
-
// Convert events array to object
|
|
263
|
+
// Convert events array to object and configure advanced options
|
|
252
264
|
const events = {};
|
|
253
|
-
answers.events
|
|
254
|
-
|
|
255
|
-
|
|
265
|
+
for (const event of answers.events) {
|
|
266
|
+
if (event === 'claude') {
|
|
267
|
+
// If claude was previously configured with advanced options, preserve or update them
|
|
268
|
+
const existingClaudeConfig = project.events && project.events.claude;
|
|
269
|
+
if (existingClaudeConfig && typeof existingClaudeConfig === 'object') {
|
|
270
|
+
const keepConfig = await inquirer.prompt([{
|
|
271
|
+
type: 'confirm',
|
|
272
|
+
name: 'keep',
|
|
273
|
+
message: 'Keep existing Claude configuration?',
|
|
274
|
+
default: true
|
|
275
|
+
}]);
|
|
276
|
+
|
|
277
|
+
if (keepConfig.keep) {
|
|
278
|
+
events[event] = existingClaudeConfig;
|
|
279
|
+
} else {
|
|
280
|
+
events[event] = await me.configureClaudeEvent();
|
|
281
|
+
}
|
|
282
|
+
} else {
|
|
283
|
+
events[event] = await me.configureClaudeEvent();
|
|
284
|
+
}
|
|
285
|
+
} else if (event === 'npm') {
|
|
286
|
+
// If npm was previously configured with advanced options, preserve or update them
|
|
287
|
+
const existingNpmConfig = project.events && project.events.npm;
|
|
288
|
+
if (existingNpmConfig && typeof existingNpmConfig === 'object') {
|
|
289
|
+
const keepConfig = await inquirer.prompt([{
|
|
290
|
+
type: 'confirm',
|
|
291
|
+
name: 'keep',
|
|
292
|
+
message: 'Keep existing NPM configuration?',
|
|
293
|
+
default: true
|
|
294
|
+
}]);
|
|
295
|
+
|
|
296
|
+
if (keepConfig.keep) {
|
|
297
|
+
events[event] = existingNpmConfig;
|
|
298
|
+
} else {
|
|
299
|
+
events[event] = await me.configureNpmEvent();
|
|
300
|
+
}
|
|
301
|
+
} else {
|
|
302
|
+
events[event] = await me.configureNpmEvent();
|
|
303
|
+
}
|
|
304
|
+
} else {
|
|
305
|
+
events[event] = 'true';
|
|
306
|
+
}
|
|
307
|
+
}
|
|
256
308
|
|
|
257
309
|
const updatedProject = {
|
|
258
310
|
path: answers.path,
|
|
@@ -371,6 +423,123 @@ class manage extends command {
|
|
|
371
423
|
return me.startManagement();
|
|
372
424
|
}
|
|
373
425
|
}
|
|
426
|
+
|
|
427
|
+
async configureClaudeEvent() {
|
|
428
|
+
let me = this;
|
|
429
|
+
|
|
430
|
+
me.log.log('\n⚙️ Configure Claude Event\n');
|
|
431
|
+
|
|
432
|
+
const claudeQuestions = [
|
|
433
|
+
{
|
|
434
|
+
type: 'confirm',
|
|
435
|
+
name: 'useAdvanced',
|
|
436
|
+
message: 'Configure advanced Claude options?',
|
|
437
|
+
default: false
|
|
438
|
+
}
|
|
439
|
+
];
|
|
440
|
+
|
|
441
|
+
const claudeAnswer = await inquirer.prompt(claudeQuestions);
|
|
442
|
+
|
|
443
|
+
if (!claudeAnswer.useAdvanced) {
|
|
444
|
+
return 'true';
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const advancedQuestions = [
|
|
448
|
+
{
|
|
449
|
+
type: 'input',
|
|
450
|
+
name: 'flags',
|
|
451
|
+
message: 'Claude flags (comma-separated, e.g. --resume,--debug):',
|
|
452
|
+
filter: (input) => {
|
|
453
|
+
if (!input.trim()) return [];
|
|
454
|
+
return input.split(',').map(flag => flag.trim()).filter(flag => flag);
|
|
455
|
+
}
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
type: 'confirm',
|
|
459
|
+
name: 'split_terminal',
|
|
460
|
+
message: 'Enable split terminal (Claude + shell side-by-side with tmux)?',
|
|
461
|
+
default: false
|
|
462
|
+
}
|
|
463
|
+
];
|
|
464
|
+
|
|
465
|
+
const advancedAnswers = await inquirer.prompt(advancedQuestions);
|
|
466
|
+
|
|
467
|
+
const config = {};
|
|
468
|
+
|
|
469
|
+
if (advancedAnswers.flags && advancedAnswers.flags.length > 0) {
|
|
470
|
+
config.flags = advancedAnswers.flags;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (advancedAnswers.split_terminal) {
|
|
474
|
+
config.split_terminal = true;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return config;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
async configureNpmEvent() {
|
|
481
|
+
let me = this;
|
|
482
|
+
|
|
483
|
+
me.log.log('\n📦 Configure NPM Event\n');
|
|
484
|
+
|
|
485
|
+
const npmQuestions = [
|
|
486
|
+
{
|
|
487
|
+
type: 'input',
|
|
488
|
+
name: 'command',
|
|
489
|
+
message: 'NPM script to run (e.g., dev, start, test):',
|
|
490
|
+
default: 'dev',
|
|
491
|
+
validate: (value) => {
|
|
492
|
+
if (!value.trim()) {
|
|
493
|
+
return 'NPM command cannot be empty';
|
|
494
|
+
}
|
|
495
|
+
return true;
|
|
496
|
+
}
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
type: 'confirm',
|
|
500
|
+
name: 'useAdvanced',
|
|
501
|
+
message: 'Configure advanced NPM options?',
|
|
502
|
+
default: false
|
|
503
|
+
}
|
|
504
|
+
];
|
|
505
|
+
|
|
506
|
+
const basicAnswers = await inquirer.prompt(npmQuestions);
|
|
507
|
+
|
|
508
|
+
if (!basicAnswers.useAdvanced) {
|
|
509
|
+
return basicAnswers.command;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
const advancedQuestions = [
|
|
513
|
+
{
|
|
514
|
+
type: 'confirm',
|
|
515
|
+
name: 'watch',
|
|
516
|
+
message: 'Enable watch mode (if supported by command)?',
|
|
517
|
+
default: true
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
type: 'confirm',
|
|
521
|
+
name: 'auto_restart',
|
|
522
|
+
message: 'Auto-restart on crashes?',
|
|
523
|
+
default: false
|
|
524
|
+
}
|
|
525
|
+
];
|
|
526
|
+
|
|
527
|
+
const advancedAnswers = await inquirer.prompt(advancedQuestions);
|
|
528
|
+
|
|
529
|
+
const config = {
|
|
530
|
+
command: basicAnswers.command
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
if (advancedAnswers.watch) {
|
|
534
|
+
config.watch = true;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if (advancedAnswers.auto_restart) {
|
|
538
|
+
config.auto_restart = true;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
return config;
|
|
542
|
+
}
|
|
374
543
|
}
|
|
375
544
|
|
|
376
545
|
manage.define({
|
package/cli/open.js
CHANGED
|
@@ -3,6 +3,7 @@ const Project = require('../lib/project');
|
|
|
3
3
|
const { ProjectEnvironment } = require('../lib/environment');
|
|
4
4
|
const spawn = require('child_process').spawn;
|
|
5
5
|
const File = require('phylo');
|
|
6
|
+
const TmuxManager = require('../lib/tmux');
|
|
6
7
|
|
|
7
8
|
class open extends command {
|
|
8
9
|
execute (params) {
|
|
@@ -15,7 +16,7 @@ class open extends command {
|
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
processProject (project) {
|
|
19
|
+
async processProject (project) {
|
|
19
20
|
let me = this;
|
|
20
21
|
let environment = me.root().environment;
|
|
21
22
|
|
|
@@ -29,7 +30,7 @@ class open extends command {
|
|
|
29
30
|
if (project in projects) {
|
|
30
31
|
let cfg = projects[project];
|
|
31
32
|
cfg.name = project;
|
|
32
|
-
me.switchTo(ProjectEnvironment.load(cfg, me.config.get('project_defaults')));
|
|
33
|
+
await me.switchTo(ProjectEnvironment.load(cfg, me.config.get('project_defaults')));
|
|
33
34
|
} else {
|
|
34
35
|
me.log.debug(`Project '${project}' not found, starting interactive mode`);
|
|
35
36
|
return me.startInteractiveMode(project);
|
|
@@ -46,7 +47,7 @@ class open extends command {
|
|
|
46
47
|
return interactiveCmd.dispatch(new me.args.constructor([project]))
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
switchTo (environment) {
|
|
50
|
+
async switchTo (environment) {
|
|
50
51
|
let me = this;
|
|
51
52
|
me.root().environment = environment;
|
|
52
53
|
let project = environment.project;
|
|
@@ -63,7 +64,30 @@ class open extends command {
|
|
|
63
64
|
me.shellCommands = [];
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
|
|
67
|
+
// Intelligent layout detection
|
|
68
|
+
const hasCwd = events.includes('cwd');
|
|
69
|
+
const hasClaudeEvent = events.includes('claude');
|
|
70
|
+
const hasNpmEvent = events.includes('npm');
|
|
71
|
+
|
|
72
|
+
if (hasCwd && hasClaudeEvent && hasNpmEvent) {
|
|
73
|
+
// Three-pane layout: Claude + Terminal + NPM
|
|
74
|
+
await me.handleThreePaneLayout(project, isShellMode);
|
|
75
|
+
// Process other events except cwd, claude, and npm
|
|
76
|
+
events.filter(e => !['cwd', 'claude', 'npm'].includes(e)).forEach(me.processEvent.bind(me));
|
|
77
|
+
} else if (hasCwd && hasNpmEvent) {
|
|
78
|
+
// Two-pane layout: Terminal + NPM (no Claude)
|
|
79
|
+
await me.handleTwoPaneNpmLayout(project, isShellMode);
|
|
80
|
+
// Process other events except cwd and npm
|
|
81
|
+
events.filter(e => !['cwd', 'npm'].includes(e)).forEach(me.processEvent.bind(me));
|
|
82
|
+
} else if (hasCwd && hasClaudeEvent) {
|
|
83
|
+
// Two-pane layout: Claude + Terminal (existing split terminal)
|
|
84
|
+
await me.handleSplitTerminal(project, isShellMode);
|
|
85
|
+
// Process other events except cwd and claude
|
|
86
|
+
events.filter(e => !['cwd', 'claude'].includes(e)).forEach(me.processEvent.bind(me));
|
|
87
|
+
} else {
|
|
88
|
+
// Normal event processing
|
|
89
|
+
events.forEach(me.processEvent.bind(me));
|
|
90
|
+
}
|
|
67
91
|
|
|
68
92
|
// Output collected shell commands if in shell mode
|
|
69
93
|
if (isShellMode && me.shellCommands.length > 0) {
|
|
@@ -71,6 +95,167 @@ class open extends command {
|
|
|
71
95
|
}
|
|
72
96
|
}
|
|
73
97
|
|
|
98
|
+
async handleSplitTerminal(project, isShellMode) {
|
|
99
|
+
let me = this;
|
|
100
|
+
const tmux = new TmuxManager();
|
|
101
|
+
const claudeConfig = project.events.claude;
|
|
102
|
+
const claudeArgs = (claudeConfig && claudeConfig.flags) ? claudeConfig.flags : [];
|
|
103
|
+
|
|
104
|
+
if (isShellMode) {
|
|
105
|
+
// Check if tmux is available
|
|
106
|
+
if (await tmux.isTmuxAvailable()) {
|
|
107
|
+
const commands = tmux.buildShellCommands(
|
|
108
|
+
project.name,
|
|
109
|
+
project.path.path,
|
|
110
|
+
claudeArgs
|
|
111
|
+
);
|
|
112
|
+
me.shellCommands.push(...commands);
|
|
113
|
+
} else {
|
|
114
|
+
// Fall back to normal behavior if tmux is not available
|
|
115
|
+
me.log.debug('Tmux not available, falling back to normal mode');
|
|
116
|
+
me.shellCommands.push(`cd "${project.path.path}"`);
|
|
117
|
+
const claudeCommand = claudeArgs.length > 0
|
|
118
|
+
? `claude ${claudeArgs.join(' ')}`
|
|
119
|
+
: 'claude';
|
|
120
|
+
me.shellCommands.push(claudeCommand);
|
|
121
|
+
}
|
|
122
|
+
} else {
|
|
123
|
+
// Direct execution mode
|
|
124
|
+
if (await tmux.isTmuxAvailable()) {
|
|
125
|
+
try {
|
|
126
|
+
const sessionName = await tmux.createSplitSession(
|
|
127
|
+
project.name,
|
|
128
|
+
project.path.path,
|
|
129
|
+
claudeArgs
|
|
130
|
+
);
|
|
131
|
+
await tmux.attachToSession(sessionName);
|
|
132
|
+
} catch (error) {
|
|
133
|
+
me.log.debug(`Failed to create tmux session: ${error.message}`);
|
|
134
|
+
// Fall back to normal behavior
|
|
135
|
+
me.processEvent('cwd');
|
|
136
|
+
me.processEvent('claude');
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
me.log.debug('Tmux not available, falling back to normal mode');
|
|
140
|
+
// Fall back to normal behavior
|
|
141
|
+
me.processEvent('cwd');
|
|
142
|
+
me.processEvent('claude');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async handleThreePaneLayout(project, isShellMode) {
|
|
148
|
+
let me = this;
|
|
149
|
+
const tmux = new TmuxManager();
|
|
150
|
+
const claudeConfig = project.events.claude;
|
|
151
|
+
const claudeArgs = (claudeConfig && claudeConfig.flags) ? claudeConfig.flags : [];
|
|
152
|
+
const npmConfig = project.events.npm;
|
|
153
|
+
const npmCommand = me.getNpmCommand(npmConfig);
|
|
154
|
+
|
|
155
|
+
if (isShellMode) {
|
|
156
|
+
// Check if tmux is available
|
|
157
|
+
if (await tmux.isTmuxAvailable()) {
|
|
158
|
+
const commands = tmux.buildThreePaneShellCommands(
|
|
159
|
+
project.name,
|
|
160
|
+
project.path.path,
|
|
161
|
+
claudeArgs,
|
|
162
|
+
npmCommand
|
|
163
|
+
);
|
|
164
|
+
me.shellCommands.push(...commands);
|
|
165
|
+
} else {
|
|
166
|
+
// Fall back to normal behavior if tmux is not available
|
|
167
|
+
me.log.debug('Tmux not available, falling back to normal mode');
|
|
168
|
+
me.shellCommands.push(`cd "${project.path.path}"`);
|
|
169
|
+
const claudeCommand = claudeArgs.length > 0
|
|
170
|
+
? `claude ${claudeArgs.join(' ')}`
|
|
171
|
+
: 'claude';
|
|
172
|
+
me.shellCommands.push(claudeCommand);
|
|
173
|
+
me.shellCommands.push(npmCommand);
|
|
174
|
+
}
|
|
175
|
+
} else {
|
|
176
|
+
// Direct execution mode
|
|
177
|
+
if (await tmux.isTmuxAvailable()) {
|
|
178
|
+
try {
|
|
179
|
+
const sessionName = await tmux.createThreePaneSession(
|
|
180
|
+
project.name,
|
|
181
|
+
project.path.path,
|
|
182
|
+
claudeArgs,
|
|
183
|
+
npmCommand
|
|
184
|
+
);
|
|
185
|
+
await tmux.attachToSession(sessionName);
|
|
186
|
+
} catch (error) {
|
|
187
|
+
me.log.debug(`Failed to create tmux session: ${error.message}`);
|
|
188
|
+
// Fall back to normal behavior
|
|
189
|
+
me.processEvent('cwd');
|
|
190
|
+
me.processEvent('claude');
|
|
191
|
+
me.processEvent('npm');
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
me.log.debug('Tmux not available, falling back to normal mode');
|
|
195
|
+
// Fall back to normal behavior
|
|
196
|
+
me.processEvent('cwd');
|
|
197
|
+
me.processEvent('claude');
|
|
198
|
+
me.processEvent('npm');
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async handleTwoPaneNpmLayout(project, isShellMode) {
|
|
204
|
+
let me = this;
|
|
205
|
+
const tmux = new TmuxManager();
|
|
206
|
+
const npmConfig = project.events.npm;
|
|
207
|
+
const npmCommand = me.getNpmCommand(npmConfig);
|
|
208
|
+
|
|
209
|
+
if (isShellMode) {
|
|
210
|
+
// Check if tmux is available
|
|
211
|
+
if (await tmux.isTmuxAvailable()) {
|
|
212
|
+
const commands = tmux.buildTwoPaneNpmShellCommands(
|
|
213
|
+
project.name,
|
|
214
|
+
project.path.path,
|
|
215
|
+
npmCommand
|
|
216
|
+
);
|
|
217
|
+
me.shellCommands.push(...commands);
|
|
218
|
+
} else {
|
|
219
|
+
// Fall back to normal behavior if tmux is not available
|
|
220
|
+
me.log.debug('Tmux not available, falling back to normal mode');
|
|
221
|
+
me.shellCommands.push(`cd "${project.path.path}"`);
|
|
222
|
+
me.shellCommands.push(npmCommand);
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
// Direct execution mode
|
|
226
|
+
if (await tmux.isTmuxAvailable()) {
|
|
227
|
+
try {
|
|
228
|
+
const sessionName = await tmux.createTwoPaneNpmSession(
|
|
229
|
+
project.name,
|
|
230
|
+
project.path.path,
|
|
231
|
+
npmCommand
|
|
232
|
+
);
|
|
233
|
+
await tmux.attachToSession(sessionName);
|
|
234
|
+
} catch (error) {
|
|
235
|
+
me.log.debug(`Failed to create tmux session: ${error.message}`);
|
|
236
|
+
// Fall back to normal behavior
|
|
237
|
+
me.processEvent('cwd');
|
|
238
|
+
me.processEvent('npm');
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
me.log.debug('Tmux not available, falling back to normal mode');
|
|
242
|
+
// Fall back to normal behavior
|
|
243
|
+
me.processEvent('cwd');
|
|
244
|
+
me.processEvent('npm');
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
getNpmCommand(npmConfig) {
|
|
250
|
+
if (typeof npmConfig === 'string') {
|
|
251
|
+
return `npm run ${npmConfig}`;
|
|
252
|
+
} else if (npmConfig && typeof npmConfig === 'object' && npmConfig.command) {
|
|
253
|
+
return `npm run ${npmConfig.command}`;
|
|
254
|
+
} else {
|
|
255
|
+
return 'npm run dev';
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
74
259
|
processEvent (event) {
|
|
75
260
|
let me = this;
|
|
76
261
|
let environment = me.root().environment;
|
|
@@ -126,10 +311,36 @@ class open extends command {
|
|
|
126
311
|
}
|
|
127
312
|
break;
|
|
128
313
|
case 'claude':
|
|
314
|
+
let claudeArgs = [];
|
|
315
|
+
let claudeConfig = project.events.claude;
|
|
316
|
+
|
|
317
|
+
// Handle advanced Claude configuration
|
|
318
|
+
if (claudeConfig && typeof claudeConfig === 'object') {
|
|
319
|
+
if (claudeConfig.flags && Array.isArray(claudeConfig.flags)) {
|
|
320
|
+
claudeArgs = claudeArgs.concat(claudeConfig.flags);
|
|
321
|
+
}
|
|
322
|
+
// Additional config options can be handled here in the future
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (isShellMode) {
|
|
326
|
+
let claudeCommand = claudeArgs.length > 0
|
|
327
|
+
? `claude ${claudeArgs.join(' ')}`
|
|
328
|
+
: 'claude';
|
|
329
|
+
me.shellCommands.push(claudeCommand);
|
|
330
|
+
} else {
|
|
331
|
+
spawn('claude', claudeArgs, {
|
|
332
|
+
cwd: environment.project.path.path,
|
|
333
|
+
stdio: 'inherit'
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
break;
|
|
337
|
+
case 'npm':
|
|
338
|
+
let npmCommand = me.getNpmCommand(project.events.npm);
|
|
339
|
+
|
|
129
340
|
if (isShellMode) {
|
|
130
|
-
me.shellCommands.push(
|
|
341
|
+
me.shellCommands.push(npmCommand);
|
|
131
342
|
} else {
|
|
132
|
-
spawn('
|
|
343
|
+
spawn('npm', ['run', npmCommand.replace('npm run ', '')], {
|
|
133
344
|
cwd: environment.project.path.path,
|
|
134
345
|
stdio: 'inherit'
|
|
135
346
|
});
|
package/docs/ideas.md
CHANGED
|
@@ -2,48 +2,92 @@
|
|
|
2
2
|
|
|
3
3
|
This document contains ideas for future enhancements to the workon project.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
### Project Configuration Editor
|
|
8
|
-
Create an interactive mode for editing project configurations through guided prompts instead of manual file editing.
|
|
9
|
-
|
|
10
|
-
**Features:**
|
|
11
|
-
- Interactive project creation wizard with step-by-step guidance
|
|
12
|
-
- Edit existing project properties (name, path, IDE, events) through prompts
|
|
13
|
-
- Validate project paths and IDE commands during configuration
|
|
14
|
-
- Preview configuration changes before saving
|
|
15
|
-
- Bulk operations for managing multiple projects
|
|
16
|
-
|
|
17
|
-
**Implementation considerations:**
|
|
18
|
-
- Extend existing inquirer-based interactive system
|
|
19
|
-
- Add new command like `workon config` or `workon manage --interactive`
|
|
20
|
-
- Provide different flows for:
|
|
21
|
-
- Creating new projects
|
|
22
|
-
- Editing existing projects
|
|
23
|
-
- Bulk project management
|
|
24
|
-
- Include validation for:
|
|
25
|
-
- Directory paths existence
|
|
26
|
-
- IDE command availability
|
|
27
|
-
- Event configuration correctness
|
|
5
|
+
## NPM Command Integration
|
|
28
6
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
7
|
+
### Three-Pane Development Layout
|
|
8
|
+
When `cwd`, `claude`, and `npm` events are enabled, create a three-pane tmux layout:
|
|
9
|
+
- **Left pane**: Claude Code running in project directory (full height)
|
|
10
|
+
- **Top-right pane**: Shell terminal in project directory
|
|
11
|
+
- **Bottom-right pane**: NPM command running (e.g., `npm run dev`, `npm test`)
|
|
12
|
+
|
|
13
|
+
**Implementation approach:**
|
|
14
|
+
- Extend current split terminal to support three panes
|
|
15
|
+
- Create initial vertical split (Claude | Terminal)
|
|
16
|
+
- Split the right terminal pane horizontally (Terminal | npm)
|
|
17
|
+
- Use tmux: `split-window -v` on the right pane
|
|
18
|
+
- Auto-run specified npm command in bottom-right pane
|
|
34
19
|
|
|
35
|
-
|
|
20
|
+
**Configuration:**
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"events": {
|
|
24
|
+
"cwd": "true",
|
|
25
|
+
"claude": {
|
|
26
|
+
"flags": ["--resume"],
|
|
27
|
+
"split_terminal": true
|
|
28
|
+
},
|
|
29
|
+
"npm": "dev"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
36
33
|
|
|
37
|
-
|
|
38
|
-
Extend the claude event with more configuration options:
|
|
34
|
+
**Alternative configuration:**
|
|
39
35
|
```json
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
36
|
+
{
|
|
37
|
+
"events": {
|
|
38
|
+
"cwd": "true",
|
|
39
|
+
"claude": "true",
|
|
40
|
+
"npm": {
|
|
41
|
+
"command": "dev",
|
|
42
|
+
"watch": true,
|
|
43
|
+
"auto_restart": false
|
|
44
|
+
}
|
|
45
|
+
}
|
|
44
46
|
}
|
|
45
47
|
```
|
|
46
48
|
|
|
49
|
+
**Benefits:**
|
|
50
|
+
- Complete development environment in one tmux session
|
|
51
|
+
- Claude AI + Terminal + Development server all visible
|
|
52
|
+
- Perfect for web development workflows
|
|
53
|
+
- Automatic npm script execution
|
|
54
|
+
|
|
55
|
+
**Tmux Layout:**
|
|
56
|
+
```
|
|
57
|
+
┌──────────────┬──────────────┐
|
|
58
|
+
│ │ Terminal │
|
|
59
|
+
│ Claude ├──────────────┤
|
|
60
|
+
│ (full │ npm run dev │
|
|
61
|
+
│ height) │ │
|
|
62
|
+
└──────────────┴──────────────┘
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Two-Pane Terminal + NPM Layout
|
|
66
|
+
When `cwd` and `npm` events are enabled (without Claude), create a two-pane tmux layout:
|
|
67
|
+
- **Left pane**: Shell terminal in project directory
|
|
68
|
+
- **Right pane**: NPM command running (e.g., `npm run dev`, `npm test`)
|
|
69
|
+
|
|
70
|
+
**Tmux Layout:**
|
|
71
|
+
```
|
|
72
|
+
┌──────────────┬──────────────┐
|
|
73
|
+
│ │ │
|
|
74
|
+
│ Terminal │ npm run dev │
|
|
75
|
+
│ │ │
|
|
76
|
+
│ │ │
|
|
77
|
+
└──────────────┴──────────────┘
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Use cases:**
|
|
81
|
+
- Traditional development workflow without AI assistance
|
|
82
|
+
- Monitoring build output while running commands
|
|
83
|
+
- Side-by-side terminal and dev server
|
|
84
|
+
|
|
47
85
|
## Future Ideas
|
|
48
86
|
|
|
87
|
+
### Auto-enable Split Terminal
|
|
88
|
+
When both `cwd` and `claude` events are enabled, automatically enable split terminal mode without requiring explicit configuration.
|
|
89
|
+
|
|
90
|
+
### Project Templates
|
|
91
|
+
Pre-configured project templates for common development stacks (React, Node.js, Python, etc.) with appropriate events and npm commands.
|
|
92
|
+
|
|
49
93
|
*Add more ideas here as they come up...*
|