lsh-framework 1.2.0 → 1.3.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 (74) hide show
  1. package/README.md +40 -3
  2. package/dist/cli.js +104 -486
  3. package/dist/commands/doctor.js +427 -0
  4. package/dist/commands/init.js +371 -0
  5. package/dist/constants/api.js +94 -0
  6. package/dist/constants/commands.js +64 -0
  7. package/dist/constants/config.js +56 -0
  8. package/dist/constants/database.js +21 -0
  9. package/dist/constants/errors.js +79 -0
  10. package/dist/constants/index.js +28 -0
  11. package/dist/constants/paths.js +28 -0
  12. package/dist/constants/ui.js +73 -0
  13. package/dist/constants/validation.js +124 -0
  14. package/dist/daemon/lshd.js +11 -32
  15. package/dist/lib/daemon-client-helper.js +7 -4
  16. package/dist/lib/daemon-client.js +9 -2
  17. package/dist/lib/format-utils.js +163 -0
  18. package/dist/lib/fuzzy-match.js +123 -0
  19. package/dist/lib/job-manager.js +2 -1
  20. package/dist/lib/platform-utils.js +211 -0
  21. package/dist/lib/secrets-manager.js +11 -1
  22. package/dist/lib/string-utils.js +128 -0
  23. package/dist/services/daemon/daemon-registrar.js +3 -2
  24. package/dist/services/secrets/secrets.js +119 -59
  25. package/package.json +10 -74
  26. package/dist/app.js +0 -33
  27. package/dist/cicd/analytics.js +0 -261
  28. package/dist/cicd/auth.js +0 -269
  29. package/dist/cicd/cache-manager.js +0 -172
  30. package/dist/cicd/data-retention.js +0 -305
  31. package/dist/cicd/performance-monitor.js +0 -224
  32. package/dist/cicd/webhook-receiver.js +0 -640
  33. package/dist/commands/api.js +0 -346
  34. package/dist/commands/theme.js +0 -261
  35. package/dist/commands/zsh-import.js +0 -240
  36. package/dist/components/App.js +0 -1
  37. package/dist/components/Divider.js +0 -29
  38. package/dist/components/REPL.js +0 -43
  39. package/dist/components/Terminal.js +0 -232
  40. package/dist/components/UserInput.js +0 -30
  41. package/dist/daemon/api-server.js +0 -316
  42. package/dist/daemon/monitoring-api.js +0 -220
  43. package/dist/lib/api-error-handler.js +0 -185
  44. package/dist/lib/associative-arrays.js +0 -285
  45. package/dist/lib/base-api-server.js +0 -290
  46. package/dist/lib/brace-expansion.js +0 -160
  47. package/dist/lib/builtin-commands.js +0 -439
  48. package/dist/lib/executors/builtin-executor.js +0 -52
  49. package/dist/lib/extended-globbing.js +0 -411
  50. package/dist/lib/extended-parameter-expansion.js +0 -227
  51. package/dist/lib/interactive-shell.js +0 -460
  52. package/dist/lib/job-builtins.js +0 -582
  53. package/dist/lib/pathname-expansion.js +0 -216
  54. package/dist/lib/script-runner.js +0 -226
  55. package/dist/lib/shell-executor.js +0 -2504
  56. package/dist/lib/shell-parser.js +0 -958
  57. package/dist/lib/shell-types.js +0 -6
  58. package/dist/lib/shell.lib.js +0 -40
  59. package/dist/lib/theme-manager.js +0 -476
  60. package/dist/lib/variable-expansion.js +0 -385
  61. package/dist/lib/zsh-compatibility.js +0 -659
  62. package/dist/lib/zsh-import-manager.js +0 -707
  63. package/dist/lib/zsh-options.js +0 -328
  64. package/dist/pipeline/job-tracker.js +0 -491
  65. package/dist/pipeline/mcli-bridge.js +0 -309
  66. package/dist/pipeline/pipeline-service.js +0 -1119
  67. package/dist/pipeline/workflow-engine.js +0 -870
  68. package/dist/services/api/api.js +0 -58
  69. package/dist/services/api/auth.js +0 -35
  70. package/dist/services/api/config.js +0 -7
  71. package/dist/services/api/file.js +0 -22
  72. package/dist/services/shell/shell.js +0 -28
  73. package/dist/services/zapier.js +0 -16
  74. package/dist/simple-api-server.js +0 -148
