g360-cli 1.4.0 → 1.6.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 (93) hide show
  1. package/README.md +122 -0
  2. package/package.json +11 -4
  3. package/src/assets/brand/brand.json +43 -0
  4. package/src/assets/brand/cipsa/logotypes/Logo_cipsa_borde.png +0 -0
  5. package/src/assets/brand/cipsa/logotypes/Logo_cipsa_borde.svg +9 -0
  6. package/src/assets/brand/cipsa/logotypes/Logo_cipsa_solid.png +0 -0
  7. package/src/assets/brand/cipsa/logotypes/Logo_cipsa_solid.svg +9 -0
  8. package/src/assets/brand/g360/logotypes/logo-g360-dark.svg +19 -0
  9. package/src/assets/brand/g360/logotypes/logo-g360-light.svg +19 -0
  10. package/src/assets/brand/g360/logotypes/logo_g360_dark_b64.txt +1 -0
  11. package/src/assets/brand/g360/logotypes/logo_g360_light_b64.txt +1 -0
  12. package/src/assets/config/g360-skills.json +60 -0
  13. package/src/assets/signature/g360_flet/__init__.py +5 -0
  14. package/src/assets/signature/g360_flet/__pycache__/__init__.cpython-312.pyc +0 -0
  15. package/src/assets/signature/g360_flet/__pycache__/__init__.cpython-314.pyc +0 -0
  16. package/src/assets/signature/g360_flet/__pycache__/g360_signature.cpython-312.pyc +0 -0
  17. package/src/assets/signature/g360_flet/__pycache__/g360_signature.cpython-314.pyc +0 -0
  18. package/src/assets/signature/g360_flet/g360_signature.py +224 -0
  19. package/src/assets/signature/index.js +130 -0
  20. package/src/assets/snippets/snippets.json +293 -101
  21. package/src/assets/templates/lit-web/src/core/skill.json +12 -5
  22. package/src/assets/templates/python-cli/build-portable.bat +19 -9
  23. package/src/assets/templates/python-cli/src/core/skill.json +25 -6
  24. package/src/assets/templates/python-customtkinter/README.md +32 -31
  25. package/src/assets/templates/python-customtkinter/assets/images/logo-g360-dark.svg +19 -0
  26. package/src/assets/templates/python-customtkinter/assets/images/logo-g360-light.svg +19 -0
  27. package/src/assets/templates/python-customtkinter/build.bat +50 -0
  28. package/src/assets/templates/python-customtkinter/pyproject.toml +26 -0
  29. package/src/assets/templates/python-customtkinter/run.bat +11 -20
  30. package/src/assets/templates/python-customtkinter/src/core/g360_theme.py +101 -0
  31. package/src/assets/templates/python-customtkinter/src/core/skill.json +23 -10
  32. package/src/assets/templates/python-customtkinter/src/main.py +61 -81
  33. package/src/assets/templates/python-flet/README.md +52 -93
  34. package/src/assets/templates/python-flet/assets/images/Logo_cipsa_borde.png +0 -0
  35. package/src/assets/templates/python-flet/assets/images/Logo_cipsa_borde.svg +9 -0
  36. package/src/assets/templates/python-flet/assets/images/Logo_cipsa_solid.png +0 -0
  37. package/src/assets/templates/python-flet/assets/images/Logo_cipsa_solid.svg +9 -0
  38. package/src/assets/templates/python-flet/assets/images/logo-g360-dark.svg +19 -0
  39. package/src/assets/templates/python-flet/assets/images/logo-g360-light.svg +19 -0
  40. package/src/assets/templates/python-flet/build.bat +36 -0
  41. package/src/assets/templates/python-flet/pyproject.toml +26 -0
  42. package/src/assets/templates/python-flet/run.bat +11 -72
  43. package/src/assets/templates/python-flet/src/core/__init__.py +0 -0
  44. package/src/assets/templates/python-flet/src/core/g360_theme.py +193 -0
  45. package/src/assets/templates/python-flet/src/core/skill.json +28 -8
  46. package/src/assets/templates/python-flet/src/export/__init__.py +0 -0
  47. package/src/assets/templates/python-flet/src/main.py +113 -68
  48. package/src/assets/templates/python-flet/src/test_app.py +41 -37
  49. package/src/assets/templates/python-flet/src/ui/__init__.py +0 -0
  50. package/src/assets/templates/python-flet-migrate/README.md +70 -0
  51. package/src/assets/templates/python-flet-migrate/assets/images/logo-g360-dark.svg +19 -0
  52. package/src/assets/templates/python-flet-migrate/assets/images/logo-g360-light.svg +19 -0
  53. package/src/assets/templates/python-flet-migrate/build.bat +36 -0
  54. package/src/assets/templates/python-flet-migrate/pyproject.toml +26 -0
  55. package/src/assets/templates/python-flet-migrate/run.bat +20 -0
  56. package/src/assets/templates/python-flet-migrate/src/core/__init__.py +0 -0
  57. package/src/assets/templates/python-flet-migrate/src/core/g360_theme.py +186 -0
  58. package/src/assets/templates/python-flet-migrate/src/core/skill.json +34 -0
  59. package/src/assets/templates/python-flet-migrate/src/main.py +71 -42
  60. package/src/assets/templates/solid-web/src/core/skill.json +12 -5
  61. package/src/assets/templates/svelte-web/src/core/skill.json +12 -5
  62. package/src/assets/templates/web-pwa/index.html +5 -7
  63. package/src/assets/templates/web-pwa/public/favicon.svg +4 -0
  64. package/src/assets/templates/web-pwa/public/manifest.json +11 -0
  65. package/src/assets/templates/web-pwa/src/App.jsx +22 -0
  66. package/src/assets/templates/web-pwa/src/components/FooterSignature.jsx +9 -0
  67. package/src/assets/templates/web-pwa/src/components/Header.jsx +12 -0
  68. package/src/assets/templates/web-pwa/src/components/LoadingOverlay.jsx +10 -0
  69. package/src/assets/templates/web-pwa/src/core/skill.json +25 -0
  70. package/src/assets/templates/web-pwa/src/main.jsx +9 -0
  71. package/src/assets/templates/web-pwa/src/styles/main.css +133 -0
  72. package/src/assets/templates/web-pwa/vite.config.js +12 -0
  73. package/src/cli.js +10 -5
  74. package/src/commands/bring.js +81 -6
  75. package/src/commands/list.js +79 -9
  76. package/src/commands/signature.js +252 -55
  77. package/src/lib/asset-validator.js +145 -0
  78. package/src/lib/auditor.js +48 -0
  79. package/src/lib/global-config.js +260 -0
  80. package/src/lib/i18n.js +238 -0
  81. package/src/lib/logger.js +100 -0
  82. package/src/lib/manifest.js +70 -1
  83. package/src/lib/validator.js +39 -0
  84. package/src/schemas/skills-schema.json +1 -0
  85. package/src/schemas/snippets-schema.json +1 -0
  86. package/dist/g360.exe +0 -0
  87. package/src/assets/templates/python-customtkinter/build-portable.bat +0 -28
  88. package/src/assets/templates/python-customtkinter/requirements.txt +0 -1
  89. package/src/assets/templates/python-flet/build-portable.bat +0 -42
  90. package/src/assets/templates/python-flet/create_shortcut.vbs +0 -35
  91. package/src/assets/templates/python-flet/requirements.txt +0 -3
  92. package/src/assets/templates/web-pwa/app.js +0 -1
  93. package/src/assets/templates/web-pwa/styles.css +0 -28
