lsh-framework 0.5.4

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 (90) hide show
  1. package/.env.example +51 -0
  2. package/README.md +399 -0
  3. package/dist/app.js +33 -0
  4. package/dist/cicd/analytics.js +261 -0
  5. package/dist/cicd/auth.js +269 -0
  6. package/dist/cicd/cache-manager.js +172 -0
  7. package/dist/cicd/data-retention.js +305 -0
  8. package/dist/cicd/performance-monitor.js +224 -0
  9. package/dist/cicd/webhook-receiver.js +634 -0
  10. package/dist/cli.js +500 -0
  11. package/dist/commands/api.js +343 -0
  12. package/dist/commands/self.js +318 -0
  13. package/dist/commands/theme.js +257 -0
  14. package/dist/commands/zsh-import.js +240 -0
  15. package/dist/components/App.js +1 -0
  16. package/dist/components/Divider.js +29 -0
  17. package/dist/components/REPL.js +43 -0
  18. package/dist/components/Terminal.js +232 -0
  19. package/dist/components/UserInput.js +30 -0
  20. package/dist/daemon/api-server.js +315 -0
  21. package/dist/daemon/job-registry.js +554 -0
  22. package/dist/daemon/lshd.js +822 -0
  23. package/dist/daemon/monitoring-api.js +220 -0
  24. package/dist/examples/supabase-integration.js +106 -0
  25. package/dist/lib/api-error-handler.js +183 -0
  26. package/dist/lib/associative-arrays.js +285 -0
  27. package/dist/lib/base-api-server.js +290 -0
  28. package/dist/lib/base-command-registrar.js +286 -0
  29. package/dist/lib/base-job-manager.js +293 -0
  30. package/dist/lib/brace-expansion.js +160 -0
  31. package/dist/lib/builtin-commands.js +439 -0
  32. package/dist/lib/cloud-config-manager.js +347 -0
  33. package/dist/lib/command-validator.js +190 -0
  34. package/dist/lib/completion-system.js +344 -0
  35. package/dist/lib/cron-job-manager.js +364 -0
  36. package/dist/lib/daemon-client-helper.js +141 -0
  37. package/dist/lib/daemon-client.js +501 -0
  38. package/dist/lib/database-persistence.js +638 -0
  39. package/dist/lib/database-schema.js +259 -0
  40. package/dist/lib/enhanced-history-system.js +246 -0
  41. package/dist/lib/env-validator.js +265 -0
  42. package/dist/lib/executors/builtin-executor.js +52 -0
  43. package/dist/lib/extended-globbing.js +411 -0
  44. package/dist/lib/extended-parameter-expansion.js +227 -0
  45. package/dist/lib/floating-point-arithmetic.js +256 -0
  46. package/dist/lib/history-system.js +245 -0
  47. package/dist/lib/interactive-shell.js +460 -0
  48. package/dist/lib/job-builtins.js +580 -0
  49. package/dist/lib/job-manager.js +386 -0
  50. package/dist/lib/job-storage-database.js +156 -0
  51. package/dist/lib/job-storage-memory.js +73 -0
  52. package/dist/lib/logger.js +274 -0
  53. package/dist/lib/lshrc-init.js +177 -0
  54. package/dist/lib/pathname-expansion.js +216 -0
  55. package/dist/lib/prompt-system.js +328 -0
  56. package/dist/lib/script-runner.js +226 -0
  57. package/dist/lib/secrets-manager.js +193 -0
  58. package/dist/lib/shell-executor.js +2504 -0
  59. package/dist/lib/shell-parser.js +958 -0
  60. package/dist/lib/shell-types.js +6 -0
  61. package/dist/lib/shell.lib.js +40 -0
  62. package/dist/lib/supabase-client.js +58 -0
  63. package/dist/lib/theme-manager.js +476 -0
  64. package/dist/lib/variable-expansion.js +385 -0
  65. package/dist/lib/zsh-compatibility.js +658 -0
  66. package/dist/lib/zsh-import-manager.js +699 -0
  67. package/dist/lib/zsh-options.js +328 -0
  68. package/dist/pipeline/job-tracker.js +491 -0
  69. package/dist/pipeline/mcli-bridge.js +302 -0
  70. package/dist/pipeline/pipeline-service.js +1116 -0
  71. package/dist/pipeline/workflow-engine.js +867 -0
  72. package/dist/services/api/api.js +58 -0
  73. package/dist/services/api/auth.js +35 -0
  74. package/dist/services/api/config.js +7 -0
  75. package/dist/services/api/file.js +22 -0
  76. package/dist/services/cron/cron-registrar.js +235 -0
  77. package/dist/services/cron/cron.js +9 -0
  78. package/dist/services/daemon/daemon-registrar.js +565 -0
  79. package/dist/services/daemon/daemon.js +9 -0
  80. package/dist/services/lib/lib.js +86 -0
  81. package/dist/services/log-file-extractor.js +170 -0
  82. package/dist/services/secrets/secrets.js +94 -0
  83. package/dist/services/shell/shell.js +28 -0
  84. package/dist/services/supabase/supabase-registrar.js +367 -0
  85. package/dist/services/supabase/supabase.js +9 -0
  86. package/dist/services/zapier.js +16 -0
  87. package/dist/simple-api-server.js +148 -0
  88. package/dist/store/store.js +31 -0
  89. package/dist/util/lib.util.js +11 -0
  90. package/package.json +144 -0