@@ -1,346 +0,0 @@
1
- /**
2
- * API Server Commands for LSH
3
- */
4
- import chalk from 'chalk';
5
- import crypto from 'crypto';
6
- import DaemonClient from '../lib/daemon-client.js';
7
- export function registerApiCommands(program) {
8
- const api = program
9
- .command('api')
10
- .description('API server management and configuration');
11
- // Start daemon with API server enabled
12
- api
13
- .command('start')
14
- .description('Start daemon with API server enabled')
15
- .option('-p, --port <port>', 'API port', '3030')
16
- .option('-k, --api-key <key>', 'API key (generated if not provided)')
17
- .option('--webhooks', 'Enable webhook support', false)
18
- .action(async (options) => {
19
- try {
20
- const apiKey = options.apiKey || crypto.randomBytes(32).toString('hex');
21
- const daemonClient = new DaemonClient();
22
- // Check if daemon is already running
23
- if (daemonClient.isDaemonRunning()) {
24
- console.log(chalk.yellow('⚠️ Daemon is already running. Restarting with API enabled...'));
25
- // Stop existing daemon
26
- await daemonClient.connect();
27
- await daemonClient.stopDaemon();
28
- await new Promise(resolve => setTimeout(resolve, 1000));
29
- }
30
- // Write API configuration to environment or a config file
31
- process.env.LSH_API_ENABLED = 'true';
32
- process.env.LSH_API_PORT = options.port;
33
- process.env.LSH_API_KEY = apiKey;
34
- process.env.LSH_ENABLE_WEBHOOKS = options.webhooks ? 'true' : 'false';
35
- // Restart the daemon which will pick up the environment variables
36
- await daemonClient.restartDaemon();
37
- console.log(chalk.green('✅ Daemon started with API server'));
38
- console.log(chalk.blue(`\n📡 API Server: http://localhost:${options.port}`));
39
- console.log(chalk.yellow(`🔑 API Key: ${apiKey}`));
40
- console.log(chalk.gray('\nStore this API key securely. You will need it to authenticate API requests.'));
41
- console.log(chalk.gray(`\nExample usage:`));
42
- console.log(chalk.gray(` curl -H "X-API-Key: ${apiKey}" http://localhost:${options.port}/api/status`));
43
- }
44
- catch (error) {
45
- const err = error;
46
- console.error(chalk.red(`❌ Failed to start API server: ${err.message}`));
47
- process.exit(1);
48
- }
49
- });
50
- // Generate API key
51
- api
52
- .command('key')
53
- .description('Generate a new API key')
54
- .action(() => {
55
- const apiKey = crypto.randomBytes(32).toString('hex');
56
- console.log(chalk.green('🔑 Generated API Key:'));
57
- console.log(apiKey);
58
- console.log(chalk.gray('\nSet this as LSH_API_KEY environment variable:'));
59
- console.log(chalk.gray(` export LSH_API_KEY="${apiKey}"`));
60
- });
61
- // Test API connection
62
- api
63
- .command('test')
64
- .description('Test API server connection')
65
- .option('-p, --port <port>', 'API port', '3030')
66
- .option('-k, --api-key <key>', 'API key')
67
- .action(async (options) => {
68
- try {
69
- const apiKey = options.apiKey || process.env.LSH_API_KEY;
70
- if (!apiKey) {
71
- console.error(chalk.red('❌ API key required. Use --api-key or set LSH_API_KEY environment variable'));
72
- process.exit(1);
73
- }
74
- const response = await fetch(`http://localhost:${options.port}/api/status`, {
75
- headers: {
76
- 'X-API-Key': apiKey
77
- }
78
- });
79
- if (response.ok) {
80
- const status = await response.json();
81
- console.log(chalk.green('✅ API server is running'));
82
- console.log('Status:', JSON.stringify(status, null, 2));
83
- }
84
- else {
85
- console.error(chalk.red(`❌ API server returned ${response.status}: ${response.statusText}`));
86
- }
87
- }
88
- catch (error) {
89
- const err = error;
90
- console.error(chalk.red(`❌ Failed to connect to API server: ${err.message}`));
91
- console.log(chalk.yellow('Make sure the daemon is running with API enabled:'));
92
- console.log(chalk.gray(' lsh api start'));
93
- }
94
- });
95
- // Configure webhooks
96
- api
97
- .command('webhook')
98
- .description('Configure webhook endpoints')
99
- .argument('<action>', 'Action: add, list, remove')
100
- .argument('[endpoint]', 'Webhook endpoint URL')
101
- .option('-p, --port <port>', 'API port', '3030')
102
- .option('-k, --api-key <key>', 'API key')
103
- .action(async (action, endpoint, options) => {
104
- try {
105
- const apiKey = options.apiKey || process.env.LSH_API_KEY;
106
- if (!apiKey) {
107
- console.error(chalk.red('❌ API key required'));
108
- process.exit(1);
109
- }
110
- const baseUrl = `http://localhost:${options.port}`;
111
- switch (action) {
112
- case 'add': {
113
- if (!endpoint) {
114
- console.error(chalk.red('❌ Endpoint URL required'));
115
- process.exit(1);
116
- }
117
- const addResponse = await fetch(`${baseUrl}/api/webhooks`, {
118
- method: 'POST',
119
- headers: {
120
- 'X-API-Key': apiKey,
121
- 'Content-Type': 'application/json'
122
- },
123
- body: JSON.stringify({ endpoint })
124
- });
125
- if (addResponse.ok) {
126
- const result = await addResponse.json();
127
- console.log(chalk.green('✅ Webhook added successfully'));
128
- console.log('Endpoints:', result.endpoints);
129
- }
130
- else {
131
- console.error(chalk.red('❌ Failed to add webhook'));
132
- }
133
- break;
134
- }
135
- case 'list': {
136
- const listResponse = await fetch(`${baseUrl}/api/webhooks`, {
137
- headers: { 'X-API-Key': apiKey }
138
- });
139
- if (listResponse.ok) {
140
- const webhooks = await listResponse.json();
141
- console.log(chalk.blue('📮 Webhook Configuration:'));
142
- console.log(`Enabled: ${webhooks.enabled}`);
143
- console.log('Endpoints:', webhooks.endpoints);
144
- }
145
- break;
146
- }
147
- default:
148
- console.error(chalk.red(`❌ Unknown action: ${action}`));
149
- console.log('Valid actions: add, list, remove');
150
- }
151
- }
152
- catch (error) {
153
- const err = error;
154
- console.error(chalk.red(`❌ Failed: ${err.message}`));
155
- }
156
- });
157
- // Example client code generator
158
- api
159
- .command('example')
160
- .description('Generate example client code')
161
- .option('-l, --language <lang>', 'Language (js, python, curl)', 'js')
162
- .option('-p, --port <port>', 'API port', '3030')
163
- .action((options) => {
164
- const apiKey = process.env.LSH_API_KEY || 'YOUR_API_KEY';
165
- switch (options.language) {
166
- case 'js':
167
- console.log(chalk.blue('// JavaScript Example Client\n'));
168
- console.log(`const LSHClient = {
169
- baseURL: 'http://localhost:${options.port}',
170
- apiKey: '${apiKey}',
171
-
172
- async request(path, options = {}) {
173
- const response = await fetch(\`\${this.baseURL}\${path}\`, {
174
- ...options,
175
- headers: {
176
- 'X-API-Key': this.apiKey,
177
- 'Content-Type': 'application/json',
178
- ...options.headers
179
- }
180
- });
181
-
182
- if (!response.ok) {
183
- throw new Error(\`API error: \${response.statusText}\`);
184
- }
185
-
186
- return response.json();
187
- },
188
-
189
- // Get daemon status
190
- async getStatus() {
191
- return this.request('/api/status');
192
- },
193
-
194
- // List all jobs
195
- async listJobs() {
196
- return this.request('/api/jobs');
197
- },
198
-
199
- // Create a new job
200
- async createJob(jobSpec) {
201
- return this.request('/api/jobs', {
202
- method: 'POST',
203
- body: JSON.stringify(jobSpec)
204
- });
205
- },
206
-
207
- // Trigger job execution
208
- async triggerJob(jobId) {
209
- return this.request(\`/api/jobs/\${jobId}/trigger\`, {
210
- method: 'POST'
211
- });
212
- },
213
-
214
- // Stream events using EventSource
215
- streamEvents() {
216
- const eventSource = new EventSource(\`\${this.baseURL}/api/events\`);
217
-
218
- eventSource.onmessage = (event) => {
219
- const data = JSON.parse(event.data);
220
- console.log('Event:', data);
221
- };
222
-
223
- eventSource.onerror = (error) => {
224
- console.error('EventSource error:', error);
225
- };
226
-
227
- return eventSource;
228
- }
229
- };
230
-
231
- // Example usage
232
- (async () => {
233
- try {
234
- const status = await LSHClient.getStatus();
235
- console.log('Daemon status:', status);
236
-
237
- // Create a job
238
- const job = await LSHClient.createJob({
239
- name: 'Example Job',
240
- command: 'echo "Hello from API"',
241
- type: 'shell'
242
- });
243
- console.log('Created job:', job);
244
-
245
- // Trigger the job
246
- const result = await LSHClient.triggerJob(job.id);
247
- console.log('Job result:', result);
248
-
249
- } catch (error) {
250
- console.error('Error:', error);
251
- }
252
- })();`);
253
- break;
254
- case 'python':
255
- console.log(chalk.blue('# Python Example Client\n'));
256
- console.log(`import requests
257
- import json
258
- from typing import Dict, Any, Optional
259
-
260
- class LSHClient:
261
- def __init__(self, base_url: str = "http://localhost:${options.port}", api_key: str = "${apiKey}"):
262
- self.base_url = base_url
263
- self.api_key = api_key
264
- self.headers = {
265
- "X-API-Key": api_key,
266
- "Content-Type": "application/json"
267
- }
268
-
269
- def request(self, method: str, path: str, data: Optional[Dict] = None) -> Dict[str, Any]:
270
- """Make an API request"""
271
- url = f"{self.base_url}{path}"
272
- response = requests.request(method, url, headers=self.headers, json=data)
273
- response.raise_for_status()
274
- return response.json()
275
-
276
- def get_status(self) -> Dict[str, Any]:
277
- """Get daemon status"""
278
- return self.request("GET", "/api/status")
279
-
280
- def list_jobs(self) -> list:
281
- """List all jobs"""
282
- return self.request("GET", "/api/jobs")
283
-
284
- def create_job(self, job_spec: Dict[str, Any]) -> Dict[str, Any]:
285
- """Create a new job"""
286
- return self.request("POST", "/api/jobs", job_spec)
287
-
288
- def trigger_job(self, job_id: str) -> Dict[str, Any]:
289
- """Trigger job execution"""
290
- return self.request("POST", f"/api/jobs/{job_id}/trigger")
291
-
292
- # Example usage
293
- if __name__ == "__main__":
294
- client = LSHClient()
295
-
296
- # Get status
297
- status = client.get_status()
298
- print(f"Daemon status: {json.dumps(status, indent=2)}")
299
-
300
- # Create a job
301
- job = client.create_job({
302
- "name": "Example Job",
303
- "command": "echo 'Hello from Python'",
304
- "type": "shell"
305
- })
306
- print(f"Created job: {job}")
307
-
308
- # Trigger the job
309
- result = client.trigger_job(job["id"])
310
- print(f"Job result: {result}")`);
311
- break;
312
- case 'curl':
313
- console.log(chalk.blue('# cURL Examples\n'));
314
- console.log(`# Get daemon status
315
- curl -H "X-API-Key: ${apiKey}" \\
316
- http://localhost:${options.port}/api/status
317
-
318
- # List all jobs
319
- curl -H "X-API-Key: ${apiKey}" \\
320
- http://localhost:${options.port}/api/jobs
321
-
322
- # Create a new job
323
- curl -X POST \\
324
- -H "X-API-Key: ${apiKey}" \\
325
- -H "Content-Type: application/json" \\
326
- -d '{
327
- "name": "Example Job",
328
- "command": "echo Hello World",
329
- "type": "shell"
330
- }' \\
331
- http://localhost:${options.port}/api/jobs
332
-
333
- # Trigger a job
334
- curl -X POST \\
335
- -H "X-API-Key: ${apiKey}" \\
336
- http://localhost:${options.port}/api/jobs/JOB_ID/trigger
337
-
338
- # Stream events
339
- curl -H "X-API-Key: ${apiKey}" \\
340
- -H "Accept: text/event-stream" \\
341
- http://localhost:${options.port}/api/events`);
342
- break;
343
- }
344
- });
345
- }
346
- export default registerApiCommands;
@@ -1,261 +0,0 @@
1
- /**
2
- * Theme Commands
3
- * Import, preview, and apply ZSH themes
4
- */
5
- import { ThemeManager } from '../lib/theme-manager.js';
6
- import chalk from 'chalk';
7
- import * as fs from 'fs';
8
- import * as path from 'path';
9
- import * as os from 'os';
10
- export function registerThemeCommands(program) {
11
- const themeCommand = program
12
- .command('theme')
13
- .description('Manage shell themes (import Oh-My-Zsh themes, apply built-in themes)');
14
- themeCommand
15
- .command('list')
16
- .description('List available themes')
17
- .option('--ohmyzsh', 'Show only Oh-My-Zsh themes')
18
- .option('--builtin', 'Show only built-in themes')
19
- .option('--custom', 'Show only custom themes')
20
- .action(async (options) => {
21
- const manager = new ThemeManager();
22
- const themes = manager.listThemes();
23
- console.log(chalk.bold('\n🎨 Available Themes\n'));
24
- if (!options.ohmyzsh && !options.custom || options.builtin) {
25
- console.log(chalk.cyan('Built-in Themes:'));
26
- themes.builtin.forEach(name => {
27
- console.log(` ${chalk.green('✓')} ${name}`);
28
- });
29
- console.log('');
30
- }
31
- if (!options.builtin && !options.custom || options.ohmyzsh) {
32
- if (themes.ohmyzsh.length > 0) {
33
- console.log(chalk.cyan('Oh-My-Zsh Themes:'));
34
- themes.ohmyzsh.slice(0, 20).forEach(name => {
35
- console.log(` ${chalk.yellow('◆')} ${name}`);
36
- });
37
- if (themes.ohmyzsh.length > 20) {
38
- console.log(chalk.dim(` ... and ${themes.ohmyzsh.length - 20} more`));
39
- }
40
- console.log('');
41
- }
42
- else {
43
- console.log(chalk.dim('No Oh-My-Zsh themes found. Install Oh-My-Zsh first.'));
44
- console.log('');
45
- }
46
- }
47
- if (!options.builtin && !options.ohmyzsh || options.custom) {
48
- if (themes.custom.length > 0) {
49
- console.log(chalk.cyan('Custom Themes:'));
50
- themes.custom.forEach(name => {
51
- console.log(` ${chalk.magenta('●')} ${name}`);
52
- });
53
- }
54
- else {
55
- console.log(chalk.dim('No custom themes found.'));
56
- }
57
- console.log('');
58
- }
59
- console.log(chalk.dim('Usage:'));
60
- console.log(chalk.dim(' lsh theme preview <name>'));
61
- console.log(chalk.dim(' lsh theme import <name> # For Oh-My-Zsh themes'));
62
- console.log(chalk.dim(' lsh theme apply <name>'));
63
- console.log('');
64
- // Note: Removed process.exit(0) to allow proper Jest testing
65
- // Commander will handle exit automatically
66
- });
67
- themeCommand
68
- .command('import <name>')
69
- .description('Import Oh-My-Zsh theme')
70
- .option('--preview', 'Preview before importing')
71
- .action(async (name, options) => {
72
- try {
73
- const manager = new ThemeManager();
74
- console.log(chalk.dim(`Importing theme: ${name}...`));
75
- const theme = await manager.importOhMyZshTheme(name);
76
- console.log(chalk.green(`✓ Successfully imported theme: ${name}`));
77
- if (options.preview) {
78
- manager.previewTheme(theme);
79
- }
80
- console.log(chalk.dim('\nTo apply this theme:'));
81
- console.log(chalk.cyan(` lsh theme apply ${name}`));
82
- console.log('');
83
- process.exit(0);
84
- }
85
- catch (error) {
86
- const err = error;
87
- console.error(chalk.red(`✗ Failed to import theme: ${err.message}`));
88
- process.exit(1);
89
- }
90
- });
91
- themeCommand
92
- .command('preview <name>')
93
- .description('Preview theme without applying')
94
- .action(async (name) => {
95
- try {
96
- const manager = new ThemeManager();
97
- // Try to load theme
98
- let theme;
99
- try {
100
- theme = await manager.importOhMyZshTheme(name);
101
- }
102
- catch {
103
- // Try built-in
104
- theme = manager.getBuiltinTheme(name);
105
- }
106
- manager.previewTheme(theme);
107
- console.log(chalk.dim('To apply this theme:'));
108
- console.log(chalk.cyan(` lsh theme apply ${name}`));
109
- console.log('');
110
- process.exit(0);
111
- }
112
- catch (_error) {
113
- console.error(chalk.red(`✗ Theme not found: ${name}`));
114
- console.log(chalk.dim('\nAvailable themes:'));
115
- console.log(chalk.dim(' lsh theme list'));
116
- process.exit(1);
117
- }
118
- });
119
- themeCommand
120
- .command('apply <name>')
121
- .description('Apply theme to current shell')
122
- .option('--save', 'Save to ~/.lshrc for persistent use')
123
- .action(async (name, options) => {
124
- try {
125
- const manager = new ThemeManager();
126
- // Try to import/load theme
127
- let theme;
128
- try {
129
- // Try as Oh-My-Zsh theme first
130
- theme = await manager.importOhMyZshTheme(name);
131
- }
132
- catch {
133
- // Try as built-in theme
134
- theme = manager.getBuiltinTheme(name);
135
- }
136
- const commands = manager.applyTheme(theme);
137
- console.log(chalk.green(`✓ Applied theme: ${name}`));
138
- console.log('');
139
- console.log(chalk.dim('Theme settings:'));
140
- console.log(commands);
141
- console.log('');
142
- if (options.save) {
143
- const lshrcPath = path.join(os.homedir(), '.lshrc');
144
- let lshrcContent = '';
145
- if (fs.existsSync(lshrcPath)) {
146
- lshrcContent = fs.readFileSync(lshrcPath, 'utf8');
147
- // Remove old theme settings
148
- lshrcContent = lshrcContent.replace(/# LSH Theme[\s\S]*?# End LSH Theme\n*/g, '');
149
- }
150
- const themeBlock = `
151
- # LSH Theme: ${name}
152
- ${commands}
153
- # End LSH Theme
154
- `;
155
- lshrcContent += themeBlock;
156
- fs.writeFileSync(lshrcPath, lshrcContent, 'utf8');
157
- console.log(chalk.green('✓ Theme saved to ~/.lshrc'));
158
- console.log(chalk.dim(' Theme will be applied automatically on next shell start'));
159
- }
160
- else {
161
- console.log(chalk.dim('To save permanently:'));
162
- console.log(chalk.cyan(` lsh theme apply ${name} --save`));
163
- }
164
- console.log('');
165
- process.exit(0);
166
- }
167
- catch (error) {
168
- const err = error;
169
- console.error(chalk.red(`✗ Failed to apply theme: ${err.message}`));
170
- process.exit(1);
171
- }
172
- });
173
- themeCommand
174
- .command('current')
175
- .description('Show current theme')
176
- .action(() => {
177
- const lshrcPath = path.join(os.homedir(), '.lshrc');
178
- if (!fs.existsSync(lshrcPath)) {
179
- console.log(chalk.dim('No theme configured (using default)'));
180
- process.exit(0);
181
- return;
182
- }
183
- const content = fs.readFileSync(lshrcPath, 'utf8');
184
- const themeMatch = content.match(/# LSH Theme: (.+)/);
185
- if (themeMatch) {
186
- console.log(chalk.bold(`Current theme: ${chalk.cyan(themeMatch[1])}`));
187
- }
188
- else {
189
- console.log(chalk.dim('No theme configured (using default)'));
190
- }
191
- process.exit(0);
192
- });
193
- themeCommand
194
- .command('reset')
195
- .description('Reset to default theme')
196
- .action(() => {
197
- const lshrcPath = path.join(os.homedir(), '.lshrc');
198
- if (fs.existsSync(lshrcPath)) {
199
- let content = fs.readFileSync(lshrcPath, 'utf8');
200
- content = content.replace(/# LSH Theme[\s\S]*?# End LSH Theme\n*/g, '');
201
- fs.writeFileSync(lshrcPath, content, 'utf8');
202
- }
203
- console.log(chalk.green('✓ Reset to default theme'));
204
- console.log(chalk.dim('Restart shell to see changes'));
205
- process.exit(0);
206
- });
207
- themeCommand
208
- .command('from-zshrc')
209
- .description('Import current ZSH theme from ~/.zshrc')
210
- .option('--apply', 'Apply after importing')
211
- .action(async (options) => {
212
- try {
213
- const zshrcPath = path.join(os.homedir(), '.zshrc');
214
- if (!fs.existsSync(zshrcPath)) {
215
- console.error(chalk.red('✗ ~/.zshrc not found'));
216
- process.exit(1);
217
- return;
218
- }
219
- const zshrcContent = fs.readFileSync(zshrcPath, 'utf8');
220
- const themeMatch = zshrcContent.match(/ZSH_THEME="([^"]+)"/);
221
- if (!themeMatch) {
222
- console.error(chalk.red('✗ No theme found in ~/.zshrc'));
223
- process.exit(1);
224
- return;
225
- }
226
- const themeName = themeMatch[1];
227
- console.log(chalk.dim(`Found ZSH theme: ${themeName}`));
228
- const manager = new ThemeManager();
229
- const theme = await manager.importOhMyZshTheme(themeName);
230
- console.log(chalk.green(`✓ Imported theme: ${themeName}`));
231
- if (options.apply) {
232
- const commands = manager.applyTheme(theme);
233
- const lshrcPath = path.join(os.homedir(), '.lshrc');
234
- let lshrcContent = '';
235
- if (fs.existsSync(lshrcPath)) {
236
- lshrcContent = fs.readFileSync(lshrcPath, 'utf8');
237
- lshrcContent = lshrcContent.replace(/# LSH Theme[\s\S]*?# End LSH Theme\n*/g, '');
238
- }
239
- const themeBlock = `
240
- # LSH Theme: ${themeName}
241
- ${commands}
242
- # End LSH Theme
243
- `;
244
- lshrcContent += themeBlock;
245
- fs.writeFileSync(lshrcPath, lshrcContent, 'utf8');
246
- console.log(chalk.green('✓ Theme applied and saved to ~/.lshrc'));
247
- }
248
- else {
249
- console.log(chalk.dim('\nTo apply:'));
250
- console.log(chalk.cyan(` lsh theme apply ${themeName} --save`));
251
- }
252
- console.log('');
253
- process.exit(0);
254
- }
255
- catch (error) {
256
- const err = error;
257
- console.error(chalk.red(`✗ Failed to import theme: ${err.message}`));
258
- process.exit(1);
259
- }
260
- });
261
- }