@@ -0,0 +1,133 @@
1
+ :root {
2
+ --g360-bg: #0b1220;
3
+ --g360-surface: #151e2e;
4
+ --g360-accent: #00d084;
5
+ --g360-text: #f0f4f8;
6
+ --g360-muted: #94a3b8;
7
+ --g360-glass: rgba(21, 30, 46, 0.75);
8
+ --g360-blur: 16px;
9
+ --g360-rounded: 16px;
10
+ --g360-font: system-ui, -apple-system, sans-serif;
11
+ }
12
+
13
+ * { box-sizing: border-box; margin: 0; padding: 0; }
14
+
15
+ body {
16
+ font-family: var(--g360-font);
17
+ background: var(--g360-bg);
18
+ color: var(--g360-text);
19
+ line-height: 1.6;
20
+ -webkit-font-smoothing: antialiased;
21
+ }
22
+
23
+ .app {
24
+ min-height: 100vh;
25
+ display: flex;
26
+ flex-direction: column;
27
+ }
28
+
29
+ .container {
30
+ max-width: 480px;
31
+ margin: 0 auto;
32
+ padding: 24px 16px;
33
+ flex: 1;
34
+ }
35
+
36
+ .g360-header {
37
+ background: var(--g360-surface);
38
+ border-bottom: 1px solid rgba(255,255,255,0.08);
39
+ padding: 16px 24px;
40
+ }
41
+
42
+ .header-content {
43
+ max-width: 480px;
44
+ margin: 0 auto;
45
+ display: flex;
46
+ align-items: baseline;
47
+ gap: 12px;
48
+ }
49
+
50
+ .logo {
51
+ font-size: 24px;
52
+ font-weight: 800;
53
+ color: var(--g360-accent);
54
+ }
55
+
56
+ .subtitle {
57
+ font-size: 14px;
58
+ color: var(--g360-muted);
59
+ }
60
+
61
+ h1 { font-size: 24px; font-weight: 700; margin-bottom: 8px; }
62
+
63
+ .muted { color: var(--g360-muted); font-size: 14px; }
64
+
65
+ .g360-footer {
66
+ text-align: center;
67
+ padding: 12px;
68
+ font-size: 11px;
69
+ color: var(--g360-muted);
70
+ background: var(--g360-surface);
71
+ border-top: 1px solid rgba(255,255,255,0.08);
72
+ }
73
+
74
+ .g360-loading-overlay {
75
+ position: fixed;
76
+ inset: 0;
77
+ background: rgba(11, 18, 32, 0.9);
78
+ display: flex;
79
+ flex-direction: column;
80
+ align-items: center;
81
+ justify-content: center;
82
+ gap: 16px;
83
+ z-index: 1000;
84
+ }
85
+
86
+ .g360-loading-spinner {
87
+ width: 40px;
88
+ height: 40px;
89
+ border: 3px solid var(--g360-surface);
90
+ border-top-color: var(--g360-accent);
91
+ border-radius: 50%;
92
+ animation: spin 0.8s linear infinite;
93
+ }
94
+
95
+ @keyframes spin { to { transform: rotate(360deg); } }
96
+
97
+ .g360-card {
98
+ background: var(--g360-surface);
99
+ border-radius: var(--g360-rounded);
100
+ padding: 16px;
101
+ border: 1px solid rgba(255,255,255,0.08);
102
+ }
103
+
104
+ .g360-glass {
105
+ background: var(--g360-glass);
106
+ backdrop-filter: blur(var(--g360-blur));
107
+ -webkit-backdrop-filter: blur(var(--g360-blur));
108
+ border: 1px solid rgba(255,255,255,0.1);
109
+ }
110
+
111
+ .g360-btn {
112
+ background: var(--g360-accent);
113
+ color: var(--g360-bg);
114
+ border: none;
115
+ border-radius: 8px;
116
+ padding: 10px 20px;
117
+ font-size: 14px;
118
+ font-weight: 700;
119
+ cursor: pointer;
120
+ transition: opacity 0.2s;
121
+ }
122
+
123
+ .g360-btn:hover { opacity: 0.85; }
124
+
125
+ @media (prefers-color-scheme: light) {
126
+ :root {
127
+ --g360-bg: #ffffff;
128
+ --g360-surface: #f5f5f5;
129
+ --g360-muted: #6b7280;
130
+ --g360-text: #1f2937;
131
+ --g360-glass: rgba(255, 255, 255, 0.75);
132
+ }
133
+ }
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ server: {
7
+ port: 3000,
8
+ },
9
+ build: {
10
+ outDir: 'dist',
11
+ },
12
+ });
package/src/cli.js CHANGED
@@ -31,7 +31,7 @@ program
31
31
  .command('init')