@@ -0,0 +1,658 @@
1
+ /**
2
+ * ZSH Compatibility Layer
3
+ * Provides compatibility with ZSH configurations and completions
4
+ */
5
+ import * as fs from 'fs';
6
+ import * as path from 'path';
7
+ import * as os from 'os';
8
+ import { parseShellCommand } from './shell-parser.js';
9
+ import { ZshImportManager } from './zsh-import-manager.js';
10
+ export class ZshCompatibility {
11
+ executor;
12
+ options;
13
+ importManager;
14
+ constructor(executor, options = {}) {
15
+ this.executor = executor;
16
+ this.options = {
17
+ sourceZshrc: true,
18
+ zshrcPath: path.join(os.homedir(), '.zshrc'),
19
+ respectZshCompletions: true,
20
+ zshCompletionsPath: path.join(os.homedir(), '.zsh/completions'),
21
+ installPackages: true,
22
+ packageManager: 'npm',
23
+ ...options,
24
+ };
25
+ // Initialize import manager with options
26
+ this.importManager = new ZshImportManager(executor, this.options.importOptions);
27
+ }
28
+ /**
29
+ * Source ZSH configuration files (enhanced version)
30
+ */
31
+ async sourceZshConfig() {
32
+ const result = await this.importManager.importZshConfig(this.options.zshrcPath);
33
+ return {
34
+ success: result.success,
35
+ message: result.message,
36
+ };
37
+ }
38
+ /**
39
+ * Source ZSH configuration files (legacy method for backward compatibility)
40
+ */
41
+ async sourceZshConfigLegacy() {
42
+ try {
43
+ // Check if .zshrc exists
44
+ if (!fs.existsSync(this.options.zshrcPath)) {
45
+ return {
46
+ success: false,
47
+ message: `ZSH configuration not found: ${this.options.zshrcPath}`,
48
+ };
49
+ }
50
+ // Read and parse .zshrc
51
+ const zshrcContent = fs.readFileSync(this.options.zshrcPath, 'utf8');
52
+ const parsedConfig = this.parseZshrc(zshrcContent);
53
+ // Apply compatible configurations
54
+ await this.applyZshConfig(parsedConfig);
55
+ return {
56
+ success: true,
57
+ message: `Successfully sourced ZSH configuration from ${this.options.zshrcPath}`,
58
+ };
59
+ }
60
+ catch (error) {
61
+ return {
62
+ success: false,
63
+ message: `Error sourcing ZSH config: ${error.message}`,
64
+ };
65
+ }
66
+ }
67
+ /**
68
+ * Parse .zshrc content and extract compatible configurations
69
+ */
70
+ parseZshrc(content) {
71
+ const lines = content.split('\n');
72
+ const config = {
73
+ aliases: [],
74
+ functions: [],
75
+ exports: [],
76
+ setopts: [],
77
+ completions: [],
78
+ plugins: [],
79
+ };
80
+ let inFunction = false;
81
+ let currentFunction = '';
82
+ let functionBody = '';
83
+ for (const line of lines) {
84
+ const trimmed = line.trim();
85
+ // Skip comments and empty lines
86
+ if (trimmed.startsWith('#') || trimmed === '') {
87
+ continue;
88
+ }
89
+ // Handle function definitions
90
+ if (trimmed.match(/^[a-zA-Z_][a-zA-Z0-9_]*\s*\(\)\s*\{/)) {
91
+ inFunction = true;
92
+ currentFunction = trimmed.split('(')[0].trim();
93
+ functionBody = trimmed;
94
+ continue;
95
+ }
96
+ if (inFunction) {
97
+ functionBody += '\n' + line;
98
+ if (trimmed === '}' || trimmed.endsWith('}')) {
99
+ inFunction = false;
100
+ config.functions.push({
101
+ name: currentFunction,
102
+ body: functionBody,
103
+ });
104
+ currentFunction = '';
105
+ functionBody = '';
106
+ }
107
+ continue;
108
+ }
109
+ // Parse aliases
110
+ const aliasMatch = trimmed.match(/^alias\s+([^=]+)=(.+)$/);
111
+ if (aliasMatch) {
112
+ config.aliases.push({
113
+ name: aliasMatch[1],
114
+ value: aliasMatch[2].replace(/^['"]|['"]$/g, ''), // Remove quotes
115
+ });
116
+ continue;
117
+ }
118
+ // Parse exports
119
+ const exportMatch = trimmed.match(/^export\s+([^=]+)=(.+)$/);
120
+ if (exportMatch) {
121
+ config.exports.push({
122
+ name: exportMatch[1],
123
+ value: exportMatch[2].replace(/^['"]|['"]$/g, ''), // Remove quotes
124
+ });
125
+ continue;
126
+ }
127
+ // Parse setopt
128
+ const setoptMatch = trimmed.match(/^setopt\s+(.+)$/);
129
+ if (setoptMatch) {
130
+ const options = setoptMatch[1].split(/\s+/);
131
+ for (const option of options) {
132
+ config.setopts.push({
133
+ option: option.trim(),
134
+ enabled: true,
135
+ });
136
+ }
137
+ continue;
138
+ }
139
+ // Parse unsetopt
140
+ const unsetoptMatch = trimmed.match(/^unsetopt\s+(.+)$/);
141
+ if (unsetoptMatch) {
142
+ const options = unsetoptMatch[1].split(/\s+/);
143
+ for (const option of options) {
144
+ config.setopts.push({
145
+ option: option.trim(),
146
+ enabled: false,
147
+ });
148
+ }
149
+ continue;
150
+ }
151
+ // Parse completions
152
+ if (trimmed.includes('compinit') || trimmed.includes('autoload')) {
153
+ config.completions.push(trimmed);
154
+ continue;
155
+ }
156
+ // Parse plugins (Oh My Zsh, etc.)
157
+ if (trimmed.includes('plugins=') || trimmed.includes('plugin=')) {
158
+ const pluginMatch = trimmed.match(/plugins?=['"]([^'"]+)['"]/);
159
+ if (pluginMatch) {
160
+ const plugins = pluginMatch[1].split(/\s+/);
161
+ config.plugins.push(...plugins);
162
+ }
163
+ continue;
164
+ }
165
+ }
166
+ return config;
167
+ }
168
+ /**
169
+ * Apply parsed ZSH configuration to LSH
170
+ */
171
+ async applyZshConfig(config) {
172
+ // Apply aliases
173
+ for (const alias of config.aliases) {
174
+ try {
175
+ const ast = parseShellCommand(`alias ${alias.name}="${alias.value}"`);
176
+ await this.executor.execute(ast);
177
+ }
178
+ catch (error) {
179
+ console.error(`Failed to apply alias ${alias.name}: ${error.message}`);
180
+ }
181
+ }
182
+ // Apply exports
183
+ for (const export_ of config.exports) {
184
+ try {
185
+ const ast = parseShellCommand(`export ${export_.name}="${export_.value}"`);
186
+ await this.executor.execute(ast);
187
+ }
188
+ catch (error) {
189
+ console.error(`Failed to apply export ${export_.name}: ${error.message}`);
190
+ }
191
+ }
192
+ // Apply setopts
193
+ for (const setopt of config.setopts) {
194
+ try {
195
+ const command = setopt.enabled ? 'setopt' : 'unsetopt';
196
+ const ast = parseShellCommand(`${command} ${setopt.option}`);
197
+ await this.executor.execute(ast);
198
+ }
199
+ catch (error) {
200
+ console.error(`Failed to apply setopt ${setopt.option}: ${error.message}`);
201
+ }
202
+ }
203
+ // Apply functions
204
+ for (const func of config.functions) {
205
+ try {
206
+ const ast = parseShellCommand(func.body);
207
+ await this.executor.execute(ast);
208
+ }
209
+ catch (error) {
210
+ console.error(`Failed to apply function ${func.name}: ${error.message}`);
211
+ }
212
+ }
213
+ // Load ZSH completions
214
+ if (this.options.respectZshCompletions) {
215
+ await this.loadZshCompletions(config.completions);
216
+ }
217
+ }
218
+ /**
219
+ * Load ZSH completions
220
+ */
221
+ async loadZshCompletions(_completionConfigs) {
222
+ // Common ZSH completion paths
223
+ const completionPaths = [
224
+ path.join(os.homedir(), '.zsh/completions'),
225
+ path.join(os.homedir(), '.oh-my-zsh/completions'),
226
+ '/usr/local/share/zsh/site-functions',
227
+ '/usr/share/zsh/site-functions',
228
+ '/usr/share/zsh/functions',
229
+ ];
230
+ for (const completionPath of completionPaths) {
231
+ if (fs.existsSync(completionPath)) {
232
+ await this.loadCompletionsFromPath(completionPath);
233
+ }
234
+ }
235
+ // Load Oh My Zsh completions
236
+ const ohMyZshPath = path.join(os.homedir(), '.oh-my-zsh');
237
+ if (fs.existsSync(ohMyZshPath)) {
238
+ await this.loadOhMyZshCompletions(ohMyZshPath);
239
+ }
240
+ }
241
+ /**
242
+ * Load completions from a specific path
243
+ */
244
+ async loadCompletionsFromPath(completionPath) {
245
+ try {
246
+ const files = fs.readdirSync(completionPath);
247
+ for (const file of files) {
248
+ if (file.endsWith('.zsh') || file.endsWith('_completion')) {
249
+ const filePath = path.join(completionPath, file);
250
+ const content = fs.readFileSync(filePath, 'utf8');
251
+ // Parse completion file and register completions
252
+ await this.parseCompletionFile(content, file);
253
+ }
254
+ }
255
+ }
256
+ catch (error) {
257
+ console.error(`Error loading completions from ${completionPath}: ${error.message}`);
258
+ }
259
+ }
260
+ /**
261
+ * Load Oh My Zsh completions
262
+ */
263
+ async loadOhMyZshCompletions(ohMyZshPath) {
264
+ const pluginsPath = path.join(ohMyZshPath, 'plugins');
265
+ const customPath = path.join(ohMyZshPath, 'custom');
266
+ // Load plugin completions
267
+ if (fs.existsSync(pluginsPath)) {
268
+ const plugins = fs.readdirSync(pluginsPath);
269
+ for (const plugin of plugins) {
270
+ const pluginPath = path.join(pluginsPath, plugin);
271
+ const completionPath = path.join(pluginPath, '_' + plugin);
272
+ if (fs.existsSync(completionPath)) {
273
+ await this.loadCompletionsFromPath(completionPath);
274
+ }
275
+ }
276
+ }
277
+ // Load custom completions
278
+ if (fs.existsSync(customPath)) {
279
+ await this.loadCompletionsFromPath(customPath);
280
+ }
281
+ }
282
+ /**
283
+ * Parse completion file and register with LSH completion system
284
+ */
285
+ async parseCompletionFile(content, filename) {
286
+ try {
287
+ // Extract command name from filename
288
+ const commandName = filename.replace(/^_/, '').replace(/\.zsh$/, '');
289
+ // Parse completion patterns
290
+ const patterns = this.extractCompletionPatterns(content);
291
+ // Register completion function
292
+ if (patterns.length > 0) {
293
+ this.executor.registerCompletion(commandName, async (context) => {
294
+ return this.generateCompletions(context, patterns);
295
+ });
296
+ }
297
+ }
298
+ catch (error) {
299
+ console.error(`Error parsing completion file ${filename}: ${error.message}`);
300
+ }
301
+ }
302
+ /**
303
+ * Extract completion patterns from ZSH completion file
304
+ */
305
+ extractCompletionPatterns(content) {
306
+ const patterns = [];
307
+ // Look for common completion patterns
308
+ const filePatterns = content.match(/compadd.*-f/g);
309
+ if (filePatterns) {
310
+ patterns.push({ type: 'files', pattern: '*' });
311
+ }
312
+ const dirPatterns = content.match(/compadd.*-d/g);
313
+ if (dirPatterns) {
314
+ patterns.push({ type: 'directories', pattern: '*/' });
315
+ }
316
+ const commandPatterns = content.match(/compadd.*-c/g);
317
+ if (commandPatterns) {
318
+ patterns.push({ type: 'commands', pattern: 'command' });
319
+ }
320
+ // Look for specific option patterns
321
+ const optionMatches = content.match(/--[a-zA-Z-]+/g);
322
+ if (optionMatches) {
323
+ for (const option of optionMatches) {
324
+ patterns.push({ type: 'options', pattern: option });
325
+ }
326
+ }
327
+ return patterns;
328
+ }
329
+ /**
330
+ * Generate completions based on patterns
331
+ */
332
+ async generateCompletions(context, patterns) {
333
+ const completions = [];
334
+ for (const pattern of patterns) {
335
+ switch (pattern.type) {
336
+ case 'files':
337
+ // Generate file completions
338
+ try {
339
+ const files = fs.readdirSync(context.cwd);
340
+ for (const file of files) {
341
+ completions.push({
342
+ word: file,
343
+ type: 'file',
344
+ description: 'File',
345
+ });
346
+ }
347
+ }
348
+ catch (_error) {
349
+ // Ignore directory read errors
350
+ }
351
+ break;
352
+ case 'directories':
353
+ // Generate directory completions
354
+ try {
355
+ const files = fs.readdirSync(context.cwd, { withFileTypes: true });
356
+ for (const file of files) {
357
+ if (file.isDirectory()) {
358
+ completions.push({
359
+ word: file.name + '/',
360
+ type: 'directory',
361
+ description: 'Directory',
362
+ });
363
+ }
364
+ }
365
+ }
366
+ catch (_error) {
367
+ // Ignore directory read errors
368
+ }
369
+ break;
370
+ case 'commands': {
371
+ // Generate command completions
372
+ const pathDirs = (context.env.PATH || '').split(':');
373
+ for (const dir of pathDirs) {
374
+ try {
375
+ const files = fs.readdirSync(dir);
376
+ for (const file of files) {
377
+ if (fs.statSync(path.join(dir, file)).isFile()) {
378
+ completions.push({
379
+ word: file,
380
+ type: 'command',
381
+ description: `Command in ${dir}`,
382
+ });
383
+ }
384
+ }
385
+ }
386
+ catch (_error) {
387
+ // Ignore directory read errors
388
+ }
389
+ }
390
+ break;
391
+ }
392
+ case 'options':
393
+ // Add specific option
394
+ completions.push({
395
+ word: pattern.pattern,
396
+ type: 'option',
397
+ description: 'Option',
398
+ });
399
+ break;
400
+ }
401
+ }
402
+ return completions;
403
+ }
404
+ /**
405
+ * Install packages using package manager
406
+ */
407
+ async installPackage(packageName) {
408
+ if (!this.options.installPackages) {
409
+ return {
410
+ success: false,
411
+ message: 'Package installation is disabled',
412
+ };
413
+ }
414
+ try {
415
+ const { spawn } = await import('child_process');
416
+ let command;
417
+ let args;
418
+ switch (this.options.packageManager) {
419
+ case 'npm':
420
+ command = 'npm';
421
+ args = ['install', '-g', packageName];
422
+ break;
423
+ case 'yarn':
424
+ command = 'yarn';
425
+ args = ['global', 'add', packageName];
426
+ break;
427
+ case 'pnpm':
428
+ command = 'pnpm';
429
+ args = ['add', '-g', packageName];
430
+ break;
431
+ case 'brew':
432
+ command = 'brew';
433
+ args = ['install', packageName];
434
+ break;
435
+ case 'apt':
436
+ command = 'sudo';
437
+ args = ['apt', 'install', packageName];
438
+ break;
439
+ case 'yum':
440
+ command = 'sudo';
441
+ args = ['yum', 'install', packageName];
442
+ break;
443
+ default:
444
+ return {
445
+ success: false,
446
+ message: `Unsupported package manager: ${this.options.packageManager}`,
447
+ };
448
+ }
449
+ return new Promise((resolve) => {
450
+ const child = spawn(command, args, {
451
+ stdio: ['pipe', 'pipe', 'pipe'],
452
+ });
453
+ let _stdout = '';
454
+ let stderr = '';
455
+ child.stdout?.on('data', (data) => {
456
+ _stdout += data.toString();
457
+ });
458
+ child.stderr?.on('data', (data) => {
459
+ stderr += data.toString();
460
+ });
461
+ child.on('close', (code) => {
462
+ if (code === 0) {
463
+ resolve({
464
+ success: true,
465
+ message: `Successfully installed ${packageName}`,
466
+ });
467
+ }
468
+ else {
469
+ resolve({
470
+ success: false,
471
+ message: `Failed to install ${packageName}: ${stderr}`,
472
+ });
473
+ }
474
+ });
475
+ child.on('error', (error) => {
476
+ resolve({
477
+ success: false,
478
+ message: `Error installing ${packageName}: ${error.message}`,
479
+ });
480
+ });
481
+ });
482
+ }
483
+ catch (error) {
484
+ return {
485
+ success: false,
486
+ message: `Error installing ${packageName}: ${error.message}`,
487
+ };
488
+ }
489
+ }
490
+ /**
491
+ * Uninstall packages using package manager
492
+ */
493
+ async uninstallPackage(packageName) {
494
+ if (!this.options.installPackages) {
495
+ return {
496
+ success: false,
497
+ message: 'Package uninstallation is disabled',
498
+ };
499
+ }
500
+ try {
501
+ const { spawn } = await import('child_process');
502
+ let command;
503
+ let args;
504
+ switch (this.options.packageManager) {
505
+ case 'npm':
506
+ command = 'npm';
507
+ args = ['uninstall', '-g', packageName];
508
+ break;
509
+ case 'yarn':
510
+ command = 'yarn';
511
+ args = ['global', 'remove', packageName];
512
+ break;
513
+ case 'pnpm':
514
+ command = 'pnpm';
515
+ args = ['remove', '-g', packageName];
516
+ break;
517
+ case 'brew':
518
+ command = 'brew';
519
+ args = ['uninstall', packageName];
520
+ break;
521
+ case 'apt':
522
+ command = 'sudo';
523
+ args = ['apt', 'remove', packageName];
524
+ break;
525
+ case 'yum':
526
+ command = 'sudo';
527
+ args = ['yum', 'remove', packageName];
528
+ break;
529
+ default:
530
+ return {
531
+ success: false,
532
+ message: `Unsupported package manager: ${this.options.packageManager}`,
533
+ };
534
+ }
535
+ return new Promise((resolve) => {
536
+ const child = spawn(command, args, {
537
+ stdio: ['pipe', 'pipe', 'pipe'],
538
+ });
539
+ let _stdout = '';
540
+ let stderr = '';
541
+ child.stdout?.on('data', (data) => {
542
+ _stdout += data.toString();
543
+ });
544
+ child.stderr?.on('data', (data) => {
545
+ stderr += data.toString();
546
+ });
547
+ child.on('close', (code) => {
548
+ if (code === 0) {
549
+ resolve({
550
+ success: true,
551
+ message: `Successfully uninstalled ${packageName}`,
552
+ });
553
+ }
554
+ else {
555
+ resolve({
556
+ success: false,
557
+ message: `Failed to uninstall ${packageName}: ${stderr}`,
558
+ });
559
+ }
560
+ });
561
+ child.on('error', (error) => {
562
+ resolve({
563
+ success: false,
564
+ message: `Error uninstalling ${packageName}: ${error.message}`,
565
+ });
566
+ });
567
+ });
568
+ }
569
+ catch (error) {
570
+ return {
571
+ success: false,
572
+ message: `Error uninstalling ${packageName}: ${error.message}`,
573
+ };
574
+ }
575
+ }
576
+ /**
577
+ * Check if ZSH is available and get version
578
+ */
579
+ async checkZshAvailability() {
580
+ try {
581
+ const { exec } = await import('child_process');
582
+ const { promisify } = await import('util');
583
+ const execAsync = promisify(exec);
584
+ const { stdout } = await execAsync('zsh --version');
585
+ const versionMatch = stdout.match(/zsh\s+([0-9.]+)/);
586
+ return {
587
+ available: true,
588
+ version: versionMatch ? versionMatch[1] : 'unknown',
589
+ path: '/bin/zsh', // This could be enhanced to find actual path
590
+ };
591
+ }
592
+ catch (_error) {
593
+ return {
594
+ available: false,
595
+ };
596
+ }
597
+ }
598
+ /**
599
+ * Migrate ZSH configuration to LSH
600
+ */
601
+ async migrateZshConfig() {
602
+ try {
603
+ const zshrcPath = this.options.zshrcPath;
604
+ const lshrcPath = path.join(os.homedir(), '.lshrc');
605
+ if (!fs.existsSync(zshrcPath)) {
606
+ return {
607
+ success: false,
608
+ message: 'No ZSH configuration found to migrate',
609
+ };
610
+ }
611
+ // Read ZSH config
612
+ const zshrcContent = fs.readFileSync(zshrcPath, 'utf8');
613
+ // Convert to LSH format
614
+ const lshrcContent = this.convertZshToLsh(zshrcContent);
615
+ // Write LSH config
616
+ fs.writeFileSync(lshrcPath, lshrcContent, 'utf8');
617
+ return {
618
+ success: true,
619
+ message: `Successfully migrated ZSH configuration to ${lshrcPath}`,
620
+ };
621
+ }
622
+ catch (error) {
623
+ return {
624
+ success: false,
625
+ message: `Migration failed: ${error.message}`,
626
+ };
627
+ }
628
+ }
629
+ /**
630
+ * Convert ZSH configuration to LSH format
631
+ */
632
+ convertZshToLsh(zshContent) {
633
+ let lshContent = '# LSH Configuration (migrated from ZSH)\n';
634
+ lshContent += '# This file was automatically generated from ~/.zshrc\n\n';
635
+ const lines = zshContent.split('\n');
636
+ for (const line of lines) {
637
+ const trimmed = line.trim();
638
+ // Skip comments and empty lines
639
+ if (trimmed.startsWith('#') || trimmed === '') {
640
+ lshContent += line + '\n';
641
+ continue;
642
+ }
643
+ // Convert ZSH-specific syntax to LSH
644
+ let convertedLine = line;
645
+ // Convert ZSH-specific functions to LSH format
646
+ if (trimmed.includes('autoload') && trimmed.includes('compinit')) {
647
+ convertedLine = '# ' + line + ' # Converted to LSH completion system';
648
+ }
649
+ // Convert Oh My Zsh specific syntax
650
+ if (trimmed.includes('ZSH_THEME=')) {
651
+ convertedLine = '# ' + line + ' # Use LSH prompt themes instead';
652
+ }
653
+ lshContent += convertedLine + '\n';
654
+ }
655
+ return lshContent;
656
+ }
657
+ }
658
+ export default ZshCompatibility;