g360-cli 1.4.0 → 1.5.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.
- package/README.md +122 -0
- package/package.json +11 -4
- package/src/assets/brand/brand.json +43 -0
- package/src/assets/brand/cipsa/logotypes/Logo_cipsa_borde.png +0 -0
- package/src/assets/brand/cipsa/logotypes/Logo_cipsa_borde.svg +9 -0
- package/src/assets/brand/cipsa/logotypes/Logo_cipsa_solid.png +0 -0
- package/src/assets/brand/cipsa/logotypes/Logo_cipsa_solid.svg +9 -0
- package/src/assets/brand/g360/logotypes/logo-g360-dark.svg +19 -0
- package/src/assets/brand/g360/logotypes/logo-g360-light.svg +19 -0
- package/src/assets/brand/g360/logotypes/logo_g360_dark_b64.txt +1 -0
- package/src/assets/brand/g360/logotypes/logo_g360_light_b64.txt +1 -0
- package/src/assets/config/g360-skills.json +60 -0
- package/src/assets/snippets/snippets.json +293 -101
- package/src/assets/templates/lit-web/src/core/skill.json +12 -5
- package/src/assets/templates/python-cli/build-portable.bat +19 -9
- package/src/assets/templates/python-cli/src/core/skill.json +25 -6
- package/src/assets/templates/python-customtkinter/README.md +32 -31
- package/src/assets/templates/python-customtkinter/assets/images/logo-g360-dark.svg +19 -0
- package/src/assets/templates/python-customtkinter/assets/images/logo-g360-light.svg +19 -0
- package/src/assets/templates/python-customtkinter/build.bat +50 -0
- package/src/assets/templates/python-customtkinter/pyproject.toml +26 -0
- package/src/assets/templates/python-customtkinter/run.bat +11 -20
- package/src/assets/templates/python-customtkinter/src/core/g360_theme.py +101 -0
- package/src/assets/templates/python-customtkinter/src/core/skill.json +23 -10
- package/src/assets/templates/python-customtkinter/src/main.py +61 -81
- package/src/assets/templates/python-flet/README.md +52 -93
- package/src/assets/templates/python-flet/assets/images/Logo_cipsa_borde.png +0 -0
- package/src/assets/templates/python-flet/assets/images/Logo_cipsa_borde.svg +9 -0
- package/src/assets/templates/python-flet/assets/images/Logo_cipsa_solid.png +0 -0
- package/src/assets/templates/python-flet/assets/images/Logo_cipsa_solid.svg +9 -0
- package/src/assets/templates/python-flet/assets/images/logo-g360-dark.svg +19 -0
- package/src/assets/templates/python-flet/assets/images/logo-g360-light.svg +19 -0
- package/src/assets/templates/python-flet/build.bat +36 -0
- package/src/assets/templates/python-flet/pyproject.toml +26 -0
- package/src/assets/templates/python-flet/run.bat +11 -72
- package/src/assets/templates/python-flet/src/core/__init__.py +0 -0
- package/src/assets/templates/python-flet/src/core/g360_theme.py +186 -0
- package/src/assets/templates/python-flet/src/core/skill.json +28 -8
- package/src/assets/templates/python-flet/src/export/__init__.py +0 -0
- package/src/assets/templates/python-flet/src/main.py +113 -68
- package/src/assets/templates/python-flet/src/test_app.py +41 -37
- package/src/assets/templates/python-flet/src/ui/__init__.py +0 -0
- package/src/assets/templates/python-flet-migrate/README.md +70 -0
- package/src/assets/templates/python-flet-migrate/assets/images/logo-g360-dark.svg +19 -0
- package/src/assets/templates/python-flet-migrate/assets/images/logo-g360-light.svg +19 -0
- package/src/assets/templates/python-flet-migrate/build.bat +36 -0
- package/src/assets/templates/python-flet-migrate/pyproject.toml +26 -0
- package/src/assets/templates/python-flet-migrate/run.bat +20 -0
- package/src/assets/templates/python-flet-migrate/src/core/__init__.py +0 -0
- package/src/assets/templates/python-flet-migrate/src/core/g360_theme.py +186 -0
- package/src/assets/templates/python-flet-migrate/src/core/skill.json +34 -0
- package/src/assets/templates/python-flet-migrate/src/main.py +71 -42
- package/src/assets/templates/solid-web/src/core/skill.json +12 -5
- package/src/assets/templates/svelte-web/src/core/skill.json +12 -5
- package/src/assets/templates/web-pwa/index.html +2 -7
- package/src/assets/templates/web-pwa/public/favicon.svg +4 -0
- package/src/assets/templates/web-pwa/public/manifest.json +11 -0
- package/src/assets/templates/web-pwa/src/App.jsx +22 -0
- package/src/assets/templates/web-pwa/src/components/FooterSignature.jsx +9 -0
- package/src/assets/templates/web-pwa/src/components/Header.jsx +12 -0
- package/src/assets/templates/web-pwa/src/components/LoadingOverlay.jsx +10 -0
- package/src/assets/templates/web-pwa/src/core/skill.json +25 -0
- package/src/assets/templates/web-pwa/src/main.jsx +9 -0
- package/src/assets/templates/web-pwa/src/styles/main.css +133 -0
- package/src/assets/templates/web-pwa/vite.config.js +12 -0
- package/src/cli.js +4 -4
- package/src/commands/bring.js +81 -6
- package/src/commands/list.js +79 -9
- package/src/lib/asset-validator.js +145 -0
- package/src/lib/auditor.js +48 -0
- package/src/lib/global-config.js +260 -0
- package/src/lib/i18n.js +238 -0
- package/src/lib/logger.js +100 -0
- package/src/lib/manifest.js +70 -1
- package/src/lib/validator.js +39 -0
- package/src/schemas/skills-schema.json +1 -0
- package/src/schemas/snippets-schema.json +1 -0
- package/dist/g360.exe +0 -0
- package/src/assets/templates/python-customtkinter/build-portable.bat +0 -28
- package/src/assets/templates/python-customtkinter/requirements.txt +0 -1
- package/src/assets/templates/python-flet/build-portable.bat +0 -42
- package/src/assets/templates/python-flet/create_shortcut.vbs +0 -35
- package/src/assets/templates/python-flet/requirements.txt +0 -3
- package/src/assets/templates/web-pwa/app.js +0 -1
- package/src/assets/templates/web-pwa/styles.css +0 -28
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { logger } from './logger.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {Object} GlobalConfig
|
|
8
|
+
* @property {string} defaultSkill - Skill por defecto
|
|
9
|
+
* @property {string} defaultTemplate - Plantilla por defecto
|
|
10
|
+
* @property {Object} preferences - Preferencias del usuario
|
|
11
|
+
* @property {string[]} recentProjects - Proyectos recientes
|
|
12
|
+
* @property {Object} cache - Configuración de caché
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Ruta del directorio de configuración global
|
|
17
|
+
* @type {string}
|
|
18
|
+
*/
|
|
19
|
+
const G360_CONFIG_DIR = path.join(os.homedir(), '.g360');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Ruta del archivo de configuración global
|
|
23
|
+
* @type {string}
|
|
24
|
+
*/
|
|
25
|
+
const G360_CONFIG_FILE = path.join(G360_CONFIG_DIR, 'config.json');
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Configuración por defecto
|
|
29
|
+
* @type {GlobalConfig}
|
|
30
|
+
*/
|
|
31
|
+
const DEFAULT_CONFIG = {
|
|
32
|
+
defaultSkill: 'corporativo-movil',
|
|
33
|
+
defaultTemplate: 'web-pwa',
|
|
34
|
+
preferences: {
|
|
35
|
+
language: 'es',
|
|
36
|
+
theme: 'dark',
|
|
37
|
+
verbose: false
|
|
38
|
+
},
|
|
39
|
+
recentProjects: [],
|
|
40
|
+
cache: {
|
|
41
|
+
enabled: true,
|
|
42
|
+
maxSize: 100
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Módulo de configuración global para G360-CLI
|
|
48
|
+
* @namespace globalConfig
|
|
49
|
+
*/
|
|
50
|
+
export const globalConfig = {
|
|
51
|
+
/**
|
|
52
|
+
* Obtiene la ruta del directorio de configuración
|
|
53
|
+
* @returns {string} Ruta del directorio de configuración
|
|
54
|
+
* @example
|
|
55
|
+
* const configDir = globalConfig.getConfigDir();
|
|
56
|
+
* console.log('Config dir:', configDir);
|
|
57
|
+
*/
|
|
58
|
+
getConfigDir() {
|
|
59
|
+
return G360_CONFIG_DIR;
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Obtiene la ruta del archivo de configuración
|
|
64
|
+
* @returns {string} Ruta del archivo de configuración
|
|
65
|
+
* @example
|
|
66
|
+
* const configFile = globalConfig.getConfigFile();
|
|
67
|
+
* console.log('Config file:', configFile);
|
|
68
|
+
*/
|
|
69
|
+
getConfigFile() {
|
|
70
|
+
return G360_CONFIG_FILE;
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Inicializa el directorio de configuración global
|
|
75
|
+
* @returns {Promise<void>}
|
|
76
|
+
* @example
|
|
77
|
+
* await globalConfig.init();
|
|
78
|
+
*/
|
|
79
|
+
async init() {
|
|
80
|
+
try {
|
|
81
|
+
await fs.ensureDir(G360_CONFIG_DIR);
|
|
82
|
+
|
|
83
|
+
if (!fs.existsSync(G360_CONFIG_FILE)) {
|
|
84
|
+
await this.save(DEFAULT_CONFIG);
|
|
85
|
+
logger.info('Global config initialized', { path: G360_CONFIG_FILE });
|
|
86
|
+
}
|
|
87
|
+
} catch (error) {
|
|
88
|
+
logger.error('Failed to initialize global config', { error: error.message });
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Carga la configuración global
|
|
95
|
+
* @returns {Promise<GlobalConfig>} Configuración global cargada
|
|
96
|
+
* @example
|
|
97
|
+
* const config = await globalConfig.load();
|
|
98
|
+
* console.log('Default skill:', config.defaultSkill);
|
|
99
|
+
*/
|
|
100
|
+
async load() {
|
|
101
|
+
try {
|
|
102
|
+
if (!fs.existsSync(G360_CONFIG_FILE)) {
|
|
103
|
+
await this.init();
|
|
104
|
+
return { ...DEFAULT_CONFIG };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const config = await fs.readJson(G360_CONFIG_FILE);
|
|
108
|
+
return { ...DEFAULT_CONFIG, ...config };
|
|
109
|
+
} catch (error) {
|
|
110
|
+
logger.error('Failed to load global config', { error: error.message });
|
|
111
|
+
return { ...DEFAULT_CONFIG };
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Guarda la configuración global
|
|
117
|
+
* @param {GlobalConfig} config - Configuración a guardar
|
|
118
|
+
* @returns {Promise<void>}
|
|
119
|
+
* @example
|
|
120
|
+
* await globalConfig.save({ defaultSkill: 'moderno' });
|
|
121
|
+
*/
|
|
122
|
+
async save(config) {
|
|
123
|
+
try {
|
|
124
|
+
await fs.ensureDir(G360_CONFIG_DIR);
|
|
125
|
+
await fs.writeJson(G360_CONFIG_FILE, config, { spaces: 2 });
|
|
126
|
+
logger.debug('Global config saved');
|
|
127
|
+
} catch (error) {
|
|
128
|
+
logger.error('Failed to save global config', { error: error.message });
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Obtiene un valor de configuración específico
|
|
135
|
+
* @param {string} key - Clave de configuración (notación de puntos)
|
|
136
|
+
* @param {*} defaultValue - Valor por defecto si no existe
|
|
137
|
+
* @returns {Promise<*>} Valor de configuración
|
|
138
|
+
* @example
|
|
139
|
+
* const skill = await globalConfig.get('defaultSkill');
|
|
140
|
+
* const theme = await globalConfig.get('preferences.theme', 'dark');
|
|
141
|
+
*/
|
|
142
|
+
async get(key, defaultValue = undefined) {
|
|
143
|
+
const config = await this.load();
|
|
144
|
+
|
|
145
|
+
const keys = key.split('.');
|
|
146
|
+
let value = config;
|
|
147
|
+
|
|
148
|
+
for (const k of keys) {
|
|
149
|
+
if (value && typeof value === 'object' && k in value) {
|
|
150
|
+
value = value[k];
|
|
151
|
+
} else {
|
|
152
|
+
return defaultValue;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return value;
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Establece un valor de configuración específico
|
|
161
|
+
* @param {string} key - Clave de configuración (notación de puntos)
|
|
162
|
+
* @param {*} value - Valor a establecer
|
|
163
|
+
* @returns {Promise<void>}
|
|
164
|
+
* @example
|
|
165
|
+
* await globalConfig.set('defaultSkill', 'moderno');
|
|
166
|
+
* await globalConfig.set('preferences.theme', 'light');
|
|
167
|
+
*/
|
|
168
|
+
async set(key, value) {
|
|
169
|
+
const config = await this.load();
|
|
170
|
+
|
|
171
|
+
const keys = key.split('.');
|
|
172
|
+
let current = config;
|
|
173
|
+
|
|
174
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
175
|
+
const k = keys[i];
|
|
176
|
+
if (!current[k] || typeof current[k] !== 'object') {
|
|
177
|
+
current[k] = {};
|
|
178
|
+
}
|
|
179
|
+
current = current[k];
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
current[keys[keys.length - 1]] = value;
|
|
183
|
+
await this.save(config);
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Agrega un proyecto a la lista de proyectos recientes
|
|
188
|
+
* @param {string} projectPath - Ruta del proyecto
|
|
189
|
+
* @param {Object} metadata - Metadatos del proyecto
|
|
190
|
+
* @returns {Promise<void>}
|
|
191
|
+
* @example
|
|
192
|
+
* await globalConfig.addRecentProject('/my/project', {
|
|
193
|
+
* template: 'web-pwa',
|
|
194
|
+
* skill: 'corporativo-movil'
|
|
195
|
+
* });
|
|
196
|
+
*/
|
|
197
|
+
async addRecentProject(projectPath, metadata = {}) {
|
|
198
|
+
const config = await this.load();
|
|
199
|
+
|
|
200
|
+
// Eliminar el proyecto si ya existe
|
|
201
|
+
config.recentProjects = config.recentProjects.filter(
|
|
202
|
+
p => p.path !== projectPath
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
// Agregar al inicio
|
|
206
|
+
config.recentProjects.unshift({
|
|
207
|
+
path: projectPath,
|
|
208
|
+
lastAccessed: new Date().toISOString(),
|
|
209
|
+
...metadata
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// Mantener solo los últimos 10 proyectos
|
|
213
|
+
config.recentProjects = config.recentProjects.slice(0, 10);
|
|
214
|
+
|
|
215
|
+
await this.save(config);
|
|
216
|
+
logger.debug('Project added to recent projects', { path: projectPath });
|
|
217
|
+
},
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Obtiene la lista de proyectos recientes
|
|
221
|
+
* @returns {Promise<Array>} Lista de proyectos recientes
|
|
222
|
+
* @example
|
|
223
|
+
* const recent = await globalConfig.getRecentProjects();
|
|
224
|
+
* recent.forEach(project => console.log(project.path));
|
|
225
|
+
*/
|
|
226
|
+
async getRecentProjects() {
|
|
227
|
+
const config = await this.load();
|
|
228
|
+
return config.recentProjects || [];
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Limpia la configuración global
|
|
233
|
+
* @returns {Promise<void>}
|
|
234
|
+
* @example
|
|
235
|
+
* await globalConfig.clear();
|
|
236
|
+
*/
|
|
237
|
+
async clear() {
|
|
238
|
+
try {
|
|
239
|
+
if (fs.existsSync(G360_CONFIG_FILE)) {
|
|
240
|
+
await fs.remove(G360_CONFIG_FILE);
|
|
241
|
+
logger.info('Global config cleared');
|
|
242
|
+
}
|
|
243
|
+
} catch (error) {
|
|
244
|
+
logger.error('Failed to clear global config', { error: error.message });
|
|
245
|
+
throw error;
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Verifica si la configuración global existe
|
|
251
|
+
* @returns {boolean} true si existe, false en caso contrario
|
|
252
|
+
* @example
|
|
253
|
+
* if (await globalConfig.exists()) {
|
|
254
|
+
* console.log('Config exists');
|
|
255
|
+
* }
|
|
256
|
+
*/
|
|
257
|
+
async exists() {
|
|
258
|
+
return fs.existsSync(G360_CONFIG_FILE);
|
|
259
|
+
}
|
|
260
|
+
};
|
package/src/lib/i18n.js
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file i18n.js
|
|
3
|
+
* @description Sistema de internacionalización para G360-CLI
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Traducciones disponibles
|
|
8
|
+
* @type {Object.<string, Object.<string, string>>}
|
|
9
|
+
*/
|
|
10
|
+
const translations = {
|
|
11
|
+
es: {
|
|
12
|
+
// Comandos
|
|
13
|
+
'command.init.title': '🚀 Inicialización de Proyecto G360',
|
|
14
|
+
'command.init.project': 'Proyecto',
|
|
15
|
+
'command.init.template': 'Plantilla',
|
|
16
|
+
'command.init.skill': 'Skill',
|
|
17
|
+
'command.init.target': 'Destino',
|
|
18
|
+
'command.init.success': '✅ Proyecto creado exitosamente',
|
|
19
|
+
'command.init.portable': '📦 Versión portable habilitada',
|
|
20
|
+
'command.init.next_steps': 'Próximos pasos',
|
|
21
|
+
|
|
22
|
+
'command.list.title': '📋 Assets G360',
|
|
23
|
+
'command.list.templates': '📁 Plantillas',
|
|
24
|
+
'command.list.components': '🧩 Componentes',
|
|
25
|
+
'command.list.skills': '⚡ Skills',
|
|
26
|
+
'command.list.snippets': '📝 Snippets',
|
|
27
|
+
'command.list.no_templates': 'No se encontraron plantillas',
|
|
28
|
+
'command.list.no_components': 'No se encontraron componentes',
|
|
29
|
+
'command.list.no_skills': 'No se encontraron skills',
|
|
30
|
+
'command.list.no_snippets': 'No se encontraron snippets',
|
|
31
|
+
|
|
32
|
+
'command.set-skill.title': '🎨 Selector de Skill G360',
|
|
33
|
+
'command.set-skill.success': '✅ Skill configurado correctamente',
|
|
34
|
+
'command.set-skill.not_found': '❌ Skill no encontrado',
|
|
35
|
+
'command.set-skill.already_exists': '⚠️ El proyecto ya tiene un skill configurado',
|
|
36
|
+
'command.set-skill.use_force': 'Usa --force para sobrescribir',
|
|
37
|
+
|
|
38
|
+
'command.audit.title': '🔍 Auditoría de Proyecto G360',
|
|
39
|
+
'command.audit.results': '📊 Resultados de Auditoría',
|
|
40
|
+
'command.audit.passed': 'Pasaron',
|
|
41
|
+
'command.audit.failed': 'Fallaron',
|
|
42
|
+
'command.audit.warnings': 'Advertencias',
|
|
43
|
+
'command.audit.issues': 'Issues Encontrados',
|
|
44
|
+
'command.audit.compliant': '✅ El proyecto es compliant con G360',
|
|
45
|
+
'command.audit.issues_found': '⚠️ Algunos issues necesitan atención',
|
|
46
|
+
|
|
47
|
+
'command.health.title': '🏥 Verificación de Salud G360',
|
|
48
|
+
'command.health.status': 'Estado del Sistema',
|
|
49
|
+
'command.health.healthy': '✅ El sistema está saludable',
|
|
50
|
+
'command.health.failed': '❌ Algunos checks fallaron',
|
|
51
|
+
|
|
52
|
+
'command.clean.title': '🧹 Limpieza de Proyecto G360',
|
|
53
|
+
'command.clean.preview': '📋 DRY RUN - No se eliminarán archivos',
|
|
54
|
+
'command.clean.success': '✅ Limpieza completada',
|
|
55
|
+
|
|
56
|
+
// Errores
|
|
57
|
+
'error.templates_not_found': '❌ Plantillas no encontradas. Ejecuta: g360 update',
|
|
58
|
+
'error.template_not_found': '❌ Plantilla no encontrada',
|
|
59
|
+
'error.directory_exists': '❌ El directorio ya existe. Usa --force para sobrescribir',
|
|
60
|
+
'error.path_not_found': '❌ Ruta no encontrada',
|
|
61
|
+
'error.config_not_found': '❌ No se pudo cargar la configuración de skills',
|
|
62
|
+
|
|
63
|
+
// General
|
|
64
|
+
'general.available': 'Disponibles',
|
|
65
|
+
'general.device': 'Dispositivo',
|
|
66
|
+
'general.description': 'Descripción',
|
|
67
|
+
'general.language': 'Lenguaje',
|
|
68
|
+
'general.loading': 'Cargando...',
|
|
69
|
+
'general.done': 'Completado',
|
|
70
|
+
'general.cancelled': 'Cancelado',
|
|
71
|
+
'general.error': 'Error',
|
|
72
|
+
'general.warning': 'Advertencia',
|
|
73
|
+
'general.info': 'Información',
|
|
74
|
+
'general.success': 'Éxito'
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
en: {
|
|
78
|
+
// Commands
|
|
79
|
+
'command.init.title': '🚀 G360 Project Initialization',
|
|
80
|
+
'command.init.project': 'Project',
|
|
81
|
+
'command.init.template': 'Template',
|
|
82
|
+
'command.init.skill': 'Skill',
|
|
83
|
+
'command.init.target': 'Target',
|
|
84
|
+
'command.init.success': '✅ Project created successfully',
|
|
85
|
+
'command.init.portable': '📦 Portable version enabled',
|
|
86
|
+
'command.init.next_steps': 'Next steps',
|
|
87
|
+
|
|
88
|
+
'command.list.title': '📋 G360 Assets',
|
|
89
|
+
'command.list.templates': '📁 Templates',
|
|
90
|
+
'command.list.components': '🧩 Components',
|
|
91
|
+
'command.list.skills': '⚡ Skills',
|
|
92
|
+
'command.list.snippets': '📝 Snippets',
|
|
93
|
+
'command.list.no_templates': 'No templates found',
|
|
94
|
+
'command.list.no_components': 'No components found',
|
|
95
|
+
'command.list.no_skills': 'No skills found',
|
|
96
|
+
'command.list.no_snippets': 'No snippets found',
|
|
97
|
+
|
|
98
|
+
'command.set-skill.title': '🎨 G360 Skill Selector',
|
|
99
|
+
'command.set-skill.success': '✅ Skill configured successfully',
|
|
100
|
+
'command.set-skill.not_found': '❌ Skill not found',
|
|
101
|
+
'command.set-skill.already_exists': '⚠️ Project already has a skill configured',
|
|
102
|
+
'command.set-skill.use_force': 'Use --force to overwrite',
|
|
103
|
+
|
|
104
|
+
'command.audit.title': '🔍 G360 Project Audit',
|
|
105
|
+
'command.audit.results': '📊 Audit Results',
|
|
106
|
+
'command.audit.passed': 'Passed',
|
|
107
|
+
'command.audit.failed': 'Failed',
|
|
108
|
+
'command.audit.warnings': 'Warnings',
|
|
109
|
+
'command.audit.issues': 'Issues Found',
|
|
110
|
+
'command.audit.compliant': '✅ Project is G360 compliant',
|
|
111
|
+
'command.audit.issues_found': '⚠️ Some issues need attention',
|
|
112
|
+
|
|
113
|
+
'command.health.title': '🏥 G360 Health Check',
|
|
114
|
+
'command.health.status': 'System Status',
|
|
115
|
+
'command.health.healthy': '✅ System is healthy',
|
|
116
|
+
'command.health.failed': '❌ Some checks failed',
|
|
117
|
+
|
|
118
|
+
'command.clean.title': '🧹 G360 Project Clean',
|
|
119
|
+
'command.clean.preview': '📋 DRY RUN - No files will be removed',
|
|
120
|
+
'command.clean.success': '✅ Cleanup completed',
|
|
121
|
+
|
|
122
|
+
// Errors
|
|
123
|
+
'error.templates_not_found': '❌ Templates not found. Run: g360 update',
|
|
124
|
+
'error.template_not_found': '❌ Template not found',
|
|
125
|
+
'error.directory_exists': '❌ Directory already exists. Use --force to overwrite',
|
|
126
|
+
'error.path_not_found': '❌ Path not found',
|
|
127
|
+
'error.config_not_found': '❌ Could not load skills configuration',
|
|
128
|
+
|
|
129
|
+
// General
|
|
130
|
+
'general.available': 'Available',
|
|
131
|
+
'general.device': 'Device',
|
|
132
|
+
'general.description': 'Description',
|
|
133
|
+
'general.language': 'Language',
|
|
134
|
+
'general.loading': 'Loading...',
|
|
135
|
+
'general.done': 'Done',
|
|
136
|
+
'general.cancelled': 'Cancelled',
|
|
137
|
+
'general.error': 'Error',
|
|
138
|
+
'general.warning': 'Warning',
|
|
139
|
+
'general.info': 'Info',
|
|
140
|
+
'general.success': 'Success'
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Idioma actual
|
|
146
|
+
* @type {string}
|
|
147
|
+
*/
|
|
148
|
+
let currentLanguage = 'es';
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Módulo de internacionalización para G360-CLI
|
|
152
|
+
* @namespace i18n
|
|
153
|
+
*/
|
|
154
|
+
export const i18n = {
|
|
155
|
+
/**
|
|
156
|
+
* Establece el idioma actual
|
|
157
|
+
* @param {string} language - Código de idioma ('es' | 'en')
|
|
158
|
+
* @returns {void}
|
|
159
|
+
* @example
|
|
160
|
+
* i18n.setLanguage('en');
|
|
161
|
+
*/
|
|
162
|
+
setLanguage(language) {
|
|
163
|
+
if (translations[language]) {
|
|
164
|
+
currentLanguage = language;
|
|
165
|
+
} else {
|
|
166
|
+
console.warn(`Language '${language}' not supported, using 'es'`);
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Obtiene el idioma actual
|
|
172
|
+
* @returns {string} Código de idioma actual
|
|
173
|
+
* @example
|
|
174
|
+
* const lang = i18n.getLanguage();
|
|
175
|
+
*/
|
|
176
|
+
getLanguage() {
|
|
177
|
+
return currentLanguage;
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Traduce una clave de texto
|
|
182
|
+
* @param {string} key - Clave de traducción
|
|
183
|
+
* @param {Object} [params] - Parámetros para interpolación
|
|
184
|
+
* @returns {string} Texto traducido
|
|
185
|
+
* @example
|
|
186
|
+
* const text = i18n.t('command.init.success');
|
|
187
|
+
* const text = i18n.t('command.init.project', { name: 'my-project' });
|
|
188
|
+
*/
|
|
189
|
+
t(key, params = {}) {
|
|
190
|
+
const lang = translations[currentLanguage] || translations.es;
|
|
191
|
+
let text = lang[key] || translations.es[key] || key;
|
|
192
|
+
|
|
193
|
+
// Interpolación de parámetros
|
|
194
|
+
Object.keys(params).forEach(param => {
|
|
195
|
+
text = text.replace(`{${param}}`, params[param]);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
return text;
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Verifica si una clave de traducción existe
|
|
203
|
+
* @param {string} key - Clave de traducción
|
|
204
|
+
* @returns {boolean} true si existe, false en caso contrario
|
|
205
|
+
* @example
|
|
206
|
+
* if (i18n.has('command.init.success')) {
|
|
207
|
+
* console.log(i18n.t('command.init.success'));
|
|
208
|
+
* }
|
|
209
|
+
*/
|
|
210
|
+
has(key) {
|
|
211
|
+
const lang = translations[currentLanguage] || translations.es;
|
|
212
|
+
return key in lang;
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Obtiene todos los idiomas disponibles
|
|
217
|
+
* @returns {string[]} Lista de códigos de idioma
|
|
218
|
+
* @example
|
|
219
|
+
* const languages = i18n.getAvailableLanguages();
|
|
220
|
+
* console.log('Available languages:', languages);
|
|
221
|
+
*/
|
|
222
|
+
getAvailableLanguages() {
|
|
223
|
+
return Object.keys(translations);
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Obtiene las traducciones para un idioma específico
|
|
228
|
+
* @param {string} language - Código de idioma
|
|
229
|
+
* @returns {Object|null} Objeto de traducciones o null si no existe
|
|
230
|
+
* @example
|
|
231
|
+
* const esTranslations = i18n.getTranslations('es');
|
|
232
|
+
*/
|
|
233
|
+
getTranslations(language) {
|
|
234
|
+
return translations[language] || null;
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
export default i18n;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import winston from 'winston';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Niveles de log disponibles
|
|
9
|
+
* @enum {string}
|
|
10
|
+
*/
|
|
11
|
+
const LogLevel = {
|
|
12
|
+
ERROR: 'error',
|
|
13
|
+
WARN: 'warn',
|
|
14
|
+
INFO: 'info',
|
|
15
|
+
DEBUG: 'debug'
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Formato personalizado para logs
|
|
20
|
+
* @param {Object} info - Información del log
|
|
21
|
+
* @returns {string} Log formateado
|
|
22
|
+
*/
|
|
23
|
+
const customFormat = winston.format.combine(
|
|
24
|
+
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
|
25
|
+
winston.format.errors({ stack: true }),
|
|
26
|
+
winston.format.printf(({ level, message, timestamp, ...metadata }) => {
|
|
27
|
+
let msg = `${timestamp} [${level.toUpperCase()}]: ${message}`;
|
|
28
|
+
|
|
29
|
+
if (Object.keys(metadata).length > 0) {
|
|
30
|
+
msg += ` ${JSON.stringify(metadata)}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return msg;
|
|
34
|
+
})
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Logger configurado para G360-CLI
|
|
39
|
+
* @type {winston.Logger}
|
|
40
|
+
*/
|
|
41
|
+
export const logger = winston.createLogger({
|
|
42
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
43
|
+
format: customFormat,
|
|
44
|
+
transports: [
|
|
45
|
+
// Console transport para desarrollo
|
|
46
|
+
new winston.transports.Console({
|
|
47
|
+
format: winston.format.combine(
|
|
48
|
+
winston.format.colorize(),
|
|
49
|
+
customFormat
|
|
50
|
+
)
|
|
51
|
+
}),
|
|
52
|
+
|
|
53
|
+
// File transport para logs persistentes
|
|
54
|
+
new winston.transports.File({
|
|
55
|
+
filename: path.join(__dirname, '../../logs/g360-cli.log'),
|
|
56
|
+
level: 'info',
|
|
57
|
+
maxsize: 5242880, // 5MB
|
|
58
|
+
maxFiles: 3
|
|
59
|
+
}),
|
|
60
|
+
|
|
61
|
+
// Error file transport
|
|
62
|
+
new winston.transports.File({
|
|
63
|
+
filename: path.join(__dirname, '../../logs/error.log'),
|
|
64
|
+
level: 'error',
|
|
65
|
+
maxsize: 5242880, // 5MB
|
|
66
|
+
maxFiles: 3
|
|
67
|
+
})
|
|
68
|
+
]
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Crea un logger hijo con contexto específico
|
|
73
|
+
* @param {string} context - Contexto del logger
|
|
74
|
+
* @returns {winston.Logger} Logger hijo con contexto
|
|
75
|
+
*/
|
|
76
|
+
export function createChildLogger(context) {
|
|
77
|
+
return logger.child({ context });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Logger para comandos CLI
|
|
82
|
+
*/
|
|
83
|
+
export const commandLogger = createChildLogger('command');
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Logger para operaciones de archivos
|
|
87
|
+
*/
|
|
88
|
+
export const fileLogger = createChildLogger('file');
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Logger para validaciones
|
|
92
|
+
*/
|
|
93
|
+
export const validationLogger = createChildLogger('validation');
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Logger para auditoría
|
|
97
|
+
*/
|
|
98
|
+
export const auditLogger = createChildLogger('audit');
|
|
99
|
+
|
|
100
|
+
export default logger;
|
package/src/lib/manifest.js
CHANGED
|
@@ -1,7 +1,42 @@
|
|
|
1
1
|
import fs from 'fs-extra';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {Object} ProjectManifest
|
|
6
|
+
* @property {string} name - Nombre del proyecto
|
|
7
|
+
* @property {string} template - Plantilla utilizada
|
|
8
|
+
* @property {string} version - Versión del proyecto
|
|
9
|
+
* @property {string} createdAt - Fecha de creación en formato ISO
|
|
10
|
+
* @property {Asset[]} assets - Lista de assets del proyecto
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {Object} Asset
|
|
15
|
+
* @property {string} name - Nombre del asset
|
|
16
|
+
* @property {string} type - Tipo del asset
|
|
17
|
+
* @property {string} addedAt - Fecha de adición en formato ISO
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Módulo para gestión de manifiestos de proyectos G360
|
|
22
|
+
* @namespace manifest
|
|
23
|
+
*/
|
|
4
24
|
export const manifest = {
|
|
25
|
+
/**
|
|
26
|
+
* Inicializa un nuevo manifiesto de proyecto
|
|
27
|
+
* @param {string} projectDir - Directorio del proyecto
|
|
28
|
+
* @param {Object} data - Datos del proyecto
|
|
29
|
+
* @param {string} data.name - Nombre del proyecto
|
|
30
|
+
* @param {string} data.template - Plantilla utilizada
|
|
31
|
+
* @param {string} data.version - Versión del proyecto
|
|
32
|
+
* @returns {Promise<ProjectManifest>} Manifiesto creado
|
|
33
|
+
* @example
|
|
34
|
+
* const manifest = await manifest.init('/my/project', {
|
|
35
|
+
* name: 'my-project',
|
|
36
|
+
* template: 'web-pwa',
|
|
37
|
+
* version: '1.0.0'
|
|
38
|
+
* });
|
|
39
|
+
*/
|
|
5
40
|
async init(projectDir, data) {
|
|
6
41
|
const manifestPath = path.join(projectDir, 'g360-manifest.json');
|
|
7
42
|
const manifest = {
|
|
@@ -15,14 +50,41 @@ export const manifest = {
|
|
|
15
50
|
return manifest;
|
|
16
51
|
},
|
|
17
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Carga el manifiesto de un proyecto existente
|
|
55
|
+
* @param {string} projectDir - Directorio del proyecto
|
|
56
|
+
* @returns {Promise<ProjectManifest|null>} Manifiesto del proyecto o null si no existe
|
|
57
|
+
* @example
|
|
58
|
+
* const manifest = await manifest.load('/my/project');
|
|
59
|
+
* if (manifest) {
|
|
60
|
+
* console.log('Proyecto:', manifest.name);
|
|
61
|
+
* }
|
|
62
|
+
*/
|
|
18
63
|
async load(projectDir) {
|
|
19
64
|
const manifestPath = path.join(projectDir, 'g360-manifest.json');
|
|
20
65
|
if (fs.existsSync(manifestPath)) {
|
|
21
|
-
|
|
66
|
+
try {
|
|
67
|
+
return await fs.readJson(manifestPath);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
22
71
|
}
|
|
23
72
|
return null;
|
|
24
73
|
},
|
|
25
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Agrega un asset al manifiesto del proyecto
|
|
77
|
+
* @param {string} projectDir - Directorio del proyecto
|
|
78
|
+
* @param {Object} asset - Asset a agregar
|
|
79
|
+
* @param {string} asset.name - Nombre del asset
|
|
80
|
+
* @param {string} asset.type - Tipo del asset
|
|
81
|
+
* @returns {Promise<void>}
|
|
82
|
+
* @example
|
|
83
|
+
* await manifest.addAsset('/my/project', {
|
|
84
|
+
* name: 'components',
|
|
85
|
+
* type: 'directory'
|
|
86
|
+
* });
|
|
87
|
+
*/
|
|
26
88
|
async addAsset(projectDir, asset) {
|
|
27
89
|
const data = await this.load(projectDir);
|
|
28
90
|
if (data) {
|
|
@@ -34,6 +96,13 @@ export const manifest = {
|
|
|
34
96
|
}
|
|
35
97
|
},
|
|
36
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Elimina el manifiesto del proyecto
|
|
101
|
+
* @param {string} projectDir - Directorio del proyecto
|
|
102
|
+
* @returns {Promise<void>}
|
|
103
|
+
* @example
|
|
104
|
+
* await manifest.remove('/my/project');
|
|
105
|
+
*/
|
|
37
106
|
async remove(projectDir) {
|
|
38
107
|
const manifestPath = path.join(projectDir, 'g360-manifest.json');
|
|
39
108
|
if (fs.existsSync(manifestPath)) {
|