awc-zns-mtd 2.9.0 → 2.10.3
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/.github/workflows/ci.yml +148 -0
- package/.husky/pre-commit +2 -0
- package/.prettierignore +31 -0
- package/.prettierrc +13 -0
- package/IMPLEMENTATION_SUMMARY.md +410 -0
- package/PHASE_2_SUMMARY.md +289 -0
- package/README.md +114 -47
- package/SECURITY.md +58 -0
- package/eslint.config.js +70 -0
- package/jest.config.js +49 -0
- package/package.json +40 -14
- package/src/modules/custom-agents/cli/awc-agent.js +505 -372
- package/test/integration/cli/cli-commands.integration.test.js +105 -0
- package/test/setup.js +22 -0
- package/test/unit/commands/version.test.js +39 -0
- package/test/unit/config/config-manager.test.js +147 -0
- package/test/unit/utils/file-utils.test.js +177 -0
- package/test/unit/utils/validators.test.js +57 -0
- package/tools/cli/commands/init.js +556 -513
- package/tools/cli/commands/new-project.js +680 -659
- package/tools/cli/commands/validate.js +13 -13
- package/tools/cli/commands/version.js +5 -3
- package/tools/cli/utils/console-logger.js +39 -15
- package/tools/cli/utils/logger.js +176 -0
- package/tools/cli/utils/project-analyzer.js +33 -16
- package/tools/cli/utils/validators.js +144 -0
- package/tools/cli/utils/version.js +6 -2
- package/tools/config/config-manager.js +243 -0
- package/tools/version/changelog-manager.js +301 -288
- package/tools/version/update-checker.js +32 -32
- package/tools/version/version-bump.js +89 -90
- package/tools/version/version-manager.js +17 -7
- package/tsconfig.json +47 -0
- package/types/index.d.ts +206 -0
- package/tools/cli/commands/init-old.js +0 -147
- package/tools/cli/commands/new-project-broken.js +0 -1302
- package/tools/cli/commands/new-project-old.js +0 -1302
- package/tools/cli/commands/new-project.js.backup +0 -1302
|
@@ -26,10 +26,10 @@ async function validateCommand() {
|
|
|
26
26
|
checksCount++;
|
|
27
27
|
if (await fs.pathExists(awcDir)) {
|
|
28
28
|
passedCount++;
|
|
29
|
-
console.log(chalk.green('✓')
|
|
29
|
+
console.log(`${chalk.green('✓')} Directorio .awc existe`);
|
|
30
30
|
} else {
|
|
31
31
|
errors.push('Directorio .awc no encontrado');
|
|
32
|
-
console.log(chalk.red('✗')
|
|
32
|
+
console.log(`${chalk.red('✗')} Directorio .awc no encontrado`);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
// Check 2: Configuración
|
|
@@ -37,10 +37,10 @@ async function validateCommand() {
|
|
|
37
37
|
const config = await loadConfig(awcDir);
|
|
38
38
|
if (config) {
|
|
39
39
|
passedCount++;
|
|
40
|
-
console.log(chalk.green('✓')
|
|
40
|
+
console.log(`${chalk.green('✓')} Archivo de configuración válido`);
|
|
41
41
|
} else {
|
|
42
42
|
errors.push('Archivo de configuración inválido o faltante');
|
|
43
|
-
console.log(chalk.red('✗')
|
|
43
|
+
console.log(`${chalk.red('✗')} Archivo de configuración inválido`);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
// Check 3: Agentes
|
|
@@ -48,18 +48,18 @@ async function validateCommand() {
|
|
|
48
48
|
const agentsPath = path.join(awcDir, 'agents');
|
|
49
49
|
if (await fs.pathExists(agentsPath)) {
|
|
50
50
|
const agentFiles = await fs.readdir(agentsPath);
|
|
51
|
-
const yamlAgents = agentFiles.filter(f => f.endsWith('.yaml'));
|
|
51
|
+
const yamlAgents = agentFiles.filter((f) => f.endsWith('.yaml'));
|
|
52
52
|
|
|
53
53
|
if (yamlAgents.length >= 4) {
|
|
54
54
|
passedCount++;
|
|
55
|
-
console.log(chalk.green('✓')
|
|
55
|
+
console.log(`${chalk.green('✓')} ${yamlAgents.length} agentes encontrados`);
|
|
56
56
|
} else {
|
|
57
57
|
warnings.push(`Solo ${yamlAgents.length} agentes encontrados (se esperan 4)`);
|
|
58
|
-
console.log(chalk.yellow('⚠')
|
|
58
|
+
console.log(`${chalk.yellow('⚠')} Solo ${yamlAgents.length} agentes (se esperan 4)`);
|
|
59
59
|
}
|
|
60
60
|
} else {
|
|
61
61
|
errors.push('Directorio de agentes no encontrado');
|
|
62
|
-
console.log(chalk.red('✗')
|
|
62
|
+
console.log(`${chalk.red('✗')} Directorio de agentes no encontrado`);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
// Check 4: Workflows
|
|
@@ -70,14 +70,14 @@ async function validateCommand() {
|
|
|
70
70
|
|
|
71
71
|
if (workflowDirs.length >= 3) {
|
|
72
72
|
passedCount++;
|
|
73
|
-
console.log(chalk.green('✓')
|
|
73
|
+
console.log(`${chalk.green('✓')} ${workflowDirs.length} workflows encontrados`);
|
|
74
74
|
} else {
|
|
75
75
|
warnings.push(`Solo ${workflowDirs.length} workflows encontrados (se esperan 3)`);
|
|
76
|
-
console.log(chalk.yellow('⚠')
|
|
76
|
+
console.log(`${chalk.yellow('⚠')} Solo ${workflowDirs.length} workflows (se esperan 3)`);
|
|
77
77
|
}
|
|
78
78
|
} else {
|
|
79
79
|
errors.push('Directorio de workflows no encontrado');
|
|
80
|
-
console.log(chalk.red('✗')
|
|
80
|
+
console.log(`${chalk.red('✗')} Directorio de workflows no encontrado`);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
// Check 5: Documentación
|
|
@@ -85,7 +85,7 @@ async function validateCommand() {
|
|
|
85
85
|
const docsPath = path.join(cwd, 'docs');
|
|
86
86
|
if (await fs.pathExists(docsPath)) {
|
|
87
87
|
passedCount++;
|
|
88
|
-
console.log(chalk.green('✓')
|
|
88
|
+
console.log(`${chalk.green('✓')} Directorio de documentación existe`);
|
|
89
89
|
|
|
90
90
|
// Sub-checks de documentación
|
|
91
91
|
const adrPath = path.join(docsPath, 'adr');
|
|
@@ -103,7 +103,7 @@ async function validateCommand() {
|
|
|
103
103
|
}
|
|
104
104
|
} else {
|
|
105
105
|
warnings.push('Directorio de documentación no encontrado');
|
|
106
|
-
console.log(chalk.yellow('⚠')
|
|
106
|
+
console.log(`${chalk.yellow('⚠')} Directorio de documentación no encontrado`);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
// Resumen
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const chalk = require('chalk');
|
|
7
|
-
const semver = require('semver');
|
|
7
|
+
// const semver = require('semver');
|
|
8
8
|
const { getVersion, checkForUpdates } = require('../utils/version');
|
|
9
9
|
const { displayLogo } = require('../utils/console-logger');
|
|
10
10
|
|
|
@@ -35,7 +35,7 @@ async function versionCommand() {
|
|
|
35
35
|
} else {
|
|
36
36
|
console.log(chalk.green('\n✅ Estás usando la versión más reciente\n'));
|
|
37
37
|
}
|
|
38
|
-
} catch
|
|
38
|
+
} catch {
|
|
39
39
|
console.log(chalk.gray('\n No se pudo verificar actualizaciones (sin conexión)\n'));
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -43,7 +43,9 @@ async function versionCommand() {
|
|
|
43
43
|
console.log(chalk.cyan('📚 Más información:\n'));
|
|
44
44
|
console.log(` Documentación: ${chalk.blue('https://github.com/awc/awc-zns-mtd')}`);
|
|
45
45
|
console.log(` Issues: ${chalk.blue('https://github.com/awc/awc-zns-mtd/issues')}`);
|
|
46
|
-
console.log(
|
|
46
|
+
console.log(
|
|
47
|
+
` Changelog: ${chalk.blue('https://github.com/awc/awc-zns-mtd/blob/main/CHANGELOG.md')}\n`
|
|
48
|
+
);
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
module.exports = { versionCommand };
|
|
@@ -11,8 +11,16 @@ const chalk = require('chalk');
|
|
|
11
11
|
function displayLogo() {
|
|
12
12
|
console.log('');
|
|
13
13
|
console.log(chalk.cyan('╔═══════════════════════════════════════════════════════════╗'));
|
|
14
|
-
console.log(
|
|
15
|
-
|
|
14
|
+
console.log(
|
|
15
|
+
chalk.cyan('║') +
|
|
16
|
+
chalk.bold.white(' ZΞNAPSΞS by ΛNWICO ') +
|
|
17
|
+
chalk.cyan('║')
|
|
18
|
+
);
|
|
19
|
+
console.log(
|
|
20
|
+
chalk.cyan('║') +
|
|
21
|
+
chalk.gray(' Minimalismo Estratégico Method ') +
|
|
22
|
+
chalk.cyan('║')
|
|
23
|
+
);
|
|
16
24
|
console.log(chalk.cyan('╚═══════════════════════════════════════════════════════════╝'));
|
|
17
25
|
console.log();
|
|
18
26
|
}
|
|
@@ -21,28 +29,28 @@ function displayLogo() {
|
|
|
21
29
|
* Muestra un mensaje de éxito
|
|
22
30
|
*/
|
|
23
31
|
function logSuccess(message) {
|
|
24
|
-
console.log(chalk.green('✓')
|
|
32
|
+
console.log(`${chalk.green('✓')} ${message}`);
|
|
25
33
|
}
|
|
26
34
|
|
|
27
35
|
/**
|
|
28
36
|
* Muestra un mensaje de error
|
|
29
37
|
*/
|
|
30
38
|
function logError(message) {
|
|
31
|
-
console.log(chalk.red('✗')
|
|
39
|
+
console.log(`${chalk.red('✗')} ${message}`);
|
|
32
40
|
}
|
|
33
41
|
|
|
34
42
|
/**
|
|
35
43
|
* Muestra un mensaje de advertencia
|
|
36
44
|
*/
|
|
37
45
|
function logWarning(message) {
|
|
38
|
-
console.log(chalk.yellow('⚠')
|
|
46
|
+
console.log(`${chalk.yellow('⚠')} ${message}`);
|
|
39
47
|
}
|
|
40
48
|
|
|
41
49
|
/**
|
|
42
50
|
* Muestra un mensaje informativo
|
|
43
51
|
*/
|
|
44
52
|
function logInfo(message) {
|
|
45
|
-
console.log(chalk.blue('ℹ')
|
|
53
|
+
console.log(`${chalk.blue('ℹ')} ${message}`);
|
|
46
54
|
}
|
|
47
55
|
|
|
48
56
|
/**
|
|
@@ -74,9 +82,9 @@ function logProgress(current, total, message) {
|
|
|
74
82
|
* Muestra una tabla simple
|
|
75
83
|
*/
|
|
76
84
|
function logTable(data) {
|
|
77
|
-
const maxKeyLength = Math.max(...data.map(item => item.key.length));
|
|
85
|
+
const maxKeyLength = Math.max(...data.map((item) => item.key.length));
|
|
78
86
|
|
|
79
|
-
data.forEach(item => {
|
|
87
|
+
data.forEach((item) => {
|
|
80
88
|
const paddedKey = item.key.padEnd(maxKeyLength + 2);
|
|
81
89
|
console.log(` ${chalk.gray(paddedKey)} ${chalk.yellow(item.value)}`);
|
|
82
90
|
});
|
|
@@ -88,7 +96,9 @@ function logTable(data) {
|
|
|
88
96
|
function displayWelcome(projectName) {
|
|
89
97
|
console.log();
|
|
90
98
|
console.log(chalk.cyan('╔═══════════════════════════════════════════════════════════╗'));
|
|
91
|
-
console.log(
|
|
99
|
+
console.log(
|
|
100
|
+
chalk.cyan('║') + chalk.bold.white(` Bienvenido a ${projectName}`.padEnd(58)) + chalk.cyan('║')
|
|
101
|
+
);
|
|
92
102
|
console.log(chalk.cyan('╚═══════════════════════════════════════════════════════════╝'));
|
|
93
103
|
console.log();
|
|
94
104
|
}
|
|
@@ -99,7 +109,7 @@ function displayWelcome(projectName) {
|
|
|
99
109
|
function displayCommands(commands) {
|
|
100
110
|
console.log(chalk.cyan('\n💡 Comandos disponibles:\n'));
|
|
101
111
|
|
|
102
|
-
commands.forEach(cmd => {
|
|
112
|
+
commands.forEach((cmd) => {
|
|
103
113
|
const command = chalk.green(cmd.command.padEnd(20));
|
|
104
114
|
const description = chalk.gray(cmd.description);
|
|
105
115
|
console.log(` ${command} ${description}`);
|
|
@@ -113,9 +123,15 @@ function displayCommands(commands) {
|
|
|
113
123
|
*/
|
|
114
124
|
function displayZnsPhilosophy() {
|
|
115
125
|
console.log(chalk.cyan('\n📖 Filosofía ZNS-MTD:\n'));
|
|
116
|
-
console.log(
|
|
117
|
-
|
|
118
|
-
|
|
126
|
+
console.log(
|
|
127
|
+
chalk.yellow(' ZEN') + chalk.gray(' → Claridad, simplicidad deliberada, ruido mínimo')
|
|
128
|
+
);
|
|
129
|
+
console.log(
|
|
130
|
+
chalk.yellow(' NEUTRO') + chalk.gray(' → Objetividad, decisiones basadas en evidencia')
|
|
131
|
+
);
|
|
132
|
+
console.log(
|
|
133
|
+
chalk.yellow(' SISTEMÁTICO') + chalk.gray(' → Procesos repetibles, documentación rigurosa')
|
|
134
|
+
);
|
|
119
135
|
console.log();
|
|
120
136
|
}
|
|
121
137
|
|
|
@@ -126,11 +142,19 @@ function displayCompletionBanner(success = true) {
|
|
|
126
142
|
console.log();
|
|
127
143
|
if (success) {
|
|
128
144
|
console.log(chalk.green('╔═══════════════════════════════════════════════════════════╗'));
|
|
129
|
-
console.log(
|
|
145
|
+
console.log(
|
|
146
|
+
chalk.green('║') +
|
|
147
|
+
chalk.bold.white(' ✅ PROCESO COMPLETADO ') +
|
|
148
|
+
chalk.green('║')
|
|
149
|
+
);
|
|
130
150
|
console.log(chalk.green('╚═══════════════════════════════════════════════════════════╝'));
|
|
131
151
|
} else {
|
|
132
152
|
console.log(chalk.red('╔═══════════════════════════════════════════════════════════╗'));
|
|
133
|
-
console.log(
|
|
153
|
+
console.log(
|
|
154
|
+
chalk.red('║') +
|
|
155
|
+
chalk.bold.white(' ❌ PROCESO FALLIDO ') +
|
|
156
|
+
chalk.red('║')
|
|
157
|
+
);
|
|
134
158
|
console.log(chalk.red('╚═══════════════════════════════════════════════════════════╝'));
|
|
135
159
|
}
|
|
136
160
|
console.log();
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger - Sistema de logging estructurado
|
|
3
|
+
* Utiliza Winston para logging centralizado
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const winston = require('winston');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const fs = require('fs-extra');
|
|
9
|
+
const ConfigManager = require('../../config/config-manager');
|
|
10
|
+
|
|
11
|
+
// Asegurar que existe el directorio de logs
|
|
12
|
+
const logsDir = path.join(process.cwd(), ConfigManager.LOGS_DIR);
|
|
13
|
+
fs.ensureDirSync(logsDir);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Formatos personalizados
|
|
17
|
+
*/
|
|
18
|
+
const customFormat = winston.format.combine(
|
|
19
|
+
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
|
20
|
+
winston.format.errors({ stack: true }),
|
|
21
|
+
winston.format.splat(),
|
|
22
|
+
winston.format.json()
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const consoleFormat = winston.format.combine(
|
|
26
|
+
winston.format.colorize(),
|
|
27
|
+
winston.format.timestamp({ format: 'HH:mm:ss' }),
|
|
28
|
+
winston.format.printf(({ level, message, timestamp, ...metadata }) => {
|
|
29
|
+
let msg = `${timestamp} [${level}]: ${message}`;
|
|
30
|
+
if (Object.keys(metadata).length > 0) {
|
|
31
|
+
msg += ` ${JSON.stringify(metadata)}`;
|
|
32
|
+
}
|
|
33
|
+
return msg;
|
|
34
|
+
})
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Crear logger
|
|
39
|
+
*/
|
|
40
|
+
const logger = winston.createLogger({
|
|
41
|
+
level: ConfigManager.DEFAULT_LOG_LEVEL,
|
|
42
|
+
format: customFormat,
|
|
43
|
+
defaultMeta: { service: 'awc-zns-mtd' },
|
|
44
|
+
transports: [
|
|
45
|
+
// Logs de error
|
|
46
|
+
new winston.transports.File({
|
|
47
|
+
filename: path.join(logsDir, 'error.log'),
|
|
48
|
+
level: 'error',
|
|
49
|
+
maxsize: 5242880, // 5MB
|
|
50
|
+
maxFiles: 5
|
|
51
|
+
}),
|
|
52
|
+
|
|
53
|
+
// Logs combinados
|
|
54
|
+
new winston.transports.File({
|
|
55
|
+
filename: path.join(logsDir, 'combined.log'),
|
|
56
|
+
maxsize: 5242880,
|
|
57
|
+
maxFiles: 5
|
|
58
|
+
}),
|
|
59
|
+
|
|
60
|
+
// Logs de comandos (solo info y superior)
|
|
61
|
+
new winston.transports.File({
|
|
62
|
+
filename: path.join(logsDir, 'commands.log'),
|
|
63
|
+
level: 'info',
|
|
64
|
+
maxsize: 5242880,
|
|
65
|
+
maxFiles: 3
|
|
66
|
+
})
|
|
67
|
+
],
|
|
68
|
+
|
|
69
|
+
// Manejo de excepciones
|
|
70
|
+
exceptionHandlers: [
|
|
71
|
+
new winston.transports.File({
|
|
72
|
+
filename: path.join(logsDir, 'exceptions.log')
|
|
73
|
+
})
|
|
74
|
+
],
|
|
75
|
+
|
|
76
|
+
// Manejo de rechazos de promesas
|
|
77
|
+
rejectionHandlers: [
|
|
78
|
+
new winston.transports.File({
|
|
79
|
+
filename: path.join(logsDir, 'rejections.log')
|
|
80
|
+
})
|
|
81
|
+
]
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// En desarrollo, también loguear a consola
|
|
85
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
86
|
+
logger.add(
|
|
87
|
+
new winston.transports.Console({
|
|
88
|
+
format: consoleFormat,
|
|
89
|
+
level: 'debug'
|
|
90
|
+
})
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Wrapper para comandos CLI
|
|
96
|
+
*/
|
|
97
|
+
class CLILogger {
|
|
98
|
+
/**
|
|
99
|
+
* Log de inicio de comando
|
|
100
|
+
* @param {string} command - Nombre del comando
|
|
101
|
+
* @param {Object} options - Opciones del comando
|
|
102
|
+
*/
|
|
103
|
+
static commandStart(command, options = {}) {
|
|
104
|
+
logger.info('Command started', {
|
|
105
|
+
command,
|
|
106
|
+
options,
|
|
107
|
+
timestamp: new Date().toISOString()
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Log de finalización de comando
|
|
113
|
+
* @param {string} command - Nombre del comando
|
|
114
|
+
* @param {boolean} success - Si fue exitoso
|
|
115
|
+
* @param {number} duration - Duración en ms
|
|
116
|
+
*/
|
|
117
|
+
static commandEnd(command, success = true, duration = 0) {
|
|
118
|
+
const level = success ? 'info' : 'error';
|
|
119
|
+
logger[level]('Command finished', {
|
|
120
|
+
command,
|
|
121
|
+
success,
|
|
122
|
+
duration: `${duration}ms`,
|
|
123
|
+
timestamp: new Date().toISOString()
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Log de error de comando
|
|
129
|
+
* @param {string} command - Nombre del comando
|
|
130
|
+
* @param {Error} error - Error ocurrido
|
|
131
|
+
*/
|
|
132
|
+
static commandError(command, error) {
|
|
133
|
+
logger.error('Command error', {
|
|
134
|
+
command,
|
|
135
|
+
error: error.message,
|
|
136
|
+
stack: error.stack,
|
|
137
|
+
timestamp: new Date().toISOString()
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Log de operación de archivo
|
|
143
|
+
* @param {string} operation - Tipo de operación
|
|
144
|
+
* @param {string} filePath - Path del archivo
|
|
145
|
+
* @param {boolean} success - Si fue exitoso
|
|
146
|
+
*/
|
|
147
|
+
static fileOperation(operation, filePath, success = true) {
|
|
148
|
+
logger.info('File operation', {
|
|
149
|
+
operation,
|
|
150
|
+
filePath,
|
|
151
|
+
success,
|
|
152
|
+
timestamp: new Date().toISOString()
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Log de validación
|
|
158
|
+
* @param {string} type - Tipo de validación
|
|
159
|
+
* @param {boolean} valid - Si es válido
|
|
160
|
+
* @param {Array<string>} errors - Errores encontrados
|
|
161
|
+
*/
|
|
162
|
+
static validation(type, valid, errors = []) {
|
|
163
|
+
const level = valid ? 'info' : 'warn';
|
|
164
|
+
logger[level]('Validation', {
|
|
165
|
+
type,
|
|
166
|
+
valid,
|
|
167
|
+
errors,
|
|
168
|
+
timestamp: new Date().toISOString()
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
module.exports = {
|
|
174
|
+
logger,
|
|
175
|
+
CLILogger
|
|
176
|
+
};
|
|
@@ -50,7 +50,6 @@ async function analyzeProject(projectPath) {
|
|
|
50
50
|
|
|
51
51
|
// Calcular tamaño
|
|
52
52
|
analysis.size = calculateSize(analysis);
|
|
53
|
-
|
|
54
53
|
} catch (error) {
|
|
55
54
|
console.error('Error en análisis:', error.message);
|
|
56
55
|
}
|
|
@@ -69,16 +68,16 @@ async function detectTechnologies(projectPath) {
|
|
|
69
68
|
'yarn.lock': ['Yarn'],
|
|
70
69
|
'pnpm-lock.yaml': ['pnpm'],
|
|
71
70
|
'requirements.txt': ['Python', 'pip'],
|
|
72
|
-
|
|
71
|
+
Pipfile: ['Python', 'Pipenv'],
|
|
73
72
|
'pyproject.toml': ['Python', 'Poetry'],
|
|
74
|
-
|
|
73
|
+
Gemfile: ['Ruby', 'Bundler'],
|
|
75
74
|
'Cargo.toml': ['Rust', 'Cargo'],
|
|
76
75
|
'go.mod': ['Go'],
|
|
77
76
|
'pom.xml': ['Java', 'Maven'],
|
|
78
77
|
'build.gradle': ['Java/Kotlin', 'Gradle'],
|
|
79
78
|
'composer.json': ['PHP', 'Composer'],
|
|
80
79
|
'.csproj': ['C#', '.NET'],
|
|
81
|
-
|
|
80
|
+
Dockerfile: ['Docker'],
|
|
82
81
|
'docker-compose.yml': ['Docker Compose'],
|
|
83
82
|
'.gitignore': ['Git'],
|
|
84
83
|
'tsconfig.json': ['TypeScript'],
|
|
@@ -198,7 +197,7 @@ async function detectBuildTools(projectPath) {
|
|
|
198
197
|
'rollup.config.js': 'Rollup',
|
|
199
198
|
'gulpfile.js': 'Gulp',
|
|
200
199
|
'Gruntfile.js': 'Grunt',
|
|
201
|
-
|
|
200
|
+
Makefile: 'Make',
|
|
202
201
|
'CMakeLists.txt': 'CMake'
|
|
203
202
|
};
|
|
204
203
|
|
|
@@ -220,21 +219,35 @@ function calculateComplexity(analysis) {
|
|
|
220
219
|
let score = 0;
|
|
221
220
|
|
|
222
221
|
// Basado en cantidad de archivos
|
|
223
|
-
if (fileCount > 1000)
|
|
224
|
-
|
|
225
|
-
else if (fileCount >
|
|
222
|
+
if (fileCount > 1000) {
|
|
223
|
+
score += 3;
|
|
224
|
+
} else if (fileCount > 500) {
|
|
225
|
+
score += 2;
|
|
226
|
+
} else if (fileCount > 100) {
|
|
227
|
+
score += 1;
|
|
228
|
+
}
|
|
226
229
|
|
|
227
230
|
// Basado en cantidad de directorios
|
|
228
|
-
if (directoryCount > 100)
|
|
229
|
-
|
|
231
|
+
if (directoryCount > 100) {
|
|
232
|
+
score += 2;
|
|
233
|
+
} else if (directoryCount > 50) {
|
|
234
|
+
score += 1;
|
|
235
|
+
}
|
|
230
236
|
|
|
231
237
|
// Basado en cantidad de tecnologías
|
|
232
|
-
if (technologies.length > 10)
|
|
233
|
-
|
|
238
|
+
if (technologies.length > 10) {
|
|
239
|
+
score += 2;
|
|
240
|
+
} else if (technologies.length > 5) {
|
|
241
|
+
score += 1;
|
|
242
|
+
}
|
|
234
243
|
|
|
235
244
|
// Determinar nivel de complejidad
|
|
236
|
-
if (score >= 5)
|
|
237
|
-
|
|
245
|
+
if (score >= 5) {
|
|
246
|
+
return 'high';
|
|
247
|
+
}
|
|
248
|
+
if (score >= 3) {
|
|
249
|
+
return 'medium';
|
|
250
|
+
}
|
|
238
251
|
return 'low';
|
|
239
252
|
}
|
|
240
253
|
|
|
@@ -244,8 +257,12 @@ function calculateComplexity(analysis) {
|
|
|
244
257
|
function calculateSize(analysis) {
|
|
245
258
|
const { fileCount } = analysis;
|
|
246
259
|
|
|
247
|
-
if (fileCount > 1000)
|
|
248
|
-
|
|
260
|
+
if (fileCount > 1000) {
|
|
261
|
+
return 'large';
|
|
262
|
+
}
|
|
263
|
+
if (fileCount > 100) {
|
|
264
|
+
return 'medium';
|
|
265
|
+
}
|
|
249
266
|
return 'small';
|
|
250
267
|
}
|
|
251
268
|
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilidades de validación
|
|
3
|
+
* Validadores para inputs del usuario y paths del filesystem
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Valida un nombre de proyecto
|
|
10
|
+
* @param {string} name - Nombre del proyecto
|
|
11
|
+
* @returns {boolean} - true si es válido
|
|
12
|
+
*/
|
|
13
|
+
function validateProjectName(name) {
|
|
14
|
+
if (!name || typeof name !== 'string') {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const trimmed = name.trim();
|
|
19
|
+
|
|
20
|
+
// Verificar que no esté vacío
|
|
21
|
+
if (trimmed.length === 0) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Solo letras, números, guiones y guiones bajos
|
|
26
|
+
const validPattern = /^[a-zA-Z0-9-_]+$/;
|
|
27
|
+
if (!validPattern.test(trimmed)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Nombres reservados
|
|
32
|
+
const reserved = [
|
|
33
|
+
'node_modules',
|
|
34
|
+
'package.json',
|
|
35
|
+
'package-lock.json',
|
|
36
|
+
'.git',
|
|
37
|
+
'.awc',
|
|
38
|
+
'test',
|
|
39
|
+
'dist',
|
|
40
|
+
'build'
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
if (reserved.includes(trimmed.toLowerCase())) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// No debe contener path traversal
|
|
48
|
+
if (trimmed.includes('..') || trimmed.includes('/') || trimmed.includes('\\')) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Valida que un path sea seguro
|
|
57
|
+
* @param {string} filePath - Path a validar
|
|
58
|
+
* @returns {boolean} - true si es seguro
|
|
59
|
+
*/
|
|
60
|
+
function validatePath(filePath) {
|
|
61
|
+
if (!filePath || typeof filePath !== 'string') {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Normalizar el path
|
|
66
|
+
const normalized = path.normalize(filePath);
|
|
67
|
+
|
|
68
|
+
// Detectar path traversal
|
|
69
|
+
if (normalized.includes('..')) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Paths del sistema prohibidos (lista básica)
|
|
74
|
+
const forbiddenPaths = [
|
|
75
|
+
'/etc',
|
|
76
|
+
'/sys',
|
|
77
|
+
'/proc',
|
|
78
|
+
'C:\\Windows',
|
|
79
|
+
'C:\\Program Files',
|
|
80
|
+
'/usr/bin',
|
|
81
|
+
'/usr/sbin'
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
for (const forbidden of forbiddenPaths) {
|
|
85
|
+
if (normalized.startsWith(forbidden)) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Sanitiza un path removiendo caracteres peligrosos
|
|
95
|
+
* @param {string} filePath - Path a sanitizar
|
|
96
|
+
* @returns {string} - Path sanitizado
|
|
97
|
+
*/
|
|
98
|
+
function sanitizePath(filePath) {
|
|
99
|
+
if (!filePath) {
|
|
100
|
+
return '';
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let sanitized = filePath;
|
|
104
|
+
|
|
105
|
+
// Normalizar separadores
|
|
106
|
+
sanitized = sanitized.replace(/\\/g, '/');
|
|
107
|
+
|
|
108
|
+
// Eliminar múltiples slashes
|
|
109
|
+
sanitized = sanitized.replace(/\/+/g, '/');
|
|
110
|
+
|
|
111
|
+
// Eliminar path traversal
|
|
112
|
+
sanitized = sanitized.replace(/\.\.\//g, '');
|
|
113
|
+
sanitized = sanitized.replace(/\.\//g, '');
|
|
114
|
+
|
|
115
|
+
return path.normalize(sanitized);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Valida opciones del comando
|
|
120
|
+
* @param {Object} options - Opciones a validar
|
|
121
|
+
* @param {Array<string>} required - Campos requeridos
|
|
122
|
+
* @returns {Object} - { valid: boolean, errors: string[] }
|
|
123
|
+
*/
|
|
124
|
+
function validateCommandOptions(options, required = []) {
|
|
125
|
+
const errors = [];
|
|
126
|
+
|
|
127
|
+
for (const field of required) {
|
|
128
|
+
if (!options[field]) {
|
|
129
|
+
errors.push(`Campo requerido: ${field}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
valid: errors.length === 0,
|
|
135
|
+
errors
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
module.exports = {
|
|
140
|
+
validateProjectName,
|
|
141
|
+
validatePath,
|
|
142
|
+
sanitizePath,
|
|
143
|
+
validateCommandOptions
|
|
144
|
+
};
|
|
@@ -47,8 +47,12 @@ async function checkForUpdates(currentVersion) {
|
|
|
47
47
|
* @returns {number} -1 si v1 < v2, 0 si v1 === v2, 1 si v1 > v2
|
|
48
48
|
*/
|
|
49
49
|
function compareVersions(v1, v2) {
|
|
50
|
-
if (semver.lt(v1, v2))
|
|
51
|
-
|
|
50
|
+
if (semver.lt(v1, v2)) {
|
|
51
|
+
return -1;
|
|
52
|
+
}
|
|
53
|
+
if (semver.gt(v1, v2)) {
|
|
54
|
+
return 1;
|
|
55
|
+
}
|
|
52
56
|
return 0;
|
|
53
57
|
}
|
|
54
58
|
|