lsh-framework 1.2.0 → 1.2.1

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