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,699 @@
1
+ /**
2
+ * Enhanced ZSH Import Manager
3
+ * Handles importing ZSH configurations with conflict resolution, diagnostics, and selective import
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
+ export class ZshImportManager {
10
+ executor;
11
+ options;
12
+ diagnostics = [];
13
+ existingAliases = new Set();
14
+ existingExports = new Set();
15
+ existingFunctions = new Set();
16
+ constructor(executor, options = {}) {
17
+ this.executor = executor;
18
+ this.options = {
19
+ autoImport: false,
20
+ selective: false,
21
+ conflictResolution: 'skip',
22
+ diagnosticLog: path.join(os.homedir(), '.lsh', 'zsh-import.log'),
23
+ includeAliases: true,
24
+ includeExports: true,
25
+ includeFunctions: true,
26
+ includeOptions: true,
27
+ includePlugins: true,
28
+ excludePatterns: [],
29
+ includePatterns: [],
30
+ ...options,
31
+ };
32
+ // Ensure diagnostic log directory exists
33
+ this.ensureLogDirectory();
34
+ }
35
+ ensureLogDirectory() {
36
+ const logDir = path.dirname(this.options.diagnosticLog);
37
+ if (!fs.existsSync(logDir)) {
38
+ fs.mkdirSync(logDir, { recursive: true });
39
+ }
40
+ }
41
+ /**
42
+ * Import ZSH configuration from .zshrc
43
+ */
44
+ async importZshConfig(zshrcPath) {
45
+ const configPath = zshrcPath || path.join(os.homedir(), '.zshrc');
46
+ this.diagnostics = [];
47
+ this.log({ type: 'alias', name: 'IMPORT_START', status: 'success', reason: `Starting import from ${configPath}` });
48
+ try {
49
+ // Check if .zshrc exists
50
+ if (!fs.existsSync(configPath)) {
51
+ const result = {
52
+ success: false,
53
+ message: `ZSH configuration not found: ${configPath}`,
54
+ diagnostics: this.diagnostics,
55
+ stats: { total: 0, succeeded: 0, failed: 0, skipped: 0, conflicts: 0 },
56
+ };
57
+ this.writeDiagnosticLog();
58
+ return result;
59
+ }
60
+ // Load existing items to detect conflicts
61
+ await this.loadExistingItems();
62
+ // Read and parse .zshrc
63
+ const zshrcContent = fs.readFileSync(configPath, 'utf8');
64
+ const parsed = this.parseZshrc(zshrcContent);
65
+ // Apply configurations based on options
66
+ const stats = {
67
+ total: 0,
68
+ succeeded: 0,
69
+ failed: 0,
70
+ skipped: 0,
71
+ conflicts: 0,
72
+ };
73
+ if (this.options.includeAliases) {
74
+ const result = await this.importAliases(parsed.aliases);
75
+ stats.total += result.total;
76
+ stats.succeeded += result.succeeded;
77
+ stats.failed += result.failed;
78
+ stats.skipped += result.skipped;
79
+ stats.conflicts += result.conflicts;
80
+ }
81
+ if (this.options.includeExports) {
82
+ const result = await this.importExports(parsed.exports);
83
+ stats.total += result.total;
84
+ stats.succeeded += result.succeeded;
85
+ stats.failed += result.failed;
86
+ stats.skipped += result.skipped;
87
+ stats.conflicts += result.conflicts;
88
+ }
89
+ if (this.options.includeFunctions) {
90
+ const result = await this.importFunctions(parsed.functions);
91
+ stats.total += result.total;
92
+ stats.succeeded += result.succeeded;
93
+ stats.failed += result.failed;
94
+ stats.skipped += result.skipped;
95
+ stats.conflicts += result.conflicts;
96
+ }
97
+ if (this.options.includeOptions) {
98
+ const result = await this.importSetopts(parsed.setopts);
99
+ stats.total += result.total;
100
+ stats.succeeded += result.succeeded;
101
+ stats.failed += result.failed;
102
+ stats.skipped += result.skipped;
103
+ }
104
+ if (this.options.includePlugins) {
105
+ const result = await this.importPlugins(parsed.plugins);
106
+ stats.total += result.total;
107
+ stats.succeeded += result.succeeded;
108
+ stats.failed += result.failed;
109
+ stats.skipped += result.skipped;
110
+ }
111
+ this.log({
112
+ type: 'alias',
113
+ name: 'IMPORT_COMPLETE',
114
+ status: 'success',
115
+ reason: `Imported ${stats.succeeded}/${stats.total} items`
116
+ });
117
+ const result = {
118
+ success: true,
119
+ message: this.formatImportMessage(stats),
120
+ diagnostics: this.diagnostics,
121
+ stats,
122
+ };
123
+ this.writeDiagnosticLog();
124
+ return result;
125
+ }
126
+ catch (error) {
127
+ this.log({
128
+ type: 'error',
129
+ name: 'IMPORT_ERROR',
130
+ status: 'failed',
131
+ reason: error.message
132
+ });
133
+ const result = {
134
+ success: false,
135
+ message: `Import failed: ${error.message}`,
136
+ diagnostics: this.diagnostics,
137
+ stats: { total: 0, succeeded: 0, failed: 1, skipped: 0, conflicts: 0 },
138
+ };
139
+ this.writeDiagnosticLog();
140
+ return result;
141
+ }
142
+ }
143
+ /**
144
+ * Parse .zshrc with enhanced function parsing
145
+ */
146
+ parseZshrc(content) {
147
+ const lines = content.split('\n');
148
+ const config = {
149
+ aliases: [],
150
+ functions: [],
151
+ exports: [],
152
+ setopts: [],
153
+ completions: [],
154
+ plugins: [],
155
+ };
156
+ let inFunction = false;
157
+ let functionName = '';
158
+ let functionBody = '';
159
+ let functionStartLine = 0;
160
+ let braceCount = 0;
161
+ for (let i = 0; i < lines.length; i++) {
162
+ const line = lines[i];
163
+ const trimmed = line.trim();
164
+ // Skip comments and empty lines
165
+ if (trimmed.startsWith('#') || trimmed === '') {
166
+ continue;
167
+ }
168
+ // Handle function definitions (multiple formats)
169
+ // Format 1: name() { ... }
170
+ // Format 2: function name { ... }
171
+ // Format 3: function name() { ... }
172
+ const funcMatch = trimmed.match(/^(?:function\s+)?([a-zA-Z_][a-zA-Z0-9_]*)\s*\(\s*\)\s*\{?/) ||
173
+ trimmed.match(/^function\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\{?/);
174
+ if (funcMatch && !inFunction) {
175
+ inFunction = true;
176
+ functionName = funcMatch[1];
177
+ functionBody = line;
178
+ functionStartLine = i + 1;
179
+ braceCount = (line.match(/\{/g) || []).length - (line.match(/\}/g) || []).length;
180
+ if (braceCount === 0 && line.includes('{') && line.includes('}')) {
181
+ // Single-line function
182
+ inFunction = false;
183
+ config.functions.push({
184
+ name: functionName,
185
+ body: functionBody,
186
+ line: functionStartLine,
187
+ });
188
+ functionName = '';
189
+ functionBody = '';
190
+ }
191
+ continue;
192
+ }
193
+ if (inFunction) {
194
+ functionBody += '\n' + line;
195
+ braceCount += (line.match(/\{/g) || []).length;
196
+ braceCount -= (line.match(/\}/g) || []).length;
197
+ if (braceCount <= 0) {
198
+ inFunction = false;
199
+ config.functions.push({
200
+ name: functionName,
201
+ body: functionBody,
202
+ line: functionStartLine,
203
+ });
204
+ functionName = '';
205
+ functionBody = '';
206
+ }
207
+ continue;
208
+ }
209
+ // Parse aliases
210
+ const aliasMatch = trimmed.match(/^alias\s+([^=]+)=(.+)$/);
211
+ if (aliasMatch) {
212
+ config.aliases.push({
213
+ name: aliasMatch[1].trim(),
214
+ value: aliasMatch[2].replace(/^['"]|['"]$/g, ''),
215
+ line: i + 1,
216
+ });
217
+ continue;
218
+ }
219
+ // Parse exports (handle various formats)
220
+ const exportMatch = trimmed.match(/^export\s+([^=]+)(?:=(.+))?$/);
221
+ if (exportMatch) {
222
+ config.exports.push({
223
+ name: exportMatch[1].trim(),
224
+ value: exportMatch[2] ? exportMatch[2].replace(/^['"]|['"]$/g, '') : '',
225
+ line: i + 1,
226
+ });
227
+ continue;
228
+ }
229
+ // Parse setopt
230
+ const setoptMatch = trimmed.match(/^setopt\s+(.+)$/);
231
+ if (setoptMatch) {
232
+ const options = setoptMatch[1].split(/\s+/);
233
+ for (const option of options) {
234
+ config.setopts.push({
235
+ option: option.trim(),
236
+ enabled: true,
237
+ line: i + 1,
238
+ });
239
+ }
240
+ continue;
241
+ }
242
+ // Parse unsetopt
243
+ const unsetoptMatch = trimmed.match(/^unsetopt\s+(.+)$/);
244
+ if (unsetoptMatch) {
245
+ const options = unsetoptMatch[1].split(/\s+/);
246
+ for (const option of options) {
247
+ config.setopts.push({
248
+ option: option.trim(),
249
+ enabled: false,
250
+ line: i + 1,
251
+ });
252
+ }
253
+ continue;
254
+ }
255
+ // Parse completions
256
+ if (trimmed.includes('compinit') || trimmed.includes('autoload')) {
257
+ config.completions.push({
258
+ config: trimmed,
259
+ line: i + 1,
260
+ });
261
+ continue;
262
+ }
263
+ // Parse plugins
264
+ const pluginMatch = trimmed.match(/plugins?=\(([^)]+)\)/);
265
+ if (pluginMatch) {
266
+ const plugins = pluginMatch[1].split(/\s+/).filter(p => p.trim());
267
+ for (const plugin of plugins) {
268
+ config.plugins.push({
269
+ name: plugin.trim(),
270
+ line: i + 1,
271
+ });
272
+ }
273
+ continue;
274
+ }
275
+ }
276
+ return config;
277
+ }
278
+ /**
279
+ * Load existing items to detect conflicts
280
+ */
281
+ async loadExistingItems() {
282
+ // Get existing aliases from context
283
+ const context = this.executor.context;
284
+ if (context && context.variables) {
285
+ for (const key in context.variables) {
286
+ if (key.startsWith('alias_')) {
287
+ this.existingAliases.add(key.substring(6));
288
+ }
289
+ else {
290
+ this.existingExports.add(key);
291
+ }
292
+ }
293
+ }
294
+ // TODO: Load existing functions when function storage is implemented
295
+ }
296
+ /**
297
+ * Import aliases with conflict resolution
298
+ */
299
+ async importAliases(aliases) {
300
+ const stats = { total: aliases.length, succeeded: 0, failed: 0, skipped: 0, conflicts: 0 };
301
+ for (const alias of aliases) {
302
+ // Check include/exclude patterns
303
+ if (!this.shouldImport(alias.name)) {
304
+ this.log({
305
+ type: 'alias',
306
+ name: alias.name,
307
+ status: 'skipped',
308
+ reason: 'Excluded by pattern',
309
+ source: `line ${alias.line}`,
310
+ });
311
+ stats.skipped++;
312
+ continue;
313
+ }
314
+ // Check for conflicts
315
+ if (this.existingAliases.has(alias.name)) {
316
+ stats.conflicts++;
317
+ const action = await this.resolveConflict('alias', alias.name);
318
+ if (action === 'skip') {
319
+ this.log({
320
+ type: 'alias',
321
+ name: alias.name,
322
+ status: 'skipped',
323
+ reason: 'Conflict - alias already exists',
324
+ source: `line ${alias.line}`,
325
+ action: 'skipped',
326
+ });
327
+ stats.skipped++;
328
+ continue;
329
+ }
330
+ else if (action === 'rename') {
331
+ alias.name = `${alias.name}_zsh`;
332
+ this.log({
333
+ type: 'alias',
334
+ name: alias.name,
335
+ status: 'success',
336
+ reason: 'Conflict - renamed to avoid collision',
337
+ source: `line ${alias.line}`,
338
+ action: 'renamed',
339
+ });
340
+ }
341
+ }
342
+ // Import alias
343
+ try {
344
+ const ast = parseShellCommand(`alias ${alias.name}="${alias.value}"`);
345
+ await this.executor.execute(ast);
346
+ this.existingAliases.add(alias.name);
347
+ this.log({
348
+ type: 'alias',
349
+ name: alias.name,
350
+ status: 'success',
351
+ source: `line ${alias.line}`,
352
+ });
353
+ stats.succeeded++;
354
+ }
355
+ catch (error) {
356
+ this.log({
357
+ type: 'alias',
358
+ name: alias.name,
359
+ status: 'failed',
360
+ reason: error.message,
361
+ source: `line ${alias.line}`,
362
+ });
363
+ stats.failed++;
364
+ }
365
+ }
366
+ return stats;
367
+ }
368
+ /**
369
+ * Import environment variables with conflict resolution
370
+ */
371
+ async importExports(exports) {
372
+ const stats = { total: exports.length, succeeded: 0, failed: 0, skipped: 0, conflicts: 0 };
373
+ for (const export_ of exports) {
374
+ // Check include/exclude patterns
375
+ if (!this.shouldImport(export_.name)) {
376
+ this.log({
377
+ type: 'export',
378
+ name: export_.name,
379
+ status: 'skipped',
380
+ reason: 'Excluded by pattern',
381
+ source: `line ${export_.line}`,
382
+ });
383
+ stats.skipped++;
384
+ continue;
385
+ }
386
+ // Check for conflicts
387
+ if (this.existingExports.has(export_.name) || process.env[export_.name]) {
388
+ stats.conflicts++;
389
+ const action = await this.resolveConflict('export', export_.name);
390
+ if (action === 'skip') {
391
+ this.log({
392
+ type: 'export',
393
+ name: export_.name,
394
+ status: 'skipped',
395
+ reason: 'Conflict - variable already exists',
396
+ source: `line ${export_.line}`,
397
+ action: 'skipped',
398
+ });
399
+ stats.skipped++;
400
+ continue;
401
+ }
402
+ }
403
+ // Import export
404
+ try {
405
+ const value = export_.value || '';
406
+ const ast = parseShellCommand(`export ${export_.name}="${value}"`);
407
+ await this.executor.execute(ast);
408
+ this.existingExports.add(export_.name);
409
+ this.log({
410
+ type: 'export',
411
+ name: export_.name,
412
+ status: 'success',
413
+ source: `line ${export_.line}`,
414
+ });
415
+ stats.succeeded++;
416
+ }
417
+ catch (error) {
418
+ this.log({
419
+ type: 'export',
420
+ name: export_.name,
421
+ status: 'failed',
422
+ reason: error.message,
423
+ source: `line ${export_.line}`,
424
+ });
425
+ stats.failed++;
426
+ }
427
+ }
428
+ return stats;
429
+ }
430
+ /**
431
+ * Import functions with enhanced parsing
432
+ */
433
+ async importFunctions(functions) {
434
+ const stats = { total: functions.length, succeeded: 0, failed: 0, skipped: 0, conflicts: 0 };
435
+ for (const func of functions) {
436
+ // Check include/exclude patterns
437
+ if (!this.shouldImport(func.name)) {
438
+ this.log({
439
+ type: 'function',
440
+ name: func.name,
441
+ status: 'skipped',
442
+ reason: 'Excluded by pattern',
443
+ source: `line ${func.line}`,
444
+ });
445
+ stats.skipped++;
446
+ continue;
447
+ }
448
+ // Check for conflicts
449
+ if (this.existingFunctions.has(func.name)) {
450
+ stats.conflicts++;
451
+ const action = await this.resolveConflict('function', func.name);
452
+ if (action === 'skip') {
453
+ this.log({
454
+ type: 'function',
455
+ name: func.name,
456
+ status: 'skipped',
457
+ reason: 'Conflict - function already exists',
458
+ source: `line ${func.line}`,
459
+ action: 'skipped',
460
+ });
461
+ stats.skipped++;
462
+ continue;
463
+ }
464
+ else if (action === 'rename') {
465
+ // Rename function in body
466
+ const oldName = func.name;
467
+ func.name = `${func.name}_zsh`;
468
+ func.body = func.body.replace(new RegExp(`^(function\\s+)?${oldName}`, 'm'), `$1${func.name}`);
469
+ this.log({
470
+ type: 'function',
471
+ name: func.name,
472
+ status: 'success',
473
+ reason: 'Conflict - renamed to avoid collision',
474
+ source: `line ${func.line}`,
475
+ action: 'renamed',
476
+ });
477
+ }
478
+ }
479
+ // Import function
480
+ try {
481
+ const ast = parseShellCommand(func.body);
482
+ await this.executor.execute(ast);
483
+ this.existingFunctions.add(func.name);
484
+ this.log({
485
+ type: 'function',
486
+ name: func.name,
487
+ status: 'success',
488
+ source: `line ${func.line}`,
489
+ });
490
+ stats.succeeded++;
491
+ }
492
+ catch (error) {
493
+ this.log({
494
+ type: 'function',
495
+ name: func.name,
496
+ status: 'disabled',
497
+ reason: `Parse error: ${error.message}`,
498
+ source: `line ${func.line}`,
499
+ });
500
+ stats.failed++;
501
+ }
502
+ }
503
+ return stats;
504
+ }
505
+ /**
506
+ * Import ZSH options
507
+ */
508
+ async importSetopts(setopts) {
509
+ const stats = { total: setopts.length, succeeded: 0, failed: 0, skipped: 0, conflicts: 0 };
510
+ for (const setopt of setopts) {
511
+ try {
512
+ const command = setopt.enabled ? 'setopt' : 'unsetopt';
513
+ const ast = parseShellCommand(`${command} ${setopt.option}`);
514
+ await this.executor.execute(ast);
515
+ this.log({
516
+ type: 'setopt',
517
+ name: setopt.option,
518
+ status: 'success',
519
+ source: `line ${setopt.line}`,
520
+ });
521
+ stats.succeeded++;
522
+ }
523
+ catch (error) {
524
+ this.log({
525
+ type: 'setopt',
526
+ name: setopt.option,
527
+ status: 'disabled',
528
+ reason: error.message,
529
+ source: `line ${setopt.line}`,
530
+ });
531
+ stats.failed++;
532
+ }
533
+ }
534
+ return stats;
535
+ }
536
+ /**
537
+ * Import Oh-My-Zsh plugins
538
+ */
539
+ async importPlugins(plugins) {
540
+ const stats = { total: plugins.length, succeeded: 0, failed: 0, skipped: 0, conflicts: 0 };
541
+ for (const plugin of plugins) {
542
+ this.log({
543
+ type: 'plugin',
544
+ name: plugin.name,
545
+ status: 'disabled',
546
+ reason: 'Oh-My-Zsh plugin support not yet implemented',
547
+ source: `line ${plugin.line}`,
548
+ });
549
+ stats.skipped++;
550
+ }
551
+ return stats;
552
+ }
553
+ /**
554
+ * Check if item should be imported based on include/exclude patterns
555
+ */
556
+ shouldImport(name) {
557
+ // Check exclude patterns first
558
+ if (this.options.excludePatterns.length > 0) {
559
+ for (const pattern of this.options.excludePatterns) {
560
+ if (this.matchPattern(name, pattern)) {
561
+ return false;
562
+ }
563
+ }
564
+ }
565
+ // Check include patterns
566
+ if (this.options.includePatterns.length > 0) {
567
+ for (const pattern of this.options.includePatterns) {
568
+ if (this.matchPattern(name, pattern)) {
569
+ return true;
570
+ }
571
+ }
572
+ return false; // If include patterns specified, only import matching items
573
+ }
574
+ return true; // No patterns specified, import everything
575
+ }
576
+ /**
577
+ * Match name against pattern (supports wildcards)
578
+ */
579
+ matchPattern(name, pattern) {
580
+ const regexPattern = pattern
581
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&') // Escape regex special chars
582
+ .replace(/\*/g, '.*') // Convert * to .*
583
+ .replace(/\?/g, '.'); // Convert ? to .
584
+ return new RegExp(`^${regexPattern}$`).test(name);
585
+ }
586
+ /**
587
+ * Resolve naming conflicts
588
+ */
589
+ async resolveConflict(_type, _name) {
590
+ switch (this.options.conflictResolution) {
591
+ case 'skip':
592
+ return 'skip';
593
+ case 'overwrite':
594
+ return 'overwrite';
595
+ case 'rename':
596
+ return 'rename';
597
+ case 'prompt':
598
+ // TODO: Implement interactive prompt for CLI
599
+ return 'skip'; // Default to skip for now
600
+ default:
601
+ return 'skip';
602
+ }
603
+ }
604
+ /**
605
+ * Log diagnostic entry
606
+ */
607
+ log(entry) {
608
+ this.diagnostics.push({
609
+ timestamp: new Date(),
610
+ ...entry,
611
+ });
612
+ }
613
+ /**
614
+ * Write diagnostic log to file
615
+ */
616
+ writeDiagnosticLog() {
617
+ try {
618
+ const logContent = this.diagnostics
619
+ .map(d => {
620
+ const parts = [
621
+ d.timestamp.toISOString(),
622
+ d.type.padEnd(10),
623
+ d.status.padEnd(10),
624
+ d.name.padEnd(30),
625
+ ];
626
+ if (d.source)
627
+ parts.push(`[${d.source}]`);
628
+ if (d.reason)
629
+ parts.push(d.reason);
630
+ if (d.action)
631
+ parts.push(`(${d.action})`);
632
+ return parts.join(' ');
633
+ })
634
+ .join('\n');
635
+ fs.appendFileSync(this.options.diagnosticLog, logContent + '\n\n', 'utf8');
636
+ }
637
+ catch (error) {
638
+ console.error(`Failed to write diagnostic log: ${error.message}`);
639
+ }
640
+ }
641
+ /**
642
+ * Format import summary message
643
+ */
644
+ formatImportMessage(stats) {
645
+ const lines = [
646
+ `ZSH Import Complete:`,
647
+ ` ✅ Succeeded: ${stats.succeeded}`,
648
+ ` ❌ Failed: ${stats.failed}`,
649
+ ` ⏭️ Skipped: ${stats.skipped}`,
650
+ ` ⚠️ Conflicts: ${stats.conflicts}`,
651
+ ` 📊 Total: ${stats.total}`,
652
+ ];
653
+ if (stats.failed > 0) {
654
+ lines.push(`\nSee diagnostic log: ${this.options.diagnosticLog}`);
655
+ }
656
+ return lines.join('\n');
657
+ }
658
+ /**
659
+ * Get import statistics from last run
660
+ */
661
+ getLastImportStats() {
662
+ if (this.diagnostics.length === 0) {
663
+ return null;
664
+ }
665
+ const stats = {
666
+ total: 0,
667
+ succeeded: 0,
668
+ failed: 0,
669
+ skipped: 0,
670
+ conflicts: 0,
671
+ byType: {},
672
+ };
673
+ for (const diagnostic of this.diagnostics) {
674
+ if (diagnostic.name === 'IMPORT_START' || diagnostic.name === 'IMPORT_COMPLETE' || diagnostic.name === 'IMPORT_ERROR') {
675
+ continue;
676
+ }
677
+ stats.total++;
678
+ if (diagnostic.status === 'success')
679
+ stats.succeeded++;
680
+ if (diagnostic.status === 'failed')
681
+ stats.failed++;
682
+ if (diagnostic.status === 'skipped')
683
+ stats.skipped++;
684
+ if (diagnostic.status === 'conflict')
685
+ stats.conflicts++;
686
+ if (!stats.byType[diagnostic.type]) {
687
+ stats.byType[diagnostic.type] = { total: 0, succeeded: 0, failed: 0, skipped: 0 };
688
+ }
689
+ stats.byType[diagnostic.type].total++;
690
+ if (diagnostic.status === 'success')
691
+ stats.byType[diagnostic.type].succeeded++;
692
+ if (diagnostic.status === 'failed')
693
+ stats.byType[diagnostic.type].failed++;
694
+ if (diagnostic.status === 'skipped')
695
+ stats.byType[diagnostic.type].skipped++;
696
+ }
697
+ return stats;
698
+ }
699
+ }