workon 2.1.2 → 3.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/README.md +19 -4
- package/bin/workon +1 -11
- package/dist/cli.js +2227 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +1216 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +280 -0
- package/dist/index.d.ts +280 -0
- package/dist/index.js +1173 -0
- package/dist/index.js.map +1 -0
- package/package.json +68 -21
- package/.claude/settings.local.json +0 -11
- package/.cursorindexingignore +0 -3
- package/.history/.gitignore_20250806202718 +0 -30
- package/.history/.gitignore_20250806231444 +0 -32
- package/.history/.gitignore_20250806231450 +0 -32
- package/.history/lib/tmux_20250806233103.js +0 -109
- package/.history/lib/tmux_20250806233219.js +0 -109
- package/.history/lib/tmux_20250806233223.js +0 -109
- package/.history/lib/tmux_20250806233230.js +0 -109
- package/.history/lib/tmux_20250806233231.js +0 -109
- package/.history/lib/tmux_20250807120751.js +0 -190
- package/.history/lib/tmux_20250807120757.js +0 -190
- package/.history/lib/tmux_20250807120802.js +0 -190
- package/.history/lib/tmux_20250807120808.js +0 -190
- package/.history/package_20250807114243.json +0 -43
- package/.history/package_20250807114257.json +0 -43
- package/.history/package_20250807114404.json +0 -43
- package/.history/package_20250807114409.json +0 -43
- package/.history/package_20250807114510.json +0 -43
- package/.history/package_20250807114637.json +0 -43
- package/.vscode/launch.json +0 -20
- package/.vscode/terminals.json +0 -11
- package/CHANGELOG.md +0 -103
- package/CLAUDE.md +0 -100
- package/cli/base.js +0 -16
- package/cli/config/index.js +0 -19
- package/cli/config/list.js +0 -26
- package/cli/config/set.js +0 -19
- package/cli/config/unset.js +0 -26
- package/cli/index.js +0 -184
- package/cli/interactive.js +0 -290
- package/cli/manage.js +0 -413
- package/cli/open.js +0 -414
- package/commands/base.js +0 -105
- package/commands/core/cwd/index.js +0 -86
- package/commands/core/ide/index.js +0 -84
- package/commands/core/web/index.js +0 -109
- package/commands/extensions/claude/index.js +0 -211
- package/commands/extensions/docker/index.js +0 -167
- package/commands/extensions/npm/index.js +0 -208
- package/commands/registry.js +0 -196
- package/demo-colon-syntax.js +0 -57
- package/docs/adr/001-command-centric-architecture.md +0 -304
- package/docs/adr/002-positional-command-arguments.md +0 -402
- package/docs/ideas.md +0 -93
- package/lib/config.js +0 -51
- package/lib/environment/base.js +0 -12
- package/lib/environment/index.js +0 -108
- package/lib/environment/project.js +0 -26
- package/lib/project.js +0 -68
- package/lib/tmux.js +0 -221
- package/lib/validation.js +0 -120
- package/test-architecture.js +0 -145
- package/test-colon-syntax.js +0 -85
- 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;
|