g360-cli 1.0.0 → 1.1.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.
- package/README.md +626 -28
- package/package.json +7 -2
- package/src/assets/config/g360-skills.json +172 -0
- package/src/assets/templates/lit-web/index.html +20 -0
- package/src/assets/templates/lit-web/package.json +19 -0
- package/src/assets/templates/lit-web/src/components/app-root.js +73 -0
- package/src/assets/templates/lit-web/src/core/skill.json +18 -0
- package/src/assets/templates/lit-web/src/index.js +8 -0
- package/src/assets/templates/lit-web/src/styles/main.css +91 -0
- package/src/assets/templates/lit-web/vite.config.js +10 -0
- package/src/assets/templates/solid-web/index.html +21 -0
- package/src/assets/templates/solid-web/package.json +20 -0
- package/src/assets/templates/solid-web/src/components/App.jsx +21 -0
- package/src/assets/templates/solid-web/src/core/skill.json +18 -0
- package/src/assets/templates/solid-web/src/index.jsx +11 -0
- package/src/assets/templates/solid-web/src/styles/main.css +115 -0
- package/src/assets/templates/solid-web/vite.config.js +12 -0
- package/src/assets/templates/svelte-web/package.json +21 -0
- package/src/assets/templates/svelte-web/src/app.css +77 -0
- package/src/assets/templates/svelte-web/src/app.html +19 -0
- package/src/assets/templates/svelte-web/src/core/skill.json +18 -0
- package/src/assets/templates/svelte-web/src/routes/+page.svelte +38 -0
- package/src/assets/templates/svelte-web/svelte.config.js +16 -0
- package/src/assets/templates/web-pwa/index.html +19 -21
- package/src/assets/templates/web-pwa/package.json +18 -2
- package/src/cli.js +26 -1
- package/src/commands/clean.js +288 -30
- package/src/commands/convert.js +388 -0
- package/src/commands/init.js +2 -1
- package/src/commands/set-skill.js +84 -0
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file convert.js
|
|
3
|
+
* @description Comando para convertir proyecto existente a identidad G360
|
|
4
|
+
* @author @carloscus
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import fs from 'fs-extra';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
|
|
12
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
|
|
14
|
+
const FRAMEWORK_INDICATORS = {
|
|
15
|
+
lit: { deps: ['lit', '@lit/lit'], files: ['.js'] },
|
|
16
|
+
react: { deps: ['react', 'react-dom'], files: ['.jsx', '.tsx'] },
|
|
17
|
+
vue: { deps: ['vue'], files: ['.vue'] },
|
|
18
|
+
solid: { deps: ['solid-js', 'solid-js/web'], files: ['.jsx', '.tsx'] },
|
|
19
|
+
svelte: { deps: ['svelte'], files: ['.svelte'] },
|
|
20
|
+
vanilla: { deps: [], files: [] }
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const SAFE_CHANGES = ['create'];
|
|
24
|
+
const DANGEROUS_CHANGES = ['modify', 'delete', 'move', 'rename'];
|
|
25
|
+
|
|
26
|
+
export async function convert(targetPath, options) {
|
|
27
|
+
const {
|
|
28
|
+
skill = 'corporativo-movil',
|
|
29
|
+
dryRun = false,
|
|
30
|
+
restructure = false,
|
|
31
|
+
force = false,
|
|
32
|
+
backup: createBackup = false
|
|
33
|
+
} = options;
|
|
34
|
+
|
|
35
|
+
const projectPath = path.join(process.cwd(), targetPath);
|
|
36
|
+
|
|
37
|
+
console.log(chalk.bold.cyan('\n🔄 G360 Convert - Proyecto a Identidad G360\n'));
|
|
38
|
+
console.log(`Proyecto: ${chalk.yellow(projectPath)}`);
|
|
39
|
+
console.log(`Skill: ${chalk.magenta(skill)}`);
|
|
40
|
+
console.log(`Modo: ${chalk.gray(dryRun ? 'DRY RUN (sin cambios)' : 'LIVE')}`);
|
|
41
|
+
if (restructure) console.log(`Restructure: ${chalk.yellow('SI')}`);
|
|
42
|
+
console.log('');
|
|
43
|
+
|
|
44
|
+
if (!fs.existsSync(projectPath)) {
|
|
45
|
+
console.error(chalk.red(`❌ El directorio "${targetPath}" no existe.`));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const projectInfo = await analyzeProject(projectPath);
|
|
51
|
+
projectInfo.force = force;
|
|
52
|
+
|
|
53
|
+
console.log(chalk.cyan('📊 Análisis del proyecto:'));
|
|
54
|
+
console.log(` Framework: ${chalk.white(projectInfo.framework)}`);
|
|
55
|
+
console.log(` Archivos: ${chalk.white(projectInfo.totalFiles)}`);
|
|
56
|
+
console.log(` CSS: ${chalk.white(projectInfo.cssFiles)}`);
|
|
57
|
+
console.log(` Componentes: ${chalk.white(projectInfo.componentFiles)}`);
|
|
58
|
+
console.log('');
|
|
59
|
+
|
|
60
|
+
const skillConfig = await loadSkill(skill);
|
|
61
|
+
|
|
62
|
+
if (!skillConfig) {
|
|
63
|
+
console.error(chalk.red(`❌ Skill "${skill}" no encontrado.`));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const changes = planChanges(projectPath, projectInfo, skillConfig, restructure);
|
|
68
|
+
|
|
69
|
+
const dangerousChanges = changes.filter(c => DANGEROUS_CHANGES.includes(c.type));
|
|
70
|
+
const safeChanges = changes.filter(c => SAFE_CHANGES.includes(c.type));
|
|
71
|
+
|
|
72
|
+
if (dangerousChanges.length > 0 && !dryRun && !force) {
|
|
73
|
+
console.log(chalk.yellow(`⚠️ Cambios peligrosos detectados: ${dangerousChanges.length}`));
|
|
74
|
+
dangerousChanges.forEach(c => console.log(` - ${chalk.gray(c.description)}`));
|
|
75
|
+
console.log(chalk.cyan('\nUsa --dry-run para ver detalles o --force para aplicar de todos modos.'));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (dryRun) {
|
|
80
|
+
console.log(chalk.bold.cyan('\n📋 PREVIEW - Cambios a aplicar:\n'));
|
|
81
|
+
changes.forEach(c => {
|
|
82
|
+
const color = c.type === 'create' ? chalk.green : c.type === 'modify' ? chalk.yellow : chalk.red;
|
|
83
|
+
console.log(` ${color('●')} ${c.type}: ${c.file}`);
|
|
84
|
+
console.log(chalk.gray(` ${c.description}`));
|
|
85
|
+
});
|
|
86
|
+
console.log(chalk.gray(`\n Total: ${changes.length} cambios`));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (createBackup) {
|
|
91
|
+
console.log(chalk.cyan('💾 Creando backup...'));
|
|
92
|
+
await createBackupFolder(projectPath);
|
|
93
|
+
console.log(chalk.green(' ✓ Backup creado'));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log(chalk.cyan('\n🚀 Aplicando cambios...'));
|
|
97
|
+
await applyChanges(projectPath, changes, skillConfig);
|
|
98
|
+
|
|
99
|
+
console.log(chalk.bold.green('\n✅ Convert completado\n'));
|
|
100
|
+
console.log(chalk.gray('Resumen:'));
|
|
101
|
+
console.log(` Archivos creados: ${chalk.green(safeChanges.length)}`);
|
|
102
|
+
console.log(` Archivos modificados: ${chalk.yellow(dangerousChanges.length)}`);
|
|
103
|
+
console.log(` Skill aplicado: ${chalk.magenta(skill)}`);
|
|
104
|
+
console.log('');
|
|
105
|
+
|
|
106
|
+
suggestFramework(projectInfo);
|
|
107
|
+
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error(chalk.red(`\n❌ Error: ${error.message}`));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function analyzeProject(projectPath) {
|
|
114
|
+
const info = {
|
|
115
|
+
framework: 'vanilla',
|
|
116
|
+
cssFiles: 0,
|
|
117
|
+
componentFiles: 0,
|
|
118
|
+
totalFiles: 0,
|
|
119
|
+
hasPackageJson: false,
|
|
120
|
+
packageJson: null,
|
|
121
|
+
files: []
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const packageJsonPath = path.join(projectPath, 'package.json');
|
|
125
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
126
|
+
info.hasPackageJson = true;
|
|
127
|
+
info.packageJson = await fs.readJson(packageJsonPath);
|
|
128
|
+
info.framework = detectFramework(info.packageJson);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const files = await getAllFiles(projectPath);
|
|
132
|
+
info.totalFiles = files.length;
|
|
133
|
+
|
|
134
|
+
files.forEach(f => {
|
|
135
|
+
if (f.endsWith('.css')) info.cssFiles++;
|
|
136
|
+
if (f.endsWith('.js') || f.endsWith('.jsx') || f.endsWith('.ts') || f.endsWith('.tsx') || f.endsWith('.svelte') || f.endsWith('.vue')) {
|
|
137
|
+
info.componentFiles++;
|
|
138
|
+
}
|
|
139
|
+
info.files.push(f);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
return info;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function detectFramework(packageJson) {
|
|
146
|
+
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
147
|
+
const depsKeys = Object.keys(deps).join(' ');
|
|
148
|
+
|
|
149
|
+
for (const [name, indicator] of Object.entries(FRAMEWORK_INDICATORS)) {
|
|
150
|
+
if (name === 'vanilla') continue;
|
|
151
|
+
if (indicator.deps.some(d => depsKeys.includes(d))) {
|
|
152
|
+
return name;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return 'vanilla';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function suggestFramework(projectInfo) {
|
|
159
|
+
console.log(chalk.cyan('\n💡 Sugerencia:'));
|
|
160
|
+
|
|
161
|
+
const suggestions = {
|
|
162
|
+
react: 'Para proyectos React, Lit sería más ligero y con el mismo rendimiento web components.',
|
|
163
|
+
vue: 'Vue es excelente, pero Lit ofrece mejor portabilidad entre proyectos.',
|
|
164
|
+
vanilla: 'Considera usar un template G360 (lit-web, solid-web) para mejor organización.',
|
|
165
|
+
lit: '¡Ya usas Lit! Perfecto para integración con G360.',
|
|
166
|
+
solid: 'Solid es muy performant, ideal para aplicaciones rápidas.',
|
|
167
|
+
svelte: 'Svelte es excelente, los templates G360 lo soportan.'
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
console.log(chalk.gray(` ${suggestions[projectInfo.framework] || 'Proyecto válido para G360.'}`));
|
|
171
|
+
console.log('');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function loadSkill(skillName) {
|
|
175
|
+
const skillsPath = path.join(__dirname, '../assets/config/g360-skills.json');
|
|
176
|
+
|
|
177
|
+
if (!fs.existsSync(skillsPath)) {
|
|
178
|
+
throw new Error('g360-skills.json no encontrado');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const skillsConfig = await fs.readJson(skillsPath);
|
|
182
|
+
const skill = skillsConfig.skills.find(s => s.name === skillName);
|
|
183
|
+
|
|
184
|
+
if (!skill) {
|
|
185
|
+
const available = skillsConfig.skills.map(s => s.name).join(', ');
|
|
186
|
+
throw new Error(`Skill "${skillName}" no encontrado. Disponibles: ${available}`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return skill;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function planChanges(projectPath, projectInfo, skillConfig, restructure) {
|
|
193
|
+
const changes = [];
|
|
194
|
+
const projectFiles = projectInfo.files.map(f => path.relative(projectPath, f).replace(/\\/g, '/'));
|
|
195
|
+
const hasSkillJson = projectFiles.some(f => f.endsWith('skill.json'));
|
|
196
|
+
const hasG360Theme = projectFiles.some(f => f.includes('g360-theme'));
|
|
197
|
+
|
|
198
|
+
if (!hasSkillJson) {
|
|
199
|
+
changes.push({
|
|
200
|
+
type: 'create',
|
|
201
|
+
file: 'src/core/skill.json',
|
|
202
|
+
description: 'Archivo de configuración del skill G360'
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (!hasG360Theme) {
|
|
207
|
+
changes.push({
|
|
208
|
+
type: 'create',
|
|
209
|
+
file: 'src/styles/g360-theme.css',
|
|
210
|
+
description: 'Theme dinámico con variables CSS del skill'
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const signatureComponents = projectInfo.files.filter(f =>
|
|
215
|
+
(f.endsWith('.js') || f.endsWith('.jsx') || f.endsWith('.tsx') || f.endsWith('.svelte')) &&
|
|
216
|
+
(f.includes('component') || f.includes('App') || f.includes('root') || f.includes('index'))
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
signatureComponents.forEach(f => {
|
|
220
|
+
const relPath = path.relative(projectPath, f);
|
|
221
|
+
if (!projectFiles.some(pf => pf.includes('g360-signature') || pf.includes('G360Signature'))) {
|
|
222
|
+
changes.push({
|
|
223
|
+
type: 'modify',
|
|
224
|
+
file: relPath,
|
|
225
|
+
description: 'Agregar import de signature G360',
|
|
226
|
+
dangerous: true
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
if (restructure) {
|
|
232
|
+
changes.push({
|
|
233
|
+
type: 'modify',
|
|
234
|
+
file: 'package.json',
|
|
235
|
+
description: 'Actualizar estructura y scripts (requiere revisión manual)',
|
|
236
|
+
dangerous: true
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return changes;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async function applyChanges(projectPath, changes, skillConfig) {
|
|
244
|
+
const coreDir = path.join(projectPath, 'src', 'core');
|
|
245
|
+
const stylesDir = path.join(projectPath, 'src', 'styles');
|
|
246
|
+
|
|
247
|
+
for (const change of changes) {
|
|
248
|
+
if (change.type === 'create') {
|
|
249
|
+
if (change.file.endsWith('skill.json')) {
|
|
250
|
+
await fs.ensureDir(coreDir);
|
|
251
|
+
const skillData = {
|
|
252
|
+
skill: skillConfig.name,
|
|
253
|
+
device: skillConfig.device,
|
|
254
|
+
template: 'converted',
|
|
255
|
+
version: '1.0.0',
|
|
256
|
+
convertedAt: new Date().toISOString(),
|
|
257
|
+
colors: skillConfig.colors,
|
|
258
|
+
signature: skillConfig.signature
|
|
259
|
+
};
|
|
260
|
+
await fs.writeJson(path.join(coreDir, 'skill.json'), skillData, { spaces: 2 });
|
|
261
|
+
console.log(chalk.green(` ✓ Creado: ${change.file}`));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (change.file.endsWith('g360-theme.css')) {
|
|
265
|
+
await fs.ensureDir(stylesDir);
|
|
266
|
+
const css = generateThemeCSS(skillConfig);
|
|
267
|
+
await fs.writeFile(path.join(stylesDir, 'g360-theme.css'), css);
|
|
268
|
+
console.log(chalk.green(` ✓ Creado: ${change.file}`));
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (change.type === 'modify' && !change.dangerous) {
|
|
273
|
+
console.log(chalk.yellow(` ⚠️ Omitido (peligroso): ${change.file}`));
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function generateThemeCSS(skillConfig) {
|
|
279
|
+
const { colors, effects } = skillConfig;
|
|
280
|
+
|
|
281
|
+
return `/* ============================================
|
|
282
|
+
* G360 Theme - Generado desde skill: ${skillConfig.name}
|
|
283
|
+
* ============================================ */
|
|
284
|
+
|
|
285
|
+
:root {
|
|
286
|
+
/* Colores G360 - ${skillConfig.name} */
|
|
287
|
+
--g360-bg: ${colors.bg};
|
|
288
|
+
--g360-surface: ${colors.surface};
|
|
289
|
+
--g360-accent: ${colors.accent};
|
|
290
|
+
--g360-text: ${colors.text};
|
|
291
|
+
--g360-muted: ${colors.muted};
|
|
292
|
+
|
|
293
|
+
/* Efectos */
|
|
294
|
+
--g360-glass: ${effects.glassmorphism ? colors.surface + 'CC' : 'transparent'};
|
|
295
|
+
--g360-blur: ${effects.blur};
|
|
296
|
+
--g360-rounded: ${effects.rounded};
|
|
297
|
+
|
|
298
|
+
/* Espaciado */
|
|
299
|
+
--g360-space-xs: 4px;
|
|
300
|
+
--g360-space-sm: 8px;
|
|
301
|
+
--g360-space-md: 16px;
|
|
302
|
+
--g360-space-lg: 24px;
|
|
303
|
+
--g360-space-xl: 32px;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/* === ESTILOS BASE === */
|
|
307
|
+
* {
|
|
308
|
+
box-sizing: border-box;
|
|
309
|
+
margin: 0;
|
|
310
|
+
padding: 0;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
body {
|
|
314
|
+
background: var(--g360-bg);
|
|
315
|
+
color: var(--g360-text);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/* === UTILIDADES === */
|
|
319
|
+
.g360-card {
|
|
320
|
+
background: var(--g360-surface);
|
|
321
|
+
border-radius: var(--g360-rounded);
|
|
322
|
+
padding: var(--g360-space-md);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
${effects.glassmorphism ? `.g360-glass {
|
|
326
|
+
background: var(--g360-glass);
|
|
327
|
+
backdrop-filter: blur(var(--g360-blur));
|
|
328
|
+
-webkit-backdrop-filter: blur(var(--g360-blur));
|
|
329
|
+
}` : ''}
|
|
330
|
+
|
|
331
|
+
.g360-btn {
|
|
332
|
+
background: var(--g360-accent);
|
|
333
|
+
color: var(--g360-bg);
|
|
334
|
+
border: none;
|
|
335
|
+
border-radius: var(--g360-rounded);
|
|
336
|
+
padding: var(--g360-space-sm) var(--g360-space-md);
|
|
337
|
+
font-weight: 700;
|
|
338
|
+
cursor: pointer;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/* === SIGNATURE === */
|
|
342
|
+
.g360-signature {
|
|
343
|
+
color: var(--g360-muted);
|
|
344
|
+
font-size: 12px;
|
|
345
|
+
text-align: center;
|
|
346
|
+
padding: var(--g360-space-md);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.g360-signature::after {
|
|
350
|
+
content: "${skillConfig.signature.text}";
|
|
351
|
+
}
|
|
352
|
+
`;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
async function createBackupFolder(projectPath) {
|
|
356
|
+
const parentDir = path.dirname(projectPath);
|
|
357
|
+
const projectName = path.basename(projectPath);
|
|
358
|
+
const backupDir = path.join(parentDir, `${projectName}-g360-backup-${Date.now()}`);
|
|
359
|
+
await fs.copy(projectPath, backupDir, {
|
|
360
|
+
filter: (src) => !src.includes('node_modules') && !src.includes('.git')
|
|
361
|
+
});
|
|
362
|
+
console.log(chalk.gray(` Backup en: ${backupDir}`));
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
async function getAllFiles(dir, baseDir = dir) {
|
|
366
|
+
const files = [];
|
|
367
|
+
|
|
368
|
+
if (!fs.existsSync(dir)) return files;
|
|
369
|
+
|
|
370
|
+
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
371
|
+
|
|
372
|
+
for (const item of items) {
|
|
373
|
+
const fullPath = path.join(dir, item.name);
|
|
374
|
+
const relativePath = path.relative(baseDir, fullPath);
|
|
375
|
+
|
|
376
|
+
if (item.name === 'node_modules' || item.name === '.git') continue;
|
|
377
|
+
|
|
378
|
+
if (item.isDirectory()) {
|
|
379
|
+
files.push(...await getAllFiles(fullPath, baseDir));
|
|
380
|
+
} else {
|
|
381
|
+
files.push(relativePath);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return files;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export default { convert };
|
package/src/commands/init.js
CHANGED
|
@@ -8,12 +8,13 @@ import { progress } from '../lib/progress.js';
|
|
|
8
8
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
9
|
|
|
10
10
|
export async function init(name, options) {
|
|
11
|
-
const { template = 'web-pwa', dir = '.', dryRun = false, force = false } = options;
|
|
11
|
+
const { template = 'web-pwa', skill = 'corporativo-movil', dir = '.', dryRun = false, force = false } = options;
|
|
12
12
|
const targetDir = path.join(process.cwd(), dir, name);
|
|
13
13
|
|
|
14
14
|
console.log(chalk.bold.cyan('\n🚀 G360 Project Initialization\n'));
|
|
15
15
|
console.log(`Project: ${chalk.yellow(name)}`);
|
|
16
16
|
console.log(`Template: ${chalk.blue(template)}`);
|
|
17
|
+
console.log(`Skill: ${chalk.magenta(skill)}`);
|
|
17
18
|
console.log(`Target: ${chalk.gray(targetDir)}\n`);
|
|
18
19
|
|
|
19
20
|
const templatesPath = path.join(__dirname, '../assets/templates');
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file set-skill.js
|
|
3
|
+
* @description Comando para seleccionar/cambiar skill del proyecto
|
|
4
|
+
* @author @carloscus
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import fs from 'fs-extra';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
|
|
11
|
+
const SKILLS_PATH = path.join(process.cwd(), 'g360-cli/src/assets/config/g360-skills.json');
|
|
12
|
+
|
|
13
|
+
export async function setSkill(skillName, options) {
|
|
14
|
+
const { verbose = false } = options;
|
|
15
|
+
|
|
16
|
+
console.log(chalk.bold.cyan('\n🎨 G360 Skill Selector\n'));
|
|
17
|
+
|
|
18
|
+
// Cargar skills disponibles
|
|
19
|
+
let skillsConfig;
|
|
20
|
+
try {
|
|
21
|
+
const cliPath = path.join(process.cwd(), 'g360-cli/src/assets/config/g360-skills.json');
|
|
22
|
+
skillsConfig = await fs.readJson(cliPath);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error(chalk.red('❌ No se pudo cargar la configuración de skills'));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Buscar skill seleccionado
|
|
29
|
+
const skill = skillsConfig.skills.find(s => s.name === skillName);
|
|
30
|
+
|
|
31
|
+
if (!skill) {
|
|
32
|
+
console.error(chalk.red(`❌ Skill "${skillName}" no encontrado.`));
|
|
33
|
+
console.log(chalk.gray('\nSkills disponibles:'));
|
|
34
|
+
skillsConfig.skills.forEach(s => {
|
|
35
|
+
console.log(chalk.gray(` - ${s.name}`) + chalk.gray(` (${s.description})`));
|
|
36
|
+
});
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Verificar si existe skill.json en el proyecto
|
|
41
|
+
const skillJsonPath = path.join(process.cwd(), 'skill.json');
|
|
42
|
+
|
|
43
|
+
if (fs.existsSync(skillJsonPath)) {
|
|
44
|
+
console.log(chalk.yellow('⚠️ El proyecto ya tiene un skill configurado.'));
|
|
45
|
+
console.log(chalk.gray('Usar --force para sobrescribir'));
|
|
46
|
+
if (!options.force) {
|
|
47
|
+
console.log(chalk.cyan('\nPara cambiar el skill:'));
|
|
48
|
+
console.log(chalk.cyan(' g360 set-skill ') + skillName + chalk.cyan(' --force'));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Guardar skill
|
|
54
|
+
try {
|
|
55
|
+
const skillData = {
|
|
56
|
+
skill: skill.name,
|
|
57
|
+
device: skill.device,
|
|
58
|
+
version: '1.0.0',
|
|
59
|
+
updatedAt: new Date().toISOString(),
|
|
60
|
+
colors: skill.colors,
|
|
61
|
+
signature: skill.signature
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
await fs.writeJson(skillJsonPath, skillData, { spaces: 2 });
|
|
65
|
+
|
|
66
|
+
console.log(chalk.green(`\n✅ Skill "${skillName}" configurado correctamente`));
|
|
67
|
+
console.log(chalk.gray('\nDetalles:'));
|
|
68
|
+
console.log(` Device: ${skill.device}`);
|
|
69
|
+
console.log(` Accent: ${skill.colors.accent}`);
|
|
70
|
+
console.log(` Signature: ${skill.signature.mode}`);
|
|
71
|
+
|
|
72
|
+
if (verbose) {
|
|
73
|
+
console.log(chalk.gray('\nColores:'));
|
|
74
|
+
Object.entries(skill.colors).forEach(([key, value]) => {
|
|
75
|
+
console.log(` ${key}: ${value}`);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.error(chalk.red(`\n❌ Error al guardar skill: ${error.message}`));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export default { setSkill };
|