g360-cli 1.3.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.
Files changed (86) hide show
  1. package/README.md +209 -5
  2. package/package.json +15 -8
  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 +112 -0
  13. package/src/assets/config/project-types.json +16 -0
  14. package/src/assets/config/skills.json +12 -0
  15. package/src/assets/snippets/snippets.json +293 -24
  16. package/src/assets/templates/lit-web/src/core/skill.json +12 -5
  17. package/src/assets/templates/python-cli/build-portable.bat +43 -0
  18. package/src/assets/templates/python-cli/src/core/skill.json +25 -6
  19. package/src/assets/templates/python-cli/src/main.py +107 -3
  20. package/src/assets/templates/python-customtkinter/README.md +48 -0
  21. package/src/assets/templates/python-customtkinter/assets/images/logo-g360-dark.svg +19 -0
  22. package/src/assets/templates/python-customtkinter/assets/images/logo-g360-light.svg +19 -0
  23. package/src/assets/templates/python-customtkinter/build.bat +50 -0
  24. package/src/assets/templates/python-customtkinter/pyproject.toml +26 -0
  25. package/src/assets/templates/python-customtkinter/run.bat +20 -0
  26. package/src/assets/templates/python-customtkinter/src/core/g360_theme.py +101 -0
  27. package/src/assets/templates/python-customtkinter/src/core/skill.json +35 -0
  28. package/src/assets/templates/python-customtkinter/src/main.py +103 -0
  29. package/src/assets/templates/python-flet/README.md +89 -0
  30. package/src/assets/templates/python-flet/assets/images/Logo_cipsa_borde.png +0 -0
  31. package/src/assets/templates/python-flet/assets/images/Logo_cipsa_borde.svg +9 -0
  32. package/src/assets/templates/python-flet/assets/images/Logo_cipsa_solid.png +0 -0
  33. package/src/assets/templates/python-flet/assets/images/Logo_cipsa_solid.svg +9 -0
  34. package/src/assets/templates/python-flet/assets/images/logo-g360-dark.svg +19 -0
  35. package/src/assets/templates/python-flet/assets/images/logo-g360-light.svg +19 -0
  36. package/src/assets/templates/python-flet/build.bat +36 -0
  37. package/src/assets/templates/python-flet/pyproject.toml +26 -0
  38. package/src/assets/templates/python-flet/run.bat +20 -0
  39. package/src/assets/templates/python-flet/src/core/__init__.py +0 -0
  40. package/src/assets/templates/python-flet/src/core/g360_theme.py +186 -0
  41. package/src/assets/templates/python-flet/src/core/skill.json +41 -0
  42. package/src/assets/templates/python-flet/src/export/__init__.py +0 -0
  43. package/src/assets/templates/python-flet/src/main.py +146 -0
  44. package/src/assets/templates/python-flet/src/test_app.py +107 -0
  45. package/src/assets/templates/python-flet/src/ui/__init__.py +0 -0
  46. package/src/assets/templates/python-flet-migrate/README.md +70 -0
  47. package/src/assets/templates/python-flet-migrate/assets/images/logo-g360-dark.svg +19 -0
  48. package/src/assets/templates/python-flet-migrate/assets/images/logo-g360-light.svg +19 -0
  49. package/src/assets/templates/python-flet-migrate/build.bat +36 -0
  50. package/src/assets/templates/python-flet-migrate/pyproject.toml +26 -0
  51. package/src/assets/templates/python-flet-migrate/run.bat +20 -0
  52. package/src/assets/templates/python-flet-migrate/src/core/__init__.py +0 -0
  53. package/src/assets/templates/python-flet-migrate/src/core/g360_theme.py +186 -0
  54. package/src/assets/templates/python-flet-migrate/src/core/skill.json +34 -0
  55. package/src/assets/templates/python-flet-migrate/src/main.py +97 -0
  56. package/src/assets/templates/python-flet-migrate/src/migrate_tkinter.py +56 -0
  57. package/src/assets/templates/solid-web/src/core/skill.json +12 -5
  58. package/src/assets/templates/svelte-web/src/core/skill.json +12 -5
  59. package/src/assets/templates/web-pwa/index.html +2 -7
  60. package/src/assets/templates/web-pwa/public/favicon.svg +4 -0
  61. package/src/assets/templates/web-pwa/public/manifest.json +11 -0
  62. package/src/assets/templates/web-pwa/src/App.jsx +22 -0
  63. package/src/assets/templates/web-pwa/src/components/FooterSignature.jsx +9 -0
  64. package/src/assets/templates/web-pwa/src/components/Header.jsx +12 -0
  65. package/src/assets/templates/web-pwa/src/components/LoadingOverlay.jsx +10 -0
  66. package/src/assets/templates/web-pwa/src/core/skill.json +25 -0
  67. package/src/assets/templates/web-pwa/src/main.jsx +9 -0
  68. package/src/assets/templates/web-pwa/src/styles/main.css +133 -0
  69. package/src/assets/templates/web-pwa/vite.config.js +12 -0
  70. package/src/cli.js +8 -4
  71. package/src/commands/bring.js +81 -6
  72. package/src/commands/clean.js +51 -4
  73. package/src/commands/init.js +60 -8
  74. package/src/commands/list.js +79 -9
  75. package/src/commands/signature.js +12 -8
  76. package/src/lib/asset-validator.js +145 -0
  77. package/src/lib/auditor.js +48 -0
  78. package/src/lib/global-config.js +260 -0
  79. package/src/lib/i18n.js +238 -0
  80. package/src/lib/logger.js +100 -0
  81. package/src/lib/manifest.js +70 -1
  82. package/src/lib/validator.js +39 -0
  83. package/src/schemas/skills-schema.json +1 -0
  84. package/src/schemas/snippets-schema.json +1 -0
  85. package/src/assets/templates/web-pwa/app.js +0 -1
  86. package/src/assets/templates/web-pwa/styles.css +0 -28
