sabia 1.1.1 → 1.1.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/sabia.mjs +20 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sabia",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "Instala, atualiza e desinstala o Sabiá e os toolkits dele no Kiro, no Claude Code e no opencode, com um inventário em ~/.sabia/.",
5
5
  "type": "module",
6
6
  "bin": {
package/sabia.mjs CHANGED
@@ -427,7 +427,10 @@ export function validarToolkit(pasta, nomeEsperado = null) {
427
427
  let m;
428
428
  try { m = JSON.parse(fs.readFileSync(f, "utf8")); } catch (e) { return [`toolkit.json inválido: ${e.message}`]; }
429
429
  const erros = [];
430
- for (const c of ["nome", "versao", "descricao", "tipos-de-projeto", "skills"]) if (!(c in m)) erros.push(`falta o campo ${c}`);
430
+ for (const c of ["nome", "versao", "descricao", "autor", "tipos-de-projeto", "skills"]) if (!(c in m)) erros.push(`falta o campo ${c}`);
431
+ // quem fez responde pelo toolkit: nome e área aparecem no listar e na setup
432
+ if (m.autor && (typeof m.autor.nome !== "string" || typeof m.autor.area !== "string" || !m.autor.nome.trim() || !m.autor.area.trim()))
433
+ erros.push("autor: precisa de nome e area");
431
434
  const nomeOk = (n) => typeof n === "string" && /^[a-z0-9][a-z0-9-]*$/.test(n) && !n.startsWith("sa-");
432
435
  if (!nomeOk(m.nome)) erros.push(`nome inválido (kebab-case, sem sa-): ${m.nome}`);
433
436
  if (nomeEsperado && m.nome !== nomeEsperado) erros.push(`nome ${m.nome} ≠ ${nomeEsperado} do índice`);
@@ -463,7 +466,7 @@ export function lerIndice(fonte) {
463
466
  const m = JSON.parse(fs.readFileSync(path.join(raiz, ".claude-plugin", "marketplace.json"), "utf8"));
464
467
  return (m.plugins || []).map((p) => {
465
468
  const s = p.source;
466
- const base = { nome: p.name, versao: p.version, descricao: p.description, indice: fonte };
469
+ const base = { nome: p.name, versao: p.version, descricao: p.description, customiza: p.customiza, autor: p.autor, indice: fonte };
467
470
  if (typeof s === "string") return { ...base, url: local ? path.resolve(raiz, dentro(s.replace(/^\.\//, ""), fonte)) : fonte, subpasta: local ? null : s.replace(/^\.\//, "") };
468
471
  if (s?.source === "github") return { ...base, url: `https://github.com/${s.repo}.git`, ref: s.ref, sha: s.sha };
469
472
  if (s?.source === "url") return { ...base, url: s.url, ref: s.ref, sha: s.sha };
@@ -581,28 +584,36 @@ export function listarToolkits(opcoes = {}) {
581
584
  const inv = lerInventario();
582
585
  const projeto = path.join(process.cwd(), ".sabia", "config", "agentes.json");
583
586
  const doProjeto = fs.existsSync(projeto) ? JSON.parse(fs.readFileSync(projeto, "utf8")).toolkits || {} : {};
584
- const linhas = [];
587
+ // autor e customiza: do toolkit baixado (o toolkit.json) ou, sem ele, da entrada do índice
588
+ const manifesto = (n) => { const f = sabia("toolkits", n, "toolkit.json"); return fs.existsSync(f) ? JSON.parse(fs.readFileSync(f, "utf8")) : {}; };
589
+ const linhas = [], doIndice = {};
585
590
  for (const i of opcoes.indice ? [opcoes.indice] : indices()) {
586
591
  try {
587
- for (const e of lerIndice(i)) linhas.push([e.nome, e.versao || "—", inv.toolkits[e.nome]?.versao || "—", doProjeto[e.nome]?.versao || "—", e.descricao || ""]);
592
+ for (const e of lerIndice(i)) {
593
+ doIndice[e.nome] = e;
594
+ linhas.push([e.nome, "", e.versao || "—", inv.toolkits[e.nome]?.versao || "—", doProjeto[e.nome]?.versao || "—", e.descricao || ""]);
595
+ }
588
596
  } catch (err) {
589
597
  ctx.log(`índice ${i}: ${err.message}`);
590
598
  }
591
599
  }
592
- for (const [n, t] of Object.entries(inv.toolkits)) if (!linhas.some((l) => l[0] === n)) linhas.push([n, "—", t.versao, doProjeto[n]?.versao || "—", "(fora dos índices)"]);
600
+ for (const [n, t] of Object.entries(inv.toolkits)) if (!linhas.some((l) => l[0] === n)) linhas.push([n, "", "—", t.versao, doProjeto[n]?.versao || "—", "(fora dos índices)"]);
601
+ for (const l of linhas) {
602
+ const a = manifesto(l[0]).autor ?? doIndice[l[0]]?.autor;
603
+ l[1] = a ? `${a.nome} (${a.area})` : "—";
604
+ }
593
605
  // a customização logo abaixo da base, com ↳ e de onde veio
594
606
  for (const l of [...linhas]) {
595
- const f = sabia("toolkits", l[0], "toolkit.json");
596
- const c = fs.existsSync(f) ? JSON.parse(fs.readFileSync(f, "utf8")).customiza : null;
607
+ const c = manifesto(l[0]).customiza ?? doIndice[l[0]]?.customiza;
597
608
  if (!c) continue;
598
609
  linhas.splice(linhas.indexOf(l), 1);
599
610
  const i = linhas.findIndex((b) => b[0] === c.toolkit);
600
611
  // só a primeira mudança (quem customizou), como resumo; a lista inteira está no toolkit.json
601
- l[4] = `customiza ${c.toolkit} ${c.versao}${c.mudancas?.length ? `: ${c.mudancas[0]}${c.mudancas.length > 1 ? "; …" : ""}` : ""}${l[4] ? " — " + l[4] : ""}`;
612
+ l[5] = `customiza ${c.toolkit} ${c.versao}${c.mudancas?.length ? `: ${c.mudancas[0]}${c.mudancas.length > 1 ? "; …" : ""}` : ""}${l[5] ? " — " + l[5] : ""}`;
602
613
  l[0] = `↳ ${l[0]}`;
603
614
  linhas.splice(i < 0 ? linhas.length : i + 1, 0, l);
604
615
  }
605
- ctx.log(["| Toolkit | No índice | Instalado | Neste projeto | Descrição |", "|---|---|---|---|---|",
616
+ ctx.log(["| Toolkit | Autor | No índice | Instalado | Neste projeto | Descrição |", "|---|---|---|---|---|---|",
606
617
  ...linhas.map((l) => `| ${l.join(" | ")} |`)].join("\n"));
607
618
  return linhas;
608
619
  }