workon 2.1.3 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +19 -4
  2. package/bin/workon +1 -11
  3. package/dist/cli.js +2364 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/index.cjs +1216 -0
  6. package/dist/index.cjs.map +1 -0
  7. package/dist/index.d.cts +280 -0
  8. package/dist/index.d.ts +280 -0
  9. package/dist/index.js +1173 -0
  10. package/dist/index.js.map +1 -0
  11. package/package.json +68 -21
  12. package/.claude/settings.local.json +0 -11
  13. package/.cursorindexingignore +0 -3
  14. package/.history/.gitignore_20250806202718 +0 -30
  15. package/.history/.gitignore_20250806231444 +0 -32
  16. package/.history/.gitignore_20250806231450 +0 -32
  17. package/.history/lib/tmux_20250806233103.js +0 -109
  18. package/.history/lib/tmux_20250806233219.js +0 -109
  19. package/.history/lib/tmux_20250806233223.js +0 -109
  20. package/.history/lib/tmux_20250806233230.js +0 -109
  21. package/.history/lib/tmux_20250806233231.js +0 -109
  22. package/.history/lib/tmux_20250807120751.js +0 -190
  23. package/.history/lib/tmux_20250807120757.js +0 -190
  24. package/.history/lib/tmux_20250807120802.js +0 -190
  25. package/.history/lib/tmux_20250807120808.js +0 -190
  26. package/.history/package_20250807114243.json +0 -43
  27. package/.history/package_20250807114257.json +0 -43
  28. package/.history/package_20250807114404.json +0 -43
  29. package/.history/package_20250807114409.json +0 -43
  30. package/.history/package_20250807114510.json +0 -43
  31. package/.history/package_20250807114637.json +0 -43
  32. package/.vscode/launch.json +0 -20
  33. package/.vscode/terminals.json +0 -11
  34. package/CHANGELOG.md +0 -110
  35. package/CLAUDE.md +0 -100
  36. package/cli/base.js +0 -16
  37. package/cli/config/index.js +0 -19
  38. package/cli/config/list.js +0 -26
  39. package/cli/config/set.js +0 -19
  40. package/cli/config/unset.js +0 -26
  41. package/cli/index.js +0 -184
  42. package/cli/interactive.js +0 -290
  43. package/cli/manage.js +0 -413
  44. package/cli/open.js +0 -414
  45. package/commands/base.js +0 -105
  46. package/commands/core/cwd/index.js +0 -86
  47. package/commands/core/ide/index.js +0 -84
  48. package/commands/core/web/index.js +0 -109
  49. package/commands/extensions/claude/index.js +0 -211
  50. package/commands/extensions/docker/index.js +0 -167
  51. package/commands/extensions/npm/index.js +0 -208
  52. package/commands/registry.js +0 -196
  53. package/demo-colon-syntax.js +0 -57
  54. package/docs/adr/001-command-centric-architecture.md +0 -304
  55. package/docs/adr/002-positional-command-arguments.md +0 -402
  56. package/docs/ideas.md +0 -93
  57. package/lib/config.js +0 -51
  58. package/lib/environment/base.js +0 -12
  59. package/lib/environment/index.js +0 -108
  60. package/lib/environment/project.js +0 -26
  61. package/lib/project.js +0 -68
  62. package/lib/tmux.js +0 -223
  63. package/lib/validation.js +0 -120
  64. package/test-architecture.js +0 -145
  65. package/test-colon-syntax.js +0 -85
  66. package/test-registry.js +0 -57
