workon 1.3.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 +25 -1
- package/cli/manage.js +29 -77
- package/cli/open.js +152 -74
- 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/docs/ideas.md +71 -65
- package/lib/tmux.js +81 -0
- package/lib/validation.js +14 -35
- package/package.json +2 -2
- package/test-architecture.js +145 -0
- package/test-registry.js +57 -0
package/lib/validation.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const File = require('phylo');
|
|
2
2
|
const { spawn } = require('child_process');
|
|
3
3
|
const path = require('path');
|
|
4
|
+
const registry = require('../commands/registry');
|
|
4
5
|
|
|
5
6
|
class ProjectValidator {
|
|
6
7
|
constructor(config) {
|
|
@@ -74,57 +75,35 @@ class ProjectValidator {
|
|
|
74
75
|
});
|
|
75
76
|
}
|
|
76
77
|
|
|
77
|
-
validateEvents(events) {
|
|
78
|
+
async validateEvents(events) {
|
|
78
79
|
if (!events || typeof events !== 'object') {
|
|
79
80
|
return 'Events must be an object';
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
|
|
83
|
+
// Initialize registry if not already done
|
|
84
|
+
await registry.initialize();
|
|
85
|
+
|
|
86
|
+
const validEvents = registry.getValidEventNames();
|
|
83
87
|
const invalidEvents = Object.keys(events).filter(event => !validEvents.includes(event));
|
|
84
88
|
|
|
85
89
|
if (invalidEvents.length > 0) {
|
|
86
90
|
return `Invalid events: ${invalidEvents.join(', ')}. Valid events: ${validEvents.join(', ')}`;
|
|
87
91
|
}
|
|
88
92
|
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
if (
|
|
93
|
-
|
|
93
|
+
// Delegate to command-specific validation
|
|
94
|
+
for (const [eventName, config] of Object.entries(events)) {
|
|
95
|
+
const command = registry.getCommandByName(eventName);
|
|
96
|
+
if (command && command.validation) {
|
|
97
|
+
const result = command.validation.validateConfig(config);
|
|
98
|
+
if (result !== true) {
|
|
99
|
+
return `${eventName}: ${result}`;
|
|
100
|
+
}
|
|
94
101
|
}
|
|
95
102
|
}
|
|
96
103
|
|
|
97
104
|
return true;
|
|
98
105
|
}
|
|
99
106
|
|
|
100
|
-
validateClaudeConfig(config) {
|
|
101
|
-
if (typeof config !== 'object') {
|
|
102
|
-
return 'Claude configuration must be an object';
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Validate flags if present
|
|
106
|
-
if (config.flags) {
|
|
107
|
-
if (!Array.isArray(config.flags)) {
|
|
108
|
-
return 'Claude flags must be an array';
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Basic flag validation - ensure they start with - or --
|
|
112
|
-
const invalidFlags = config.flags.filter(flag =>
|
|
113
|
-
typeof flag !== 'string' || (!flag.startsWith('-') && !flag.startsWith('--'))
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
if (invalidFlags.length > 0) {
|
|
117
|
-
return `Invalid Claude flags: ${invalidFlags.join(', ')}. Flags must start with - or --`;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Validate split_terminal if present
|
|
122
|
-
if (config.split_terminal !== undefined && typeof config.split_terminal !== 'boolean') {
|
|
123
|
-
return 'Claude split_terminal must be a boolean';
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return true;
|
|
127
|
-
}
|
|
128
107
|
|
|
129
108
|
validateUrl(url) {
|
|
130
109
|
if (!url) return true; // Optional field
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "workon",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Work on something great!",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"license": "MIT",
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"cz-conventional-changelog": "^2.0.0",
|
|
20
|
-
"standard-version": "^
|
|
20
|
+
"standard-version": "^9.5.0"
|
|
21
21
|
},
|
|
22
22
|
"config": {
|
|
23
23
|
"commitizen": {
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
const registry = require('./commands/registry');
|
|
2
|
+
const ProjectValidator = require('./lib/validation');
|
|
3
|
+
|
|
4
|
+
async function comprehensiveTest() {
|
|
5
|
+
console.log('🧪 Testing Command-Centric Architecture\n');
|
|
6
|
+
|
|
7
|
+
try {
|
|
8
|
+
// Test 1: Registry initialization
|
|
9
|
+
console.log('1. Testing registry initialization...');
|
|
10
|
+
await registry.initialize();
|
|
11
|
+
console.log(' ✅ Registry initialized successfully');
|
|
12
|
+
|
|
13
|
+
// Test 2: Command discovery
|
|
14
|
+
console.log('\n2. Testing command discovery...');
|
|
15
|
+
const commands = registry.getValidEventNames();
|
|
16
|
+
const expectedCommands = ['cwd', 'ide', 'web', 'claude', 'npm'];
|
|
17
|
+
const hasAllCommands = expectedCommands.every(cmd => commands.includes(cmd));
|
|
18
|
+
console.log(` 📋 Discovered commands: ${commands.join(', ')}`);
|
|
19
|
+
console.log(` ${hasAllCommands ? '✅' : '❌'} All expected commands found`);
|
|
20
|
+
|
|
21
|
+
// Test 3: Command metadata
|
|
22
|
+
console.log('\n3. Testing command metadata...');
|
|
23
|
+
for (const cmdName of expectedCommands) {
|
|
24
|
+
const command = registry.getCommandByName(cmdName);
|
|
25
|
+
if (command && command.metadata) {
|
|
26
|
+
console.log(` ✅ ${cmdName}: ${command.metadata.displayName}`);
|
|
27
|
+
} else {
|
|
28
|
+
console.log(` ❌ ${cmdName}: Missing metadata`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Test 4: Validation system
|
|
33
|
+
console.log('\n4. Testing validation system...');
|
|
34
|
+
const validator = new ProjectValidator({});
|
|
35
|
+
|
|
36
|
+
// Test valid configurations
|
|
37
|
+
const validConfigs = [
|
|
38
|
+
{ cwd: true, ide: true },
|
|
39
|
+
{ claude: { flags: ['--debug'] } },
|
|
40
|
+
{ npm: 'dev' },
|
|
41
|
+
{ npm: { command: 'test', watch: true } }
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
for (const config of validConfigs) {
|
|
45
|
+
const result = await validator.validateEvents(config);
|
|
46
|
+
console.log(` ${result === true ? '✅' : '❌'} Valid config: ${JSON.stringify(config)}`);
|
|
47
|
+
if (result !== true) console.log(` Error: ${result}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Test invalid configurations
|
|
51
|
+
const invalidConfigs = [
|
|
52
|
+
{ invalid: true },
|
|
53
|
+
{ claude: { flags: 'invalid' } },
|
|
54
|
+
{ npm: { command: 123 } }
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
for (const config of invalidConfigs) {
|
|
58
|
+
const result = await validator.validateEvents(config);
|
|
59
|
+
console.log(` ${result !== true ? '✅' : '❌'} Invalid config rejected: ${JSON.stringify(config)}`);
|
|
60
|
+
if (result !== true) console.log(` Error: ${result}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Test 5: Command configuration
|
|
64
|
+
console.log('\n5. Testing command configuration...');
|
|
65
|
+
const claudeCommand = registry.getCommandByName('claude');
|
|
66
|
+
if (claudeCommand) {
|
|
67
|
+
const defaultConfig = claudeCommand.configuration.getDefaultConfig();
|
|
68
|
+
console.log(` ✅ Claude default config: ${defaultConfig}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const npmCommand = registry.getCommandByName('npm');
|
|
72
|
+
if (npmCommand) {
|
|
73
|
+
const defaultConfig = npmCommand.configuration.getDefaultConfig();
|
|
74
|
+
console.log(` ✅ NPM default config: ${defaultConfig}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Test 6: Tmux integration
|
|
78
|
+
console.log('\n6. Testing tmux integration...');
|
|
79
|
+
const tmuxCommands = registry.getTmuxEnabledCommands();
|
|
80
|
+
console.log(` 📋 Tmux-enabled commands: ${tmuxCommands.map(c => `${c.name}(${c.priority})`).join(', ')}`);
|
|
81
|
+
console.log(` ${tmuxCommands.length > 0 ? '✅' : '❌'} Tmux commands found`);
|
|
82
|
+
|
|
83
|
+
// Test 7: Management UI data
|
|
84
|
+
console.log('\n7. Testing management UI data...');
|
|
85
|
+
const manageCommands = registry.getCommandsForManageUI();
|
|
86
|
+
console.log(` 📋 Management UI commands: ${manageCommands.length}`);
|
|
87
|
+
for (const cmd of manageCommands) {
|
|
88
|
+
console.log(` - ${cmd.name} (${cmd.value})`);
|
|
89
|
+
}
|
|
90
|
+
console.log(` ${manageCommands.length === expectedCommands.length ? '✅' : '❌'} All commands available for management`);
|
|
91
|
+
|
|
92
|
+
// Test 8: Command processing simulation
|
|
93
|
+
console.log('\n8. Testing command processing...');
|
|
94
|
+
const mockProject = {
|
|
95
|
+
name: 'test-project',
|
|
96
|
+
path: { path: '/test/path' },
|
|
97
|
+
ide: 'code',
|
|
98
|
+
homepage: 'https://example.com',
|
|
99
|
+
events: {
|
|
100
|
+
cwd: true,
|
|
101
|
+
ide: true,
|
|
102
|
+
claude: { flags: ['--debug'] },
|
|
103
|
+
npm: 'dev'
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const shellCommands = [];
|
|
108
|
+
const context = {
|
|
109
|
+
project: mockProject,
|
|
110
|
+
isShellMode: true,
|
|
111
|
+
shellCommands
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
for (const eventName of ['cwd', 'ide', 'claude', 'npm']) {
|
|
115
|
+
const command = registry.getCommandByName(eventName);
|
|
116
|
+
if (command && command.processing) {
|
|
117
|
+
try {
|
|
118
|
+
await command.processing.processEvent(context);
|
|
119
|
+
console.log(` ✅ ${eventName} command processed`);
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.log(` ❌ ${eventName} command failed: ${error.message}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
console.log(` 📋 Generated shell commands: ${shellCommands.length}`);
|
|
127
|
+
shellCommands.forEach((cmd, i) => console.log(` ${i + 1}. ${cmd}`));
|
|
128
|
+
|
|
129
|
+
console.log('\n🎉 All tests completed successfully!');
|
|
130
|
+
console.log('\n📊 Architecture Benefits Achieved:');
|
|
131
|
+
console.log(' ✅ Commands are self-contained');
|
|
132
|
+
console.log(' ✅ Validation is delegated to commands');
|
|
133
|
+
console.log(' ✅ Configuration is handled by commands');
|
|
134
|
+
console.log(' ✅ No hardcoded command lists');
|
|
135
|
+
console.log(' ✅ Auto-discovery works');
|
|
136
|
+
console.log(' ✅ Adding new commands requires touching only command directory');
|
|
137
|
+
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error('❌ Test failed:', error.message);
|
|
140
|
+
console.error(error.stack);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
comprehensiveTest();
|
package/test-registry.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const registry = require('./commands/registry');
|
|
2
|
+
|
|
3
|
+
async function testRegistry() {
|
|
4
|
+
console.log('Testing Command Registry...\n');
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
await registry.initialize();
|
|
8
|
+
console.log('✅ Registry initialized successfully\n');
|
|
9
|
+
|
|
10
|
+
// Test getting valid event names
|
|
11
|
+
const eventNames = registry.getValidEventNames();
|
|
12
|
+
console.log('Valid event names:', eventNames);
|
|
13
|
+
|
|
14
|
+
// Test getting commands for manage UI
|
|
15
|
+
const manageCommands = registry.getCommandsForManageUI();
|
|
16
|
+
console.log('\nCommands for manage UI:');
|
|
17
|
+
manageCommands.forEach(cmd => {
|
|
18
|
+
console.log(` - ${cmd.name} (${cmd.value}): ${cmd.description}`);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Test getting specific commands
|
|
22
|
+
console.log('\nTesting specific commands:');
|
|
23
|
+
const cwdCommand = registry.getCommandByName('cwd');
|
|
24
|
+
if (cwdCommand) {
|
|
25
|
+
console.log('✅ CWD command found');
|
|
26
|
+
console.log(' Metadata:', cwdCommand.metadata);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const claudeCommand = registry.getCommandByName('claude');
|
|
30
|
+
if (claudeCommand) {
|
|
31
|
+
console.log('✅ Claude command found');
|
|
32
|
+
console.log(' Metadata:', claudeCommand.metadata);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Test tmux commands
|
|
36
|
+
const tmuxCommands = registry.getTmuxEnabledCommands();
|
|
37
|
+
console.log('\nTmux-enabled commands:');
|
|
38
|
+
tmuxCommands.forEach(cmd => {
|
|
39
|
+
console.log(` - ${cmd.name} (priority: ${cmd.priority})`);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Test validation
|
|
43
|
+
console.log('\nTesting validation:');
|
|
44
|
+
if (cwdCommand) {
|
|
45
|
+
console.log('CWD validation (true):', cwdCommand.validation.validateConfig(true));
|
|
46
|
+
console.log('CWD validation (invalid):', cwdCommand.validation.validateConfig(123));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log('\n✅ All tests passed!');
|
|
50
|
+
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error('❌ Error testing registry:', error.message);
|
|
53
|
+
console.error(error.stack);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
testRegistry();
|