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,6 +0,0 @@
1
- /**
2
- * Shell Type Definitions
3
- * Shared types used across shell execution modules
4
- * Extracted from shell-executor.ts to break circular dependencies
5
- */
6
- export {};
@@ -1,40 +0,0 @@
1
- import { exec, spawn } from 'child_process';
2
- import { promisify } from 'util';
3
- const execAsync = promisify(exec);
4
- // Execute shell command and return result
5
- export async function shell_exec(command) {
6
- try {
7
- const { stdout, stderr } = await execAsync(command);
8
- return { stdout: stdout.trim(), stderr: stderr.trim() };
9
- }
10
- catch (error) {
11
- return {
12
- stdout: '',
13
- stderr: '',
14
- error: error.message
15
- };
16
- }
17
- }
18
- // Execute shell command with streaming output
19
- export function shell_spawn(command, args = []) {
20
- return new Promise((resolve) => {
21
- const [cmd, ...defaultArgs] = command.split(' ');
22
- const finalArgs = args.length > 0 ? args : defaultArgs;
23
- const child = spawn(cmd, finalArgs);
24
- let stdout = '';
25
- let stderr = '';
26
- child.stdout.on('data', (data) => {
27
- stdout += data.toString();
28
- });
29
- child.stderr.on('data', (data) => {
30
- stderr += data.toString();
31
- });
32
- child.on('close', (code) => {
33
- resolve({
34
- stdout: stdout.trim(),
35
- stderr: stderr.trim(),
36
- code: code || 0
37
- });
38
- });
39
- });
40
- }
@@ -1,476 +0,0 @@
1
- /**
2
- * Theme Manager
3
- * Import and apply ZSH themes (Oh-My-Zsh, Powerlevel10k, custom)
4
- */
5
- import * as fs from 'fs';
6
- import * as path from 'path';
7
- import * as os from 'os';
8
- import chalk from 'chalk';
9
- export class ThemeManager {
10
- themesPath;
11
- customThemesPath;
12
- currentTheme = null;
13
- constructor() {
14
- this.themesPath = path.join(os.homedir(), '.oh-my-zsh', 'themes');
15
- this.customThemesPath = path.join(os.homedir(), '.lsh', 'themes');
16
- // Ensure custom themes directory exists
17
- if (!fs.existsSync(this.customThemesPath)) {
18
- fs.mkdirSync(this.customThemesPath, { recursive: true });
19
- }
20
- }
21
- /**
22
- * List available themes
23
- */
24
- listThemes() {
25
- const ohmyzsh = [];
26
- const custom = [];
27
- // Oh-My-Zsh themes
28
- if (fs.existsSync(this.themesPath)) {
29
- ohmyzsh.push(...fs.readdirSync(this.themesPath)
30
- .filter(f => f.endsWith('.zsh-theme'))
31
- .map(f => f.replace('.zsh-theme', '')));
32
- }
33
- // Custom themes
34
- if (fs.existsSync(this.customThemesPath)) {
35
- custom.push(...fs.readdirSync(this.customThemesPath)
36
- .filter(f => f.endsWith('.lsh-theme'))
37
- .map(f => f.replace('.lsh-theme', '')));
38
- }
39
- // Built-in LSH themes
40
- const builtin = [
41
- 'default',
42
- 'minimal',
43
- 'powerline',
44
- 'simple',
45
- 'git',
46
- 'robbyrussell', // Popular Oh-My-Zsh theme
47
- 'agnoster',
48
- ];
49
- return { ohmyzsh, custom, builtin };
50
- }
51
- /**
52
- * Import Oh-My-Zsh theme
53
- */
54
- async importOhMyZshTheme(themeName) {
55
- const themePath = path.join(this.themesPath, `${themeName}.zsh-theme`);
56
- if (!fs.existsSync(themePath)) {
57
- throw new Error(`Theme not found: ${themeName}`);
58
- }
59
- const themeContent = fs.readFileSync(themePath, 'utf8');
60
- const parsed = this.parseZshTheme(themeName, themeContent);
61
- // Convert to LSH format and save
62
- this.saveAsLshTheme(parsed);
63
- return parsed;
64
- }
65
- /**
66
- * Parse ZSH theme file
67
- */
68
- parseZshTheme(name, content) {
69
- const theme = {
70
- name,
71
- colors: new Map(),
72
- prompts: { left: '' },
73
- variables: new Map(),
74
- hooks: [],
75
- dependencies: [],
76
- };
77
- const lines = content.split('\n');
78
- for (const line of lines) {
79
- let trimmed = line.trim();
80
- // Skip comments and empty lines
81
- if (trimmed.startsWith('#') || trimmed === '') {
82
- continue;
83
- }
84
- // Strip inline comments (but preserve # inside quotes)
85
- const inlineCommentMatch = trimmed.match(/^([^#]*?["'][^"']*["'])\s*#/);
86
- if (inlineCommentMatch) {
87
- trimmed = inlineCommentMatch[1].trim();
88
- }
89
- else if (trimmed.includes('#')) {
90
- // Simple inline comment without quotes
91
- const parts = trimmed.split('#');
92
- if (parts[0].includes('=')) {
93
- trimmed = parts[0].trim();
94
- }
95
- }
96
- // Parse color definitions
97
- const colorMatch = trimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*)=["']?%\{.*?%F\{(\d+|[a-z]+)\}.*?\}["']?/);
98
- if (colorMatch) {
99
- theme.colors.set(colorMatch[1], colorMatch[2]);
100
- continue;
101
- }
102
- // Parse PROMPT
103
- const promptMatch = trimmed.match(/^PROMPT=["'](.+)["']$/);
104
- if (promptMatch) {
105
- theme.prompts.left = promptMatch[1];
106
- continue;
107
- }
108
- // Parse RPROMPT
109
- const rpromptMatch = trimmed.match(/^RPROMPT=["'](.+)["']$/);
110
- if (rpromptMatch) {
111
- theme.prompts.right = rpromptMatch[1];
112
- continue;
113
- }
114
- // Parse PS2 (continuation)
115
- const ps2Match = trimmed.match(/^PS2=["'](.+)["']$/);
116
- if (ps2Match) {
117
- theme.prompts.continuation = ps2Match[1];
118
- continue;
119
- }
120
- // Parse PS3 (select)
121
- const ps3Match = trimmed.match(/^PS3=["'](.+)["']$/);
122
- if (ps3Match) {
123
- theme.prompts.select = ps3Match[1];
124
- continue;
125
- }
126
- // Parse git formats (custom formats)
127
- if (trimmed.includes('FMT_BRANCH')) {
128
- const fmtMatch = trimmed.match(/FMT_BRANCH=["'](.+)["']$/);
129
- if (fmtMatch) {
130
- if (!theme.gitFormats)
131
- theme.gitFormats = {};
132
- theme.gitFormats.branch = fmtMatch[1];
133
- }
134
- }
135
- if (trimmed.includes('FMT_UNSTAGED')) {
136
- const fmtMatch = trimmed.match(/FMT_UNSTAGED=["'](.+)["']$/);
137
- if (fmtMatch) {
138
- if (!theme.gitFormats)
139
- theme.gitFormats = {};
140
- theme.gitFormats.unstaged = fmtMatch[1];
141
- }
142
- }
143
- if (trimmed.includes('FMT_STAGED')) {
144
- const fmtMatch = trimmed.match(/FMT_STAGED=["'](.+)["']$/);
145
- if (fmtMatch) {
146
- if (!theme.gitFormats)
147
- theme.gitFormats = {};
148
- theme.gitFormats.staged = fmtMatch[1];
149
- }
150
- }
151
- // Parse Oh-My-Zsh git prompt variables
152
- const gitPromptVars = {
153
- 'ZSH_THEME_GIT_PROMPT_PREFIX': 'prefix',
154
- 'ZSH_THEME_GIT_PROMPT_SUFFIX': 'suffix',
155
- 'ZSH_THEME_GIT_PROMPT_DIRTY': 'dirty',
156
- 'ZSH_THEME_GIT_PROMPT_CLEAN': 'clean',
157
- 'ZSH_THEME_GIT_PROMPT_UNTRACKED': 'untracked',
158
- };
159
- for (const [varName, formatKey] of Object.entries(gitPromptVars)) {
160
- if (trimmed.startsWith(varName)) {
161
- const match = trimmed.match(new RegExp(`${varName}=["'](.+?)["']$`));
162
- if (match) {
163
- if (!theme.gitFormats)
164
- theme.gitFormats = {};
165
- // Construct branch format from prefix/suffix if we have them
166
- if (formatKey === 'prefix' || formatKey === 'suffix') {
167
- const prefix = formatKey === 'prefix' ? match[1] : theme.gitFormats.prefix || '';
168
- const suffix = formatKey === 'suffix' ? match[1] : theme.gitFormats.suffix || '';
169
- if (prefix || suffix) {
170
- theme.gitFormats.branch = `${prefix}%b${suffix}`;
171
- }
172
- // Store the raw values too
173
- theme.gitFormats[formatKey] = match[1];
174
- }
175
- else {
176
- theme.gitFormats[formatKey] = match[1];
177
- }
178
- }
179
- }
180
- }
181
- // Parse variables (skip prompts, formats, and git theme variables)
182
- const varMatch = trimmed.match(/^([A-Z_][A-Z0-9_]*)=["']?(.+?)["']?$/);
183
- if (varMatch &&
184
- !trimmed.includes('PROMPT') &&
185
- !trimmed.includes('FMT_') &&
186
- !trimmed.startsWith('ZSH_THEME_GIT_PROMPT_') &&
187
- !trimmed.startsWith('PS2') &&
188
- !trimmed.startsWith('PS3')) {
189
- theme.variables.set(varMatch[1], varMatch[2]);
190
- }
191
- // Parse hooks
192
- if (trimmed.includes('add-zsh-hook')) {
193
- const hookMatch = trimmed.match(/add-zsh-hook\s+(\w+)\s+(\w+)/);
194
- if (hookMatch) {
195
- theme.hooks.push(`${hookMatch[1]}:${hookMatch[2]}`);
196
- }
197
- }
198
- // Detect dependencies
199
- if (trimmed.includes('vcs_info')) {
200
- if (!theme.dependencies.includes('vcs_info')) {
201
- theme.dependencies.push('vcs_info');
202
- }
203
- }
204
- if (trimmed.includes('git_branch') || trimmed.includes('git_prompt_info') || trimmed.includes('$(git ')) {
205
- if (!theme.dependencies.includes('git')) {
206
- theme.dependencies.push('git');
207
- }
208
- }
209
- if (trimmed.includes('virtualenv_prompt_info')) {
210
- if (!theme.dependencies.includes('virtualenv')) {
211
- theme.dependencies.push('virtualenv');
212
- }
213
- }
214
- if (trimmed.includes('ruby_prompt_info')) {
215
- if (!theme.dependencies.includes('ruby')) {
216
- theme.dependencies.push('ruby');
217
- }
218
- }
219
- }
220
- // Post-processing: Check prompts for dependencies
221
- const allPrompts = [
222
- theme.prompts.left,
223
- theme.prompts.right,
224
- theme.prompts.continuation,
225
- theme.prompts.select
226
- ].filter(Boolean).join(' ');
227
- if (allPrompts.includes('git_branch') || allPrompts.includes('git_prompt_info') || allPrompts.includes('$(git ')) {
228
- if (!theme.dependencies.includes('git')) {
229
- theme.dependencies.push('git');
230
- }
231
- }
232
- if (allPrompts.includes('virtualenv_prompt_info')) {
233
- if (!theme.dependencies.includes('virtualenv')) {
234
- theme.dependencies.push('virtualenv');
235
- }
236
- }
237
- if (allPrompts.includes('vcs_info')) {
238
- if (!theme.dependencies.includes('vcs_info')) {
239
- theme.dependencies.push('vcs_info');
240
- }
241
- }
242
- return theme;
243
- }
244
- /**
245
- * Convert ZSH prompt format to LSH format
246
- */
247
- convertPromptToLsh(zshPrompt, colors) {
248
- let lshPrompt = zshPrompt;
249
- // Convert color variables
250
- for (const [name, color] of colors.entries()) {
251
- // Map ZSH color codes to chalk colors
252
- const chalkColor = this.mapColorToChalk(color);
253
- lshPrompt = lshPrompt.replace(new RegExp(`\\$\\{${name}\\}`, 'g'), chalkColor);
254
- }
255
- // Convert ZSH prompt escapes to LSH equivalents
256
- const conversions = [
257
- [/%n/g, process.env.USER || 'user'], // username
258
- [/%m/g, os.hostname().split('.')[0]], // hostname (short)
259
- [/%M/g, os.hostname()], // hostname (full)
260
- [/%~|%d/g, '$(pwd | sed "s|^$HOME|~|")'], // current directory
261
- [/%\//g, '$(pwd)'], // current directory (full)
262
- [/%c/g, '$(basename "$(pwd)")'], // current directory (basename)
263
- [/%D/g, '$(date +"%m/%d/%y")'], // date
264
- [/%T/g, '$(date +"%H:%M")'], // time (24h)
265
- [/%t/g, '$(date +"%I:%M %p")'], // time (12h)
266
- [/%#/g, '\\$'], // # for root, $ for user
267
- [/%\{.*?reset_color.*?\}/g, chalk.reset('')], // reset color
268
- [/%\{.*?\}/g, ''], // remove other ZSH escapes
269
- ];
270
- for (const [pattern, replacement] of conversions) {
271
- lshPrompt = lshPrompt.replace(pattern, replacement);
272
- }
273
- // Handle git info
274
- if (lshPrompt.includes('$vcs_info_msg_0_')) {
275
- lshPrompt = lshPrompt.replace(/\$vcs_info_msg_0_/g, '$(git_prompt_info)');
276
- }
277
- // Handle virtualenv
278
- if (lshPrompt.includes('$(virtualenv_prompt_info)')) {
279
- lshPrompt = lshPrompt.replace(/\$\(virtualenv_prompt_info\)/g, '$([ -n "$VIRTUAL_ENV" ] && echo " ($(basename $VIRTUAL_ENV))")');
280
- }
281
- return lshPrompt;
282
- }
283
- /**
284
- * Map ZSH color code to chalk color
285
- */
286
- mapColorToChalk(zshColor) {
287
- // 256 color codes
288
- const colorMap = {
289
- '81': 'cyan',
290
- '166': 'yellow',
291
- '135': 'magenta',
292
- '161': 'red',
293
- '118': 'green',
294
- '208': 'yellow',
295
- '39': 'blue',
296
- '214': 'yellow',
297
- // Standard colors
298
- 'black': 'black',
299
- 'red': 'red',
300
- 'green': 'green',
301
- 'yellow': 'yellow',
302
- 'blue': 'blue',
303
- 'magenta': 'magenta',
304
- 'cyan': 'cyan',
305
- 'white': 'white',
306
- };
307
- const mapped = colorMap[zshColor] || 'white';
308
- return `\\[\\033[${this.getAnsiCode(mapped)}m\\]`;
309
- }
310
- /**
311
- * Get ANSI code for color name
312
- */
313
- getAnsiCode(color) {
314
- const codes = {
315
- 'black': '30',
316
- 'red': '31',
317
- 'green': '32',
318
- 'yellow': '33',
319
- 'blue': '34',
320
- 'magenta': '35',
321
- 'cyan': '36',
322
- 'white': '37',
323
- 'reset': '0',
324
- 'bold': '1',
325
- 'dim': '2',
326
- 'italic': '3',
327
- 'underline': '4',
328
- };
329
- return codes[color] || '37';
330
- }
331
- /**
332
- * Save theme in LSH format
333
- */
334
- saveAsLshTheme(theme) {
335
- try {
336
- const lshThemePath = path.join(this.customThemesPath, `${theme.name}.lsh-theme`);
337
- const lshThemeContent = {
338
- name: theme.name,
339
- prompts: {
340
- left: theme.prompts.left,
341
- right: theme.prompts.right,
342
- continuation: theme.prompts.continuation,
343
- select: theme.prompts.select,
344
- },
345
- colors: Object.fromEntries(theme.colors),
346
- gitFormats: theme.gitFormats,
347
- variables: Object.fromEntries(theme.variables),
348
- hooks: theme.hooks,
349
- dependencies: theme.dependencies,
350
- };
351
- fs.writeFileSync(lshThemePath, JSON.stringify(lshThemeContent, null, 2), 'utf8');
352
- }
353
- catch (error) {
354
- // Gracefully handle save errors (e.g., permission issues, invalid paths)
355
- console.error(`Failed to save theme ${theme.name}:`, error);
356
- }
357
- }
358
- /**
359
- * Apply theme
360
- */
361
- applyTheme(theme) {
362
- this.currentTheme = theme;
363
- // Generate prompt setting commands
364
- const commands = [];
365
- // Set main prompt
366
- if (theme.prompts.left) {
367
- const lshPrompt = this.convertPromptToLsh(theme.prompts.left, theme.colors);
368
- commands.push(`export LSH_PROMPT='${lshPrompt}'`);
369
- }
370
- // Set right prompt
371
- if (theme.prompts.right) {
372
- const lshRPrompt = this.convertPromptToLsh(theme.prompts.right, theme.colors);
373
- commands.push(`export LSH_RPROMPT='${lshRPrompt}'`);
374
- }
375
- return commands.join('\n');
376
- }
377
- /**
378
- * Get built-in theme
379
- */
380
- getBuiltinTheme(name) {
381
- const themes = {
382
- 'default': {
383
- name: 'default',
384
- colors: new Map([['blue', '34'], ['green', '32'], ['reset', '0']]),
385
- prompts: {
386
- left: chalk.blue('%n@%m') + ':' + chalk.green('%~') + '$ ',
387
- },
388
- variables: new Map(),
389
- hooks: [],
390
- dependencies: [],
391
- },
392
- 'minimal': {
393
- name: 'minimal',
394
- colors: new Map([['cyan', '36'], ['reset', '0']]),
395
- prompts: {
396
- left: chalk.cyan('%~') + ' ❯ ',
397
- },
398
- variables: new Map(),
399
- hooks: [],
400
- dependencies: [],
401
- },
402
- 'powerline': {
403
- name: 'powerline',
404
- colors: new Map([
405
- ['blue', '34'],
406
- ['white', '37'],
407
- ['green', '32'],
408
- ['yellow', '33'],
409
- ]),
410
- prompts: {
411
- left: chalk.bgBlue.white(' %n ') + '' + chalk.bgGreen.black(' %~ ') + '' + ' ',
412
- },
413
- variables: new Map(),
414
- hooks: [],
415
- dependencies: ['git'],
416
- },
417
- 'robbyrussell': {
418
- name: 'robbyrussell',
419
- colors: new Map([['cyan', '36'], ['red', '31'], ['green', '32']]),
420
- prompts: {
421
- left: chalk.cyan('➜ ') + chalk.cyan('%~') + ' $(git_prompt_info)',
422
- },
423
- gitFormats: {
424
- branch: chalk.red(' git:') + chalk.green('(%b)'),
425
- },
426
- variables: new Map(),
427
- hooks: [],
428
- dependencies: ['git'],
429
- },
430
- 'simple': {
431
- name: 'simple',
432
- colors: new Map([['reset', '0']]),
433
- prompts: {
434
- left: '%~ $ ',
435
- },
436
- variables: new Map(),
437
- hooks: [],
438
- dependencies: [],
439
- },
440
- };
441
- if (!themes[name]) {
442
- throw new Error(`Theme not found: ${name}`);
443
- }
444
- return themes[name];
445
- }
446
- /**
447
- * Preview theme without applying
448
- */
449
- previewTheme(theme) {
450
- console.log(chalk.bold(`\n📦 Theme: ${theme.name}\n`));
451
- // Show prompt
452
- console.log(chalk.dim('Prompt:'));
453
- const examplePrompt = this.convertPromptToLsh(theme.prompts.left, theme.colors)
454
- .replace('$(pwd | sed "s|^$HOME|~|")', '~/projects/my-app')
455
- .replace('$(git_prompt_info)', ' git:(main)');
456
- console.log(' ' + examplePrompt);
457
- if (theme.prompts.right) {
458
- console.log(chalk.dim('\nRight Prompt:'));
459
- console.log(' ' + this.convertPromptToLsh(theme.prompts.right, theme.colors));
460
- }
461
- // Show colors
462
- if (theme.colors.size > 0) {
463
- console.log(chalk.dim('\nColors:'));
464
- for (const [name, code] of theme.colors.entries()) {
465
- const color = this.mapColorToChalk(code);
466
- console.log(` ${name}: ${color}${name}${chalk.reset('')}`);
467
- }
468
- }
469
- // Show dependencies
470
- if (theme.dependencies.length > 0) {
471
- console.log(chalk.dim('\nDependencies:'));
472
- theme.dependencies.forEach(dep => console.log(` - ${dep}`));
473
- }
474
- console.log('');
475
- }
476
- }