package/cli/open.js DELETED
@@ -1,414 +0,0 @@
1
- const { command } = require('./base');
2
- const Project = require('../lib/project');
3
- const { ProjectEnvironment } = require('../lib/environment');
4
- const spawn = require('child_process').spawn;
5
- const File = require('phylo');
6
- const TmuxManager = require('../lib/tmux');
7
- const registry = require('../commands/registry');
8
-
9
- class open extends command {
10
- async execute (params) {
11
- let me = this;
12
-
13
- // Initialize command registry
14
- await registry.initialize();
15
-
16
- if (params.project) {
17
- return me.processProject(params.project);
18
- } else {
19
- me.log.debug(`No project name provided, starting interactive mode`);
20
- return me.startInteractiveMode();
21
- }
22
- }
23
-
24
- async processProject (projectParam) {
25
- let me = this;
26
- let environment = me.root().environment;
27
-
28
- // Parse colon syntax: project:command1,command2
29
- const [projectName, commandsString] = projectParam.split(':');
30
- const requestedCommands = commandsString ? commandsString.split(',').map(cmd => cmd.trim()) : null;
31
-
32
- // Special case: project:help shows available commands for that project
33
- if (commandsString === 'help') {
34
- return me.showProjectHelp(projectName);
35
- }
36
-
37
- me.log.debug(`Project: ${projectName}, Commands: ${requestedCommands ? requestedCommands.join(', ') : 'all'}`);
38
-
39
- let projects = me.config.get('projects');
40
- if (!projects) {
41
- me.config.set('projects', {});
42
- } else {
43
- if (environment.$isProjectEnvironment && (projectName === 'this' || projectName === '.')) {
44
- me.log.info(`Open current: ${environment.project.name}`);
45
- } else {
46
- if (projectName in projects) {
47
- let cfg = projects[projectName];
48
- cfg.name = projectName;
49
-
50
- // Validate requested commands if specified
51
- if (requestedCommands) {
52
- me.validateRequestedCommands(requestedCommands, cfg, projectName);
53
- }
54
-
55
- const projectEnv = ProjectEnvironment.load(cfg, me.config.get('project_defaults'));
56
- await me.switchTo(projectEnv, requestedCommands);
57
- } else {
58
- me.log.debug(`Project '${projectName}' not found, starting interactive mode`);
59
- return me.startInteractiveMode(projectName);
60
- }
61
- }
62
- }
63
- }
64
-
65
- startInteractiveMode (project) {
66
- let me = this;
67
- let root = me.root();
68
-
69
- let interactiveCmd = root.commands.lookup('interactive').create(root);
70
- return interactiveCmd.dispatch(new me.args.constructor([project]))
71
- }
72
-
73
- validateRequestedCommands(requestedCommands, projectConfig, projectName) {
74
- const configuredEvents = Object.keys(projectConfig.events || {});
75
- const invalidCommands = requestedCommands.filter(cmd => !configuredEvents.includes(cmd));
76
-
77
- if (invalidCommands.length > 0) {
78
- const availableCommands = configuredEvents.join(', ');
79
- throw new Error(
80
- `Commands not configured for project '${projectName}': ${invalidCommands.join(', ')}\n` +
81
- `Available commands: ${availableCommands}`
82
- );
83
- }
84
- }
85
-
86
- async switchTo (environment, requestedCommands = null) {
87
- let me = this;
88
- me.root().environment = environment;
89
- let project = environment.project;
90
-
91
- // Determine which events to execute
92
- let events;
93
- if (requestedCommands) {
94
- // Use requested commands (already validated)
95
- events = me.resolveCommandDependencies(requestedCommands, project);
96
- me.log.debug(`Executing requested commands: ${events.join(', ')}`);
97
- } else {
98
- // Execute all configured events (current behavior)
99
- events = Object.keys(project.events).filter((e) => project.events[e]);
100
- me.log.debug(`Executing all configured commands: ${events.join(', ')}`);
101
- }
102
-
103
- me.log.debug(`Shell is ${process.env.SHELL}`);
104
- me.log.debug(`Project path is ${project.path.path}`);
105
- me.log.debug(`IDE command is: ${project.ide}`);
106
- me.log.debug(`Final events to execute: ${events.join(', ')}`);
107
-
108
- // Initialize shell commands collector if in shell mode
109
- let isShellMode = me.params.shell || me.root().params.shell;
110
- if (isShellMode) {
111
- me.shellCommands = [];
112
- }
113
-
114
- // Intelligent layout detection based on actual events being executed
115
- const hasCwd = events.includes('cwd');
116
- const hasClaudeEvent = events.includes('claude');
117
- const hasNpmEvent = events.includes('npm');
118
-
119
- if (hasCwd && hasClaudeEvent && hasNpmEvent) {
120
- // Three-pane layout: Claude + Terminal + NPM
121
- await me.handleThreePaneLayout(project, isShellMode);
122
- // Process other events except cwd, claude, and npm
123
- for (const event of events.filter(e => !['cwd', 'claude', 'npm'].includes(e))) {
124
- await me.processEvent(event);
125
- }
126
- } else if (hasCwd && hasNpmEvent) {
127
- // Two-pane layout: Terminal + NPM (no Claude)
128
- await me.handleTwoPaneNpmLayout(project, isShellMode);
129
- // Process other events except cwd and npm
130
- for (const event of events.filter(e => !['cwd', 'npm'].includes(e))) {
131
- await me.processEvent(event);
132
- }
133
- } else if (hasCwd && hasClaudeEvent) {
134
- // Two-pane layout: Claude + Terminal (existing split terminal)
135
- await me.handleSplitTerminal(project, isShellMode);
136
- // Process other events except cwd and claude
137
- for (const event of events.filter(e => !['cwd', 'claude'].includes(e))) {
138
- await me.processEvent(event);
139
- }
140
- } else {
141
- // Normal event processing - execute commands individually
142
- for (const event of events) {
143
- await me.processEvent(event);
144
- }
145
- }
146
-
147
- // Output collected shell commands if in shell mode
148
- if (isShellMode && me.shellCommands.length > 0) {
149
- console.log(me.shellCommands.join('\n'));
150
- }
151
- }
152
-
153
- showProjectHelp(projectName) {
154
- let me = this;
155
- let projects = me.config.get('projects');
156
-
157
- if (!projects || !(projectName in projects)) {
158
- me.log.error(`Project '${projectName}' not found`);
159
- return;
160
- }
161
-
162
- const projectConfig = projects[projectName];
163
- const configuredEvents = Object.keys(projectConfig.events || {});
164
-
165
- console.log(`\n📋 Available commands for '${projectName}':`);
166
- console.log('─'.repeat(50));
167
-
168
- for (const eventName of configuredEvents) {
169
- const command = registry.getCommandByName(eventName);
170
- if (command && command.metadata) {
171
- const config = projectConfig.events[eventName];
172
- let configDesc = '';
173
- if (config !== true && config !== 'true') {
174
- if (typeof config === 'object') {
175
- configDesc = ` (${JSON.stringify(config)})`;
176
- } else {
177
- configDesc = ` (${config})`;
178
- }
179
- }
180
- console.log(` ${eventName.padEnd(8)} - ${command.metadata.description}${configDesc}`);
181
- }
182
- }
183
-
184
- console.log('\n💡 Usage examples:');
185
- console.log(` workon ${projectName} # Execute all commands`);
186
- console.log(` workon ${projectName}:cwd # Just change directory`);
187
- console.log(` workon ${projectName}:claude # Just Claude (auto-adds cwd)`);
188
-
189
- if (configuredEvents.length > 1) {
190
- const twoCommands = configuredEvents.slice(0, 2).join(',');
191
- console.log(` workon ${projectName}:${twoCommands.padEnd(12)} # Multiple commands`);
192
- }
193
-
194
- console.log(` workon ${projectName}:cwd --shell # Output shell commands\n`);
195
- }
196
-
197
- resolveCommandDependencies(requestedCommands, project) {
198
- const resolved = [...requestedCommands];
199
-
200
- // Auto-add cwd dependency for commands that need it
201
- const needsCwd = ['claude', 'npm', 'ide'];
202
- const needsCwdCommands = requestedCommands.filter(cmd => needsCwd.includes(cmd));
203
-
204
- if (needsCwdCommands.length > 0 && !requestedCommands.includes('cwd')) {
205
- resolved.unshift('cwd'); // Add cwd at the beginning
206
- this.log.debug(`Auto-added 'cwd' dependency for commands: ${needsCwdCommands.join(', ')}`);
207
- }
208
-
209
- // Remove duplicates while preserving order
210
- return [...new Set(resolved)];
211
- }
212
-
213
- async handleSplitTerminal(project, isShellMode) {
214
- let me = this;
215
- const tmux = new TmuxManager();
216
- const claudeConfig = project.events.claude;
217
- const claudeArgs = (claudeConfig && claudeConfig.flags) ? claudeConfig.flags : [];
218
-
219
- if (isShellMode) {
220
- // Check if tmux is available
221
- if (await tmux.isTmuxAvailable()) {
222
- const commands = tmux.buildShellCommands(
223
- project.name,
224
- project.path.path,
225
- claudeArgs
226
- );
227
- me.shellCommands.push(...commands);
228
- } else {
229
- // Fall back to normal behavior if tmux is not available
230
- me.log.debug('Tmux not available, falling back to normal mode');
231
- me.shellCommands.push(`cd "${project.path.path}"`);
232
- const claudeCommand = claudeArgs.length > 0
233
- ? `claude ${claudeArgs.join(' ')}`
234
- : 'claude';
235
- me.shellCommands.push(claudeCommand);
236
- }
237
- } else {
238
- // Direct execution mode
239
- if (await tmux.isTmuxAvailable()) {
240
- try {
241
- const sessionName = await tmux.createSplitSession(
242
- project.name,
243
- project.path.path,
244
- claudeArgs
245
- );
246
- await tmux.attachToSession(sessionName);
247
- } catch (error) {
248
- me.log.debug(`Failed to create tmux session: ${error.message}`);
249
- // Fall back to normal behavior
250
- await me.processEvent('cwd');
251
- await me.processEvent('claude');
252
- }
253
- } else {
254
- me.log.debug('Tmux not available, falling back to normal mode');
255
- // Fall back to normal behavior
256
- await me.processEvent('cwd');
257
- await me.processEvent('claude');
258
- }
259
- }
260
- }
261
-
262
- async handleThreePaneLayout(project, isShellMode) {
263
- let me = this;
264
- const tmux = new TmuxManager();
265
- const claudeConfig = project.events.claude;
266
- const claudeArgs = (claudeConfig && claudeConfig.flags) ? claudeConfig.flags : [];
267
- const npmConfig = project.events.npm;
268
- const NpmCommand = registry.getCommandByName('npm');
269
- const npmCommand = NpmCommand ? NpmCommand._getNpmCommand(npmConfig) : 'npm run dev';
270
-
271
- if (isShellMode) {
272
- // Check if tmux is available
273
- if (await tmux.isTmuxAvailable()) {
274
- const commands = tmux.buildThreePaneShellCommands(
275
- project.name,
276
- project.path.path,
277
- claudeArgs,
278
- npmCommand
279
- );
280
- me.shellCommands.push(...commands);
281
- } else {
282
- // Fall back to normal behavior if tmux is not available
283
- me.log.debug('Tmux not available, falling back to normal mode');
284
- me.shellCommands.push(`cd "${project.path.path}"`);
285
- const claudeCommand = claudeArgs.length > 0
286
- ? `claude ${claudeArgs.join(' ')}`
287
- : 'claude';
288
- me.shellCommands.push(claudeCommand);
289
- me.shellCommands.push(npmCommand);
290
- }
291
- } else {
292
- // Direct execution mode
293
- if (await tmux.isTmuxAvailable()) {
294
- try {
295
- const sessionName = await tmux.createThreePaneSession(
296
- project.name,
297
- project.path.path,
298
- claudeArgs,
299
- npmCommand
300
- );
301
- await tmux.attachToSession(sessionName);
302
- } catch (error) {
303
- me.log.debug(`Failed to create tmux session: ${error.message}`);
304
- // Fall back to normal behavior
305
- await me.processEvent('cwd');
306
- await me.processEvent('claude');
307
- await me.processEvent('npm');
308
- }
309
- } else {
310
- me.log.debug('Tmux not available, falling back to normal mode');
311
- // Fall back to normal behavior
312
- await me.processEvent('cwd');
313
- await me.processEvent('claude');
314
- await me.processEvent('npm');
315
- }
316
- }
317
- }
318
-
319
- async handleTwoPaneNpmLayout(project, isShellMode) {
320
- let me = this;
321
- const tmux = new TmuxManager();
322
- const npmConfig = project.events.npm;
323
- const NpmCommand = registry.getCommandByName('npm');
324
- const npmCommand = NpmCommand ? NpmCommand._getNpmCommand(npmConfig) : 'npm run dev';
325
-
326
- if (isShellMode) {
327
- // Check if tmux is available
328
- if (await tmux.isTmuxAvailable()) {
329
- const commands = tmux.buildTwoPaneNpmShellCommands(
330
- project.name,
331
- project.path.path,
332
- npmCommand
333
- );
334
- me.shellCommands.push(...commands);
335
- } else {
336
- // Fall back to normal behavior if tmux is not available
337
- me.log.debug('Tmux not available, falling back to normal mode');
338
- me.shellCommands.push(`cd "${project.path.path}"`);
339
- me.shellCommands.push(npmCommand);
340
- }
341
- } else {
342
- // Direct execution mode
343
- if (await tmux.isTmuxAvailable()) {
344
- try {
345
- const sessionName = await tmux.createTwoPaneNpmSession(
346
- project.name,
347
- project.path.path,
348
- npmCommand
349
- );
350
- await tmux.attachToSession(sessionName);
351
- } catch (error) {
352
- me.log.debug(`Failed to create tmux session: ${error.message}`);
353
- // Fall back to normal behavior
354
- await me.processEvent('cwd');
355
- await me.processEvent('npm');
356
- }
357
- } else {
358
- me.log.debug('Tmux not available, falling back to normal mode');
359
- // Fall back to normal behavior
360
- await me.processEvent('cwd');
361
- await me.processEvent('npm');
362
- }
363
- }
364
- }
365
-
366
-
367
- async processEvent (event) {
368
- let me = this;
369
- let environment = me.root().environment;
370
- let project = environment.project;
371
- let scripts = project.scripts || {};
372
- let capitalEvt = `${event[0].toUpperCase()}${event.substring(1)}`;
373
-
374
- me.log.debug(`Processing event ${event}`);
375
- if (`before${capitalEvt}` in scripts) {
376
- me.log.debug(`Found 'before' script, unfortunately scripts are not yet supported.`);
377
- }
378
- if (event in scripts) {
379
- me.log.debug(`Found script with event name, unfortunately scripts are not yet supported.`);
380
- }
381
- if (!me.params['dry-run']) {
382
- // Check if we're in shell mode
383
- let isShellMode = me.params.shell || me.root().params.shell;
384
- me.log.debug(`Shell mode is: ${isShellMode}`);
385
-
386
- // Use CommandRegistry to process the event
387
- const command = registry.getCommandByName(event);
388
- if (command && command.processing) {
389
- await command.processing.processEvent({
390
- project,
391
- isShellMode,
392
- shellCommands: me.shellCommands || []
393
- });
394
- } else {
395
- me.log.debug(`No command handler found for event: ${event}`);
396
- }
397
- }
398
- if (`after${capitalEvt}` in scripts) {
399
- me.log.debug(`Found 'after' script, unfortunately scripts are not yet supported.`);
400
- }
401
- }
402
- }
403
-
404
- open.define({
405
- help: {
406
- '': 'open a project by passing its project id',
407
- project: 'The id of the project to open',
408
- shell: 'Output shell commands instead of spawning processes'
409
- },
410
- switches: '[d#debug:boolean=false] [n#dry-run:boolean=false] [shell:boolean=false]',
411
- parameters: '[p#project:string]'
412
- });
413
-
414
- module.exports = open;
package/commands/base.js DELETED
@@ -1,105 +0,0 @@
1
- /**
2
- * Base command interface that all commands must implement
3
- * Provides standardized structure for command-centric architecture
4
- */
5
- class BaseCommand {
6
- /**
7
- * Command metadata - must be implemented by each command
8
- * @returns {Object} metadata object with name, displayName, description, etc.
9
- */
10
- static get metadata() {
11
- throw new Error('Command must implement static metadata getter');
12
- }
13
-
14
- /**
15
- * Validation rules for command configuration
16
- * @returns {Object} validation methods
17
- */
18
- static get validation() {
19
- return {
20
- /**
21
- * Validate command-specific configuration
22
- * @param {*} config - Configuration to validate
23
- * @returns {true|string} true if valid, error message if invalid
24
- */
25
- validateConfig(config) {
26
- return true; // Default: accept any config
27
- }
28
- };
29
- }
30
-
31
- /**
32
- * Interactive configuration setup
33
- * @returns {Object} configuration methods
34
- */
35
- static get configuration() {
36
- return {
37
- /**
38
- * Interactive setup prompts for the command
39
- * @returns {*} Configuration object or primitive value
40
- */
41
- async configureInteractive() {
42
- return 'true'; // Default: simple boolean enable
43
- },
44
-
45
- /**
46
- * Get default configuration for the command
47
- * @returns {*} Default configuration
48
- */
49
- getDefaultConfig() {
50
- return 'true';
51
- }
52
- };
53
- }
54
-
55
- /**
56
- * Event processing logic
57
- * @returns {Object} processing methods
58
- */
59
- static get processing() {
60
- return {
61
- /**
62
- * Process the command event
63
- * @param {Object} context - Processing context
64
- * @param {Object} context.project - Project configuration
65
- * @param {boolean} context.isShellMode - Whether in shell mode
66
- * @param {string[]} context.shellCommands - Array to collect shell commands
67
- * @returns {Promise<void>}
68
- */
69
- async processEvent(context) {
70
- throw new Error('Command must implement processEvent method');
71
- },
72
-
73
- /**
74
- * Generate shell command for the event
75
- * @param {Object} context - Processing context
76
- * @returns {string[]} Array of shell commands
77
- */
78
- generateShellCommand(context) {
79
- return [];
80
- }
81
- };
82
- }
83
-
84
- /**
85
- * Tmux integration (optional)
86
- * @returns {Object|null} tmux methods or null if not supported
87
- */
88
- static get tmux() {
89
- return null; // Default: no tmux integration
90
- }
91
-
92
- /**
93
- * Help and documentation
94
- * @returns {Object} help information
95
- */
96
- static get help() {
97
- return {
98
- usage: `${this.metadata.name}: <configuration>`,
99
- description: this.metadata.description,
100
- examples: []
101
- };
102
- }
103
- }
104
-
105
- module.exports = BaseCommand;
@@ -1,86 +0,0 @@
1
- const BaseCommand = require('../../base');
2
- const spawn = require('child_process').spawn;
3
-
4
- /**
5
- * CWD (Change Working Directory) Command
6
- * Changes the current working directory to the project path
7
- */
8
- class CwdCommand extends BaseCommand {
9
- static get metadata() {
10
- return {
11
- name: 'cwd',
12
- displayName: 'Change directory (cwd)',
13
- description: 'Change current working directory to project path',
14
- category: 'core',
15
- requiresTmux: false,
16
- dependencies: []
17
- };
18
- }
19
-
20
- static get validation() {
21
- return {
22
- validateConfig(config) {
23
- // CWD command accepts boolean or string 'true'
24
- if (config === true || config === 'true' || config === false || config === 'false') {
25
- return true;
26
- }
27
- return 'CWD configuration must be a boolean or string "true"/"false"';
28
- }
29
- };
30
- }
31
-
32
- static get configuration() {
33
- return {
34
- async configureInteractive() {
35
- // CWD is typically just enabled/disabled, no advanced config needed
36
- return 'true';
37
- },
38
-
39
- getDefaultConfig() {
40
- return 'true';
41
- }
42
- };
43
- }
44
-
45
- static get processing() {
46
- return {
47
- async processEvent(context) {
48
- const { project, isShellMode, shellCommands } = context;
49
-
50
- if (isShellMode) {
51
- shellCommands.push(`cd "${project.path.path}"`);
52
- } else {
53
- // In non-shell mode, spawn a new shell in the project directory
54
- spawn(process.env.SHELL, ['-i'], {
55
- cwd: project.path.path,
56
- stdio: 'inherit'
57
- });
58
- }
59
- },
60
-
61
- generateShellCommand(context) {
62
- const { project } = context;
63
- return [`cd "${project.path.path}"`];
64
- }
65
- };
66
- }
67
-
68
- static get help() {
69
- return {
70
- usage: 'cwd: true',
71
- description: 'Changes the current working directory to the project path',
72
- examples: [
73
- {
74
- config: 'cwd: true',
75
- description: 'Enable directory change when opening project'
76
- },
77
- {
78
- config: 'cwd: false',
79
- description: 'Disable directory change (stay in current directory)'
80
- }
81
- ]
82
- };
83
- }
84
- }
85
-
86
- module.exports = CwdCommand;
@@ -1,84 +0,0 @@
1
- const BaseCommand = require('../../base');
2
- const spawn = require('child_process').spawn;
3
-
4
- /**
5
- * IDE Command
6
- * Opens the project in the configured IDE/editor
7
- */
8
- class IdeCommand extends BaseCommand {
9
- static get metadata() {
10
- return {
11
- name: 'ide',
12
- displayName: 'Open in IDE',
13
- description: 'Open project in configured IDE/editor',
14
- category: 'core',
15
- requiresTmux: false,
16
- dependencies: []
17
- };
18
- }
19
-
20
- static get validation() {
21
- return {
22
- validateConfig(config) {
23
- // IDE command accepts boolean or string 'true'
24
- if (config === true || config === 'true' || config === false || config === 'false') {
25
- return true;
26
- }
27
- return 'IDE configuration must be a boolean or string "true"/"false"';
28
- }
29
- };
30
- }
31
-
32
- static get configuration() {
33
- return {
34
- async configureInteractive() {
35
- // IDE configuration is handled at the project level (project.ide)
36
- // This event just enables/disables opening the IDE
37
- return 'true';
38
- },
39
-
40
- getDefaultConfig() {
41
- return 'true';
42
- }
43
- };
44
- }
45
-
46
- static get processing() {
47
- return {
48
- async processEvent(context) {
49
- const { project, isShellMode, shellCommands } = context;
50
-
51
- if (isShellMode) {
52
- shellCommands.push(`${project.ide} "${project.path.path}" &`);
53
- } else {
54
- // In non-shell mode, spawn the IDE directly
55
- spawn(project.ide, [project.path.path]);
56
- }
57
- },
58
-
59
- generateShellCommand(context) {
60
- const { project } = context;
61
- return [`${project.ide} "${project.path.path}" &`];
62
- }
63
- };
64
- }
65
-
66
- static get help() {
67
- return {
68
- usage: 'ide: true',
69
- description: 'Opens the project in the configured IDE/editor',
70
- examples: [
71
- {
72
- config: 'ide: true',
73
- description: 'Enable opening project in IDE when switching to project'
74
- },
75
- {
76
- config: 'ide: false',
77
- description: 'Disable automatic IDE opening'
78
- }
79
- ]
80
- };
81
- }
82
- }
83
-
84
- module.exports = IdeCommand;