workon 1.4.0 → 2.0.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/package_20250807114243.json +43 -0
- package/.history/package_20250807114257.json +43 -0
- package/.history/package_20250807114404.json +43 -0
- package/.history/package_20250807114409.json +43 -0
- package/.history/package_20250807114510.json +43 -0
- package/.history/package_20250807114637.json +43 -0
- package/CHANGELOG.md +15 -1
- package/cli/manage.js +29 -166
- package/cli/open.js +48 -103
- package/commands/base.js +105 -0
- package/commands/core/cwd/index.js +86 -0
- package/commands/core/ide/index.js +84 -0
- package/commands/core/web/index.js +109 -0
- package/commands/extensions/claude/index.js +211 -0
- package/commands/extensions/docker/index.js +167 -0
- package/commands/extensions/npm/index.js +208 -0
- package/commands/registry.js +196 -0
- package/docs/adr/001-command-centric-architecture.md +304 -0
- package/docs/adr/002-positional-command-arguments.md +396 -0
- package/lib/validation.js +14 -68
- package/package.json +2 -2
- package/test-architecture.js +145 -0
- package/test-registry.js +57 -0
package/cli/open.js
CHANGED
|
@@ -4,10 +4,15 @@ const { ProjectEnvironment } = require('../lib/environment');
|
|
|
4
4
|
const spawn = require('child_process').spawn;
|
|
5
5
|
const File = require('phylo');
|
|
6
6
|
const TmuxManager = require('../lib/tmux');
|
|
7
|
+
const registry = require('../commands/registry');
|
|
7
8
|
|
|
8
9
|
class open extends command {
|
|
9
|
-
execute (params) {
|
|
10
|
+
async execute (params) {
|
|
10
11
|
let me = this;
|
|
12
|
+
|
|
13
|
+
// Initialize command registry
|
|
14
|
+
await registry.initialize();
|
|
15
|
+
|
|
11
16
|
if (params.project) {
|
|
12
17
|
return me.processProject(params.project);
|
|
13
18
|
} else {
|
|
@@ -73,20 +78,28 @@ class open extends command {
|
|
|
73
78
|
// Three-pane layout: Claude + Terminal + NPM
|
|
74
79
|
await me.handleThreePaneLayout(project, isShellMode);
|
|
75
80
|
// Process other events except cwd, claude, and npm
|
|
76
|
-
events.filter(e => !['cwd', 'claude', 'npm'].includes(e))
|
|
81
|
+
for (const event of events.filter(e => !['cwd', 'claude', 'npm'].includes(e))) {
|
|
82
|
+
await me.processEvent(event);
|
|
83
|
+
}
|
|
77
84
|
} else if (hasCwd && hasNpmEvent) {
|
|
78
85
|
// Two-pane layout: Terminal + NPM (no Claude)
|
|
79
86
|
await me.handleTwoPaneNpmLayout(project, isShellMode);
|
|
80
87
|
// Process other events except cwd and npm
|
|
81
|
-
events.filter(e => !['cwd', 'npm'].includes(e))
|
|
88
|
+
for (const event of events.filter(e => !['cwd', 'npm'].includes(e))) {
|
|
89
|
+
await me.processEvent(event);
|
|
90
|
+
}
|
|
82
91
|
} else if (hasCwd && hasClaudeEvent) {
|
|
83
92
|
// Two-pane layout: Claude + Terminal (existing split terminal)
|
|
84
93
|
await me.handleSplitTerminal(project, isShellMode);
|
|
85
94
|
// Process other events except cwd and claude
|
|
86
|
-
events.filter(e => !['cwd', 'claude'].includes(e))
|
|
95
|
+
for (const event of events.filter(e => !['cwd', 'claude'].includes(e))) {
|
|
96
|
+
await me.processEvent(event);
|
|
97
|
+
}
|
|
87
98
|
} else {
|
|
88
99
|
// Normal event processing
|
|
89
|
-
events
|
|
100
|
+
for (const event of events) {
|
|
101
|
+
await me.processEvent(event);
|
|
102
|
+
}
|
|
90
103
|
}
|
|
91
104
|
|
|
92
105
|
// Output collected shell commands if in shell mode
|
|
@@ -132,14 +145,14 @@ class open extends command {
|
|
|
132
145
|
} catch (error) {
|
|
133
146
|
me.log.debug(`Failed to create tmux session: ${error.message}`);
|
|
134
147
|
// Fall back to normal behavior
|
|
135
|
-
me.processEvent('cwd');
|
|
136
|
-
me.processEvent('claude');
|
|
148
|
+
await me.processEvent('cwd');
|
|
149
|
+
await me.processEvent('claude');
|
|
137
150
|
}
|
|
138
151
|
} else {
|
|
139
152
|
me.log.debug('Tmux not available, falling back to normal mode');
|
|
140
153
|
// Fall back to normal behavior
|
|
141
|
-
me.processEvent('cwd');
|
|
142
|
-
me.processEvent('claude');
|
|
154
|
+
await me.processEvent('cwd');
|
|
155
|
+
await me.processEvent('claude');
|
|
143
156
|
}
|
|
144
157
|
}
|
|
145
158
|
}
|
|
@@ -150,7 +163,8 @@ class open extends command {
|
|
|
150
163
|
const claudeConfig = project.events.claude;
|
|
151
164
|
const claudeArgs = (claudeConfig && claudeConfig.flags) ? claudeConfig.flags : [];
|
|
152
165
|
const npmConfig = project.events.npm;
|
|
153
|
-
const
|
|
166
|
+
const NpmCommand = registry.getCommandByName('npm');
|
|
167
|
+
const npmCommand = NpmCommand ? NpmCommand._getNpmCommand(npmConfig) : 'npm run dev';
|
|
154
168
|
|
|
155
169
|
if (isShellMode) {
|
|
156
170
|
// Check if tmux is available
|
|
@@ -186,16 +200,16 @@ class open extends command {
|
|
|
186
200
|
} catch (error) {
|
|
187
201
|
me.log.debug(`Failed to create tmux session: ${error.message}`);
|
|
188
202
|
// Fall back to normal behavior
|
|
189
|
-
me.processEvent('cwd');
|
|
190
|
-
me.processEvent('claude');
|
|
191
|
-
me.processEvent('npm');
|
|
203
|
+
await me.processEvent('cwd');
|
|
204
|
+
await me.processEvent('claude');
|
|
205
|
+
await me.processEvent('npm');
|
|
192
206
|
}
|
|
193
207
|
} else {
|
|
194
208
|
me.log.debug('Tmux not available, falling back to normal mode');
|
|
195
209
|
// Fall back to normal behavior
|
|
196
|
-
me.processEvent('cwd');
|
|
197
|
-
me.processEvent('claude');
|
|
198
|
-
me.processEvent('npm');
|
|
210
|
+
await me.processEvent('cwd');
|
|
211
|
+
await me.processEvent('claude');
|
|
212
|
+
await me.processEvent('npm');
|
|
199
213
|
}
|
|
200
214
|
}
|
|
201
215
|
}
|
|
@@ -204,7 +218,8 @@ class open extends command {
|
|
|
204
218
|
let me = this;
|
|
205
219
|
const tmux = new TmuxManager();
|
|
206
220
|
const npmConfig = project.events.npm;
|
|
207
|
-
const
|
|
221
|
+
const NpmCommand = registry.getCommandByName('npm');
|
|
222
|
+
const npmCommand = NpmCommand ? NpmCommand._getNpmCommand(npmConfig) : 'npm run dev';
|
|
208
223
|
|
|
209
224
|
if (isShellMode) {
|
|
210
225
|
// Check if tmux is available
|
|
@@ -234,34 +249,24 @@ class open extends command {
|
|
|
234
249
|
} catch (error) {
|
|
235
250
|
me.log.debug(`Failed to create tmux session: ${error.message}`);
|
|
236
251
|
// Fall back to normal behavior
|
|
237
|
-
me.processEvent('cwd');
|
|
238
|
-
me.processEvent('npm');
|
|
252
|
+
await me.processEvent('cwd');
|
|
253
|
+
await me.processEvent('npm');
|
|
239
254
|
}
|
|
240
255
|
} else {
|
|
241
256
|
me.log.debug('Tmux not available, falling back to normal mode');
|
|
242
257
|
// Fall back to normal behavior
|
|
243
|
-
me.processEvent('cwd');
|
|
244
|
-
me.processEvent('npm');
|
|
258
|
+
await me.processEvent('cwd');
|
|
259
|
+
await me.processEvent('npm');
|
|
245
260
|
}
|
|
246
261
|
}
|
|
247
262
|
}
|
|
248
263
|
|
|
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
264
|
|
|
259
|
-
processEvent (event) {
|
|
265
|
+
async processEvent (event) {
|
|
260
266
|
let me = this;
|
|
261
267
|
let environment = me.root().environment;
|
|
262
268
|
let project = environment.project;
|
|
263
269
|
let scripts = project.scripts || {};
|
|
264
|
-
let homepage = project.homepage;
|
|
265
270
|
let capitalEvt = `${event[0].toUpperCase()}${event.substring(1)}`;
|
|
266
271
|
|
|
267
272
|
me.log.debug(`Processing event ${event}`);
|
|
@@ -276,79 +281,19 @@ class open extends command {
|
|
|
276
281
|
let isShellMode = me.params.shell || me.root().params.shell;
|
|
277
282
|
me.log.debug(`Shell mode is: ${isShellMode}`);
|
|
278
283
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
me.shellCommands.push(`cd "${environment.project.path.path}"`);
|
|
290
|
-
} else {
|
|
291
|
-
spawn(process.env.SHELL, ['-i'], {
|
|
292
|
-
cwd: environment.project.path.path,
|
|
293
|
-
stdio: 'inherit'
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
break;
|
|
297
|
-
case 'web':
|
|
298
|
-
if (homepage) {
|
|
299
|
-
if (isShellMode) {
|
|
300
|
-
// Different approaches based on OS
|
|
301
|
-
let openCmd;
|
|
302
|
-
switch (process.platform) {
|
|
303
|
-
case 'darwin': openCmd = 'open'; break;
|
|
304
|
-
case 'win32': openCmd = 'start'; break;
|
|
305
|
-
default: openCmd = 'xdg-open'; break;
|
|
306
|
-
}
|
|
307
|
-
me.shellCommands.push(`${openCmd} "${homepage}" &`);
|
|
308
|
-
} else {
|
|
309
|
-
require("openurl2").open(homepage);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
break;
|
|
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
|
-
|
|
340
|
-
if (isShellMode) {
|
|
341
|
-
me.shellCommands.push(npmCommand);
|
|
342
|
-
} else {
|
|
343
|
-
spawn('npm', ['run', npmCommand.replace('npm run ', '')], {
|
|
344
|
-
cwd: environment.project.path.path,
|
|
345
|
-
stdio: 'inherit'
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
break;
|
|
284
|
+
// Use CommandRegistry to process the event
|
|
285
|
+
const command = registry.getCommandByName(event);
|
|
286
|
+
if (command && command.processing) {
|
|
287
|
+
await command.processing.processEvent({
|
|
288
|
+
project,
|
|
289
|
+
isShellMode,
|
|
290
|
+
shellCommands: me.shellCommands || []
|
|
291
|
+
});
|
|
292
|
+
} else {
|
|
293
|
+
me.log.debug(`No command handler found for event: ${event}`);
|
|
349
294
|
}
|
|
350
295
|
}
|
|
351
|
-
if (`
|
|
296
|
+
if (`after${capitalEvt}` in scripts) {
|
|
352
297
|
me.log.debug(`Found 'after' script, unfortunately scripts are not yet supported.`);
|
|
353
298
|
}
|
|
354
299
|
}
|
package/commands/base.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
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;
|
|
@@ -0,0 +1,86 @@
|
|
|
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;
|
|
@@ -0,0 +1,84 @@
|
|
|
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;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
const BaseCommand = require('../../base');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Web Command
|
|
5
|
+
* Opens the project homepage in a web browser
|
|
6
|
+
*/
|
|
7
|
+
class WebCommand extends BaseCommand {
|
|
8
|
+
static get metadata() {
|
|
9
|
+
return {
|
|
10
|
+
name: 'web',
|
|
11
|
+
displayName: 'Open homepage in browser',
|
|
12
|
+
description: 'Open project homepage in web browser',
|
|
13
|
+
category: 'core',
|
|
14
|
+
requiresTmux: false,
|
|
15
|
+
dependencies: []
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static get validation() {
|
|
20
|
+
return {
|
|
21
|
+
validateConfig(config) {
|
|
22
|
+
// Web command accepts boolean or string 'true'
|
|
23
|
+
if (config === true || config === 'true' || config === false || config === 'false') {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
return 'Web configuration must be a boolean or string "true"/"false"';
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static get configuration() {
|
|
32
|
+
return {
|
|
33
|
+
async configureInteractive() {
|
|
34
|
+
// Web event just enables/disables opening the homepage
|
|
35
|
+
// The actual homepage URL is configured at the project level
|
|
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
|
+
const homepage = project.homepage;
|
|
50
|
+
|
|
51
|
+
if (!homepage) {
|
|
52
|
+
// No homepage configured, skip
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (isShellMode) {
|
|
57
|
+
// Different approaches based on OS
|
|
58
|
+
let openCmd;
|
|
59
|
+
switch (process.platform) {
|
|
60
|
+
case 'darwin': openCmd = 'open'; break;
|
|
61
|
+
case 'win32': openCmd = 'start'; break;
|
|
62
|
+
default: openCmd = 'xdg-open'; break;
|
|
63
|
+
}
|
|
64
|
+
shellCommands.push(`${openCmd} "${homepage}" &`);
|
|
65
|
+
} else {
|
|
66
|
+
// In non-shell mode, use openurl2
|
|
67
|
+
require("openurl2").open(homepage);
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
generateShellCommand(context) {
|
|
72
|
+
const { project } = context;
|
|
73
|
+
const homepage = project.homepage;
|
|
74
|
+
|
|
75
|
+
if (!homepage) {
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let openCmd;
|
|
80
|
+
switch (process.platform) {
|
|
81
|
+
case 'darwin': openCmd = 'open'; break;
|
|
82
|
+
case 'win32': openCmd = 'start'; break;
|
|
83
|
+
default: openCmd = 'xdg-open'; break;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return [`${openCmd} "${homepage}" &`];
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
static get help() {
|
|
92
|
+
return {
|
|
93
|
+
usage: 'web: true',
|
|
94
|
+
description: 'Opens the project homepage in the default web browser',
|
|
95
|
+
examples: [
|
|
96
|
+
{
|
|
97
|
+
config: 'web: true',
|
|
98
|
+
description: 'Enable opening project homepage when switching to project'
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
config: 'web: false',
|
|
102
|
+
description: 'Disable automatic homepage opening'
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
module.exports = WebCommand;
|