workon 2.1.3 → 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.
Files changed (66) hide show
  1. package/README.md +19 -4
  2. package/bin/workon +1 -11
  3. package/dist/cli.js +2227 -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
@@ -1,196 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- /**
5
- * Command Registry for auto-discovery and management of commands
6
- * Scans the commands directory and provides unified access to all commands
7
- */
8
- class CommandRegistry {
9
- constructor() {
10
- this._commands = new Map();
11
- this._initialized = false;
12
- }
13
-
14
- /**
15
- * Initialize the registry by discovering all commands
16
- */
17
- async initialize() {
18
- if (this._initialized) return;
19
-
20
- await this._discoverCommands();
21
- this._initialized = true;
22
- }
23
-
24
- /**
25
- * Discover commands from the commands directory
26
- * @private
27
- */
28
- async _discoverCommands() {
29
- const commandsDir = path.join(__dirname);
30
-
31
- // Discover core commands
32
- await this._discoverCommandsInDirectory(path.join(commandsDir, 'core'));
33
-
34
- // Discover extension commands
35
- await this._discoverCommandsInDirectory(path.join(commandsDir, 'extensions'));
36
- }
37
-
38
- /**
39
- * Discover commands in a specific directory
40
- * @param {string} dir - Directory to scan
41
- * @private
42
- */
43
- async _discoverCommandsInDirectory(dir) {
44
- if (!fs.existsSync(dir)) return;
45
-
46
- const entries = fs.readdirSync(dir, { withFileTypes: true });
47
-
48
- for (const entry of entries) {
49
- if (entry.isDirectory()) {
50
- const commandDir = path.join(dir, entry.name);
51
- const indexFile = path.join(commandDir, 'index.js');
52
-
53
- if (fs.existsSync(indexFile)) {
54
- try {
55
- const CommandClass = require(indexFile);
56
-
57
- // Validate command structure
58
- if (this._isValidCommand(CommandClass)) {
59
- const metadata = CommandClass.metadata;
60
- this._commands.set(metadata.name, CommandClass);
61
- } else {
62
- console.error(`Invalid command structure in ${indexFile}`);
63
- }
64
- } catch (error) {
65
- console.error(`Failed to load command from ${indexFile}:`, error.message);
66
- }
67
- }
68
- }
69
- }
70
- }
71
-
72
- /**
73
- * Validate if a class is a proper command
74
- * @param {Function} CommandClass - Command class to validate
75
- * @returns {boolean}
76
- * @private
77
- */
78
- _isValidCommand(CommandClass) {
79
- try {
80
- // Check if it has required static properties
81
- const metadata = CommandClass.metadata;
82
- return metadata &&
83
- typeof metadata.name === 'string' &&
84
- typeof metadata.displayName === 'string' &&
85
- typeof CommandClass.validation === 'object' &&
86
- typeof CommandClass.configuration === 'object' &&
87
- typeof CommandClass.processing === 'object';
88
- } catch (error) {
89
- return false;
90
- }
91
- }
92
-
93
- /**
94
- * Get all valid event names from discovered commands
95
- * @returns {string[]}
96
- */
97
- getValidEventNames() {
98
- this._ensureInitialized();
99
- return Array.from(this._commands.keys());
100
- }
101
-
102
- /**
103
- * Get command by name
104
- * @param {string} name - Command name
105
- * @returns {Function|null} Command class or null if not found
106
- */
107
- getCommandByName(name) {
108
- this._ensureInitialized();
109
- return this._commands.get(name) || null;
110
- }
111
-
112
- /**
113
- * Get all commands for management UI
114
- * @returns {Array<Object>} Array of command info objects
115
- */
116
- getCommandsForManageUI() {
117
- this._ensureInitialized();
118
-
119
- const commands = [];
120
- for (const [name, CommandClass] of this._commands) {
121
- const metadata = CommandClass.metadata;
122
- commands.push({
123
- name: metadata.displayName,
124
- value: name,
125
- description: metadata.description
126
- });
127
- }
128
-
129
- return commands.sort((a, b) => a.name.localeCompare(b.name));
130
- }
131
-
132
- /**
133
- * Get commands that support tmux integration
134
- * @returns {Array<Object>} Array of commands with tmux support
135
- */
136
- getTmuxEnabledCommands() {
137
- this._ensureInitialized();
138
-
139
- const tmuxCommands = [];
140
- for (const [name, CommandClass] of this._commands) {
141
- if (CommandClass.tmux) {
142
- tmuxCommands.push({
143
- name,
144
- command: CommandClass,
145
- priority: CommandClass.tmux.getLayoutPriority ? CommandClass.tmux.getLayoutPriority() : 0
146
- });
147
- }
148
- }
149
-
150
- return tmuxCommands.sort((a, b) => b.priority - a.priority);
151
- }
152
-
153
- /**
154
- * Get all available commands with their metadata
155
- * @returns {Array<Object>} Array of command metadata
156
- */
157
- getAllCommands() {
158
- this._ensureInitialized();
159
-
160
- const commands = [];
161
- for (const [name, CommandClass] of this._commands) {
162
- commands.push({
163
- name,
164
- metadata: CommandClass.metadata,
165
- hasValidation: !!CommandClass.validation,
166
- hasConfiguration: !!CommandClass.configuration,
167
- hasProcessing: !!CommandClass.processing,
168
- hasTmux: !!CommandClass.tmux,
169
- hasHelp: !!CommandClass.help
170
- });
171
- }
172
-
173
- return commands;
174
- }
175
-
176
- /**
177
- * Ensure registry is initialized
178
- * @private
179
- */
180
- _ensureInitialized() {
181
- if (!this._initialized) {
182
- throw new Error('CommandRegistry must be initialized before use. Call initialize() first.');
183
- }
184
- }
185
-
186
- /**
187
- * Clear the registry (useful for testing)
188
- */
189
- clear() {
190
- this._commands.clear();
191
- this._initialized = false;
192
- }
193
- }
194
-
195
- // Export singleton instance
196
- module.exports = new CommandRegistry();
@@ -1,57 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- console.log(`
4
- 🎯 COLON SYNTAX FEATURE DEMONSTRATION
5
- =====================================
6
-
7
- The new colon syntax allows selective command execution for projects:
8
-
9
- 📋 SYNTAX:
10
- workon <project> # Execute all configured commands
11
- workon <project>:<command> # Execute single command
12
- workon <project>:<cmd1,cmd2> # Execute multiple commands
13
- workon <project>:help # Show available commands
14
-
15
- ✨ KEY FEATURES:
16
-
17
- 1. BACKWARD COMPATIBLE
18
- workon my-project # Still works exactly as before
19
-
20
- 2. SELECTIVE EXECUTION
21
- workon my-project:cwd # Just change directory
22
- workon my-project:claude # Just open Claude
23
-
24
- 3. SMART DEPENDENCIES
25
- workon my-project:claude # Auto-adds 'cwd' dependency
26
- workon my-project:npm # Auto-adds 'cwd' dependency
27
-
28
- 4. MULTIPLE COMMANDS
29
- workon my-project:cwd,claude,npm # Custom combinations
30
-
31
- 5. PROJECT HELP
32
- workon my-project:help # Show what commands are available
33
-
34
- 6. ERROR VALIDATION
35
- workon my-project:invalid # Clear error messages
36
-
37
- 7. SHELL MODE SUPPORT
38
- workon my-project:cwd --shell # Works with all flags
39
-
40
- 🏗️ IMPLEMENTATION HIGHLIGHTS:
41
-
42
- • Zero switchit changes needed - uses existing parameter parsing
43
- • Simple string split logic: project.split(':')
44
- • Integrates perfectly with Command-Centric Architecture
45
- • Smart layout detection works with any command combination
46
- • Comprehensive validation and dependency resolution
47
-
48
- 🎉 BENEFITS:
49
-
50
- • Faster startup for individual commands
51
- • Flexible workflow matching
52
- • Resource efficiency
53
- • Better testing and debugging
54
- • Foundation for future features (aliases, profiles, etc.)
55
-
56
- This feature transforms workon from "all-or-nothing" to "pick-what-you-need"!
57
- `);
@@ -1,304 +0,0 @@
1
- # ADR-001: Command-Centric Architecture
2
-
3
- **Status:** Implemented
4
- **Date:** 2025-08-07
5
- **Deciders:** Israel Roldan
6
-
7
- ## Context
8
-
9
- The current architecture of the workon CLI has several maintainability issues:
10
-
11
- ### Current Problems
12
-
13
- 1. **Scattered Logic**: Command definitions, validation, help text, and processing logic are spread across multiple files
14
- 2. **Manual Maintenance**: Hardcoded lists in multiple locations require updates when adding new commands
15
- 3. **Tight Coupling**: Adding a new command requires touching 4-6 different files
16
- 4. **No Auto-Discovery**: System doesn't automatically detect available commands
17
- 5. **Inconsistent Patterns**: Different commands follow different implementation patterns
18
-
19
- ### Current Architecture Issues
20
-
21
- When adding the NPM command, we had to modify:
22
- - `lib/validation.js` - Add to valid events list + validation logic
23
- - `cli/manage.js` - Add to event choices + configuration logic
24
- - `cli/open.js` - Add event processing logic
25
- - `lib/tmux.js` - Add layout management
26
- - Multiple hardcoded arrays and switch statements
27
-
28
- This creates:
29
- - High cognitive load when adding features
30
- - Risk of forgetting to update all locations
31
- - Inconsistent user experience
32
- - Difficult testing and debugging
33
-
34
- ## Decision
35
-
36
- We will refactor to a **Command-Centric Architecture** where each command is self-contained and owns its complete lifecycle.
37
-
38
- ## Proposed Architecture
39
-
40
- ### 1. Directory Structure
41
-
42
- ```
43
- commands/
44
- ├── core/ # Built-in system commands
45
- │ ├── cwd/
46
- │ │ ├── index.js # Main command class
47
- │ │ ├── processing.js # Event processing logic
48
- │ │ └── tmux.js # Tmux integration
49
- │ ├── ide/
50
- │ │ ├── index.js
51
- │ │ ├── validation.js # IDE validation logic
52
- │ │ └── processing.js
53
- │ └── web/
54
- │ ├── index.js
55
- │ ├── validation.js
56
- │ └── processing.js
57
- ├── extensions/ # Feature-rich commands
58
- │ ├── claude/
59
- │ │ ├── index.js
60
- │ │ ├── validation.js # Claude config validation
61
- │ │ ├── configuration.js # Interactive setup
62
- │ │ ├── processing.js # Event processing
63
- │ │ └── tmux.js # Split terminal logic
64
- │ └── npm/
65
- │ ├── index.js
66
- │ ├── validation.js # NPM config validation
67
- │ ├── configuration.js # Interactive setup
68
- │ ├── processing.js # Event processing
69
- │ └── tmux.js # Multi-pane layouts
70
- └── registry.js # Auto-discovery system
71
- ```
72
-
73
- ### 2. Command Interface
74
-
75
- Each command implements a standardized interface:
76
-
77
- ```javascript
78
- // commands/extensions/npm/index.js
79
- class NPMCommand {
80
- static metadata = {
81
- name: 'npm',
82
- displayName: 'Run NPM command',
83
- description: 'Execute NPM scripts in project directory',
84
- category: 'development',
85
- requiresTmux: true,
86
- dependencies: ['npm']
87
- }
88
-
89
- static validation = {
90
- // Command-specific validation
91
- validateConfig(config) { /* ... */ }
92
- }
93
-
94
- static configuration = {
95
- // Interactive setup prompts
96
- async configureInteractive() { /* ... */ },
97
- getDefaultConfig() { /* ... */ }
98
- }
99
-
100
- static processing = {
101
- // Event processing
102
- async processEvent(context) { /* ... */ },
103
- generateShellCommand(context) { /* ... */ }
104
- }
105
-
106
- static tmux = {
107
- // Tmux layout contributions
108
- getLayoutPriority() { return 10; },
109
- contributeToLayout(existingCommands) { /* ... */ }
110
- }
111
-
112
- static help = {
113
- usage: 'npm: <script-name>',
114
- examples: [
115
- { config: 'npm: "dev"', description: 'Run npm run dev' },
116
- { config: 'npm: { command: "test", watch: true }', description: 'Run tests in watch mode' }
117
- ]
118
- }
119
- }
120
- ```
121
-
122
- ### 3. Auto-Discovery System
123
-
124
- ```javascript
125
- // commands/registry.js
126
- class CommandRegistry {
127
- static async discoverCommands() {
128
- // Scan commands/ directory
129
- // Load command classes
130
- // Build registry of available commands
131
- }
132
-
133
- static getValidEventNames() {
134
- // Auto-generate from discovered commands
135
- }
136
-
137
- static getCommandByName(name) {
138
- // Lookup command instance
139
- }
140
-
141
- static getCommandsForManageUI() {
142
- // Get display info for interactive management
143
- }
144
- }
145
- ```
146
-
147
- ### 4. Integration Points
148
-
149
- #### Validation System
150
- ```javascript
151
- // lib/validation.js (simplified)
152
- class ProjectValidator {
153
- validateEvents(events) {
154
- const validCommands = CommandRegistry.getValidEventNames();
155
- // Delegate to command-specific validation
156
- for (const [eventName, config] of Object.entries(events)) {
157
- const command = CommandRegistry.getCommandByName(eventName);
158
- if (command) {
159
- const result = command.validation.validateConfig(config);
160
- if (result !== true) return result;
161
- }
162
- }
163
- }
164
- }
165
- ```
166
-
167
- #### Interactive Management
168
- ```javascript
169
- // cli/manage.js (simplified)
170
- class manage extends command {
171
- async getEventChoices() {
172
- return CommandRegistry.getCommandsForManageUI();
173
- }
174
-
175
- async configureEvent(eventName) {
176
- const command = CommandRegistry.getCommandByName(eventName);
177
- return await command.configuration.configureInteractive();
178
- }
179
- }
180
- ```
181
-
182
- #### Event Processing
183
- ```javascript
184
- // cli/open.js (simplified)
185
- class open extends command {
186
- async processEvent(eventName, project, context) {
187
- const command = CommandRegistry.getCommandByName(eventName);
188
- return await command.processing.processEvent({
189
- project,
190
- isShellMode: context.isShellMode,
191
- shellCommands: context.shellCommands
192
- });
193
- }
194
- }
195
- ```
196
-
197
- ### 5. Tmux Layout System
198
-
199
- Commands can contribute to tmux layouts with priority-based composition:
200
-
201
- ```javascript
202
- // Smart layout detection
203
- const enabledCommands = getEnabledCommands(project);
204
- const tmuxContributors = enabledCommands
205
- .filter(cmd => cmd.tmux)
206
- .sort((a, b) => b.tmux.getLayoutPriority() - a.tmux.getLayoutPriority());
207
-
208
- const layout = tmuxContributors[0].tmux.contributeToLayout(enabledCommands);
209
- ```
210
-
211
- ## Implementation Plan
212
-
213
- ### Phase 1: Foundation
214
- 1. Create `commands/` directory structure
215
- 2. Implement `CommandRegistry` with auto-discovery
216
- 3. Create base command interface/abstract class
217
- 4. Add command loading and registration system
218
-
219
- ### Phase 2: Core Command Migration
220
- 1. Extract `cwd` command to `commands/core/cwd/`
221
- 2. Extract `ide` command to `commands/core/ide/`
222
- 3. Extract `web` command to `commands/core/web/`
223
- 4. Update validation system to use registry
224
-
225
- ### Phase 3: Extension Command Migration
226
- 1. Extract `claude` command to `commands/extensions/claude/`
227
- 2. Extract `npm` command to `commands/extensions/npm/`
228
- 3. Update interactive management to use command definitions
229
- 4. Update event processing to use command registry
230
-
231
- ### Phase 4: Enhanced Features
232
- 1. Add command dependency checking
233
- 2. Implement plugin-like command loading
234
- 3. Add command-specific help system
235
- 4. Add command testing framework
236
-
237
- ### Phase 5: Advanced Tmux Integration
238
- 1. Implement priority-based layout composition
239
- 2. Add layout conflict resolution
240
- 3. Add custom layout definitions per command
241
- 4. Add layout preview/testing
242
-
243
- ## Benefits
244
-
245
- ### For Developers
246
- - **Single Responsibility**: Each command owns its complete lifecycle
247
- - **Easy Testing**: Isolated command logic with clear interfaces
248
- - **Plugin Architecture**: Easy to add/remove commands
249
- - **Better Organization**: Everything related to a command in one place
250
- - **Reduced Coupling**: Commands don't need to know about each other
251
-
252
- ### For Users
253
- - **Consistent Experience**: All commands follow same patterns
254
- - **Better Help**: Command-specific documentation and examples
255
- - **Auto-Discovery**: New commands automatically available
256
- - **Extensibility**: Easy to add custom commands
257
-
258
- ### For Maintenance
259
- - **No More Hardcoded Lists**: Commands auto-register themselves
260
- - **Easier Debugging**: Clear command boundaries
261
- - **Simpler Refactoring**: Changes contained within command scope
262
- - **Better Documentation**: Command documentation co-located with code
263
-
264
- ## Risks and Mitigations
265
-
266
- ### Risk: Increased Complexity
267
- **Mitigation**: Provide clear base classes and documentation. Start with simple commands.
268
-
269
- ### Risk: Performance Impact
270
- **Mitigation**: Lazy loading of commands. Cache registry after initial discovery.
271
-
272
- ### Risk: Breaking Changes
273
- **Mitigation**: Implement alongside existing system. Gradual migration path.
274
-
275
- ### Risk: Over-Engineering
276
- **Mitigation**: Start simple. Add complexity only when needed. Focus on current pain points.
277
-
278
- ## Success Criteria
279
-
280
- 1. Adding a new command requires touching only files in that command's directory
281
- 2. Command validation, configuration, and processing logic is co-located
282
- 3. Interactive management automatically discovers and presents new commands
283
- 4. Tmux layouts compose automatically based on enabled commands
284
- 5. Help system provides command-specific guidance
285
- 6. Testing can be done per-command with clear interfaces
286
-
287
- ## Alternative Considered
288
-
289
- ### Plugin System with External Commands
290
- - **Pros**: Ultimate flexibility, true plugin architecture
291
- - **Cons**: Complexity of plugin loading, versioning, security concerns
292
- - **Decision**: Too complex for current needs. Start with internal command structure.
293
-
294
- ### Monolithic Command Classes
295
- - **Pros**: Single file per command
296
- - **Cons**: Large files, harder to test specific aspects
297
- - **Decision**: Multi-file approach provides better separation of concerns.
298
-
299
- ## References
300
-
301
- - Current architecture pain points identified during NPM command implementation
302
- - Command pattern design principles
303
- - Plugin architecture best practices
304
- - CLI framework patterns (e.g., Angular CLI, Vue CLI command structures)