@saulwade/swl-ses 1.3.2 → 1.3.4
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 +199 -199
- package/README.md +1 -1
- package/bin/swl-ses.js +77 -5
- package/comandos/swl/aprender.md +1 -1
- package/comandos/swl/claudemd.md +141 -136
- package/comandos/swl/configurar-ci.md +227 -227
- package/comandos/swl/evolucion-estado.md +1 -1
- package/comandos/swl/evolucionar.md +3 -3
- package/comandos/swl/inbox.md +1 -1
- package/comandos/swl/reflect-skills.md +1 -1
- package/comandos/swl/salud.md +7 -7
- package/comandos/swl/skill-search.md +4 -4
- package/habilidades/swl-claudemd/SKILL.md +2 -2
- package/hooks/extraccion-aprendizajes.js +24 -0
- package/manifiestos/perfiles.json +2 -1
- package/manifiestos/skills-lock.json +5 -5
- package/package.json +87 -87
- package/plugin.json +343 -343
- package/scripts/auditar-claudemd.js +297 -297
- package/scripts/bootstrap-instintos.js +1 -1
- package/scripts/cli/audit-agents-gaps.js +36 -0
- package/scripts/cli/audit-claudemd.js +43 -0
- package/scripts/cli/audit-coverage-frameworks.js +39 -0
- package/scripts/cli/bootstrap-instincts.js +38 -0
- package/scripts/cli/configure-branch-protection.js +42 -0
- package/scripts/cli/generate-skills-lock.js +31 -0
- package/scripts/cli/inbox-tmux-inject.js +49 -0
- package/scripts/cli/reflect-skills.js +40 -0
- package/scripts/cli/run-skill-evals.js +47 -0
- package/scripts/cli/skill-discovery.js +38 -0
- package/scripts/cli/verify-evolution.js +36 -0
- package/scripts/generar-skills-lock.js +190 -190
- package/scripts/inbox-tmux-inject.js +6 -0
- package/scripts/lib/skill-discovery.js +11 -3
- package/scripts/verificar-evolucion.js +1 -1
|
@@ -1,190 +1,190 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* generar-skills-lock.js
|
|
4
|
-
*
|
|
5
|
-
* Genera `manifiestos/skills-lock.json` con SHA256 de cada `habilidades/*\/SKILL.md`.
|
|
6
|
-
* Permite detectar drift silencioso: alguien edita un skill localmente sin
|
|
7
|
-
* commit y nadie lo nota hasta que falla. El lock se regenera en cada
|
|
8
|
-
* `/swl:release` y se valida en `/swl:salud`.
|
|
9
|
-
*
|
|
10
|
-
* Uso:
|
|
11
|
-
* node scripts/generar-skills-lock.js # genera y escribe
|
|
12
|
-
* node scripts/generar-skills-lock.js --check # compara, falla si hay drift
|
|
13
|
-
* node scripts/generar-skills-lock.js --json # imprime resumen JSON
|
|
14
|
-
*
|
|
15
|
-
* Origen: análisis de temp/design.md-main/skills-lock.json (mayo 2026).
|
|
16
|
-
*
|
|
17
|
-
* Zero-dependencies. Compatible Windows (CRLF-safe), Node 18+.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
'use strict';
|
|
21
|
-
|
|
22
|
-
const fs = require('fs');
|
|
23
|
-
const path = require('path');
|
|
24
|
-
const crypto = require('crypto');
|
|
25
|
-
|
|
26
|
-
const ROOT = path.resolve(__dirname, '..');
|
|
27
|
-
|
|
28
|
-
// Escritura atómica obligatoria (regla CLAUDE.md). Fallback defensivo.
|
|
29
|
-
let atomicWriteSync;
|
|
30
|
-
try {
|
|
31
|
-
({ atomicWriteSync } = require(path.join(ROOT, 'hooks', 'lib', 'atomic-write.js')));
|
|
32
|
-
} catch {
|
|
33
|
-
atomicWriteSync = (p, c, e) => fs.writeFileSync(p, c, e);
|
|
34
|
-
}
|
|
35
|
-
const HABILIDADES_DIR = path.join(ROOT, 'habilidades');
|
|
36
|
-
const LOCK_FILE = path.join(ROOT, 'manifiestos', 'skills-lock.json');
|
|
37
|
-
const LOCK_VERSION = 1;
|
|
38
|
-
|
|
39
|
-
const CHECK = process.argv.includes('--check');
|
|
40
|
-
const JSON_OUT = process.argv.includes('--json');
|
|
41
|
-
|
|
42
|
-
function sha256(buffer) {
|
|
43
|
-
return crypto.createHash('sha256').update(buffer).digest('hex');
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function leerFrontmatter(contenido) {
|
|
47
|
-
const m = contenido.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n/);
|
|
48
|
-
if (!m) return {};
|
|
49
|
-
const fm = {};
|
|
50
|
-
for (const linea of m[1].split(/\r?\n/)) {
|
|
51
|
-
const kv = linea.match(/^([a-zA-Z_][a-zA-Z0-9_-]*):\s*(.*)$/);
|
|
52
|
-
if (kv) fm[kv[1]] = kv[2].trim();
|
|
53
|
-
}
|
|
54
|
-
return fm;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function generarLock() {
|
|
58
|
-
if (!fs.existsSync(HABILIDADES_DIR)) {
|
|
59
|
-
console.error(`ERROR: directorio no encontrado: ${HABILIDADES_DIR}`);
|
|
60
|
-
process.exit(2);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const skills = [];
|
|
64
|
-
const dirs = fs
|
|
65
|
-
.readdirSync(HABILIDADES_DIR, { withFileTypes: true })
|
|
66
|
-
.filter((d) => d.isDirectory())
|
|
67
|
-
.map((d) => d.name)
|
|
68
|
-
.sort();
|
|
69
|
-
|
|
70
|
-
for (const dir of dirs) {
|
|
71
|
-
const skillFile = path.join(HABILIDADES_DIR, dir, 'SKILL.md');
|
|
72
|
-
if (!fs.existsSync(skillFile)) {
|
|
73
|
-
// Skill sin SKILL.md — anomalía pero no fatal
|
|
74
|
-
skills.push({
|
|
75
|
-
nombre: dir,
|
|
76
|
-
path: `habilidades/${dir}/SKILL.md`,
|
|
77
|
-
hash: null,
|
|
78
|
-
bytes: 0,
|
|
79
|
-
warning: 'skill-md-faltante',
|
|
80
|
-
});
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const buffer = fs.readFileSync(skillFile);
|
|
85
|
-
const contenido = buffer.toString('utf-8');
|
|
86
|
-
const fm = leerFrontmatter(contenido);
|
|
87
|
-
|
|
88
|
-
skills.push({
|
|
89
|
-
nombre: fm.name || dir,
|
|
90
|
-
path: `habilidades/${dir}/SKILL.md`,
|
|
91
|
-
hash: `sha256:${sha256(buffer)}`,
|
|
92
|
-
bytes: buffer.length,
|
|
93
|
-
version: fm.version || null,
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Hash agregado del lock completo (excluyendo timestamps)
|
|
98
|
-
const datosCanonicos = JSON.stringify(
|
|
99
|
-
skills.map((s) => ({ nombre: s.nombre, hash: s.hash })),
|
|
100
|
-
null,
|
|
101
|
-
0
|
|
102
|
-
);
|
|
103
|
-
const lockHash = `sha256:${sha256(Buffer.from(datosCanonicos, 'utf-8'))}`;
|
|
104
|
-
|
|
105
|
-
return {
|
|
106
|
-
lockfileVersion: LOCK_VERSION,
|
|
107
|
-
generatedAt: new Date().toISOString(),
|
|
108
|
-
skillsCount: skills.length,
|
|
109
|
-
lockHash,
|
|
110
|
-
skills,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function comparar(actual, anterior) {
|
|
115
|
-
if (!anterior) return { drifted: [], faltantes: actual.skills, nuevos: [] };
|
|
116
|
-
|
|
117
|
-
const anteriorMap = new Map(anterior.skills.map((s) => [s.nombre, s]));
|
|
118
|
-
const actualMap = new Map(actual.skills.map((s) => [s.nombre, s]));
|
|
119
|
-
|
|
120
|
-
const drifted = [];
|
|
121
|
-
const nuevos = [];
|
|
122
|
-
const faltantes = [];
|
|
123
|
-
|
|
124
|
-
for (const s of actual.skills) {
|
|
125
|
-
const prev = anteriorMap.get(s.nombre);
|
|
126
|
-
if (!prev) {
|
|
127
|
-
nuevos.push(s.nombre);
|
|
128
|
-
} else if (prev.hash !== s.hash) {
|
|
129
|
-
drifted.push({ nombre: s.nombre, antes: prev.hash, ahora: s.hash });
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
for (const s of anterior.skills) {
|
|
133
|
-
if (!actualMap.has(s.nombre)) {
|
|
134
|
-
faltantes.push(s.nombre);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return { drifted, faltantes, nuevos };
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function main() {
|
|
142
|
-
const actual = generarLock();
|
|
143
|
-
|
|
144
|
-
if (CHECK) {
|
|
145
|
-
if (!fs.existsSync(LOCK_FILE)) {
|
|
146
|
-
console.error(`ERROR: ${LOCK_FILE} no existe. Ejecutar sin --check para generarlo.`);
|
|
147
|
-
process.exit(1);
|
|
148
|
-
}
|
|
149
|
-
const anterior = JSON.parse(fs.readFileSync(LOCK_FILE, 'utf-8'));
|
|
150
|
-
const diff = comparar(actual, anterior);
|
|
151
|
-
const total = diff.drifted.length + diff.faltantes.length + diff.nuevos.length;
|
|
152
|
-
|
|
153
|
-
if (JSON_OUT) {
|
|
154
|
-
console.log(JSON.stringify({ ok: total === 0, ...diff }, null, 2));
|
|
155
|
-
} else if (total === 0) {
|
|
156
|
-
console.log(`✓ skills-lock OK: ${actual.skillsCount} skills sin drift`);
|
|
157
|
-
} else {
|
|
158
|
-
console.log(`✗ skills-lock DRIFT detectado:`);
|
|
159
|
-
if (diff.drifted.length) {
|
|
160
|
-
console.log(` Modificados (${diff.drifted.length}):`);
|
|
161
|
-
diff.drifted.forEach((d) => console.log(` - ${d.nombre}`));
|
|
162
|
-
}
|
|
163
|
-
if (diff.nuevos.length) {
|
|
164
|
-
console.log(` Nuevos (${diff.nuevos.length}):`);
|
|
165
|
-
diff.nuevos.forEach((n) => console.log(` - ${n}`));
|
|
166
|
-
}
|
|
167
|
-
if (diff.faltantes.length) {
|
|
168
|
-
console.log(` Faltantes (${diff.faltantes.length}):`);
|
|
169
|
-
diff.faltantes.forEach((n) => console.log(` - ${n}`));
|
|
170
|
-
}
|
|
171
|
-
console.log(`\n Para regenerar: node scripts/generar-skills-lock.js`);
|
|
172
|
-
}
|
|
173
|
-
process.exit(total === 0 ? 0 : 1);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Generar
|
|
177
|
-
atomicWriteSync(LOCK_FILE, JSON.stringify(actual, null, 2) + '\n', 'utf-8');
|
|
178
|
-
if (JSON_OUT) {
|
|
179
|
-
console.log(JSON.stringify({ ok: true, file: LOCK_FILE, count: actual.skillsCount }));
|
|
180
|
-
} else {
|
|
181
|
-
console.log(`✓ Generado ${LOCK_FILE}`);
|
|
182
|
-
console.log(` ${actual.skillsCount} skills, lockHash=${actual.lockHash.slice(0, 23)}...`);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (require.main === module) {
|
|
187
|
-
main();
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
module.exports = { generarLock, comparar };
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* generar-skills-lock.js
|
|
4
|
+
*
|
|
5
|
+
* Genera `manifiestos/skills-lock.json` con SHA256 de cada `habilidades/*\/SKILL.md`.
|
|
6
|
+
* Permite detectar drift silencioso: alguien edita un skill localmente sin
|
|
7
|
+
* commit y nadie lo nota hasta que falla. El lock se regenera en cada
|
|
8
|
+
* `/swl:release` y se valida en `/swl:salud`.
|
|
9
|
+
*
|
|
10
|
+
* Uso:
|
|
11
|
+
* node scripts/generar-skills-lock.js # genera y escribe
|
|
12
|
+
* node scripts/generar-skills-lock.js --check # compara, falla si hay drift
|
|
13
|
+
* node scripts/generar-skills-lock.js --json # imprime resumen JSON
|
|
14
|
+
*
|
|
15
|
+
* Origen: análisis de temp/design.md-main/skills-lock.json (mayo 2026).
|
|
16
|
+
*
|
|
17
|
+
* Zero-dependencies. Compatible Windows (CRLF-safe), Node 18+.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
'use strict';
|
|
21
|
+
|
|
22
|
+
const fs = require('fs');
|
|
23
|
+
const path = require('path');
|
|
24
|
+
const crypto = require('crypto');
|
|
25
|
+
|
|
26
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
27
|
+
|
|
28
|
+
// Escritura atómica obligatoria (regla CLAUDE.md). Fallback defensivo.
|
|
29
|
+
let atomicWriteSync;
|
|
30
|
+
try {
|
|
31
|
+
({ atomicWriteSync } = require(path.join(ROOT, 'hooks', 'lib', 'atomic-write.js')));
|
|
32
|
+
} catch {
|
|
33
|
+
atomicWriteSync = (p, c, e) => fs.writeFileSync(p, c, e);
|
|
34
|
+
}
|
|
35
|
+
const HABILIDADES_DIR = path.join(ROOT, 'habilidades');
|
|
36
|
+
const LOCK_FILE = path.join(ROOT, 'manifiestos', 'skills-lock.json');
|
|
37
|
+
const LOCK_VERSION = 1;
|
|
38
|
+
|
|
39
|
+
const CHECK = process.argv.includes('--check');
|
|
40
|
+
const JSON_OUT = process.argv.includes('--json');
|
|
41
|
+
|
|
42
|
+
function sha256(buffer) {
|
|
43
|
+
return crypto.createHash('sha256').update(buffer).digest('hex');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function leerFrontmatter(contenido) {
|
|
47
|
+
const m = contenido.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n/);
|
|
48
|
+
if (!m) return {};
|
|
49
|
+
const fm = {};
|
|
50
|
+
for (const linea of m[1].split(/\r?\n/)) {
|
|
51
|
+
const kv = linea.match(/^([a-zA-Z_][a-zA-Z0-9_-]*):\s*(.*)$/);
|
|
52
|
+
if (kv) fm[kv[1]] = kv[2].trim();
|
|
53
|
+
}
|
|
54
|
+
return fm;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function generarLock() {
|
|
58
|
+
if (!fs.existsSync(HABILIDADES_DIR)) {
|
|
59
|
+
console.error(`ERROR: directorio no encontrado: ${HABILIDADES_DIR}`);
|
|
60
|
+
process.exit(2);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const skills = [];
|
|
64
|
+
const dirs = fs
|
|
65
|
+
.readdirSync(HABILIDADES_DIR, { withFileTypes: true })
|
|
66
|
+
.filter((d) => d.isDirectory())
|
|
67
|
+
.map((d) => d.name)
|
|
68
|
+
.sort();
|
|
69
|
+
|
|
70
|
+
for (const dir of dirs) {
|
|
71
|
+
const skillFile = path.join(HABILIDADES_DIR, dir, 'SKILL.md');
|
|
72
|
+
if (!fs.existsSync(skillFile)) {
|
|
73
|
+
// Skill sin SKILL.md — anomalía pero no fatal
|
|
74
|
+
skills.push({
|
|
75
|
+
nombre: dir,
|
|
76
|
+
path: `habilidades/${dir}/SKILL.md`,
|
|
77
|
+
hash: null,
|
|
78
|
+
bytes: 0,
|
|
79
|
+
warning: 'skill-md-faltante',
|
|
80
|
+
});
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const buffer = fs.readFileSync(skillFile);
|
|
85
|
+
const contenido = buffer.toString('utf-8');
|
|
86
|
+
const fm = leerFrontmatter(contenido);
|
|
87
|
+
|
|
88
|
+
skills.push({
|
|
89
|
+
nombre: fm.name || dir,
|
|
90
|
+
path: `habilidades/${dir}/SKILL.md`,
|
|
91
|
+
hash: `sha256:${sha256(buffer)}`,
|
|
92
|
+
bytes: buffer.length,
|
|
93
|
+
version: fm.version || null,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Hash agregado del lock completo (excluyendo timestamps)
|
|
98
|
+
const datosCanonicos = JSON.stringify(
|
|
99
|
+
skills.map((s) => ({ nombre: s.nombre, hash: s.hash })),
|
|
100
|
+
null,
|
|
101
|
+
0
|
|
102
|
+
);
|
|
103
|
+
const lockHash = `sha256:${sha256(Buffer.from(datosCanonicos, 'utf-8'))}`;
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
lockfileVersion: LOCK_VERSION,
|
|
107
|
+
generatedAt: new Date().toISOString(),
|
|
108
|
+
skillsCount: skills.length,
|
|
109
|
+
lockHash,
|
|
110
|
+
skills,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function comparar(actual, anterior) {
|
|
115
|
+
if (!anterior) return { drifted: [], faltantes: actual.skills, nuevos: [] };
|
|
116
|
+
|
|
117
|
+
const anteriorMap = new Map(anterior.skills.map((s) => [s.nombre, s]));
|
|
118
|
+
const actualMap = new Map(actual.skills.map((s) => [s.nombre, s]));
|
|
119
|
+
|
|
120
|
+
const drifted = [];
|
|
121
|
+
const nuevos = [];
|
|
122
|
+
const faltantes = [];
|
|
123
|
+
|
|
124
|
+
for (const s of actual.skills) {
|
|
125
|
+
const prev = anteriorMap.get(s.nombre);
|
|
126
|
+
if (!prev) {
|
|
127
|
+
nuevos.push(s.nombre);
|
|
128
|
+
} else if (prev.hash !== s.hash) {
|
|
129
|
+
drifted.push({ nombre: s.nombre, antes: prev.hash, ahora: s.hash });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
for (const s of anterior.skills) {
|
|
133
|
+
if (!actualMap.has(s.nombre)) {
|
|
134
|
+
faltantes.push(s.nombre);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return { drifted, faltantes, nuevos };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function main() {
|
|
142
|
+
const actual = generarLock();
|
|
143
|
+
|
|
144
|
+
if (CHECK) {
|
|
145
|
+
if (!fs.existsSync(LOCK_FILE)) {
|
|
146
|
+
console.error(`ERROR: ${LOCK_FILE} no existe. Ejecutar sin --check para generarlo.`);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
const anterior = JSON.parse(fs.readFileSync(LOCK_FILE, 'utf-8'));
|
|
150
|
+
const diff = comparar(actual, anterior);
|
|
151
|
+
const total = diff.drifted.length + diff.faltantes.length + diff.nuevos.length;
|
|
152
|
+
|
|
153
|
+
if (JSON_OUT) {
|
|
154
|
+
console.log(JSON.stringify({ ok: total === 0, ...diff }, null, 2));
|
|
155
|
+
} else if (total === 0) {
|
|
156
|
+
console.log(`✓ skills-lock OK: ${actual.skillsCount} skills sin drift`);
|
|
157
|
+
} else {
|
|
158
|
+
console.log(`✗ skills-lock DRIFT detectado:`);
|
|
159
|
+
if (diff.drifted.length) {
|
|
160
|
+
console.log(` Modificados (${diff.drifted.length}):`);
|
|
161
|
+
diff.drifted.forEach((d) => console.log(` - ${d.nombre}`));
|
|
162
|
+
}
|
|
163
|
+
if (diff.nuevos.length) {
|
|
164
|
+
console.log(` Nuevos (${diff.nuevos.length}):`);
|
|
165
|
+
diff.nuevos.forEach((n) => console.log(` - ${n}`));
|
|
166
|
+
}
|
|
167
|
+
if (diff.faltantes.length) {
|
|
168
|
+
console.log(` Faltantes (${diff.faltantes.length}):`);
|
|
169
|
+
diff.faltantes.forEach((n) => console.log(` - ${n}`));
|
|
170
|
+
}
|
|
171
|
+
console.log(`\n Para regenerar: node scripts/generar-skills-lock.js`);
|
|
172
|
+
}
|
|
173
|
+
process.exit(total === 0 ? 0 : 1);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Generar
|
|
177
|
+
atomicWriteSync(LOCK_FILE, JSON.stringify(actual, null, 2) + '\n', 'utf-8');
|
|
178
|
+
if (JSON_OUT) {
|
|
179
|
+
console.log(JSON.stringify({ ok: true, file: LOCK_FILE, count: actual.skillsCount }));
|
|
180
|
+
} else {
|
|
181
|
+
console.log(`✓ Generado ${LOCK_FILE}`);
|
|
182
|
+
console.log(` ${actual.skillsCount} skills, lockHash=${actual.lockHash.slice(0, 23)}...`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (require.main === module) {
|
|
187
|
+
main();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
module.exports = { generarLock, comparar, main };
|
|
@@ -159,3 +159,9 @@ function main() {
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
if (require.main === module) main();
|
|
162
|
+
|
|
163
|
+
// Exporta main para que el wrapper del bin CLI
|
|
164
|
+
// (`scripts/cli/inbox-tmux-inject.js`) pueda invocarla sin depender del
|
|
165
|
+
// guard `require.main === module`, que solo es true cuando el script se
|
|
166
|
+
// ejecuta como punto de entrada directo (no cuando es require()d).
|
|
167
|
+
module.exports = { main };
|
|
@@ -187,13 +187,15 @@ function buscar(cwd, query) {
|
|
|
187
187
|
// Exports
|
|
188
188
|
// ---------------------------------------------------------------------------
|
|
189
189
|
|
|
190
|
-
module.exports = { descubrir, buscar, cargarIndice, escanear };
|
|
191
|
-
|
|
192
190
|
// ---------------------------------------------------------------------------
|
|
193
191
|
// CLI directo
|
|
194
192
|
// ---------------------------------------------------------------------------
|
|
195
193
|
|
|
196
|
-
|
|
194
|
+
// Extraído a función nombrada para que el wrapper del bin CLI
|
|
195
|
+
// (`scripts/cli/skill-discovery.js`) pueda invocarla sin depender del guard
|
|
196
|
+
// `require.main === module`, que solo se cumple cuando el script se ejecuta
|
|
197
|
+
// como punto de entrada directo (no cuando es require()d desde otro módulo).
|
|
198
|
+
function main() {
|
|
197
199
|
const cwd = process.cwd();
|
|
198
200
|
const args = process.argv.slice(2);
|
|
199
201
|
|
|
@@ -232,3 +234,9 @@ if (require.main === module) {
|
|
|
232
234
|
process.stdout.write(`Uso: Skill("nombre-de-habilidad")\n\n`);
|
|
233
235
|
}
|
|
234
236
|
}
|
|
237
|
+
|
|
238
|
+
if (require.main === module) {
|
|
239
|
+
main();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
module.exports = { descubrir, buscar, cargarIndice, escanear, main };
|
|
@@ -286,4 +286,4 @@ function main() {
|
|
|
286
286
|
|
|
287
287
|
if (require.main === module) main();
|
|
288
288
|
|
|
289
|
-
module.exports = { verificarArchivo, extraerFrontmatter, leerCampo, obtenerVersionEnHEAD };
|
|
289
|
+
module.exports = { verificarArchivo, extraerFrontmatter, leerCampo, obtenerVersionEnHEAD, main };
|