@@ -0,0 +1,145 @@
1
+ import Ajv from 'ajv';
2
+ import fs from 'fs-extra';
3
+ import path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import { logger } from './logger.js';
6
+
7
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
+
9
+ /**
10
+ * @typedef {Object} ValidationResult
11
+ * @property {boolean} valid - Indica si la validación fue exitosa
12
+ * @property {string[]} errors - Lista de errores encontrados
13
+ */
14
+
15
+ /**
16
+ * Módulo de validación de assets con JSON Schema
17
+ * @namespace assetValidator
18
+ */
19
+ export const assetValidator = {
20
+ /**
21
+ * Instancia de AJV para validación de schemas
22
+ * @type {Ajv}
23
+ */
24
+ ajv: new Ajv({ allErrors: true }),
25
+
26
+ /**
27
+ * Cache de schemas compilados
28
+ * @type {Map<string, any>}
29
+ */
30
+ schemaCache: new Map(),
31
+
32
+ /**
33
+ * Carga y compila un schema JSON
34
+ * @param {string} schemaName - Nombre del schema a cargar
35
+ * @returns {Promise<any>} Schema compilado
36
+ * @private
37
+ */
38
+ async loadSchema(schemaName) {
39
+ if (this.schemaCache.has(schemaName)) {
40
+ return this.schemaCache.get(schemaName);
41
+ }
42
+
43
+ const schemaPath = path.join(__dirname, '../schemas', `${schemaName}.json`);
44
+
45
+ if (!fs.existsSync(schemaPath)) {
46
+ throw new Error(`Schema not found: ${schemaName}`);
47
+ }
48
+
49
+ const schema = await fs.readJson(schemaPath);
50
+ const compiled = this.ajv.compile(schema);
51
+
52
+ this.schemaCache.set(schemaName, compiled);
53
+ return compiled;
54
+ },
55
+
56
+ /**
57
+ * Valida un objeto contra un schema específico
58
+ * @param {string} schemaName - Nombre del schema a usar
59
+ * @param {Object} data - Datos a validar
60
+ * @returns {Promise<ValidationResult>} Resultado de la validación
61
+ * @example
62
+ * const result = await assetValidator.validate('skills-schema', skillsData);
63
+ * if (result.valid) {
64
+ * console.log('Skills válidos');
65
+ * } else {
66
+ * console.error('Errores:', result.errors);
67
+ * }
68
+ */
69
+ async validate(schemaName, data) {
70
+ try {
71
+ const validate = await this.loadSchema(schemaName);
72
+ const valid = validate(data);
73
+
74
+ if (valid) {
75
+ logger.debug(`Validation passed for schema: ${schemaName}`);
76
+ return { valid: true, errors: [] };
77
+ }
78
+
79
+ const errors = validate.errors.map(err =>
80
+ `${err.instancePath} ${err.message}`
81
+ );
82
+
83
+ logger.warn(`Validation failed for schema: ${schemaName}`, { errors });
84
+ return { valid: false, errors };
85
+ } catch (error) {
86
+ logger.error(`Error validating against schema: ${schemaName}`, { error: error.message });
87
+ return {
88
+ valid: false,
89
+ errors: [`Validation error: ${error.message}`]
90
+ };
91
+ }
92
+ },
93
+
94
+ /**
95
+ * Valida la configuración de skills
96
+ * @param {Object} skillsData - Datos de skills a validar
97
+ * @returns {Promise<ValidationResult>} Resultado de la validación
98
+ * @example
99
+ * const result = await assetValidator.validateSkills(skillsConfig);
100
+ */
101
+ async validateSkills(skillsData) {
102
+ return this.validate('skills-schema', skillsData);
103
+ },
104
+
105
+ /**
106
+ * Valida la configuración de snippets
107
+ * @param {Object} snippetsData - Datos de snippets a validar
108
+ * @returns {Promise<ValidationResult>} Resultado de la validación
109
+ * @example
110
+ * const result = await assetValidator.validateSnippets(snippetsConfig);
111
+ */
112
+ async validateSnippets(snippetsData) {
113
+ return this.validate('snippets-schema', snippetsData);
114
+ },
115
+
116
+ /**
117
+ * Valida un asset individual
118
+ * @param {string} assetType - Tipo de asset ('skill' | 'snippet')
119
+ * @param {Object} assetData - Datos del asset a validar
120
+ * @returns {Promise<ValidationResult>} Resultado de la validación
121
+ * @example
122
+ * const result = await assetValidator.validateAsset('skill', skillData);
123
+ */
124
+ async validateAsset(assetType, assetData) {
125
+ const schemaName = assetType === 'skill' ? 'skills-schema' : 'snippets-schema';
126
+
127
+ // Para validación individual, envolvemos el asset en la estructura esperada
128
+ const wrapper = assetType === 'skill'
129
+ ? { skills: [assetData] }
130
+ : { snippets: [assetData] };
131
+
132
+ const result = await this.validate(schemaName, wrapper);
133
+
134
+ return result;
135
+ },
136
+
137
+ /**
138
+ * Limpia el cache de schemas
139
+ * @returns {void}
140
+ */
141
+ clearCache() {
142
+ this.schemaCache.clear();
143
+ logger.debug('Schema cache cleared');
144
+ }
145
+ };
@@ -2,7 +2,37 @@ import fs from 'fs-extra';
2
2
  import path from 'path';