32
32
  .argument('<name>', 'Project name')
33
33
  .option('-t, --template <type>', 'Project template type', 'web-pwa')
34
- .option('-s, --skill <skill>', 'Skill to use (corporativo, corporativo-movil, moderno, moderno-movil, minimalista, custom, flet-desktop)', 'corporativo-movil')
34
+ .option('-s, --skill <skill>', 'Skill to use (corporativo, corporativo-movil, moderno, moderno-movil, minimalista, custom, flet-desktop, flet-desktop-corporativo, cipsa, cipsa-movil)', 'corporativo-movil')
35
35
  .option('-d, --dir <path>', 'Target directory', '.')
36
36
  .option('--dry-run', 'Preview without creating files')
37
37
  .option('--force', 'Overwrite existing files')
@@ -48,7 +48,7 @@ program
48
48
 
49
49
  program
50
50
  .command('bring')
51
- .argument('[asset]', 'Specific asset to bring (template/component/skill/all)')
51
+ .argument('[asset]', 'Asset to bring: brand, brand/cipsa, brand/g360, components, templates, skills, all')
52
52
  .option('-p, --path <path>', 'Target path', '.')
53
53
  .option('--dry-run', 'Preview without copying')
54
54
  .option('--force', 'Overwrite existing files')
@@ -56,7 +56,7 @@ program
56
56
 
