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
|
@@ -1,109 +0,0 @@
|
|
|
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;
|
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
const BaseCommand = require('../../base');
|
|
2
|
-
const inquirer = require('inquirer');
|
|
3
|
-
const spawn = require('child_process').spawn;
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Claude Command
|
|
7
|
-
* Launches Claude Code with optional configuration
|
|
8
|
-
*/
|
|
9
|
-
class ClaudeCommand extends BaseCommand {
|
|
10
|
-
static get metadata() {
|
|
11
|
-
return {
|
|
12
|
-
name: 'claude',
|
|
13
|
-
displayName: 'Launch Claude Code',
|
|
14
|
-
description: 'Launch Claude Code with optional flags and configuration',
|
|
15
|
-
category: 'development',
|
|
16
|
-
requiresTmux: true,
|
|
17
|
-
dependencies: ['claude']
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
static get validation() {
|
|
22
|
-
return {
|
|
23
|
-
validateConfig(config) {
|
|
24
|
-
if (config === true || config === 'true' || config === false || config === 'false') {
|
|
25
|
-
return true;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (typeof config === 'object' && config !== null) {
|
|
29
|
-
// Validate flags if present
|
|
30
|
-
if (config.flags) {
|
|
31
|
-
if (!Array.isArray(config.flags)) {
|
|
32
|
-
return 'Claude flags must be an array';
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Basic flag validation - ensure they start with - or --
|
|
36
|
-
const invalidFlags = config.flags.filter(flag =>
|
|
37
|
-
typeof flag !== 'string' || (!flag.startsWith('-') && !flag.startsWith('--'))
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
if (invalidFlags.length > 0) {
|
|
41
|
-
return `Invalid Claude flags: ${invalidFlags.join(', ')}. Flags must start with - or --`;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Validate split_terminal if present
|
|
46
|
-
if (config.split_terminal !== undefined && typeof config.split_terminal !== 'boolean') {
|
|
47
|
-
return 'Claude split_terminal must be a boolean';
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return true;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return 'Claude configuration must be a boolean, string "true"/"false", or configuration object';
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
static get configuration() {
|
|
59
|
-
return {
|
|
60
|
-
async configureInteractive() {
|
|
61
|
-
console.log('\nāļø Configure Claude Event\n');
|
|
62
|
-
|
|
63
|
-
const claudeQuestions = [
|
|
64
|
-
{
|
|
65
|
-
type: 'confirm',
|
|
66
|
-
name: 'useAdvanced',
|
|
67
|
-
message: 'Configure advanced Claude options?',
|
|
68
|
-
default: false
|
|
69
|
-
}
|
|
70
|
-
];
|
|
71
|
-
|
|
72
|
-
const claudeAnswer = await inquirer.prompt(claudeQuestions);
|
|
73
|
-
|
|
74
|
-
if (!claudeAnswer.useAdvanced) {
|
|
75
|
-
return 'true';
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const advancedQuestions = [
|
|
79
|
-
{
|
|
80
|
-
type: 'input',
|
|
81
|
-
name: 'flags',
|
|
82
|
-
message: 'Claude flags (comma-separated, e.g. --resume,--debug):',
|
|
83
|
-
filter: (input) => {
|
|
84
|
-
if (!input.trim()) return [];
|
|
85
|
-
return input.split(',').map(flag => flag.trim()).filter(flag => flag);
|
|
86
|
-
}
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
type: 'confirm',
|
|
90
|
-
name: 'split_terminal',
|
|
91
|
-
message: 'Enable split terminal (Claude + shell side-by-side with tmux)?',
|
|
92
|
-
default: false
|
|
93
|
-
}
|
|
94
|
-
];
|
|
95
|
-
|
|
96
|
-
const advancedAnswers = await inquirer.prompt(advancedQuestions);
|
|
97
|
-
|
|
98
|
-
const configObj = {};
|
|
99
|
-
|
|
100
|
-
if (advancedAnswers.flags && advancedAnswers.flags.length > 0) {
|
|
101
|
-
configObj.flags = advancedAnswers.flags;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (advancedAnswers.split_terminal) {
|
|
105
|
-
configObj.split_terminal = true;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return Object.keys(configObj).length > 0 ? configObj : 'true';
|
|
109
|
-
},
|
|
110
|
-
|
|
111
|
-
getDefaultConfig() {
|
|
112
|
-
return 'true';
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
static get processing() {
|
|
118
|
-
return {
|
|
119
|
-
async processEvent(context) {
|
|
120
|
-
const { project, isShellMode, shellCommands } = context;
|
|
121
|
-
const claudeConfig = project.events.claude;
|
|
122
|
-
|
|
123
|
-
let claudeArgs = [];
|
|
124
|
-
|
|
125
|
-
// Handle advanced Claude configuration
|
|
126
|
-
if (claudeConfig && typeof claudeConfig === 'object') {
|
|
127
|
-
if (claudeConfig.flags && Array.isArray(claudeConfig.flags)) {
|
|
128
|
-
claudeArgs = claudeArgs.concat(claudeConfig.flags);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (isShellMode) {
|
|
133
|
-
let claudeCommand = claudeArgs.length > 0
|
|
134
|
-
? `claude ${claudeArgs.join(' ')}`
|
|
135
|
-
: 'claude';
|
|
136
|
-
shellCommands.push(claudeCommand);
|
|
137
|
-
} else {
|
|
138
|
-
spawn('claude', claudeArgs, {
|
|
139
|
-
cwd: project.path.path,
|
|
140
|
-
stdio: 'inherit'
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
},
|
|
144
|
-
|
|
145
|
-
generateShellCommand(context) {
|
|
146
|
-
const { project } = context;
|
|
147
|
-
const claudeConfig = project.events.claude;
|
|
148
|
-
|
|
149
|
-
let claudeArgs = [];
|
|
150
|
-
|
|
151
|
-
// Handle advanced Claude configuration
|
|
152
|
-
if (claudeConfig && typeof claudeConfig === 'object') {
|
|
153
|
-
if (claudeConfig.flags && Array.isArray(claudeConfig.flags)) {
|
|
154
|
-
claudeArgs = claudeArgs.concat(claudeConfig.flags);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const claudeCommand = claudeArgs.length > 0
|
|
159
|
-
? `claude ${claudeArgs.join(' ')}`
|
|
160
|
-
: 'claude';
|
|
161
|
-
|
|
162
|
-
return [claudeCommand];
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
static get tmux() {
|
|
168
|
-
return {
|
|
169
|
-
getLayoutPriority() {
|
|
170
|
-
return 100; // High priority for Claude
|
|
171
|
-
},
|
|
172
|
-
|
|
173
|
-
contributeToLayout(enabledCommands) {
|
|
174
|
-
// Claude prefers split terminal layouts
|
|
175
|
-
const hasCwd = enabledCommands.some(cmd => cmd.name === 'cwd');
|
|
176
|
-
const hasNpm = enabledCommands.some(cmd => cmd.name === 'npm');
|
|
177
|
-
|
|
178
|
-
if (hasCwd && hasNpm) {
|
|
179
|
-
return 'three-pane'; // Claude + Terminal + NPM
|
|
180
|
-
} else if (hasCwd) {
|
|
181
|
-
return 'split-terminal'; // Claude + Terminal
|
|
182
|
-
} else {
|
|
183
|
-
return 'single'; // Just Claude
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
static get help() {
|
|
190
|
-
return {
|
|
191
|
-
usage: 'claude: <configuration>',
|
|
192
|
-
description: 'Launch Claude Code with optional flags and configuration',
|
|
193
|
-
examples: [
|
|
194
|
-
{
|
|
195
|
-
config: 'claude: true',
|
|
196
|
-
description: 'Launch Claude Code with default settings'
|
|
197
|
-
},
|
|
198
|
-
{
|
|
199
|
-
config: 'claude: { flags: ["--resume", "--debug"] }',
|
|
200
|
-
description: 'Launch Claude with specific flags'
|
|
201
|
-
},
|
|
202
|
-
{
|
|
203
|
-
config: 'claude: { split_terminal: true }',
|
|
204
|
-
description: 'Launch Claude in split terminal with tmux'
|
|
205
|
-
}
|
|
206
|
-
]
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
module.exports = ClaudeCommand;
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
const BaseCommand = require('../../base');
|
|
2
|
-
const inquirer = require('inquirer');
|
|
3
|
-
const spawn = require('child_process').spawn;
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Docker Command
|
|
7
|
-
* Manages Docker containers for the project
|
|
8
|
-
*
|
|
9
|
-
* This demonstrates how easy it is to add a new command to the system!
|
|
10
|
-
* Just create this file and the system automatically discovers it.
|
|
11
|
-
*/
|
|
12
|
-
class DockerCommand extends BaseCommand {
|
|
13
|
-
static get metadata() {
|
|
14
|
-
return {
|
|
15
|
-
name: 'docker',
|
|
16
|
-
displayName: 'Docker container management',
|
|
17
|
-
description: 'Start/stop Docker containers for the project',
|
|
18
|
-
category: 'development',
|
|
19
|
-
requiresTmux: false,
|
|
20
|
-
dependencies: ['docker']
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
static get validation() {
|
|
25
|
-
return {
|
|
26
|
-
validateConfig(config) {
|
|
27
|
-
if (config === true || config === 'true' || config === false || config === 'false') {
|
|
28
|
-
return true;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (typeof config === 'string' && config.trim() !== '') {
|
|
32
|
-
return true; // Docker compose file path
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (typeof config === 'object' && config !== null) {
|
|
36
|
-
if (config.compose_file && typeof config.compose_file !== 'string') {
|
|
37
|
-
return 'Docker compose_file must be a string';
|
|
38
|
-
}
|
|
39
|
-
if (config.services && !Array.isArray(config.services)) {
|
|
40
|
-
return 'Docker services must be an array';
|
|
41
|
-
}
|
|
42
|
-
return true;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return 'Docker configuration must be a boolean, string, or configuration object';
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
static get configuration() {
|
|
51
|
-
return {
|
|
52
|
-
async configureInteractive() {
|
|
53
|
-
console.log('\nš³ Configure Docker Event\n');
|
|
54
|
-
|
|
55
|
-
const dockerQuestions = [
|
|
56
|
-
{
|
|
57
|
-
type: 'input',
|
|
58
|
-
name: 'compose_file',
|
|
59
|
-
message: 'Docker Compose file path (relative to project):',
|
|
60
|
-
default: 'docker-compose.yml'
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
type: 'input',
|
|
64
|
-
name: 'services',
|
|
65
|
-
message: 'Services to start (comma-separated, or leave empty for all):',
|
|
66
|
-
filter: (input) => {
|
|
67
|
-
if (!input.trim()) return [];
|
|
68
|
-
return input.split(',').map(s => s.trim()).filter(s => s);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
];
|
|
72
|
-
|
|
73
|
-
const answers = await inquirer.prompt(dockerQuestions);
|
|
74
|
-
|
|
75
|
-
const config = {
|
|
76
|
-
compose_file: answers.compose_file
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
if (answers.services.length > 0) {
|
|
80
|
-
config.services = answers.services;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return config;
|
|
84
|
-
},
|
|
85
|
-
|
|
86
|
-
getDefaultConfig() {
|
|
87
|
-
return { compose_file: 'docker-compose.yml' };
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
static get processing() {
|
|
93
|
-
return {
|
|
94
|
-
async processEvent(context) {
|
|
95
|
-
const { project, isShellMode, shellCommands } = context;
|
|
96
|
-
const dockerConfig = project.events.docker;
|
|
97
|
-
|
|
98
|
-
let composeFile = 'docker-compose.yml';
|
|
99
|
-
let services = [];
|
|
100
|
-
|
|
101
|
-
if (typeof dockerConfig === 'string') {
|
|
102
|
-
composeFile = dockerConfig;
|
|
103
|
-
} else if (dockerConfig && typeof dockerConfig === 'object') {
|
|
104
|
-
composeFile = dockerConfig.compose_file || composeFile;
|
|
105
|
-
services = dockerConfig.services || [];
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const servicesArg = services.length > 0 ? services.join(' ') : '';
|
|
109
|
-
const dockerCommand = `docker-compose -f ${composeFile} up -d ${servicesArg}`.trim();
|
|
110
|
-
|
|
111
|
-
if (isShellMode) {
|
|
112
|
-
shellCommands.push(dockerCommand);
|
|
113
|
-
} else {
|
|
114
|
-
const args = ['-f', composeFile, 'up', '-d'];
|
|
115
|
-
if (services.length > 0) {
|
|
116
|
-
args.push(...services);
|
|
117
|
-
}
|
|
118
|
-
spawn('docker-compose', args, {
|
|
119
|
-
cwd: project.path.path,
|
|
120
|
-
stdio: 'inherit'
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
},
|
|
124
|
-
|
|
125
|
-
generateShellCommand(context) {
|
|
126
|
-
const { project } = context;
|
|
127
|
-
const dockerConfig = project.events.docker;
|
|
128
|
-
|
|
129
|
-
let composeFile = 'docker-compose.yml';
|
|
130
|
-
let services = [];
|
|
131
|
-
|
|
132
|
-
if (typeof dockerConfig === 'string') {
|
|
133
|
-
composeFile = dockerConfig;
|
|
134
|
-
} else if (dockerConfig && typeof dockerConfig === 'object') {
|
|
135
|
-
composeFile = dockerConfig.compose_file || composeFile;
|
|
136
|
-
services = dockerConfig.services || [];
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const servicesArg = services.length > 0 ? services.join(' ') : '';
|
|
140
|
-
return [`docker-compose -f ${composeFile} up -d ${servicesArg}`.trim()];
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
static get help() {
|
|
146
|
-
return {
|
|
147
|
-
usage: 'docker: <compose-file> | <configuration>',
|
|
148
|
-
description: 'Start Docker containers using docker-compose',
|
|
149
|
-
examples: [
|
|
150
|
-
{
|
|
151
|
-
config: 'docker: true',
|
|
152
|
-
description: 'Start containers using default docker-compose.yml'
|
|
153
|
-
},
|
|
154
|
-
{
|
|
155
|
-
config: 'docker: "docker-compose.dev.yml"',
|
|
156
|
-
description: 'Use specific compose file'
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
config: 'docker: { compose_file: "docker-compose.yml", services: ["web", "db"] }',
|
|
160
|
-
description: 'Start only specific services'
|
|
161
|
-
}
|
|
162
|
-
]
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
module.exports = DockerCommand;
|
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
const BaseCommand = require('../../base');
|
|
2
|
-
const inquirer = require('inquirer');
|
|
3
|
-
const spawn = require('child_process').spawn;
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* NPM Command
|
|
7
|
-
* Executes NPM scripts in the project directory
|
|
8
|
-
*/
|
|
9
|
-
class NpmCommand extends BaseCommand {
|
|
10
|
-
static get metadata() {
|
|
11
|
-
return {
|
|
12
|
-
name: 'npm',
|
|
13
|
-
displayName: 'Run NPM command',
|
|
14
|
-
description: 'Execute NPM scripts in project directory',
|
|
15
|
-
category: 'development',
|
|
16
|
-
requiresTmux: true,
|
|
17
|
-
dependencies: ['npm']
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
static get validation() {
|
|
22
|
-
return {
|
|
23
|
-
validateConfig(config) {
|
|
24
|
-
if (config === true || config === 'true' || config === false || config === 'false') {
|
|
25
|
-
return true;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (typeof config === 'string' && config.trim() !== '') {
|
|
29
|
-
return true; // Simple string command
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (typeof config === 'object' && config !== null) {
|
|
33
|
-
// Validate command if present
|
|
34
|
-
if (config.command) {
|
|
35
|
-
if (typeof config.command !== 'string' || config.command.trim() === '') {
|
|
36
|
-
return 'NPM command must be a non-empty string';
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Validate watch if present
|
|
41
|
-
if (config.watch !== undefined && typeof config.watch !== 'boolean') {
|
|
42
|
-
return 'NPM watch must be a boolean';
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Validate auto_restart if present
|
|
46
|
-
if (config.auto_restart !== undefined && typeof config.auto_restart !== 'boolean') {
|
|
47
|
-
return 'NPM auto_restart must be a boolean';
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return true;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return 'NPM configuration must be a boolean, string command, or configuration object';
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
static get configuration() {
|
|
59
|
-
return {
|
|
60
|
-
async configureInteractive() {
|
|
61
|
-
console.log('\nš¦ Configure NPM Event\n');
|
|
62
|
-
|
|
63
|
-
const npmQuestions = [
|
|
64
|
-
{
|
|
65
|
-
type: 'input',
|
|
66
|
-
name: 'command',
|
|
67
|
-
message: 'NPM script to run (e.g., dev, start, test):',
|
|
68
|
-
default: 'dev',
|
|
69
|
-
validate: (value) => {
|
|
70
|
-
if (!value.trim()) {
|
|
71
|
-
return 'NPM command cannot be empty';
|
|
72
|
-
}
|
|
73
|
-
return true;
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
type: 'confirm',
|
|
78
|
-
name: 'useAdvanced',
|
|
79
|
-
message: 'Configure advanced NPM options?',
|
|
80
|
-
default: false
|
|
81
|
-
}
|
|
82
|
-
];
|
|
83
|
-
|
|
84
|
-
const basicAnswers = await inquirer.prompt(npmQuestions);
|
|
85
|
-
|
|
86
|
-
if (!basicAnswers.useAdvanced) {
|
|
87
|
-
return basicAnswers.command;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const advancedQuestions = [
|
|
91
|
-
{
|
|
92
|
-
type: 'confirm',
|
|
93
|
-
name: 'watch',
|
|
94
|
-
message: 'Enable watch mode (if supported by command)?',
|
|
95
|
-
default: true
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
type: 'confirm',
|
|
99
|
-
name: 'auto_restart',
|
|
100
|
-
message: 'Auto-restart on crashes?',
|
|
101
|
-
default: false
|
|
102
|
-
}
|
|
103
|
-
];
|
|
104
|
-
|
|
105
|
-
const advancedAnswers = await inquirer.prompt(advancedQuestions);
|
|
106
|
-
|
|
107
|
-
const configObj = {
|
|
108
|
-
command: basicAnswers.command
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
if (advancedAnswers.watch) {
|
|
112
|
-
configObj.watch = true;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (advancedAnswers.auto_restart) {
|
|
116
|
-
configObj.auto_restart = true;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return configObj;
|
|
120
|
-
},
|
|
121
|
-
|
|
122
|
-
getDefaultConfig() {
|
|
123
|
-
return 'dev';
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
static get processing() {
|
|
129
|
-
return {
|
|
130
|
-
async processEvent(context) {
|
|
131
|
-
const { project, isShellMode, shellCommands } = context;
|
|
132
|
-
const npmConfig = project.events.npm;
|
|
133
|
-
const npmCommand = NpmCommand._getNpmCommand(npmConfig);
|
|
134
|
-
|
|
135
|
-
if (isShellMode) {
|
|
136
|
-
shellCommands.push(npmCommand);
|
|
137
|
-
} else {
|
|
138
|
-
const scriptName = npmCommand.replace('npm run ', '');
|
|
139
|
-
spawn('npm', ['run', scriptName], {
|
|
140
|
-
cwd: project.path.path,
|
|
141
|
-
stdio: 'inherit'
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
},
|
|
145
|
-
|
|
146
|
-
generateShellCommand(context) {
|
|
147
|
-
const { project } = context;
|
|
148
|
-
const npmConfig = project.events.npm;
|
|
149
|
-
return [NpmCommand._getNpmCommand(npmConfig)];
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
static _getNpmCommand(npmConfig) {
|
|
155
|
-
if (typeof npmConfig === 'string') {
|
|
156
|
-
return `npm run ${npmConfig}`;
|
|
157
|
-
} else if (npmConfig && typeof npmConfig === 'object' && npmConfig.command) {
|
|
158
|
-
return `npm run ${npmConfig.command}`;
|
|
159
|
-
} else {
|
|
160
|
-
return 'npm run dev';
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
static get tmux() {
|
|
165
|
-
return {
|
|
166
|
-
getLayoutPriority() {
|
|
167
|
-
return 50; // Medium priority for NPM
|
|
168
|
-
},
|
|
169
|
-
|
|
170
|
-
contributeToLayout(enabledCommands) {
|
|
171
|
-
// NPM works well in multi-pane layouts
|
|
172
|
-
const hasCwd = enabledCommands.some(cmd => cmd.name === 'cwd');
|
|
173
|
-
const hasClaude = enabledCommands.some(cmd => cmd.name === 'claude');
|
|
174
|
-
|
|
175
|
-
if (hasCwd && hasClaude) {
|
|
176
|
-
return 'three-pane'; // Claude + Terminal + NPM
|
|
177
|
-
} else if (hasCwd) {
|
|
178
|
-
return 'two-pane-npm'; // Terminal + NPM
|
|
179
|
-
} else {
|
|
180
|
-
return 'single'; // Just NPM
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
static get help() {
|
|
187
|
-
return {
|
|
188
|
-
usage: 'npm: <script-name> | <configuration>',
|
|
189
|
-
description: 'Execute NPM scripts in the project directory',
|
|
190
|
-
examples: [
|
|
191
|
-
{
|
|
192
|
-
config: 'npm: "dev"',
|
|
193
|
-
description: 'Run npm run dev'
|
|
194
|
-
},
|
|
195
|
-
{
|
|
196
|
-
config: 'npm: { command: "test", watch: true }',
|
|
197
|
-
description: 'Run tests in watch mode'
|
|
198
|
-
},
|
|
199
|
-
{
|
|
200
|
-
config: 'npm: { command: "start", auto_restart: true }',
|
|
201
|
-
description: 'Run start script with auto-restart on crashes'
|
|
202
|
-
}
|
|
203
|
-
]
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
module.exports = NpmCommand;
|