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,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
+ };
@@ -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;
@@ -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
- return fs.readJson(manifestPath);
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)) {