57
57
  program
58
58
  .command('list')
59
- .argument('[type]', 'Type to list (templates/components/skills/all)', 'all')
59
+ .argument('[type]', 'Type to list (templates/components/skills/snippets/brands/all)', 'all')
60
60
  .option('--json', 'Output as JSON')
61
61
  .action(list);
62
62
 
@@ -100,7 +100,7 @@ program
100
100
  program
101
101
  .command('convert')
102
102
  .argument('[path]', 'Path to convert', '.')
103
- .option('-s, --skill <skill>', 'Skill to apply', 'corporativo-movil')
103
+ .option('-s, --skill <skill>', 'Skill to apply (corporativo, corporativo-movil, moderno, moderno-movil, minimalista, custom, flet-desktop, flet-desktop-corporativo, cipsa, cipsa-movil)', 'corporativo-movil')
104
104
  .option('--dry-run', 'Preview without applying')
105
105
  .option('--restructure', 'Restructure project files')
106
106
  .option('--force', 'Force dangerous changes')
@@ -109,9 +109,14 @@ program
109
109
 
110
110
  program
111
111
  .command('signature')
112
- .argument('<command>', 'Command to execute (install)')
112
+ .argument('<command>', 'Command to execute: install, positions')
113
+ .description('Install g360-signature branding component')
113
114
  .option('-p, --path <path>', 'Target project path', '.')
114
115
  .option('--force', 'Force reinstall if already exists')
116
+ .option('-m, --mode <mode>', 'Signature mode: own or powered', 'powered')
117
+ .option('-v, --version <version>', 'Version to display')
118
+ .option('--position <position>', 'Signature position: bottom-right, bottom-left, bottom-center, footer-right, footer-left', 'bottom-right')
119
+ .option('-i, --interactive', 'Interactive mode with guided suggestions')
115
120
  .action(signature);
116
121
 
117
122
  program.parse();
