specleap-framework 2.1.9 → 2.1.10
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/CHANGELOG.md +17 -0
- package/README.md +1 -1
- package/bin/specleap +109 -45
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.1.10] - 2026-04-30
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Installer now also asks for the folder name**, on top of the base folder prompt added in v2.1.9. The user can keep `specleap-framework` (the default) or pick a custom name like `mi-app-tienda` straight from the prompt — no more renaming after install with `mv`.
|
|
15
|
+
- **Two-argument CLI form:** `npx specleap-framework@latest <base> [<name>]`. Both positional. If `<name>` is omitted, the prompt asks. If `<base>` is also omitted, both prompts run interactively.
|
|
16
|
+
- **Folder name validation:** allows letters, numbers, `-`, `_` and `.`, must not start with `.`, must be ≤100 chars. Invalid input on the prompt re-asks until valid; invalid input on the CLI argument aborts with a clear bilingual error and exit 1.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- The "ya existe" abort message now reads "elige otra carpeta o nombre" (instead of just "elige otra carpeta base"), matching the new flexibility.
|
|
21
|
+
|
|
22
|
+
### Notes
|
|
23
|
+
|
|
24
|
+
- Backwards compatible with v2.1.9: same env var (`SPECLEAP_INSTALL_PATH`) handed off to `setup.sh`, same final summary block, same fallback behaviour for direct `bash setup.sh` invocations.
|
|
25
|
+
- Decision rationale: keep the prompt-based flow (no native GUI dialog) so the installer keeps working over SSH, in CI/CD pipelines and inside containers — exactly the kind of environments where a Mac-only `osascript` dialog would have broken things.
|
|
26
|
+
|
|
10
27
|
## [2.1.9] - 2026-04-30
|
|
11
28
|
|
|
12
29
|
### Added
|
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
SpecLeap convierte cualquier IDE con asistente de IA en un entorno de desarrollo spec-first. Combina un contrato de proyecto inmutable, agentes conversacionales especializados, 34 Agent Skills profesionales y un pipeline de validación en tres capas para entregar código consistente y probado sin improvisación.
|
|
22
22
|
|
|
23
|
-
**Versión actual:** 2.1.
|
|
23
|
+
**Versión actual:** 2.1.10 · **Licencia:** MIT · **Autor:** Styng Arias ([ConceptualCreative](https://conceptualcreative.com))
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
package/bin/specleap
CHANGED
|
@@ -6,9 +6,19 @@ const fs = require('fs');
|
|
|
6
6
|
const readline = require('readline');
|
|
7
7
|
const os = require('os');
|
|
8
8
|
|
|
9
|
-
// Nombre
|
|
10
|
-
//
|
|
11
|
-
const
|
|
9
|
+
// Nombre por defecto de la carpeta destino. El usuario puede cambiarlo
|
|
10
|
+
// en el prompt; si pulsa Enter, se usa este valor.
|
|
11
|
+
const DEFAULT_FOLDER_NAME = 'specleap-framework';
|
|
12
|
+
|
|
13
|
+
// Validador de nombre de carpeta: caracteres seguros para sistemas de
|
|
14
|
+
// archivos cross-platform (sin `/`, sin nulos, sin barras). Permite letras,
|
|
15
|
+
// números, guiones, guiones bajos y puntos. No puede empezar con punto.
|
|
16
|
+
function isValidFolderName(name) {
|
|
17
|
+
if (!name || name.length === 0) return false;
|
|
18
|
+
if (name.length > 100) return false;
|
|
19
|
+
if (name.startsWith('.')) return false;
|
|
20
|
+
return /^[A-Za-z0-9_][A-Za-z0-9_.\-]*$/.test(name);
|
|
21
|
+
}
|
|
12
22
|
|
|
13
23
|
// Directorio del paquete npm (donde están todos los archivos a copiar)
|
|
14
24
|
const packageDir = path.join(__dirname, '..');
|
|
@@ -26,49 +36,104 @@ function resolveBasePath(input) {
|
|
|
26
36
|
return path.resolve(process.cwd(), trimmed);
|
|
27
37
|
}
|
|
28
38
|
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
// Crea una interfaz readline reutilizable; cerramos al final del flujo.
|
|
40
|
+
function createPrompt() {
|
|
41
|
+
return readline.createInterface({
|
|
42
|
+
input: process.stdin,
|
|
43
|
+
output: process.stdout
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function ask(rl, question) {
|
|
48
|
+
return new Promise((resolve) => rl.question(question, (a) => resolve(a)));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Pregunta la carpeta base donde poner el proyecto. Bilingüe porque el
|
|
52
|
+
// idioma de SpecLeap se elige después dentro de setup.sh.
|
|
53
|
+
async function askBasePath(rl, defaultBase) {
|
|
54
|
+
console.log('');
|
|
55
|
+
console.log('📁 ¿En qué carpeta base instalar SpecLeap?');
|
|
56
|
+
console.log(' Where do you want to install SpecLeap?');
|
|
57
|
+
console.log('');
|
|
58
|
+
console.log(` Default: ${defaultBase}`);
|
|
59
|
+
console.log('');
|
|
60
|
+
console.log(' Ejemplos / Examples:');
|
|
61
|
+
console.log(' ~/Downloads → ~/Downloads/<nombre>');
|
|
62
|
+
console.log(' ~/Desktop/work → ~/Desktop/work/<nombre>');
|
|
63
|
+
console.log(' . → ./<nombre>');
|
|
64
|
+
console.log(' (Enter) → default arriba');
|
|
65
|
+
console.log('');
|
|
66
|
+
|
|
67
|
+
const answer = await ask(rl, 'Carpeta base / Base folder: ');
|
|
68
|
+
return resolveBasePath(answer);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Pregunta el nombre de la carpeta. Default = "specleap-framework".
|
|
72
|
+
// Reintenta si el nombre tiene caracteres inválidos.
|
|
73
|
+
async function askFolderName(rl) {
|
|
74
|
+
console.log('');
|
|
75
|
+
console.log('📝 ¿Qué nombre quieres para la carpeta?');
|
|
76
|
+
console.log(' What name do you want for the folder?');
|
|
77
|
+
console.log('');
|
|
78
|
+
console.log(` Default: ${DEFAULT_FOLDER_NAME}`);
|
|
79
|
+
console.log(' Permitido / Allowed: letras, números, guiones, guiones bajos, puntos');
|
|
80
|
+
console.log(' No permitido / Not allowed: espacios, "/", caracteres especiales');
|
|
81
|
+
console.log('');
|
|
82
|
+
|
|
83
|
+
while (true) {
|
|
84
|
+
const answer = await ask(rl, 'Nombre / Name: ');
|
|
85
|
+
const name = answer.trim();
|
|
86
|
+
|
|
87
|
+
// Enter → default
|
|
88
|
+
if (!name) return DEFAULT_FOLDER_NAME;
|
|
89
|
+
|
|
90
|
+
if (isValidFolderName(name)) return name;
|
|
38
91
|
|
|
39
92
|
console.log('');
|
|
40
|
-
console.log(
|
|
41
|
-
console.log('
|
|
42
|
-
console.log('');
|
|
43
|
-
console.log(` Se creará / Will create: <base>/${FOLDER_NAME}`);
|
|
44
|
-
console.log(` Default: ${defaultBase}`);
|
|
45
|
-
console.log('');
|
|
46
|
-
console.log(' Ejemplos / Examples:');
|
|
47
|
-
console.log(' ~/Downloads → ~/Downloads/specleap-framework');
|
|
48
|
-
console.log(' ~/Desktop/work → ~/Desktop/work/specleap-framework');
|
|
49
|
-
console.log(' . → ./specleap-framework');
|
|
50
|
-
console.log(' (Enter) → default arriba');
|
|
93
|
+
console.log(`❌ Nombre inválido / Invalid name: "${name}"`);
|
|
94
|
+
console.log(' Usa solo letras, números, "-", "_" y "." (sin empezar con ".")');
|
|
95
|
+
console.log(' Use only letters, numbers, "-", "_" and "." (cannot start with ".")');
|
|
51
96
|
console.log('');
|
|
52
|
-
|
|
53
|
-
rl.question('Carpeta base / Base folder: ', (answer) => {
|
|
54
|
-
rl.close();
|
|
55
|
-
resolve(resolveBasePath(answer));
|
|
56
|
-
});
|
|
57
|
-
});
|
|
97
|
+
}
|
|
58
98
|
}
|
|
59
99
|
|
|
60
100
|
async function main() {
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
101
|
+
// Argumentos CLI opcionales:
|
|
102
|
+
// npx specleap-framework@latest <carpeta-base> [<nombre>]
|
|
103
|
+
// p.ej. npx specleap-framework@latest ~/Downloads
|
|
104
|
+
// npx specleap-framework@latest ~/Downloads mi-app
|
|
105
|
+
const args = process.argv.slice(2).filter((a) => !a.startsWith('-'));
|
|
106
|
+
const argBase = args[0];
|
|
107
|
+
const argName = args[1];
|
|
108
|
+
|
|
109
|
+
// Validar el nombre del argumento CLI antes de seguir
|
|
110
|
+
if (argName && !isValidFolderName(argName)) {
|
|
111
|
+
console.error(`\n❌ Nombre inválido / Invalid name: "${argName}"`);
|
|
112
|
+
console.error(' Usa solo letras, números, "-", "_" y "." (sin empezar con ".")');
|
|
113
|
+
console.error(' Use only letters, numbers, "-", "_" and "." (cannot start with ".")\n');
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Si hay argumentos CLI, no abrimos prompt; si faltan, preguntamos
|
|
118
|
+
let basePath, folderName;
|
|
119
|
+
const rl = (!argBase || !argName) ? createPrompt() : null;
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
if (argBase) {
|
|
123
|
+
basePath = resolveBasePath(argBase);
|
|
124
|
+
console.log(`\n📁 Carpeta base: ${basePath}`);
|
|
125
|
+
} else {
|
|
126
|
+
basePath = await askBasePath(rl, process.cwd());
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (argName) {
|
|
130
|
+
folderName = argName;
|
|
131
|
+
console.log(`📝 Nombre de carpeta: ${folderName}`);
|
|
132
|
+
} else {
|
|
133
|
+
folderName = await askFolderName(rl);
|
|
134
|
+
}
|
|
135
|
+
} finally {
|
|
136
|
+
if (rl) rl.close();
|
|
72
137
|
}
|
|
73
138
|
|
|
74
139
|
// Verificar que la carpeta base existe (o se puede crear)
|
|
@@ -83,21 +148,20 @@ async function main() {
|
|
|
83
148
|
}
|
|
84
149
|
}
|
|
85
150
|
|
|
86
|
-
// Verificar que sea un directorio (no un archivo con ese nombre)
|
|
87
151
|
const baseStat = fs.statSync(basePath);
|
|
88
152
|
if (!baseStat.isDirectory()) {
|
|
89
153
|
console.error(`\n❌ La ruta no es una carpeta / Path is not a directory: ${basePath}\n`);
|
|
90
154
|
process.exit(1);
|
|
91
155
|
}
|
|
92
156
|
|
|
93
|
-
//
|
|
94
|
-
const targetDir = path.join(basePath,
|
|
157
|
+
// Destino final: <basePath>/<folderName>
|
|
158
|
+
const targetDir = path.join(basePath, folderName);
|
|
95
159
|
|
|
96
160
|
// No sobrescribir si ya existe
|
|
97
161
|
if (fs.existsSync(targetDir)) {
|
|
98
162
|
console.error(`\n❌ Ya existe / Already exists: ${targetDir}`);
|
|
99
|
-
console.error(' Elige otra carpeta
|
|
100
|
-
console.error(' Choose another
|
|
163
|
+
console.error(' Elige otra carpeta o nombre, o elimina la existente.');
|
|
164
|
+
console.error(' Choose another folder or name, or delete the existing one.\n');
|
|
101
165
|
process.exit(1);
|
|
102
166
|
}
|
|
103
167
|
|
package/package.json
CHANGED