@saulwade/swl-ses 2.5.1 → 2.5.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/CLAUDE.md +4 -2
- package/README.md +1 -1
- package/bin/swl-ses.js +1 -0
- package/habilidades/harness-claude-code/SKILL.md +4 -2
- package/habilidades/release-semver/SKILL.md +4 -2
- package/llms.txt +1 -1
- package/manifiestos/canonical-hashes.json +660 -0
- package/manifiestos/skills-lock.json +8 -8
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/scripts/evidencia-valor.js +14 -6
- package/scripts/field-report.js +17 -1
- package/scripts/instalador.js +21 -0
- package/scripts/instalar-git-hook.js +8 -1
- package/scripts/lib/activar-hooks-proyecto.js +104 -0
- package/scripts/lib/evidencia-valor.js +42 -3
- package/scripts/tui/index.js +10 -1
- package/scripts/tui/pantallas/inspect.js +1 -1
- package/scripts/tui/pantallas/install-wizard.js +21 -8
- package/scripts/tui/pantallas/uninstall-wizard.js +1 -1
- package/scripts/tui/pantallas/update-wizard.js +1 -1
- package/scripts/tui/pantallas/welcome.js +1 -2
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"lockfileVersion": 1,
|
|
3
|
-
"generatedAt": "2026-07-
|
|
3
|
+
"generatedAt": "2026-07-09T21:51:05.079Z",
|
|
4
4
|
"skillsCount": 181,
|
|
5
|
-
"lockHash": "sha256:
|
|
5
|
+
"lockHash": "sha256:016f7b1362443918147698bca6428463dcc70770d3acb61b84bb65267b195cf4",
|
|
6
6
|
"skills": [
|
|
7
7
|
{
|
|
8
8
|
"nombre": "accesibilidad-a11y",
|
|
@@ -602,9 +602,9 @@
|
|
|
602
602
|
{
|
|
603
603
|
"nombre": "harness-claude-code",
|
|
604
604
|
"path": "habilidades/harness-claude-code/SKILL.md",
|
|
605
|
-
"hash": "sha256:
|
|
606
|
-
"bytes":
|
|
607
|
-
"version": "\"1.0.
|
|
605
|
+
"hash": "sha256:d4c7d4283a35fb5d1dcfcf18d2c0f45dca012bbc518fa2f5df8c3a88517069b0",
|
|
606
|
+
"bytes": 18370,
|
|
607
|
+
"version": "\"1.0.5\""
|
|
608
608
|
},
|
|
609
609
|
{
|
|
610
610
|
"nombre": "iam-secretos",
|
|
@@ -1043,9 +1043,9 @@
|
|
|
1043
1043
|
{
|
|
1044
1044
|
"nombre": "release-semver",
|
|
1045
1045
|
"path": "habilidades/release-semver/SKILL.md",
|
|
1046
|
-
"hash": "sha256:
|
|
1047
|
-
"bytes":
|
|
1048
|
-
"version": "\"1.0.
|
|
1046
|
+
"hash": "sha256:c77cc73cc1f6ef635f9f79d9d008c5ee05f5de557b5f94ffa85b67575b2e9a33",
|
|
1047
|
+
"bytes": 18514,
|
|
1048
|
+
"version": "\"1.0.4\""
|
|
1049
1049
|
},
|
|
1050
1050
|
{
|
|
1051
1051
|
"nombre": "rust-experto",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saulwade/swl-ses",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.3",
|
|
4
4
|
"description": "Sistema de ingenieria de software auto-evolutivo multi-runtime polyglot con 61 agentes, 181 habilidades, 47 comandos, 77 reglas y 49 hooks. Soporta 11 lenguajes y 7 runtimes: Claude Code, OpenClaude, OpenCode, Gemini CLI, Cursor, Codex CLI (soporte completo); GitHub Copilot (soporte parcial). 100% en espanol (Mexico). Multi-target install (--target CSV / --all-runtimes), autoconfig MCP en Cursor/Codex con --with-mcp, agentes Codex en TOML, hooks Cursor (17 eventos) y Codex (6 eventos). Gateway bidireccional con relay Telegram y auditoria profunda Nemesis con loop evaluator-optimizer opt-in (ADR-0021) y 8 tools ejecutables. v1.8.0 unifica los directorios runtime de .planning/ al ingles (evolution/, auto-evolution/, user-profile/, archive/), manteniendo fases/ en espanol, con guard validar-planning-paths y allowlist canonica (ADR-0031). Hereda de v1.7.4: skill calidad-anti-patrones-universales + scripts/lib/pr-analyzer.js + 3 sub-secciones en estilo-sin-ai-isms.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"swl-ses": "bin/swl-ses.js",
|
package/plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "swl-ses",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.3",
|
|
4
4
|
"description": "Sistema de ingenieria de software auto-evolutivo multi-runtime polyglot. 61 agentes, 181 habilidades, 47 comandos, 77 reglas y 49 hooks. 76 librerias. 11 lenguajes. Soporta Claude Code, Copilot, OpenCode, Codex y Gemini CLI. Loop evaluator-optimizer en /swl:nemesis (ADR-0021). v1.8.0 unifica los directorios runtime de .planning/ al ingles (evolution/, auto-evolution/, user-profile/, archive/), manteniendo fases/ en espanol, con guard validar-planning-paths y allowlist canonica (ADR-0031). Hereda de v1.7.4: skill calidad-anti-patrones-universales + scripts/lib/pr-analyzer.js + 3 sub-secciones en estilo-sin-ai-isms.",
|
|
5
5
|
"author": "Saul Wade Leon",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,11 +18,14 @@
|
|
|
18
18
|
|
|
19
19
|
const { panelEvidencia } = require('./lib/evidencia-valor.js');
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Contrato del CLI (bin/swl-ses.js): exportar UNA función(opciones) sin
|
|
23
|
+
* efectos al require. Violarlo = "modulo is not a function" tras imprimir
|
|
24
|
+
* el panel (caso real: SIGM, 2026-07-09).
|
|
25
|
+
*/
|
|
26
|
+
function valor(opciones = {}) {
|
|
27
|
+
const json = !!opciones.json;
|
|
28
|
+
const ventanaDias = parseInt(opciones.dias, 10) || 30;
|
|
26
29
|
|
|
27
30
|
const p = panelEvidencia(process.cwd(), { ventanaDias });
|
|
28
31
|
|
|
@@ -90,4 +93,9 @@ function main() {
|
|
|
90
93
|
console.log('Compartir agregados con el autor (opt-in): swl-ses field-report — ver docs/evidencia-valor.md');
|
|
91
94
|
}
|
|
92
95
|
|
|
93
|
-
|
|
96
|
+
module.exports = valor;
|
|
97
|
+
|
|
98
|
+
if (require.main === module) {
|
|
99
|
+
const { parsearOpciones } = require('./lib/parsear-opciones.js');
|
|
100
|
+
valor(parsearOpciones(process.argv.slice(2)));
|
|
101
|
+
}
|
package/scripts/field-report.js
CHANGED
|
@@ -196,4 +196,20 @@ function generarReporte() {
|
|
|
196
196
|
console.log(`\n> Generado por \`node scripts/field-report.js\``);
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
|
|
199
|
+
function fieldReport(opciones = {}) {
|
|
200
|
+
DAYS = parseInt(opciones.days || opciones.dias, 10) || 0;
|
|
201
|
+
PROJECT = opciones.project || null;
|
|
202
|
+
OUTPUT = opciones.output || 'md';
|
|
203
|
+
generarReporte();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
module.exports = fieldReport;
|
|
207
|
+
|
|
208
|
+
if (require.main === module) {
|
|
209
|
+
const args = process.argv.slice(2).reduce((acc, arg) => {
|
|
210
|
+
const [k, v] = arg.replace(/^--/, '').split('=');
|
|
211
|
+
acc[k] = v || true;
|
|
212
|
+
return acc;
|
|
213
|
+
}, {});
|
|
214
|
+
fieldReport(args);
|
|
215
|
+
}
|
package/scripts/instalador.js
CHANGED
|
@@ -84,6 +84,27 @@ function nombreLegibleEvolucion(rutaAbs) {
|
|
|
84
84
|
* El comportamiento sin TUI no cambia.
|
|
85
85
|
*/
|
|
86
86
|
async function install(opciones) {
|
|
87
|
+
// Camino "solo hooks" (DT-HOOKS-SOLO-PROYECTO): registra en el settings.json
|
|
88
|
+
// del PROYECTO los hooks del scope GLOBAL ya instalado, sin copiar ningún
|
|
89
|
+
// componente — evita duplicar agentes/comandos en la UI de Claude Code
|
|
90
|
+
// (que lee ambos scopes) solo para encender la telemetría.
|
|
91
|
+
if (opciones['solo-hooks'] || opciones.solo_hooks) {
|
|
92
|
+
const { activarHooksProyecto } = require('./lib/activar-hooks-proyecto');
|
|
93
|
+
const r = activarHooksProyecto({ target: opciones.target || 'claude' });
|
|
94
|
+
if (!r.ok) {
|
|
95
|
+
console.error(`\n✘ --solo-hooks: ${r.error}`);
|
|
96
|
+
process.exitCode = 1;
|
|
97
|
+
return r;
|
|
98
|
+
}
|
|
99
|
+
console.log('\n✔ Hooks activados a nivel proyecto SIN copiar componentes:');
|
|
100
|
+
console.log(` settings: ${r.settingsPath}`);
|
|
101
|
+
console.log(` hooks de: ${r.hooksDirGlobal} (scope global — update global los refresca aquí también)`);
|
|
102
|
+
console.log(` registrados: ${r.registrados} | externos preservados: ${r.preservados}`);
|
|
103
|
+
console.log('\n La telemetría (.planning/evolution/nudges.jsonl, skill-metrics.json)');
|
|
104
|
+
console.log(' empieza a acumularse desde la siguiente sesión de Claude en este proyecto.');
|
|
105
|
+
return r;
|
|
106
|
+
}
|
|
107
|
+
|
|
87
108
|
// Leer manifest .swl-ses si existe — sus valores son defaults que el CLI puede sobreescribir
|
|
88
109
|
const manifest = leerManifest(process.cwd());
|
|
89
110
|
if (manifest && !opciones.target && !opciones.profile) {
|
|
@@ -119,4 +119,11 @@ function main() {
|
|
|
119
119
|
|
|
120
120
|
if (require.main === module) main();
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
// Contrato del CLI (bin/swl-ses.js): el export default es UNA función que el
|
|
123
|
+
// bin invoca como modulo(opciones); main re-lee process.argv, que en la
|
|
124
|
+
// invocación vía bin ya trae los flags. Los named exports se preservan como
|
|
125
|
+
// propiedades para los tests existentes.
|
|
126
|
+
module.exports = main;
|
|
127
|
+
module.exports.instalar = instalar;
|
|
128
|
+
module.exports.MARCADOR = MARCADOR;
|
|
129
|
+
module.exports.LINEA_CHECK = LINEA_CHECK;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* activar-hooks-proyecto.js — Camino "solo hooks" (DT-HOOKS-SOLO-PROYECTO).
|
|
5
|
+
*
|
|
6
|
+
* Registra en el `.claude/settings.json` del PROYECTO los hooks del scope
|
|
7
|
+
* GLOBAL ya instalado, sin copiar ningún componente. Cierra el dilema del
|
|
8
|
+
* install local: los hooks solo funcionan registrados a nivel proyecto
|
|
9
|
+
* (ADR-0009), pero el install local completo duplica agentes/comandos en la
|
|
10
|
+
* UI de Claude Code (lee ambos scopes). Con este camino:
|
|
11
|
+
*
|
|
12
|
+
* - Los comandos del settings apuntan con ruta ABSOLUTA al hooks/ global
|
|
13
|
+
* (esGlobal: true en registrarHooks) → cero archivos copiados.
|
|
14
|
+
* - `swl-ses update` del global refresca los hooks de TODOS los proyectos
|
|
15
|
+
* activados así, gratis (apuntan a los mismos archivos).
|
|
16
|
+
* - Hooks externos (no-SWL) del settings del proyecto se preservan
|
|
17
|
+
* (garantía de registrarHooks).
|
|
18
|
+
*
|
|
19
|
+
* Uso: `swl-ses install --target claude --solo-hooks` (en la raíz del
|
|
20
|
+
* proyecto). Requiere instalación global previa del runtime.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
const path = require('path');
|
|
25
|
+
|
|
26
|
+
const RUNTIMES_SOPORTADOS = new Set(['claude', 'openclaude']);
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {object} opciones
|
|
30
|
+
* @param {string} [opciones.target='claude'] Runtime (claude|openclaude — formato de settings de Claude Code)
|
|
31
|
+
* @param {string} [opciones.dirProyecto=cwd] Raíz del proyecto destino
|
|
32
|
+
* @param {string} [opciones.hooksDirGlobal] Override del hooks/ global (tests)
|
|
33
|
+
* @param {object} [opciones.deps] Inyección para tests: { registrarHooks, registrarOptimizaciones, cargarHooksConfig, scopesReales, obtenerRuntime }
|
|
34
|
+
* @returns {{ok: boolean, error?: string, settingsPath?: string, hooksDirGlobal?: string, registrados?: number, preservados?: number}}
|
|
35
|
+
*/
|
|
36
|
+
function activarHooksProyecto(opciones = {}) {
|
|
37
|
+
const target = opciones.target || 'claude';
|
|
38
|
+
if (!RUNTIMES_SOPORTADOS.has(target)) {
|
|
39
|
+
return { ok: false, error: `--solo-hooks soporta por ahora targets con settings formato Claude Code (claude, openclaude); recibido: "${target}".` };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const deps = opciones.deps || {};
|
|
43
|
+
const hs = require('./hooks-settings');
|
|
44
|
+
const registrarHooks = deps.registrarHooks || hs.registrarHooks;
|
|
45
|
+
const registrarOptimizaciones = deps.registrarOptimizaciones || hs.registrarOptimizaciones;
|
|
46
|
+
const cargarHooksConfig = deps.cargarHooksConfig || hs.cargarHooksConfig;
|
|
47
|
+
|
|
48
|
+
// 1. Localizar el hooks/ del scope GLOBAL del runtime.
|
|
49
|
+
let hooksDirGlobal = opciones.hooksDirGlobal;
|
|
50
|
+
if (!hooksDirGlobal) {
|
|
51
|
+
try {
|
|
52
|
+
const dr = require('./detectar-runtime');
|
|
53
|
+
const obtenerRuntime = deps.obtenerRuntime || dr.obtenerRuntime;
|
|
54
|
+
const scopesReales = deps.scopesReales || dr.scopesReales;
|
|
55
|
+
const runtime = obtenerRuntime(target);
|
|
56
|
+
const global = scopesReales(runtime).find(s => s.esGlobal);
|
|
57
|
+
if (!global) return { ok: false, error: `No se pudo resolver el scope global del runtime "${target}".` };
|
|
58
|
+
hooksDirGlobal = path.join(global.dir, 'hooks');
|
|
59
|
+
} catch (err) {
|
|
60
|
+
return { ok: false, error: `No se pudo detectar el runtime "${target}": ${err.message}` };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (!fs.existsSync(hooksDirGlobal)) {
|
|
64
|
+
return { ok: false, error: `No hay instalación global con hooks en ${hooksDirGlobal}. Instala primero el global: npx -y @saulwade/swl-ses@latest install --target ${target} --global --perfil completo` };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 2. Hooks disponibles = archivos del global ∩ hooks-config del paquete.
|
|
68
|
+
const hooksConfig = cargarHooksConfig();
|
|
69
|
+
const hookFiles = fs.readdirSync(hooksDirGlobal)
|
|
70
|
+
.filter(f => f.endsWith('.js') && hooksConfig[f]);
|
|
71
|
+
if (hookFiles.length === 0) {
|
|
72
|
+
return { ok: false, error: `El hooks/ global (${hooksDirGlobal}) no contiene hooks reconocidos por hooks-config.json — instalación global corrupta o muy antigua; corre update primero.` };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 3. Registrar en el settings.json del PROYECTO, apuntando al global.
|
|
76
|
+
const dirProyecto = opciones.dirProyecto || process.cwd();
|
|
77
|
+
const dirClaude = path.join(dirProyecto, '.claude');
|
|
78
|
+
fs.mkdirSync(dirClaude, { recursive: true });
|
|
79
|
+
const settingsPath = path.join(dirClaude, 'settings.json');
|
|
80
|
+
|
|
81
|
+
const r = registrarHooks({
|
|
82
|
+
settingsPath,
|
|
83
|
+
hooksDir: hooksDirGlobal,
|
|
84
|
+
esGlobal: true, // semántica: comandos con ruta ABSOLUTA al hooksDir (que es el global)
|
|
85
|
+
hookFiles,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
let optimizaciones = 0;
|
|
89
|
+
try {
|
|
90
|
+
const opt = registrarOptimizaciones(settingsPath);
|
|
91
|
+
optimizaciones = (opt && opt.registradas) || 0;
|
|
92
|
+
} catch { /* opcional — no bloquea la activación */ }
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
ok: true,
|
|
96
|
+
settingsPath,
|
|
97
|
+
hooksDirGlobal,
|
|
98
|
+
registrados: r.registrados,
|
|
99
|
+
preservados: r.preservados,
|
|
100
|
+
optimizaciones,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = { activarHooksProyecto, RUNTIMES_SOPORTADOS };
|
|
@@ -44,9 +44,31 @@ const TIPOS_INTERVENCION = new Set([
|
|
|
44
44
|
|
|
45
45
|
// ─── 1. Intercepciones (nudges.jsonl) ────────────────────────────────────────
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Diagnóstico de POR QUÉ no hay telemetría: los hooks solo se registran a
|
|
49
|
+
* nivel PROYECTO (ADR-0009 — un install --global copia los archivos pero NO
|
|
50
|
+
* los registra en ~/.claude/settings.json, para no correr 49 hooks en cada
|
|
51
|
+
* sesión de cualquier proyecto). Sin registro local, nudges.jsonl y
|
|
52
|
+
* skill-metrics.json jamás aparecerán — el panel debe decir CÓMO activarlo,
|
|
53
|
+
* no solo "no disponible" (reporte de campo SIGM, 2026-07-09).
|
|
54
|
+
*/
|
|
55
|
+
function hooksRegistradosEnProyecto(raiz) {
|
|
56
|
+
try {
|
|
57
|
+
const s = JSON.parse(fs.readFileSync(path.join(raiz, '.claude', 'settings.json'), 'utf8'));
|
|
58
|
+
return Object.values(s.hooks || {}).some(m => Array.isArray(m) && m.length > 0);
|
|
59
|
+
} catch { return false; }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const HINT_ACTIVACION = 'hooks sin registrar a nivel proyecto (el install --global no los registra por diseño, ADR-0009) — activar SIN duplicar componentes con: npx -y @saulwade/swl-ses@latest install --target claude --solo-hooks (en la raíz del proyecto)';
|
|
63
|
+
|
|
47
64
|
function leerNudges(raiz, { ventanaDias = 30, hoy = new Date() } = {}) {
|
|
48
65
|
const ruta = path.join(raiz, '.planning', 'evolution', 'nudges.jsonl');
|
|
49
|
-
if (!fs.existsSync(ruta))
|
|
66
|
+
if (!fs.existsSync(ruta)) {
|
|
67
|
+
const razon = hooksRegistradosEnProyecto(raiz)
|
|
68
|
+
? 'sin nudges.jsonl aún (hooks registrados; se acumula con el uso de sesiones)'
|
|
69
|
+
: HINT_ACTIVACION;
|
|
70
|
+
return { disponible: false, razon, eventos: [] };
|
|
71
|
+
}
|
|
50
72
|
const corte = hoy.getTime() - ventanaDias * DIA_MS;
|
|
51
73
|
const eventos = [];
|
|
52
74
|
for (const linea of fs.readFileSync(ruta, 'utf8').split(/\r?\n/)) {
|
|
@@ -102,6 +124,10 @@ function presionDefectos(raiz, { ventanaDias = 30, hoy = new Date(), git = ejecu
|
|
|
102
124
|
|
|
103
125
|
// Escape aproximado: fixes con fecha POSTERIOR a la última verificación de
|
|
104
126
|
// fase (VERIFICACION.md más reciente). Sin fases verificadas → no aplica.
|
|
127
|
+
// La fecha se toma de GIT (autoría del último commit que tocó el archivo),
|
|
128
|
+
// no del mtime del filesystem: tras un clone/checkout todos los archivos
|
|
129
|
+
// comparten el mtime de la operación y el proxy daba falso negativo
|
|
130
|
+
// (reporte de campo SIGM, 2026-07-09). Fallback a mtime fuera de git.
|
|
105
131
|
let escapes = { disponible: false, razon: 'sin VERIFICACION.md en .planning/fases/ (proyecto sin ciclo GSD verificado)' };
|
|
106
132
|
try {
|
|
107
133
|
const dirFases = path.join(raiz, '.planning', 'fases');
|
|
@@ -109,7 +135,15 @@ function presionDefectos(raiz, { ventanaDias = 30, hoy = new Date(), git = ejecu
|
|
|
109
135
|
if (fs.existsSync(dirFases)) {
|
|
110
136
|
for (const fase of fs.readdirSync(dirFases)) {
|
|
111
137
|
const v = path.join(dirFases, fase, 'VERIFICACION.md');
|
|
112
|
-
if (fs.existsSync(v))
|
|
138
|
+
if (!fs.existsSync(v)) continue;
|
|
139
|
+
let fecha = 0;
|
|
140
|
+
try {
|
|
141
|
+
const rel = path.relative(raiz, v);
|
|
142
|
+
const ct = parseInt(git(raiz, ['log', '-1', '--format=%ct', '--', rel]).trim(), 10);
|
|
143
|
+
if (Number.isFinite(ct) && ct > 0) fecha = ct * 1000;
|
|
144
|
+
} catch { /* fuera de git o archivo sin commits → fallback mtime */ }
|
|
145
|
+
if (fecha === 0) fecha = fs.statSync(v).mtimeMs;
|
|
146
|
+
ultimaVerificacion = Math.max(ultimaVerificacion, fecha);
|
|
113
147
|
}
|
|
114
148
|
}
|
|
115
149
|
if (ultimaVerificacion > 0) {
|
|
@@ -152,7 +186,12 @@ function doraCi(raiz, { ventanaDias = 30, hoy = new Date() } = {}) {
|
|
|
152
186
|
|
|
153
187
|
function usoSistema(raiz) {
|
|
154
188
|
const ruta = path.join(raiz, '.planning', 'skill-metrics.json');
|
|
155
|
-
if (!fs.existsSync(ruta))
|
|
189
|
+
if (!fs.existsSync(ruta)) {
|
|
190
|
+
const razon = hooksRegistradosEnProyecto(raiz)
|
|
191
|
+
? 'sin skill-metrics.json aún (hooks registrados; se acumula con el uso de skills)'
|
|
192
|
+
: HINT_ACTIVACION;
|
|
193
|
+
return { disponible: false, razon };
|
|
194
|
+
}
|
|
156
195
|
try {
|
|
157
196
|
// Forma real: { version, entries: [{skill, estado, timestamp}, ...] }
|
|
158
197
|
// (eventos individuales del hook telemetria-skill-routing) — agregamos aquí.
|
package/scripts/tui/index.js
CHANGED
|
@@ -118,7 +118,16 @@ async function _flujoInstall() {
|
|
|
118
118
|
}
|
|
119
119
|
return { ok: false };
|
|
120
120
|
}
|
|
121
|
-
|
|
121
|
+
// Multi-runtime: una corrida del instalador por runtime marcado en el paso 1.
|
|
122
|
+
const targets = w.opciones.targets && w.opciones.targets.length > 0
|
|
123
|
+
? w.opciones.targets
|
|
124
|
+
: [w.opciones.target];
|
|
125
|
+
let ultimo = { ok: true };
|
|
126
|
+
for (const target of targets) {
|
|
127
|
+
ultimo = await ejecutarInstall({ ...w.opciones, target });
|
|
128
|
+
if (!ultimo.ok) return ultimo; // reporta el runtime que falló, sin continuar a ciegas
|
|
129
|
+
}
|
|
130
|
+
return ultimo;
|
|
122
131
|
}
|
|
123
132
|
|
|
124
133
|
async function _flujoUpdate() {
|
|
@@ -23,7 +23,7 @@ const teclas = require('../lib/teclas');
|
|
|
23
23
|
const { colores, semantico, iconos } = require('../lib/colores');
|
|
24
24
|
|
|
25
25
|
function _cargarDatosInstalaciones() {
|
|
26
|
-
let detectarRuntimes, cargarEstado;
|
|
26
|
+
let detectarRuntimes, scopesReales, cargarEstado;
|
|
27
27
|
try {
|
|
28
28
|
({ detectarRuntimes, scopesReales } = require('../../lib/detectar-runtime'));
|
|
29
29
|
({ cargarEstado } = require('../../lib/estado'));
|
|
@@ -170,7 +170,7 @@ function pantallaPreview(opciones, perfiles) {
|
|
|
170
170
|
render.escribirEn(3, 3, colores.dim('Revisa antes de confirmar:'));
|
|
171
171
|
|
|
172
172
|
const filas = [
|
|
173
|
-
['Runtime',
|
|
173
|
+
['Runtime(s)', (opciones.targets || [opciones.target]).join(', ')],
|
|
174
174
|
['Perfil', opciones.profile],
|
|
175
175
|
['Scope', opciones.global ? 'global (~/.claude)' : 'proyecto (./.claude)'],
|
|
176
176
|
['With MCP', opciones.with_mcp ? 'sí' : 'no'],
|
|
@@ -243,13 +243,25 @@ async function ejecutarWizardInstall() {
|
|
|
243
243
|
return { exito: false, error: 'No se pudo cargar manifiestos/perfiles.json' };
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
-
// 1. Runtime
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
246
|
+
// 1. Runtime(s) — multiselección: instala en varios runtimes en una corrida
|
|
247
|
+
// (fix 2026-07-09: era selector único; el flujo multi-runtime obligaba a
|
|
248
|
+
// repetir el wizard completo por cada runtime).
|
|
249
|
+
let targets = null;
|
|
250
|
+
let tituloPaso1 = 'Paso 1/6 — ¿En qué runtime(s) instalas SWL? (espacio para marcar)';
|
|
251
|
+
for (;;) {
|
|
252
|
+
targets = await selectorMulti({ titulo: tituloPaso1, items: RUNTIMES });
|
|
253
|
+
if (targets === null) return { exito: false, cancelado: true };
|
|
254
|
+
if (targets.length > 0) break;
|
|
255
|
+
// no-TTY: selectorMulti resuelve [] al instante — re-preguntar sería un
|
|
256
|
+
// spin infinito en caliente (colgó la suite 2.7h el 2026-07-09). Default:
|
|
257
|
+
// primer runtime, igual que hacía el selector único.
|
|
258
|
+
if (!render.ES_TTY || !process.stdin.isTTY) {
|
|
259
|
+
targets = [RUNTIMES[0].valor];
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
tituloPaso1 = 'Paso 1/6 — Marca AL MENOS un runtime con espacio (Esc para cancelar)';
|
|
263
|
+
}
|
|
264
|
+
const target = targets[0]; // compat: preview/ejecutores leen .target
|
|
253
265
|
|
|
254
266
|
// 2. Perfil
|
|
255
267
|
const profile = await selectorUnico({
|
|
@@ -287,6 +299,7 @@ async function ejecutarWizardInstall() {
|
|
|
287
299
|
|
|
288
300
|
const opciones = {
|
|
289
301
|
target,
|
|
302
|
+
targets,
|
|
290
303
|
profile,
|
|
291
304
|
global: scope === 'global',
|
|
292
305
|
with_mcp: flagsSeleccionados.includes('with-mcp'),
|
|
@@ -21,7 +21,7 @@ const teclas = require('../lib/teclas');
|
|
|
21
21
|
const { colores, semantico, iconos } = require('../lib/colores');
|
|
22
22
|
|
|
23
23
|
function _cargarInstalaciones() {
|
|
24
|
-
let detectarRuntimes, cargarEstado;
|
|
24
|
+
let detectarRuntimes, scopesReales, cargarEstado;
|
|
25
25
|
try {
|
|
26
26
|
({ detectarRuntimes, scopesReales } = require('../../lib/detectar-runtime'));
|
|
27
27
|
({ cargarEstado } = require('../../lib/estado'));
|
|
@@ -40,7 +40,7 @@ const CATEGORIAS = [
|
|
|
40
40
|
// ---------------------------------------------------------------------------
|
|
41
41
|
|
|
42
42
|
function _detectarInstalaciones() {
|
|
43
|
-
let detectarRuntimes, cargarEstado;
|
|
43
|
+
let detectarRuntimes, scopesReales, cargarEstado;
|
|
44
44
|
try {
|
|
45
45
|
({ detectarRuntimes, scopesReales } = require('../../lib/detectar-runtime'));
|
|
46
46
|
({ cargarEstado } = require('../../lib/estado'));
|
|
@@ -33,8 +33,7 @@ const SUBTITULO = 'Sistema de ingeniería de software auto-evolutivo multi-runti
|
|
|
33
33
|
* No falla si la lib no está disponible — devuelve array vacío.
|
|
34
34
|
*/
|
|
35
35
|
function detectarInstalaciones() {
|
|
36
|
-
let detectarRuntimes;
|
|
37
|
-
let cargarEstado;
|
|
36
|
+
let detectarRuntimes, scopesReales, cargarEstado;
|
|
38
37
|
try {
|
|
39
38
|
({ detectarRuntimes, scopesReales } = require('../../lib/detectar-runtime'));
|
|
40
39
|
({ cargarEstado } = require('../../lib/estado'));
|