3
3
  import chalk from 'chalk';
4
4
 
5
+ /**
6
+ * @typedef {Object} AuditIssue
7
+ * @property {string} file - Archivo donde se encontró el issue
8
+ * @property {string} message - Mensaje descriptivo del issue
9
+ * @property {string} severity - Severidad del issue ('error' | 'warning')
10
+ */
11
+
12
+ /**
13
+ * @typedef {Object} AuditResult
14
+ * @property {number} passed - Cantidad de checks que pasaron
15
+ * @property {number} failed - Cantidad de checks que fallaron
16
+ * @property {number} warnings - Cantidad de advertencias
17
+ * @property {number} total - Cantidad total de checks
18
+ * @property {AuditIssue[]} issues - Lista de issues encontrados
19
+ */
20
+
21
+ /**
22
+ * Módulo de auditoría para proyectos G360
23
+ * @namespace auditor
24
+ */
5
25
  export const auditor = {
26
+ /**
27
+ * Ejecuta auditoría completa del proyecto
28
+ * @param {string} projectDir - Directorio del proyecto a auditar
29
+ * @param {Object} options - Opciones de auditoría
30
+ * @param {boolean} [options.verbose=false] - Mostrar información detallada
31
+ * @returns {Promise<AuditResult>} Resultado de la auditoría
32
+ * @example
33
+ * const result = await auditor.audit('/my/project', { verbose: true });
34
+ * console.log(`Passed: ${result.passed}, Failed: ${result.failed}`);
35
+ */
6
36
  async audit(projectDir, options = {}) {
7
37
  const { verbose = false } = options;
8
38
  const results = {
@@ -41,6 +71,12 @@ export const auditor = {
41
71
  return results;
42
72
  },
43
73
 
74
+ /**
75
+ * Verifica si existe el manifiesto del proyecto
76
+ * @param {string} projectDir - Directorio del proyecto
77
+ * @returns {Object} Resultado del check
78
+ * @private
79
+ */
44
80
  checkManifest(projectDir) {
45
81
  const manifestPath = path.join(projectDir, 'g360-manifest.json');
46
82
  if (fs.existsSync(manifestPath)) {
@@ -67,6 +103,12 @@ export const auditor = {
67
103
  return { status: 'warn', issue: { file: 'g360-manifest.json', message: 'No manifest found - run "g360 bring" to initialize' } };
68
104
  },
69
105
 
106
+ /**
107
+ * Verifica la estructura del proyecto
108
+ * @param {string} projectDir - Directorio del proyecto
109
+ * @returns {Object} Resultado del check
110
+ * @private
111
+ */
70
112
  checkStructure(projectDir) {
71
113
  const g360Dir = path.join(projectDir, 'g360');
72
114
  if (fs.existsSync(g360Dir)) {
@@ -94,6 +136,12 @@ export const auditor = {
94
136
  return { status: 'warn', issue: { file: 'g360/', message: 'No G360 assets found - run "g360 bring" to initialize assets' } };
95
137
  },
96
138
 
139
+ /**
140
+ * Verifica la configuración del proyecto
141
+ * @param {string} projectDir - Directorio del proyecto
142
+ * @returns {Object} Resultado del check
143
+ * @private
144
+ */
97
145
  checkConfig(projectDir) {
98
146
  const configPath = path.join(projectDir, 'g360', 'config');
99
147
  if (fs.existsSync(configPath)) {
@@ -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
+ };