@saulwade/swl-ses 2.4.1 → 2.4.2
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/CLAUDE.md +2 -1
- package/README.md +1 -1
- package/habilidades/harness-claude-code/SKILL.md +2 -1
- package/habilidades/tdd-workflow/SKILL.md +3 -1
- package/manifiestos/canonical-hashes.json +328 -0
- package/manifiestos/skills-lock.json +1 -1
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/reglas/arreglar-al-detectar.md +16 -0
- package/reglas/pruebas.md +7 -3
- package/scripts/actualizar.js +253 -254
- package/scripts/doctor.js +25 -17
- package/scripts/lib/detectar-runtime.js +58 -0
- package/scripts/lib/hooks-settings.js +40 -3
- package/scripts/tui/pantallas/inspect.js +175 -173
- package/scripts/tui/pantallas/uninstall-wizard.js +210 -208
- package/scripts/tui/pantallas/update-wizard.js +234 -232
- package/scripts/tui/pantallas/welcome.js +189 -187
package/scripts/actualizar.js
CHANGED
|
@@ -1,254 +1,253 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* swl-ses update
|
|
5
|
-
* Actualiza componentes instalados preservando _userland/.
|
|
6
|
-
* Interactivo: muestra versiones, pide confirmación, muestra progreso.
|
|
7
|
-
* No-interactivo con --force o sin TTY.
|
|
8
|
-
*
|
|
9
|
-
* Modo --check (read-only):
|
|
10
|
-
* Solo muestra el estado actual de versiones (local vs npm vs runtimes instalados).
|
|
11
|
-
* No pide confirmación. No actualiza nada. Util para el autor o usuarios que
|
|
12
|
-
* quieren verificar si están al dia sin esperar al hook automatico.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
const fs = require('fs');
|
|
16
|
-
const os = require('os');
|
|
17
|
-
const path = require('path');
|
|
18
|
-
const { detectarRuntimes } = require('./lib/detectar-runtime');
|
|
19
|
-
const { cargarEstado } = require('./lib/estado');
|
|
20
|
-
const {
|
|
21
|
-
color, icono, encabezado, formatearPaso, formatearAdvertencia,
|
|
22
|
-
spinner, preguntarSiNo, lineaSeparadora, ES_TTY,
|
|
23
|
-
} = require('./lib/ui');
|
|
24
|
-
const { versionRemotaParalela, compararSemver } = require('./lib/npm-version');
|
|
25
|
-
|
|
26
|
-
const VERSION_LOCAL = require('../package.json').version;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Consulta la versión más reciente publicada en npm. Si la consulta falla
|
|
30
|
-
* (sin red, timeout), retorna la versión local como fallback razonable.
|
|
31
|
-
*/
|
|
32
|
-
async function obtenerVersionRemota() {
|
|
33
|
-
const r = await versionRemotaParalela({ timeoutMs: 10000 });
|
|
34
|
-
return r.version || VERSION_LOCAL;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Modo --check: muestra el estado actual sin actualizar. Read-only.
|
|
39
|
-
*
|
|
40
|
-
* Salida:
|
|
41
|
-
* - Version del paquete que esta ejecutando este CLI (VERSION_LOCAL).
|
|
42
|
-
* - Version mas reciente publicada en npm.
|
|
43
|
-
* - Comparacion semver: al dia / adelantado / atras.
|
|
44
|
-
* - Estado del hook check-update.js: ultima verificacion automatica.
|
|
45
|
-
* - Tabla por runtime instalado (si los detecta) con su versionSistema.
|
|
46
|
-
*/
|
|
47
|
-
async function checkStatus() {
|
|
48
|
-
console.log(encabezado('swl-ses update --check', VERSION_LOCAL));
|
|
49
|
-
|
|
50
|
-
// Resolver version remota con timeout corto
|
|
51
|
-
const remota = await obtenerVersionRemota();
|
|
52
|
-
const cmp = compararSemver(VERSION_LOCAL, remota);
|
|
53
|
-
|
|
54
|
-
let estadoVersion;
|
|
55
|
-
if (cmp === null) {
|
|
56
|
-
estadoVersion = color.amarillo('Comparacion no disponible (formato inesperado)');
|
|
57
|
-
} else if (cmp === 0) {
|
|
58
|
-
estadoVersion = color.verde('Al dia');
|
|
59
|
-
} else if (cmp < 0) {
|
|
60
|
-
estadoVersion = color.amarillo(`Nueva version disponible (${remota})`);
|
|
61
|
-
} else {
|
|
62
|
-
estadoVersion = color.dim(`Local mas reciente que npm (${VERSION_LOCAL} > ${remota}, publicacion pendiente)`);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
console.log(` Paquete ejecutado: ${color.negrita('v' + VERSION_LOCAL)}`);
|
|
66
|
-
console.log(` Version en npm: ${color.negrita('v' + remota)}`);
|
|
67
|
-
console.log(` Estado: ${estadoVersion}`);
|
|
68
|
-
console.log('');
|
|
69
|
-
|
|
70
|
-
// Estado del hook automatico (check-update.js).
|
|
71
|
-
// SWL_UPDATE_FLAG_PATH override solo para tests (evita race con flag único).
|
|
72
|
-
const flagPath = process.env.SWL_UPDATE_FLAG_PATH || path.join(os.tmpdir(), 'swl-ses-update-check.json');
|
|
73
|
-
try {
|
|
74
|
-
const flag = JSON.parse(fs.readFileSync(flagPath, 'utf8'));
|
|
75
|
-
const horas = ((Date.now() - flag.timestamp) / 3600000).toFixed(1);
|
|
76
|
-
console.log(` ${color.negrita('Hook automatico (check-update.js):')}`);
|
|
77
|
-
console.log(` Ultima verificacion: hace ${horas}h`);
|
|
78
|
-
console.log(` Local registrada: ${flag.local}`);
|
|
79
|
-
console.log(` Remota registrada: ${flag.remota || color.dim('(no resuelta)')}`);
|
|
80
|
-
console.log(` hayNueva: ${flag.hayNueva ? color.amarillo('si') : color.verde('no')}`);
|
|
81
|
-
console.log(` Flag: ${flagPath}`);
|
|
82
|
-
} catch {
|
|
83
|
-
console.log(` ${color.negrita('Hook automatico (check-update.js):')}`);
|
|
84
|
-
console.log(` ${color.dim('Sin flag de throttle — el hook nunca se ha ejecutado en este equipo,')}`);
|
|
85
|
-
console.log(` ${color.dim('o el archivo temp/swl-ses-update-check.json fue removido.')}`);
|
|
86
|
-
}
|
|
87
|
-
console.log('');
|
|
88
|
-
|
|
89
|
-
// Estado por runtime instalado
|
|
90
|
-
const runtimes = detectarRuntimes();
|
|
91
|
-
if (runtimes.length === 0) {
|
|
92
|
-
console.log(formatearAdvertencia('Runtimes', 'No se detectaron runtimes con instalacion SWL'));
|
|
93
|
-
console.log('');
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
console.log(` ${color.negrita('Runtimes detectados:')}`);
|
|
98
|
-
let totalRuntimes = 0;
|
|
99
|
-
for (const runtime of runtimes) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const ubicacion = esGlobal ? 'global' : 'proyecto';
|
|
108
|
-
const v = estado.versionSistema || 'desconocida';
|
|
109
|
-
|
|
110
|
-
const cmpRuntime = compararSemver(v, remota);
|
|
111
|
-
let etiqueta;
|
|
112
|
-
if (cmpRuntime === null) {
|
|
113
|
-
etiqueta = color.dim('(version no reconocida)');
|
|
114
|
-
} else if (cmpRuntime === 0) {
|
|
115
|
-
etiqueta = color.verde('al dia');
|
|
116
|
-
} else if (cmpRuntime < 0) {
|
|
117
|
-
etiqueta = color.amarillo(`atrasada (npm: ${remota})`);
|
|
118
|
-
} else {
|
|
119
|
-
etiqueta = color.dim('adelantada');
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
console.log(` ${runtime.nombre} (${ubicacion}): v${v} ${etiqueta}`);
|
|
123
|
-
totalRuntimes++;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
if (totalRuntimes === 0) {
|
|
127
|
-
console.log(` ${color.dim('(ninguno con .swl-install-state.json valido)')}`);
|
|
128
|
-
}
|
|
129
|
-
console.log('');
|
|
130
|
-
console.log(` ${color.dim('Modo --check es read-only. Para actualizar:')} npx -y @saulwade/swl-ses@latest update`);
|
|
131
|
-
console.log('');
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
async function update(opciones) {
|
|
135
|
-
// Modo read-only: solo reportar estado, no tocar nada
|
|
136
|
-
if (opciones.check) {
|
|
137
|
-
await checkStatus();
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const force = opciones.force || !ES_TTY;
|
|
142
|
-
|
|
143
|
-
// Obtener la versión más reciente del registro npm
|
|
144
|
-
const VERSION = await obtenerVersionRemota();
|
|
145
|
-
|
|
146
|
-
console.log(encabezado('swl-ses update', VERSION_LOCAL));
|
|
147
|
-
|
|
148
|
-
// 1. Detectar runtimes
|
|
149
|
-
const runtimes = detectarRuntimes();
|
|
150
|
-
|
|
151
|
-
if (runtimes.length === 0) {
|
|
152
|
-
console.log(formatearAdvertencia('Runtimes', 'No se detectaron runtimes con instalación SWL'));
|
|
153
|
-
console.log(`\n Ejecuta primero: npx @saulwade/swl-ses@latest install --target claude`);
|
|
154
|
-
process.exit(1);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
let actualizados = 0;
|
|
158
|
-
let omitidos = 0;
|
|
159
|
-
|
|
160
|
-
// 2. Iterar runtimes
|
|
161
|
-
for (const runtime of runtimes) {
|
|
162
|
-
|
|
163
|
-
for (const dir of
|
|
164
|
-
if (!fs.existsSync(dir)) continue;
|
|
165
|
-
|
|
166
|
-
const estado = cargarEstado(dir);
|
|
167
|
-
if (!estado) continue;
|
|
168
|
-
|
|
169
|
-
const versionInstalada = estado.versionSistema || 'desconocida';
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
console.log(` ${color.
|
|
177
|
-
console.log(` Versión
|
|
178
|
-
console.log(`
|
|
179
|
-
console.log(`
|
|
180
|
-
console.log(`
|
|
181
|
-
console.log(
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
`
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
console.log(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
);
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
console.log(
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
module.exports = update;
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* swl-ses update
|
|
5
|
+
* Actualiza componentes instalados preservando _userland/.
|
|
6
|
+
* Interactivo: muestra versiones, pide confirmación, muestra progreso.
|
|
7
|
+
* No-interactivo con --force o sin TTY.
|
|
8
|
+
*
|
|
9
|
+
* Modo --check (read-only):
|
|
10
|
+
* Solo muestra el estado actual de versiones (local vs npm vs runtimes instalados).
|
|
11
|
+
* No pide confirmación. No actualiza nada. Util para el autor o usuarios que
|
|
12
|
+
* quieren verificar si están al dia sin esperar al hook automatico.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const os = require('os');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const { detectarRuntimes, scopesReales } = require('./lib/detectar-runtime');
|
|
19
|
+
const { cargarEstado } = require('./lib/estado');
|
|
20
|
+
const {
|
|
21
|
+
color, icono, encabezado, formatearPaso, formatearAdvertencia,
|
|
22
|
+
spinner, preguntarSiNo, lineaSeparadora, ES_TTY,
|
|
23
|
+
} = require('./lib/ui');
|
|
24
|
+
const { versionRemotaParalela, compararSemver } = require('./lib/npm-version');
|
|
25
|
+
|
|
26
|
+
const VERSION_LOCAL = require('../package.json').version;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Consulta la versión más reciente publicada en npm. Si la consulta falla
|
|
30
|
+
* (sin red, timeout), retorna la versión local como fallback razonable.
|
|
31
|
+
*/
|
|
32
|
+
async function obtenerVersionRemota() {
|
|
33
|
+
const r = await versionRemotaParalela({ timeoutMs: 10000 });
|
|
34
|
+
return r.version || VERSION_LOCAL;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Modo --check: muestra el estado actual sin actualizar. Read-only.
|
|
39
|
+
*
|
|
40
|
+
* Salida:
|
|
41
|
+
* - Version del paquete que esta ejecutando este CLI (VERSION_LOCAL).
|
|
42
|
+
* - Version mas reciente publicada en npm.
|
|
43
|
+
* - Comparacion semver: al dia / adelantado / atras.
|
|
44
|
+
* - Estado del hook check-update.js: ultima verificacion automatica.
|
|
45
|
+
* - Tabla por runtime instalado (si los detecta) con su versionSistema.
|
|
46
|
+
*/
|
|
47
|
+
async function checkStatus() {
|
|
48
|
+
console.log(encabezado('swl-ses update --check', VERSION_LOCAL));
|
|
49
|
+
|
|
50
|
+
// Resolver version remota con timeout corto
|
|
51
|
+
const remota = await obtenerVersionRemota();
|
|
52
|
+
const cmp = compararSemver(VERSION_LOCAL, remota);
|
|
53
|
+
|
|
54
|
+
let estadoVersion;
|
|
55
|
+
if (cmp === null) {
|
|
56
|
+
estadoVersion = color.amarillo('Comparacion no disponible (formato inesperado)');
|
|
57
|
+
} else if (cmp === 0) {
|
|
58
|
+
estadoVersion = color.verde('Al dia');
|
|
59
|
+
} else if (cmp < 0) {
|
|
60
|
+
estadoVersion = color.amarillo(`Nueva version disponible (${remota})`);
|
|
61
|
+
} else {
|
|
62
|
+
estadoVersion = color.dim(`Local mas reciente que npm (${VERSION_LOCAL} > ${remota}, publicacion pendiente)`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log(` Paquete ejecutado: ${color.negrita('v' + VERSION_LOCAL)}`);
|
|
66
|
+
console.log(` Version en npm: ${color.negrita('v' + remota)}`);
|
|
67
|
+
console.log(` Estado: ${estadoVersion}`);
|
|
68
|
+
console.log('');
|
|
69
|
+
|
|
70
|
+
// Estado del hook automatico (check-update.js).
|
|
71
|
+
// SWL_UPDATE_FLAG_PATH override solo para tests (evita race con flag único).
|
|
72
|
+
const flagPath = process.env.SWL_UPDATE_FLAG_PATH || path.join(os.tmpdir(), 'swl-ses-update-check.json');
|
|
73
|
+
try {
|
|
74
|
+
const flag = JSON.parse(fs.readFileSync(flagPath, 'utf8'));
|
|
75
|
+
const horas = ((Date.now() - flag.timestamp) / 3600000).toFixed(1);
|
|
76
|
+
console.log(` ${color.negrita('Hook automatico (check-update.js):')}`);
|
|
77
|
+
console.log(` Ultima verificacion: hace ${horas}h`);
|
|
78
|
+
console.log(` Local registrada: ${flag.local}`);
|
|
79
|
+
console.log(` Remota registrada: ${flag.remota || color.dim('(no resuelta)')}`);
|
|
80
|
+
console.log(` hayNueva: ${flag.hayNueva ? color.amarillo('si') : color.verde('no')}`);
|
|
81
|
+
console.log(` Flag: ${flagPath}`);
|
|
82
|
+
} catch {
|
|
83
|
+
console.log(` ${color.negrita('Hook automatico (check-update.js):')}`);
|
|
84
|
+
console.log(` ${color.dim('Sin flag de throttle — el hook nunca se ha ejecutado en este equipo,')}`);
|
|
85
|
+
console.log(` ${color.dim('o el archivo temp/swl-ses-update-check.json fue removido.')}`);
|
|
86
|
+
}
|
|
87
|
+
console.log('');
|
|
88
|
+
|
|
89
|
+
// Estado por runtime instalado
|
|
90
|
+
const runtimes = detectarRuntimes();
|
|
91
|
+
if (runtimes.length === 0) {
|
|
92
|
+
console.log(formatearAdvertencia('Runtimes', 'No se detectaron runtimes con instalacion SWL'));
|
|
93
|
+
console.log('');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
console.log(` ${color.negrita('Runtimes detectados:')}`);
|
|
98
|
+
let totalRuntimes = 0;
|
|
99
|
+
for (const runtime of runtimes) {
|
|
100
|
+
// Dedup + guard anti-fantasma (ver scopesReales): desde HOME el local
|
|
101
|
+
// colapsa con el global propio o con el de otro runtime.
|
|
102
|
+
for (const { dir, esGlobal } of scopesReales(runtime)) {
|
|
103
|
+
if (!fs.existsSync(dir)) continue;
|
|
104
|
+
const estado = cargarEstado(dir);
|
|
105
|
+
if (!estado) continue;
|
|
106
|
+
|
|
107
|
+
const ubicacion = esGlobal ? 'global' : 'proyecto';
|
|
108
|
+
const v = estado.versionSistema || 'desconocida';
|
|
109
|
+
|
|
110
|
+
const cmpRuntime = compararSemver(v, remota);
|
|
111
|
+
let etiqueta;
|
|
112
|
+
if (cmpRuntime === null) {
|
|
113
|
+
etiqueta = color.dim('(version no reconocida)');
|
|
114
|
+
} else if (cmpRuntime === 0) {
|
|
115
|
+
etiqueta = color.verde('al dia');
|
|
116
|
+
} else if (cmpRuntime < 0) {
|
|
117
|
+
etiqueta = color.amarillo(`atrasada (npm: ${remota})`);
|
|
118
|
+
} else {
|
|
119
|
+
etiqueta = color.dim('adelantada');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
console.log(` ${runtime.nombre} (${ubicacion}): v${v} ${etiqueta}`);
|
|
123
|
+
totalRuntimes++;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (totalRuntimes === 0) {
|
|
127
|
+
console.log(` ${color.dim('(ninguno con .swl-install-state.json valido)')}`);
|
|
128
|
+
}
|
|
129
|
+
console.log('');
|
|
130
|
+
console.log(` ${color.dim('Modo --check es read-only. Para actualizar:')} npx -y @saulwade/swl-ses@latest update`);
|
|
131
|
+
console.log('');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function update(opciones) {
|
|
135
|
+
// Modo read-only: solo reportar estado, no tocar nada
|
|
136
|
+
if (opciones.check) {
|
|
137
|
+
await checkStatus();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const force = opciones.force || !ES_TTY;
|
|
142
|
+
|
|
143
|
+
// Obtener la versión más reciente del registro npm
|
|
144
|
+
const VERSION = await obtenerVersionRemota();
|
|
145
|
+
|
|
146
|
+
console.log(encabezado('swl-ses update', VERSION_LOCAL));
|
|
147
|
+
|
|
148
|
+
// 1. Detectar runtimes
|
|
149
|
+
const runtimes = detectarRuntimes();
|
|
150
|
+
|
|
151
|
+
if (runtimes.length === 0) {
|
|
152
|
+
console.log(formatearAdvertencia('Runtimes', 'No se detectaron runtimes con instalación SWL'));
|
|
153
|
+
console.log(`\n Ejecuta primero: npx @saulwade/swl-ses@latest install --target claude`);
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let actualizados = 0;
|
|
158
|
+
let omitidos = 0;
|
|
159
|
+
|
|
160
|
+
// 2. Iterar runtimes
|
|
161
|
+
for (const runtime of runtimes) {
|
|
162
|
+
// Dedup + guard anti-fantasma (ver scopesReales).
|
|
163
|
+
for (const { dir, esGlobal } of scopesReales(runtime)) {
|
|
164
|
+
if (!fs.existsSync(dir)) continue;
|
|
165
|
+
|
|
166
|
+
const estado = cargarEstado(dir);
|
|
167
|
+
if (!estado) continue;
|
|
168
|
+
|
|
169
|
+
const versionInstalada = estado.versionSistema || 'desconocida';
|
|
170
|
+
const ubicacion = esGlobal ? 'global' : 'proyecto';
|
|
171
|
+
|
|
172
|
+
const necesitaActualizar = versionInstalada !== VERSION;
|
|
173
|
+
const paqueteDesactualizado = VERSION_LOCAL !== VERSION;
|
|
174
|
+
|
|
175
|
+
console.log(` ${color.negrita(`Runtime: ${runtime.nombre}`)} ${color.dim(`(${ubicacion})`)}`);
|
|
176
|
+
console.log(` Versión instalada: ${necesitaActualizar ? color.amarillo(versionInstalada) : color.verde(versionInstalada)}`);
|
|
177
|
+
console.log(` Versión disponible: ${color.verde(VERSION)}${paqueteDesactualizado ? color.dim(` (ejecutando con v${VERSION_LOCAL})`) : ''}`);
|
|
178
|
+
console.log(` Perfil: ${estado.perfil}`);
|
|
179
|
+
console.log(` Componentes: ${estado.componentesInstalados.length}`);
|
|
180
|
+
console.log(` Archivos: ${estado.archivosInstalados.length}`);
|
|
181
|
+
console.log('');
|
|
182
|
+
|
|
183
|
+
// Si la versión disponible es más reciente pero el paquete ejecutándose es viejo,
|
|
184
|
+
// indicar al usuario que use @latest
|
|
185
|
+
if (paqueteDesactualizado && necesitaActualizar) {
|
|
186
|
+
console.log(formatearAdvertencia(runtime.nombre,
|
|
187
|
+
`Este paquete es v${VERSION_LOCAL} pero la v${VERSION} está disponible.\n` +
|
|
188
|
+
` Para actualizar, ejecuta: npx @saulwade/swl-ses@latest update --force`));
|
|
189
|
+
console.log('');
|
|
190
|
+
omitidos++;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Verificar si ya está actualizado
|
|
195
|
+
if (!necesitaActualizar && !force) {
|
|
196
|
+
console.log(formatearPaso(runtime.nombre, 'Ya está en la versión más reciente'));
|
|
197
|
+
console.log('');
|
|
198
|
+
omitidos++;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Confirmar
|
|
203
|
+
if (!force) {
|
|
204
|
+
const confirmar = await preguntarSiNo(
|
|
205
|
+
!necesitaActualizar
|
|
206
|
+
? `¿Re-instalar ${runtime.nombre} v${VERSION}?`
|
|
207
|
+
: `¿Actualizar ${runtime.nombre} de ${versionInstalada} a ${VERSION}?`
|
|
208
|
+
);
|
|
209
|
+
console.log('');
|
|
210
|
+
|
|
211
|
+
if (!confirmar) {
|
|
212
|
+
console.log(formatearAdvertencia(runtime.nombre, 'Actualización cancelada por el usuario'));
|
|
213
|
+
console.log('');
|
|
214
|
+
omitidos++;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Ejecutar actualización con spinner
|
|
220
|
+
const sp = spinner(`Actualizando ${runtime.nombre}...`);
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
const instalador = require('./instalador');
|
|
224
|
+
await instalador({
|
|
225
|
+
target: runtime.id,
|
|
226
|
+
profile: estado.perfil,
|
|
227
|
+
global: estado.global,
|
|
228
|
+
force: true,
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
sp.exito(`${runtime.nombre} actualizado a v${VERSION} (${estado.archivosInstalados.length} archivos)`);
|
|
232
|
+
actualizados++;
|
|
233
|
+
} catch (err) {
|
|
234
|
+
sp.fallo(`Error actualizando ${runtime.nombre}: ${err.message}`);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
console.log('');
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// 3. Reporte final
|
|
242
|
+
console.log(` ${lineaSeparadora()}`);
|
|
243
|
+
if (actualizados > 0) {
|
|
244
|
+
console.log(formatearPaso('Resultado', `${actualizados} runtime(s) actualizado(s)${omitidos > 0 ? `, ${omitidos} omitido(s)` : ''}`));
|
|
245
|
+
} else if (omitidos > 0) {
|
|
246
|
+
console.log(formatearPaso('Resultado', 'Todo está actualizado'));
|
|
247
|
+
} else {
|
|
248
|
+
console.log(formatearAdvertencia('Resultado', 'Ningún runtime fue actualizado'));
|
|
249
|
+
}
|
|
250
|
+
console.log('');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
module.exports = update;
|
package/scripts/doctor.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
const fs = require('fs');
|
|
11
11
|
const path = require('path');
|
|
12
12
|
|
|
13
|
-
const { detectarRuntimes, detectarInstalacionesDuales } = require('./lib/detectar-runtime');
|
|
13
|
+
const { detectarRuntimes, detectarInstalacionesDuales, esDirGlobalDeAlgunRuntime, scopesReales } = require('./lib/detectar-runtime');
|
|
14
14
|
const { cargarEstado, verificarIntegridad } = require('./lib/estado');
|
|
15
15
|
const { verificarHooks, repararHooks, rutaSettings, cargarHooksConfig, desregistrarHooks, esHookSWL, verificarOptimizaciones, registrarOptimizaciones, validarEstructuraSettings } = require('./lib/hooks-settings');
|
|
16
16
|
const {
|
|
@@ -106,7 +106,10 @@ async function doctor(opciones = {}) {
|
|
|
106
106
|
try {
|
|
107
107
|
const runtimesParaDrift = detectarRuntimes();
|
|
108
108
|
for (const runtime of runtimesParaDrift) {
|
|
109
|
-
|
|
109
|
+
// Dedup + guard anti-fantasma: desde HOME, global y local colapsan al
|
|
110
|
+
// mismo dir (reporte doble) o el local resuelve al GLOBAL de otro
|
|
111
|
+
// runtime (drift "proyecto" espurio). Caso real doctor 2026-07-03.
|
|
112
|
+
for (const { dir, esGlobal } of scopesReales(runtime)) {
|
|
110
113
|
if (!fs.existsSync(dir)) continue;
|
|
111
114
|
const estado = cargarEstado(dir);
|
|
112
115
|
if (!estado || !estado.versionSistema) continue;
|
|
@@ -115,7 +118,7 @@ async function doctor(opciones = {}) {
|
|
|
115
118
|
driftsDetectados.push({
|
|
116
119
|
runtime: runtime.nombre,
|
|
117
120
|
runtimeId: runtime.id,
|
|
118
|
-
scope:
|
|
121
|
+
scope: esGlobal ? 'global' : 'proyecto',
|
|
119
122
|
dir,
|
|
120
123
|
versionInstalada: estado.versionSistema,
|
|
121
124
|
perfil: estado.perfil || 'desconocido',
|
|
@@ -259,13 +262,10 @@ async function doctor(opciones = {}) {
|
|
|
259
262
|
|
|
260
263
|
// 5. Verificar estado de instalación por runtime
|
|
261
264
|
for (const runtime of runtimes) {
|
|
262
|
-
// Deduplicar
|
|
263
|
-
|
|
264
|
-
const
|
|
265
|
-
for (const dir of dirs) {
|
|
265
|
+
// Deduplicar + descartar locales que resuelven al global de otro runtime
|
|
266
|
+
// (ej: doctor corrido desde el home dir)
|
|
267
|
+
for (const { dir, esGlobal } of scopesReales(runtime)) {
|
|
266
268
|
if (!fs.existsSync(dir)) continue;
|
|
267
|
-
|
|
268
|
-
const esGlobal = dir === runtime.global;
|
|
269
269
|
const estado = cargarEstado(dir);
|
|
270
270
|
if (estado) {
|
|
271
271
|
const { integro, problemas } = verificarIntegridad(estado);
|
|
@@ -379,11 +379,7 @@ async function doctor(opciones = {}) {
|
|
|
379
379
|
// Sub-fase 12 v1.5.0: deduplicar paths cuando cwd === homedir (runtime.local
|
|
380
380
|
// y runtime.global colapsan al mismo directorio) — evita reporte doble.
|
|
381
381
|
for (const runtime of runtimes) {
|
|
382
|
-
const
|
|
383
|
-
path.resolve(runtime.global),
|
|
384
|
-
path.resolve(runtime.local),
|
|
385
|
-
]));
|
|
386
|
-
for (const dir of dirsUnicos) {
|
|
382
|
+
for (const { dir } of scopesReales(runtime)) {
|
|
387
383
|
if (!fs.existsSync(dir)) continue;
|
|
388
384
|
const estado = cargarEstado(dir);
|
|
389
385
|
if (!estado || !estado.perfil) continue;
|
|
@@ -559,12 +555,20 @@ async function doctor(opciones = {}) {
|
|
|
559
555
|
for (const runtime of runtimes) {
|
|
560
556
|
const dir = path.resolve(runtime.local);
|
|
561
557
|
if (!fs.existsSync(dir)) continue;
|
|
558
|
+
// Local que resuelve al global de OTRO runtime = scope fantasma (ver scopesReales)
|
|
559
|
+
if (esDirGlobalDeAlgunRuntime(dir) && dir !== path.resolve(runtime.global)) continue;
|
|
562
560
|
const estado = cargarEstado(dir);
|
|
563
561
|
if (!estado) continue;
|
|
564
562
|
|
|
565
563
|
const settingsPath = rutaSettings(runtime, dir);
|
|
566
564
|
if (fs.existsSync(settingsPath)) {
|
|
567
|
-
|
|
565
|
+
// Cursor usa hooks.json con eventos camelCase propios y entradas PLANAS
|
|
566
|
+
// ({command, type}) — el esquema Claude produce falsos errores
|
|
567
|
+
// ("afterFileEdit: evento no reconocido", "[0].hooks debe ser array").
|
|
568
|
+
// Codex también usa hooks.json pero con FORMA Claude ({matcher,
|
|
569
|
+
// hooks:[...]}, eventos PascalCase) — ver transformadores/codex.js.
|
|
570
|
+
const formatoHooks = runtime.id === 'cursor' ? 'plano' : 'claude';
|
|
571
|
+
const structResult = validarEstructuraSettings(settingsPath, { formatoHooks });
|
|
568
572
|
if (structResult.ok) {
|
|
569
573
|
console.log(formatearPaso(`Estructura settings ${runtime.nombre}`, 'Formato válido'));
|
|
570
574
|
ok++;
|
|
@@ -624,8 +628,7 @@ async function doctor(opciones = {}) {
|
|
|
624
628
|
|
|
625
629
|
// 8. Verificar componentes externos
|
|
626
630
|
for (const runtime of runtimes) {
|
|
627
|
-
const
|
|
628
|
-
for (const dir of dirs) {
|
|
631
|
+
for (const { dir } of scopesReales(runtime)) {
|
|
629
632
|
if (!fs.existsSync(dir)) continue;
|
|
630
633
|
const estado = cargarEstado(dir);
|
|
631
634
|
if (estado && estado.componentesExternos && estado.componentesExternos.length > 0) {
|
|
@@ -712,6 +715,11 @@ async function doctor(opciones = {}) {
|
|
|
712
715
|
} else {
|
|
713
716
|
console.log(` ${reparados} reparado(s), ${color.rojo(`${fallos} fallo(s)`)}.`);
|
|
714
717
|
}
|
|
718
|
+
// Las reparaciones no se re-verifican en esta corrida: honestidad sobre
|
|
719
|
+
// el alcance (caso repair-loop 2026-07-03 — un falso positivo del check
|
|
720
|
+
// se "reparaba" y reaparecía indefinidamente).
|
|
721
|
+
console.log(` ${color.dim('Re-ejecuta `doctor` para confirmar. Si el MISMO problema reaparece tras reparar,')}`);
|
|
722
|
+
console.log(` ${color.dim('es un falso positivo del check (repórtalo) — no falta de reparación.')}`);
|
|
715
723
|
} else {
|
|
716
724
|
console.log(` ${color.dim('Reparación cancelada. Usa --force para reparar sin confirmar.')}`);
|
|
717
725
|
}
|