nexakit 0.1.0 → 0.2.0
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-plugin/marketplace.json +18 -1
- package/README.md +40 -30
- package/assets/brand/README.md +0 -2
- package/assets/runtime/package-lock.json +2 -2
- package/bin/agent-task.js +12 -10
- package/bin/agent-toolkit-gemini.js +11 -9
- package/bin/agent-toolkit-inbox.js +12 -10
- package/bin/agent-toolkit-mcp.js +14 -8
- package/bin/agent-toolkit.js +12 -9
- package/bin/nexakit.js +12 -9
- package/bin/project-memory-mcp.js +12 -9
- package/bin/project-memory.js +5 -3
- package/{skills/depurar → catalogs/skills/vercel-composition-patterns}/LICENSE.txt +6 -6
- package/catalogs/skills/vercel-react-best-practices/LICENSE.txt +27 -0
- package/catalogs/skills.json +70 -35
- package/docs/skills/evidencias/2026-10-28-recomendador-preparacion.md +1 -1
- package/package.json +5 -3
- package/plugins/nexakit-modelo/.claude-plugin/plugin.json +1 -0
- package/plugins/nexakit-modelo/hooks/hooks.json +1 -0
- package/plugins/nexakit-modelo/hooks/register.js +48 -0
- package/plugins/nexakit-modelo/hooks/status.js +8 -0
- package/plugins/nexakit-modelo/tests/register.test.ts +21 -0
- package/plugins/nexakit-panel/.claude-plugin/plugin.json +1 -0
- package/plugins/nexakit-panel/README.md +20 -0
- package/plugins/nexakit-panel/hooks/hooks.json +1 -0
- package/plugins/nexakit-panel/hooks/lienzo.js +382 -0
- package/plugins/nexakit-panel/hooks/panel.js +279 -0
- package/plugins/nexakit-panel/hooks/register.tsx +248 -0
- package/plugins/nexakit-panel/tests/lienzo.test.ts +142 -0
- package/plugins/nexakit-panel/tests/panel.test.ts +240 -0
- package/plugins/nexakit-panel/tests/register.test.ts +167 -0
- package/plugins/nexakit-panel/types/index.d.ts +41 -0
- package/plugins/nexakit-sesion/.claude-plugin/plugin.json +1 -0
- package/plugins/nexakit-sesion/README.md +12 -0
- package/plugins/nexakit-sesion/hooks/aviso.js +38 -0
- package/plugins/nexakit-sesion/hooks/hooks.json +1 -0
- package/plugins/nexakit-sesion/hooks/register.js +28 -0
- package/plugins/nexakit-sesion/tests/register.test.ts +96 -0
- package/schemas/skill-catalog.schema.json +9 -2
- package/skills/cerrar-memoria/SKILL.md +6 -2
- package/skills/cerrar-memoria/references/cierre.md +5 -1
- package/skills/elegir-modelo/SKILL.md +68 -0
- package/skills/elegir-modelo/modelos.mjs +108 -0
- package/skills/gemini/SKILL.md +1 -1
- package/skills/project-handoff/SKILL.md +39 -12
- package/skills/project-handoff/references/handoff-template.md +24 -17
- package/skills/quality-gate/SKILL.md +6 -12
- package/skills/revisar/SKILL.md +6 -16
- package/skills/{desarrollo-con-pruebas/LICENSE.txt → revisar-skill/LICENSE-find-skills.txt} +5 -5
- package/skills/revisar-skill/SKILL.md +94 -0
- package/skills/revision-cruzada/SKILL.md +53 -475
- package/skills/web-design-guidelines/AGENTS.md +156 -0
- package/skills/{spec-driven-development → web-design-guidelines}/LICENSE.txt +1 -6
- package/skills/web-design-guidelines/SKILL.md +20 -30
- package/src/agent-models.js +53 -13
- package/src/brand.js +117 -0
- package/src/cli-executable.js +5 -3
- package/src/cli.js +16 -9
- package/src/connect.js +56 -4
- package/src/init.js +9 -1
- package/src/install-lock.js +127 -0
- package/src/install-registry.js +61 -0
- package/src/legacy-repair.js +71 -0
- package/src/link-states.js +172 -0
- package/src/memory-locator.js +118 -0
- package/src/missing-dependencies.js +21 -0
- package/src/model-command.js +147 -0
- package/src/panel-cli.js +182 -0
- package/src/project-root.js +41 -0
- package/src/project-signals.js +339 -0
- package/src/recommend-report.js +77 -0
- package/src/review-instructions.js +211 -0
- package/src/review-policy.js +35 -0
- package/src/revision-adapters.js +157 -0
- package/src/revision-context.js +86 -0
- package/src/revision-isolation.js +222 -0
- package/src/revision-process.js +103 -0
- package/src/revision-provenance.js +89 -0
- package/src/revision-snapshot.js +120 -0
- package/src/revision.js +242 -0
- package/src/setup-apply.js +231 -0
- package/src/setup-plan.js +231 -0
- package/src/setup-state.js +277 -0
- package/src/setup-tui.js +555 -0
- package/src/skill-catalog-add.js +91 -0
- package/src/skill-catalog-audit.js +26 -27
- package/src/skill-files.js +39 -0
- package/src/skill-recommendations.js +321 -208
- package/src/skill-scan-report.js +14 -0
- package/src/skill-scan.js +178 -0
- package/src/stable-command.js +53 -0
- package/src/stable-install.js +222 -0
- package/src/superpowers.js +133 -0
- package/src/sync-apply.js +8 -5
- package/src/sync-files.js +2 -1
- package/src/terminal-text.js +68 -0
- package/src/toolkit-cli.js +397 -70
- package/src/toolkit-commands-guide.js +53 -39
- package/src/toolkit-setup.js +507 -110
- package/assets/brand/agent-toolkit-mark-v1.png +0 -0
- package/skills/depurar/SKILL.md +0 -69
- package/skills/depurar/agents/openai.yaml +0 -7
- package/skills/depurar/references/tecnicas-de-diagnostico.md +0 -37
- package/skills/desarrollo-con-pruebas/SKILL.md +0 -55
- package/skills/desarrollo-con-pruebas/agents/openai.yaml +0 -7
- package/skills/desarrollo-con-pruebas/references/buenas-pruebas.md +0 -33
- package/skills/estado/SKILL.md +0 -41
- package/skills/programar/SKILL.md +0 -19
- package/skills/spec-driven-development/SKILL.md +0 -48
- package/skills/spec-driven-development/agents/openai.yaml +0 -7
- package/skills/spec-driven-development/references/spec-template.md +0 -55
- package/skills/spec-driven-development/references/workflow.md +0 -70
- package/src/toolkit-tui.js +0 -961
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
1
|
import fs from 'node:fs/promises';
|
|
3
2
|
import path from 'node:path';
|
|
4
3
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
5
4
|
import { loadSkillCatalog, resolveCatalogResource, SkillCatalogError } from './skill-catalog.js';
|
|
5
|
+
import { hashSkillFiles, readSkillFiles } from './skill-files.js';
|
|
6
|
+
import { scanSkillFiles } from './skill-scan.js';
|
|
7
|
+
|
|
8
|
+
// Re-exported so existing callers keep their import path.
|
|
9
|
+
export { hashSkillFiles, readSkillFiles };
|
|
6
10
|
|
|
7
11
|
export const DEFAULT_CATALOG_ROOT = fileURLToPath(new URL('../', import.meta.url));
|
|
8
12
|
|
|
@@ -12,31 +16,7 @@ export async function inventoryCatalogSkill({ toolkitRoot = DEFAULT_CATALOG_ROOT
|
|
|
12
16
|
throw new SkillCatalogError('La carpeta debe ser skills/<nombre> o catalogs/skills/<nombre>.');
|
|
13
17
|
}
|
|
14
18
|
const root = await resolveCatalogResource(toolkitRoot, installedPath);
|
|
15
|
-
|
|
16
|
-
const files = [];
|
|
17
|
-
let totalBytes = 0;
|
|
18
|
-
async function visit(dir, prefix = '') {
|
|
19
|
-
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
20
|
-
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
21
|
-
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
22
|
-
if (rel.length > 200 || rel.split('/').length > 32) throw new SkillCatalogError(`Ruta demasiado larga en ${installedPath}.`);
|
|
23
|
-
const full = path.join(dir, entry.name);
|
|
24
|
-
const stat = await fs.lstat(full);
|
|
25
|
-
if (stat.isSymbolicLink()) throw new SkillCatalogError(`Enlace no admitido en ${installedPath}/${rel}.`);
|
|
26
|
-
if (stat.isDirectory()) await visit(full, rel);
|
|
27
|
-
else if (stat.isFile()) {
|
|
28
|
-
if (files.length >= 256 || stat.size > 512 * 1024 || totalBytes + stat.size > 64 * 1024 * 1024) {
|
|
29
|
-
throw new SkillCatalogError(`Límite de archivos o tamaño excedido en ${installedPath}.`);
|
|
30
|
-
}
|
|
31
|
-
const bytes = await fs.readFile(full);
|
|
32
|
-
totalBytes += bytes.length;
|
|
33
|
-
files.push({ path: rel, sha256: createHash('sha256').update(bytes).digest('hex') });
|
|
34
|
-
} else throw new SkillCatalogError(`Archivo no regular en ${installedPath}/${rel}.`);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
await visit(root);
|
|
38
|
-
if (!files.some(file => file.path === 'SKILL.md')) throw new SkillCatalogError(`Falta SKILL.md en ${installedPath}.`);
|
|
39
|
-
return files.sort((a, b) => a.path.localeCompare(b.path));
|
|
19
|
+
return hashSkillFiles(await readSkillFiles(root, installedPath));
|
|
40
20
|
}
|
|
41
21
|
|
|
42
22
|
export async function auditSkillCatalog({ toolkitRoot = DEFAULT_CATALOG_ROOT } = {}) {
|
|
@@ -52,18 +32,37 @@ export async function auditSkillCatalog({ toolkitRoot = DEFAULT_CATALOG_ROOT } =
|
|
|
52
32
|
report.skillsCount = catalog.skills.length;
|
|
53
33
|
for (const skill of catalog.skills) {
|
|
54
34
|
try {
|
|
55
|
-
|
|
35
|
+
if (!/^(?:skills|catalogs\/skills)\/[a-z0-9-]+$/u.test(skill.installedPath ?? '')) {
|
|
36
|
+
throw new SkillCatalogError('La carpeta debe ser skills/<nombre> o catalogs/skills/<nombre>.');
|
|
37
|
+
}
|
|
38
|
+
const bytes = await readSkillFiles(await resolveCatalogResource(toolkitRoot, skill.installedPath), skill.installedPath);
|
|
39
|
+
const actual = hashSkillFiles(bytes);
|
|
56
40
|
const expected = [...skill.files].sort((a, b) => a.path.localeCompare(b.path));
|
|
57
41
|
if (actual.length !== expected.length || actual.some((file, index) => file.path !== expected[index].path || file.sha256 !== expected[index].sha256)) {
|
|
58
42
|
throw new SkillCatalogError(`${skill.name}: inventario o hashes SHA-256 distintos del manifiesto.`);
|
|
59
43
|
}
|
|
60
44
|
report.verifiedSkillsCount++;
|
|
45
|
+
const scan = scanSkillFiles(bytes);
|
|
46
|
+
for (const f of scan.findings) {
|
|
47
|
+
if (f.severity === 'bloqueo') report.errors.push(`${skill.name}: bloqueo del escáner (${f.rule} en ${f.file}:${f.line}).`);
|
|
48
|
+
else if (f.severity === 'aviso') report.warnings.push(`${skill.name}: aviso del escáner (${f.rule} en ${f.file}:${f.line}).`);
|
|
49
|
+
}
|
|
50
|
+
for (const [capability, detected] of Object.entries(scan.capabilities)) {
|
|
51
|
+
if (detected && skill.review.capabilities[capability] === 'none') report.warnings.push(`${skill.name}: capacidad no declarada: ${capability}.`);
|
|
52
|
+
}
|
|
61
53
|
if (!skill.scenarios.some(scenario => scenario.status === 'passed')) report.warnings.push(`${skill.name}: sin escenario funcional pasado; no acredita utilidad.`);
|
|
62
54
|
if (skill.review.status === 'pending' || skill.review.status === 'rejected') report.warnings.push(`${skill.name}: revisión ${skill.review.status}; no puede recomendarse.`);
|
|
63
55
|
} catch (error) {
|
|
64
56
|
report.errors.push(error.message);
|
|
65
57
|
}
|
|
66
58
|
}
|
|
59
|
+
// Anything left in catalogs/skills without an entry (an interrupted registration, a stray copy) would ship in the package.
|
|
60
|
+
const listed = new Set(catalog.skills.map(skill => skill.installedPath));
|
|
61
|
+
const entries = await fs.readdir(path.join(toolkitRoot, 'catalogs/skills'), { withFileTypes: true }).catch(() => []);
|
|
62
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
63
|
+
const rel = `catalogs/skills/${entry.name}`;
|
|
64
|
+
if (!listed.has(rel)) report.warnings.push(`Carpeta sin entrada en el catálogo: ${rel}.`);
|
|
65
|
+
}
|
|
67
66
|
report.ok = report.errors.length === 0;
|
|
68
67
|
return report;
|
|
69
68
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { SkillCatalogError } from './skill-catalog.js';
|
|
5
|
+
|
|
6
|
+
export function hashSkillFiles(files) {
|
|
7
|
+
return files
|
|
8
|
+
.map(file => ({ path: file.path, sha256: createHash('sha256').update(file.bytes).digest('hex') }))
|
|
9
|
+
.sort((a, b) => a.path.localeCompare(b.path));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// The one safe walk of a skill folder: no links, only regular files, bounded size. Never executes anything.
|
|
13
|
+
export async function readSkillFiles(root, label) {
|
|
14
|
+
if (!(await fs.lstat(root)).isDirectory()) throw new SkillCatalogError(`No es una carpeta de skill: ${label}.`);
|
|
15
|
+
const files = [];
|
|
16
|
+
let totalBytes = 0;
|
|
17
|
+
async function visit(dir, prefix = '') {
|
|
18
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
19
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
20
|
+
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
21
|
+
if (rel.length > 200 || rel.split('/').length > 32) throw new SkillCatalogError(`Ruta demasiado larga en ${label}.`);
|
|
22
|
+
const full = path.join(dir, entry.name);
|
|
23
|
+
const stat = await fs.lstat(full);
|
|
24
|
+
if (stat.isSymbolicLink()) throw new SkillCatalogError(`Enlace no admitido en ${label}/${rel}.`);
|
|
25
|
+
if (stat.isDirectory()) await visit(full, rel);
|
|
26
|
+
else if (stat.isFile()) {
|
|
27
|
+
if (files.length >= 256 || stat.size > 512 * 1024 || totalBytes + stat.size > 64 * 1024 * 1024) {
|
|
28
|
+
throw new SkillCatalogError(`Límite de archivos o tamaño excedido en ${label}.`);
|
|
29
|
+
}
|
|
30
|
+
const bytes = await fs.readFile(full);
|
|
31
|
+
totalBytes += bytes.length;
|
|
32
|
+
files.push({ path: rel, bytes });
|
|
33
|
+
} else throw new SkillCatalogError(`Archivo no regular en ${label}/${rel}.`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
await visit(root);
|
|
37
|
+
if (!files.some(file => file.path === 'SKILL.md')) throw new SkillCatalogError(`Falta SKILL.md en ${label}.`);
|
|
38
|
+
return files;
|
|
39
|
+
}
|