@@ -10,7 +10,7 @@ export async function bring(asset, options) {
10
10
  const { path: targetPath = '.', dryRun = false, force = false } = options;
11
11
  const targetDir = path.join(process.cwd(), targetPath);
12
12
  const assetsPath = path.join(__dirname, '../assets');
13
-
13
+
14
14
  console.log(chalk.bold.cyan('\n📦 G360 Asset Manager\n'));
15
15
 
16
16
  if (!asset || asset === 'all') {
@@ -19,14 +19,89 @@ export async function bring(asset, options) {
19
19
  }
20
20
 
21
21
  const assetPath = path.join(assetsPath, asset);
22
-
22
+
23
23
  if (!fs.existsSync(assetPath)) {
24
24
  console.error(chalk.red(`❌ Asset "${asset}" not found.`));
25
- console.log(chalk.gray('\nRun "g360 list assets" to see available assets.'));
25
+ console.log(chalk.gray('\nRun "g360 list all" to see available assets.'));
26
26
  return;
27
27
  }
28
28
 
29
29
  await copyAssets(assetPath, targetDir, dryRun, force);
30
+
31
+ if (asset.startsWith('brand')) {
32
+ const brandName = asset.split('/')[1];
33
+ if (brandName) {
34
+ await applyBrand(targetDir, brandName, dryRun);
35
+ }
36
+ }
37
+ }
38
+
39
+ async function applyBrand(targetDir, brandName, dryRun) {
40
+ const brandConfigPath = path.join(targetDir, 'g360', 'brand', 'brand.json');
41
+ if (!fs.existsSync(brandConfigPath)) {
42
+ console.log(chalk.yellow(`\n⚠ brand.json not found. Run 'g360 bring brand' first.`));
43
+ return;
44
+ }
45
+
46
+ let brandConfig;
47
+ try {
48
+ brandConfig = fs.readJsonSync(brandConfigPath);
49
+ } catch {
50
+ console.log(chalk.yellow(`\n⚠ Could not parse brand.json.`));
51
+ return;
52
+ }
53
+
54
+ const brand = brandConfig.brands?.[brandName];
55
+ if (!brand) {
56
+ console.log(chalk.yellow(`\n⚠ Brand "${brandName}" not found in brand.json`));
57
+ return;
58
+ }
59
+
60
+ const skillCandidates = [
61
+ path.join(targetDir, 'skill.json'),
62
+ path.join(targetDir, 'src', 'core', 'skill.json'),
63
+ ];
64
+
65
+ let skillPath = null;
66
+ for (const c of skillCandidates) {
67
+ if (fs.existsSync(c)) {
68
+ skillPath = c;
69
+ break;
70
+ }
71
+ }
72
+
73
+ if (!skillPath) {
74
+ console.log(chalk.yellow(`\n⚠ No skill.json found. Create one with 'g360 init'.`));
75
+ return;
76
+ }
77
+
78
+ if (dryRun) {
79
+ console.log(chalk.yellow(`\n📋 DRY RUN - Would update ${skillPath}`));
80
+ return;
81
+ }
82
+
83
+ const skill = fs.readJsonSync(skillPath);
84
+ skill.brand = brandName;
85
+
86
+ if (!skill.guidelines) skill.guidelines = {};
87
+ skill.guidelines.logo = brand.default_logo;
88
+ skill.guidelines.description = brand.description;
89
+
90
+ if (brand.signature) {
91
+ if (!skill.signature) skill.signature = {};
92
+ skill.signature.mode = brand.signature.mode;
93
+ skill.signature.text = brand.signature.text;
94
+ }
95
+
96
+ if (brand.primary_color) {
97
+ if (!skill.colors) skill.colors = {};
98
+ skill.colors.accent = brand.primary_color;
99
+ }
100
+
101
+ fs.writeJsonSync(skillPath, skill, { spaces: 2 });
102
+ console.log(chalk.green(`\n✅ Brand "${brandName}" applied to ${skillPath}`));
103
+ console.log(chalk.gray(` Logo: ${brand.default_logo}`));
104
+ console.log(chalk.gray(` Signature: ${brand.signature?.text || 'none'}`));
30
105
  }
31
106
 
32
107
  async function copyAssets(src, dest, dryRun, force) {
@@ -36,18 +111,18 @@ async function copyAssets(src, dest, dryRun, force) {
36
111
  }
37
112
 
38
113
  const progressBar = progress('Copying assets...');
39
-
114
+
40
115
  try {
41
116
  const items = fs.readdirSync(src);
42
117
  for (const item of items) {
43
118
  const srcPath = path.join(src, item);
44
119
  const destPath = path.join(dest, 'g360', item);
45
-
120
+
46
121
  if (fs.statSync(srcPath).isDirectory()) {
47
122
  await fs.copy(srcPath, destPath, { overwrite: force });
48
123
  }
49
124
  }
50
-
125
+
51
126
  progressBar.stop();
52
127
  console.log(chalk.green('\n✅ Assets copied successfully!\n'));
53
128
  } catch (error) {
@@ -14,13 +14,15 @@ export async function list(type, options) {
14
14
  const assets = {
15
15
  templates: [],
16
16
  components: [],
17
- skills: []
17
+ skills: [],
18
+ brands: []
18
19
  };
19
20
 
20
21
  const templatesPath = path.join(assetsPath, 'templates');
21
22
  const componentsPath = path.join(assetsPath, 'components');
22
- const skillsPath = path.join(assetsPath, 'skills');
23
- const enginePath = path.join(assetsPath, 'engine');
23
+ const configPath = path.join(assetsPath, 'config');
24
+ const snippetsPath = path.join(assetsPath, 'snippets');
25
+ const brandPath = path.join(assetsPath, 'brand');
24
26
 
25
27
  if (fs.existsSync(templatesPath)) {
26
28
  assets.templates = fs.readdirSync(templatesPath);
@@ -28,11 +30,53 @@ export async function list(type, options) {
28
30
  if (fs.existsSync(componentsPath)) {
29
31
  assets.components = fs.readdirSync(componentsPath).filter(f => f.endsWith('.jsx') || f.endsWith('.js'));
30
32
  }
31
- if (fs.existsSync(skillsPath)) {
32
- assets.skills = fs.readdirSync(skillsPath).filter(f => f.endsWith('.js') || f.endsWith('.mjs'));
33
+
34
+ if (fs.existsSync(configPath)) {
35
+ const g360SkillsPath = path.join(configPath, 'g360-skills.json');
36
+ if (fs.existsSync(g360SkillsPath)) {
37
+ try {
38
+ const skillsData = fs.readJsonSync(g360SkillsPath);
39
+ assets.skills = skillsData.skills.map(skill => ({
40
+ name: skill.name,
41
+ description: skill.description,
42
+ device: skill.device
43
+ }));
44
+ } catch (error) {
45
+ console.log(chalk.yellow(`Warning: Could not read skills config: ${error.message}`));
46
+ }
47
+ }
48
+ }
49
+
50
+ if (fs.existsSync(snippetsPath)) {
51
+ const snippetsJsonPath = path.join(snippetsPath, 'snippets.json');
52
+ if (fs.existsSync(snippetsJsonPath)) {
53
+ try {
54
+ const snippetsData = fs.readJsonSync(snippetsJsonPath);
55
+ assets.snippets = snippetsData.snippets.map(snippet => ({
56
+ name: snippet.name,
57
+ description: snippet.description,
58
+ language: snippet.language
59
+ }));
60
+ } catch (error) {
61
+ console.log(chalk.yellow(`Warning: Could not read snippets config: ${error.message}`));
62
+ }
63
+ }
33
64
  }
34
- if (fs.existsSync(enginePath)) {
35
- assets.engine = fs.readdirSync(enginePath).filter(f => f.startsWith('g360-skill-'));
65
+
66
+ if (fs.existsSync(brandPath)) {
67
+ const brandConfigPath = path.join(brandPath, 'brand.json');
68
+ if (fs.existsSync(brandConfigPath)) {
69
+ try {
70
+ const brandData = fs.readJsonSync(brandConfigPath);
71
+ assets.brands = Object.entries(brandData.brands || {}).map(([key, val]) => ({
72
+ name: key,
73
+ description: val.description,
74
+ logo: val.default_logo
75
+ }));
76
+ } catch (error) {
77
+ console.log(chalk.yellow(`Warning: Could not read brand config: ${error.message}`));
78
+ }
79
+ }
36
80
  }
37
81
 
38
82
  if (json) {
@@ -60,12 +104,38 @@ export async function list(type, options) {
60
104
 
61
105
  if (!type || type === 'all' || type === 'skills') {
62
106
  console.log(chalk.bold.yellow('\n⚡ Skills:'));
63
- if (assets.engine.length) {
64
- assets.engine.forEach(s => console.log(chalk.gray(` - ${s}`)));
107
+ if (assets.skills.length) {
108
+ assets.skills.forEach(skill => {
109
+ console.log(chalk.gray(` - ${skill.name}`));
110
+ console.log(chalk.gray(` ${skill.description}`));
111
+ console.log(chalk.gray(` Device: ${skill.device}`));
112
+ });
65
113
  } else {
66
114
  console.log(chalk.gray(' No skills found'));
67
115
  }
68
116
  }
69
117
 
118
+ if (!type || type === 'all' || type === 'brands') {
119
+ console.log(chalk.bold.yellow('\n🎨 Brands:'));
120
+ if (assets.brands.length) {
121
+ assets.brands.forEach(b => console.log(chalk.gray(` - ${b.name}`)));
122
+ } else {
123
+ console.log(chalk.gray(' No brands found'));
124
+ }
125
+ }
126
+
127
+ if (!type || type === 'all' || type === 'snippets') {
128
+ console.log(chalk.bold.yellow('\n📝 Snippets:'));
129
+ if (assets.snippets && assets.snippets.length) {
130
+ assets.snippets.forEach(snippet => {
131
+ console.log(chalk.gray(` - ${snippet.name}`));
132
+ console.log(chalk.gray(` ${snippet.description}`));
133
+ console.log(chalk.gray(` Language: ${snippet.language}`));
134
+ });
135
+ } else {
136
+ console.log(chalk.gray(' No snippets found'));
137
+ }
138
+ }
139
+
70
140
  console.log();
71
141
  }