expxdev 0.1.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.
Files changed (156) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +137 -0
  3. package/dist/cli/argumentos.d.ts +22 -0
  4. package/dist/cli/argumentos.js +85 -0
  5. package/dist/cli/argumentos.js.map +1 -0
  6. package/dist/cli/expx-bin.d.ts +2 -0
  7. package/dist/cli/expx-bin.js +40 -0
  8. package/dist/cli/expx-bin.js.map +1 -0
  9. package/dist/cli/expx.d.ts +13 -0
  10. package/dist/cli/expx.js +123 -0
  11. package/dist/cli/expx.js.map +1 -0
  12. package/dist/cli/init-flags.d.ts +34 -0
  13. package/dist/cli/init-flags.js +83 -0
  14. package/dist/cli/init-flags.js.map +1 -0
  15. package/dist/cli/init.d.ts +34 -0
  16. package/dist/cli/init.js +103 -0
  17. package/dist/cli/init.js.map +1 -0
  18. package/dist/cli/principal.d.ts +2 -0
  19. package/dist/cli/principal.js +107 -0
  20. package/dist/cli/principal.js.map +1 -0
  21. package/dist/cli/projeto.d.ts +19 -0
  22. package/dist/cli/projeto.js +28 -0
  23. package/dist/cli/projeto.js.map +1 -0
  24. package/dist/cli/selecao.d.ts +19 -0
  25. package/dist/cli/selecao.js +28 -0
  26. package/dist/cli/selecao.js.map +1 -0
  27. package/dist/cli/selecionar.d.ts +23 -0
  28. package/dist/cli/selecionar.js +45 -0
  29. package/dist/cli/selecionar.js.map +1 -0
  30. package/dist/cli/subcomandos.d.ts +25 -0
  31. package/dist/cli/subcomandos.js +43 -0
  32. package/dist/cli/subcomandos.js.map +1 -0
  33. package/dist/doctor/efeito.d.ts +17 -0
  34. package/dist/doctor/efeito.js +18 -0
  35. package/dist/doctor/efeito.js.map +1 -0
  36. package/dist/doctor/verificadores.d.ts +21 -0
  37. package/dist/doctor/verificadores.js +158 -0
  38. package/dist/doctor/verificadores.js.map +1 -0
  39. package/dist/harness/backup.d.ts +15 -0
  40. package/dist/harness/backup.js +29 -0
  41. package/dist/harness/backup.js.map +1 -0
  42. package/dist/harness/instalar.d.ts +31 -0
  43. package/dist/harness/instalar.js +43 -0
  44. package/dist/harness/instalar.js.map +1 -0
  45. package/dist/harness/opencode.d.ts +18 -0
  46. package/dist/harness/opencode.js +32 -0
  47. package/dist/harness/opencode.js.map +1 -0
  48. package/dist/harness/settings.d.ts +36 -0
  49. package/dist/harness/settings.js +66 -0
  50. package/dist/harness/settings.js.map +1 -0
  51. package/dist/index.d.ts +8 -0
  52. package/dist/index.js +9 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/nucleo/busca.d.ts +28 -0
  55. package/dist/nucleo/busca.js +29 -0
  56. package/dist/nucleo/busca.js.map +1 -0
  57. package/dist/nucleo/caminhos.d.ts +21 -0
  58. package/dist/nucleo/caminhos.js +64 -0
  59. package/dist/nucleo/caminhos.js.map +1 -0
  60. package/dist/nucleo/catalogo.d.ts +20 -0
  61. package/dist/nucleo/catalogo.js +50 -0
  62. package/dist/nucleo/catalogo.js.map +1 -0
  63. package/dist/nucleo/integridade.d.ts +20 -0
  64. package/dist/nucleo/integridade.js +75 -0
  65. package/dist/nucleo/integridade.js.map +1 -0
  66. package/dist/nucleo/layout.d.ts +23 -0
  67. package/dist/nucleo/layout.js +73 -0
  68. package/dist/nucleo/layout.js.map +1 -0
  69. package/dist/nucleo/lock.d.ts +61 -0
  70. package/dist/nucleo/lock.js +96 -0
  71. package/dist/nucleo/lock.js.map +1 -0
  72. package/dist/nucleo/versao.d.ts +35 -0
  73. package/dist/nucleo/versao.js +63 -0
  74. package/dist/nucleo/versao.js.map +1 -0
  75. package/dist/parser/conformidade/regras.d.ts +41 -0
  76. package/dist/parser/conformidade/regras.js +209 -0
  77. package/dist/parser/conformidade/regras.js.map +1 -0
  78. package/dist/parser/descoberta/trabalhos.d.ts +35 -0
  79. package/dist/parser/descoberta/trabalhos.js +28 -0
  80. package/dist/parser/descoberta/trabalhos.js.map +1 -0
  81. package/dist/parser/descoberta/varredura.d.ts +4 -0
  82. package/dist/parser/descoberta/varredura.js +79 -0
  83. package/dist/parser/descoberta/varredura.js.map +1 -0
  84. package/dist/parser/esquema/cabecalho.d.ts +60 -0
  85. package/dist/parser/esquema/cabecalho.js +57 -0
  86. package/dist/parser/esquema/cabecalho.js.map +1 -0
  87. package/dist/parser/esquema/enums.d.ts +114 -0
  88. package/dist/parser/esquema/enums.js +64 -0
  89. package/dist/parser/esquema/enums.js.map +1 -0
  90. package/dist/parser/esquema/kinds.d.ts +768 -0
  91. package/dist/parser/esquema/kinds.js +231 -0
  92. package/dist/parser/esquema/kinds.js.map +1 -0
  93. package/dist/parser/leitura/arquivo.d.ts +9 -0
  94. package/dist/parser/leitura/arquivo.js +25 -0
  95. package/dist/parser/leitura/arquivo.js.map +1 -0
  96. package/dist/parser/leitura/frontmatter.d.ts +31 -0
  97. package/dist/parser/leitura/frontmatter.js +77 -0
  98. package/dist/parser/leitura/frontmatter.js.map +1 -0
  99. package/dist/parser/leitura/rejeicao.d.ts +43 -0
  100. package/dist/parser/leitura/rejeicao.js +73 -0
  101. package/dist/parser/leitura/rejeicao.js.map +1 -0
  102. package/dist/parser/projeto/montar.d.ts +62 -0
  103. package/dist/parser/projeto/montar.js +149 -0
  104. package/dist/parser/projeto/montar.js.map +1 -0
  105. package/dist/plugin/atomico.d.ts +13 -0
  106. package/dist/plugin/atomico.js +42 -0
  107. package/dist/plugin/atomico.js.map +1 -0
  108. package/dist/plugin/manifestos.d.ts +38 -0
  109. package/dist/plugin/manifestos.js +57 -0
  110. package/dist/plugin/manifestos.js.map +1 -0
  111. package/dist/plugin/montagem.d.ts +22 -0
  112. package/dist/plugin/montagem.js +30 -0
  113. package/dist/plugin/montagem.js.map +1 -0
  114. package/dist/relatorio/markdown.d.ts +22 -0
  115. package/dist/relatorio/markdown.js +0 -0
  116. package/dist/relatorio/markdown.js.map +1 -0
  117. package/dist/relatorio/pagina.d.ts +22 -0
  118. package/dist/relatorio/pagina.js +338 -0
  119. package/dist/relatorio/pagina.js.map +1 -0
  120. package/dist/servidor/estado.d.ts +16 -0
  121. package/dist/servidor/estado.js +16 -0
  122. package/dist/servidor/estado.js.map +1 -0
  123. package/dist/servidor/http.d.ts +22 -0
  124. package/dist/servidor/http.js +164 -0
  125. package/dist/servidor/http.js.map +1 -0
  126. package/dist/servidor/observador.d.ts +12 -0
  127. package/dist/servidor/observador.js +37 -0
  128. package/dist/servidor/observador.js.map +1 -0
  129. package/dist/servidor/painel.d.ts +24 -0
  130. package/dist/servidor/painel.js +49 -0
  131. package/dist/servidor/painel.js.map +1 -0
  132. package/dist/teste/layouts-fixture.d.ts +16 -0
  133. package/dist/teste/layouts-fixture.js +9 -0
  134. package/dist/teste/layouts-fixture.js.map +1 -0
  135. package/dist/teste/projeto-temporario.d.ts +14 -0
  136. package/dist/teste/projeto-temporario.js +16 -0
  137. package/dist/teste/projeto-temporario.js.map +1 -0
  138. package/dist/teste/repo-fixture.d.ts +27 -0
  139. package/dist/teste/repo-fixture.js +49 -0
  140. package/dist/teste/repo-fixture.js.map +1 -0
  141. package/dist/update/comparar.d.ts +33 -0
  142. package/dist/update/comparar.js +67 -0
  143. package/dist/update/comparar.js.map +1 -0
  144. package/dist/update/compatibilidade.d.ts +19 -0
  145. package/dist/update/compatibilidade.js +36 -0
  146. package/dist/update/compatibilidade.js.map +1 -0
  147. package/dist/update/flags.d.ts +47 -0
  148. package/dist/update/flags.js +123 -0
  149. package/dist/update/flags.js.map +1 -0
  150. package/dist/update/modificacao.d.ts +23 -0
  151. package/dist/update/modificacao.js +36 -0
  152. package/dist/update/modificacao.js.map +1 -0
  153. package/package.json +73 -0
  154. package/ui/dist/assets/index-BmDTogQZ.js +14 -0
  155. package/ui/dist/assets/index-DUzbt2M7.css +1 -0
  156. package/ui/dist/index.html +13 -0
@@ -0,0 +1,158 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { lerLock } from "../nucleo/lock.js";
4
+ import { verificarCaminhos } from "../nucleo/caminhos.js";
5
+ import { expxNoGitignore } from "../cli/projeto.js";
6
+ import { verificarModificacaoLocal, pastaDaSkill } from "../update/modificacao.js";
7
+ import { MarketplaceJson, PluginJson, ORIGEM_DO_PLUGIN, NOME_DO_PLUGIN, NOME_DO_MARKETPLACE } from "../plugin/manifestos.js";
8
+ import { lerPluginsHabilitados, caminhoDoSettings } from "../harness/settings.js";
9
+ function lerJson(caminho) {
10
+ if (!existsSync(caminho))
11
+ return undefined;
12
+ try {
13
+ return JSON.parse(readFileSync(caminho, "utf8"));
14
+ }
15
+ catch {
16
+ return undefined;
17
+ }
18
+ }
19
+ export function diagnosticar(raiz) {
20
+ const achados = [];
21
+ const push = (a) => {
22
+ achados.push(a);
23
+ };
24
+ // O .gitignore é verificado ANTES de exigir o .expx/: um .gitignore que
25
+ // ignora .expx/ é justamente a causa provável de ele não estar ali para quem
26
+ // clonou o repositório. Sair cedo esconderia o defeito que explica o resto.
27
+ if (expxNoGitignore(raiz)) {
28
+ push({
29
+ id: "gitignore-ignora-expx",
30
+ severidade: "erro",
31
+ problema: "o .gitignore esta ignorando .expx/, que precisa ser commitado",
32
+ correcao: "remova a linha .expx do .gitignore: quem clona o projeto depende dela",
33
+ });
34
+ }
35
+ if (!existsSync(join(raiz, ".expx"))) {
36
+ push({
37
+ id: "sem-expx",
38
+ severidade: "erro",
39
+ problema: "este projeto nao tem .expx/",
40
+ correcao: "rode `expx init` para instalar as skills neste projeto",
41
+ });
42
+ return { saudavel: false, achados };
43
+ }
44
+ const l = lerLock(raiz);
45
+ if (!l.ok) {
46
+ push({
47
+ id: "lock-ilegivel",
48
+ severidade: "erro",
49
+ problema: `o lock nao pode ser lido: ${l.erro}`,
50
+ correcao: "rode `expx init` para reconstruir a instalacao",
51
+ });
52
+ return { saudavel: false, achados };
53
+ }
54
+ if (l.incompativel) {
55
+ push({
56
+ id: "lock-futuro",
57
+ severidade: "erro",
58
+ problema: "o .expx/ foi criado por uma versao MAIS NOVA deste CLI",
59
+ correcao: "atualize o CLI (`npx expxdev@latest`) antes de operar este projeto",
60
+ });
61
+ }
62
+ const raizPlugin = join(raiz, ".expx", "marketplace", ORIGEM_DO_PLUGIN);
63
+ const pj = PluginJson.safeParse(lerJson(join(raizPlugin, ".claude-plugin", "plugin.json")));
64
+ if (!pj.success) {
65
+ push({
66
+ id: "plugin-json-invalido",
67
+ severidade: "erro",
68
+ problema: `plugin.json ausente ou fora do formato (esperado name "${NOME_DO_PLUGIN}")`,
69
+ correcao: "rode `expx init` para remontar o plugin",
70
+ });
71
+ }
72
+ const mj = MarketplaceJson.safeParse(lerJson(join(raiz, ".expx", "marketplace", ".claude-plugin", "marketplace.json")));
73
+ if (!mj.success) {
74
+ push({
75
+ id: "marketplace-json-invalido",
76
+ severidade: "erro",
77
+ problema: `marketplace.json ausente ou fora do formato (esperado name "${NOME_DO_MARKETPLACE}")`,
78
+ correcao: "rode `expx init` para remontar o marketplace",
79
+ });
80
+ }
81
+ for (const [nome, travada] of Object.entries(l.lock.skills)) {
82
+ const pasta = pastaDaSkill(raiz, nome);
83
+ if (!existsSync(pasta)) {
84
+ push({
85
+ id: "skill-ausente",
86
+ severidade: "erro",
87
+ problema: `${nome} esta no lock mas nao esta em disco`,
88
+ correcao: `rode \`expx init\` ou \`expx add ${nome}\` para reinstalar`,
89
+ });
90
+ continue;
91
+ }
92
+ const fora = verificarCaminhos(pasta);
93
+ if (fora.length > 0) {
94
+ const amostra = fora.slice(0, 3).map((a) => `${a.arquivo}:${String(a.linha)} → ${a.referencia}`);
95
+ push({
96
+ id: "caminho-fora",
97
+ severidade: "erro",
98
+ problema: `${nome} referencia caminho fora da propria pasta: ${amostra.join("; ")}`,
99
+ correcao: "o plugin e copiado para o cache e esse caminho nao resolve la: reporte no repositorio da skill",
100
+ });
101
+ }
102
+ const mod = verificarModificacaoLocal(raiz, nome);
103
+ if (mod.temModificacao) {
104
+ const lista = [...mod.alterados, ...mod.removidos, ...mod.novos].slice(0, 5);
105
+ push({
106
+ id: "modificacao-local",
107
+ severidade: "aviso",
108
+ problema: `${nome} foi modificada localmente: ${lista.join(", ")}`,
109
+ correcao: "o update nao vai sobrescrever; decida manter, substituir ou salvar ao lado",
110
+ });
111
+ }
112
+ if (!travada.travado) {
113
+ push({
114
+ id: "skill-nao-travada",
115
+ severidade: "aviso",
116
+ problema: `${nome} nao esta travada em versao publicada (segue a branch ${travada.referencia})`,
117
+ correcao: "quando o repositorio publicar uma tag, rode `expx update` para travar",
118
+ });
119
+ }
120
+ }
121
+ if (l.lock.harness.includes("claude")) {
122
+ const s = lerJson(caminhoDoSettings(raiz));
123
+ if (s === undefined) {
124
+ push({
125
+ id: "settings-ausente",
126
+ severidade: "erro",
127
+ problema: ".claude/settings.json ausente ou invalido",
128
+ correcao: "rode `expx init` para reescrever a configuracao do harness",
129
+ });
130
+ }
131
+ else {
132
+ const d = s;
133
+ const habilitados = lerPluginsHabilitados(d["enabledPlugins"]);
134
+ if (habilitados[`${NOME_DO_PLUGIN}@${NOME_DO_MARKETPLACE}`] !== true) {
135
+ push({
136
+ id: "plugin-nao-habilitado",
137
+ severidade: "erro",
138
+ problema: "o plugin expx nao esta habilitado no .claude/settings.json",
139
+ correcao: "rode `expx init` para habilitar",
140
+ });
141
+ }
142
+ }
143
+ }
144
+ const skillsClaude = join(raiz, ".claude", "skills");
145
+ const skillsOpen = join(raiz, ".opencode", "skills");
146
+ for (const nome of Object.keys(l.lock.skills)) {
147
+ if (existsSync(join(skillsClaude, nome)) && existsSync(join(skillsOpen, nome))) {
148
+ push({
149
+ id: "colisao-de-nome",
150
+ severidade: "erro",
151
+ problema: `${nome} existe em .claude/skills e em .opencode/skills, e o OpenCode le os dois`,
152
+ correcao: "remova a copia em .opencode/skills: o OpenCode le .claude/skills nativamente",
153
+ });
154
+ }
155
+ }
156
+ return { saudavel: achados.filter((a) => a.severidade === "erro").length === 0, achados };
157
+ }
158
+ //# sourceMappingURL=verificadores.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verificadores.js","sourceRoot":"","sources":["../../src/doctor/verificadores.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Cópia datada antes de qualquer escrita em arquivo do usuário.
3
+ *
4
+ * O `settings.json` costuma conter configuração que a pessoa ajustou à mão e
5
+ * que não está em lugar nenhum além dali. Um merge com defeito sem backup é
6
+ * perda de trabalho irrecuperável.
7
+ */
8
+ /** `AAAA-MM-DD` do relógio do sistema. */
9
+ export declare function hojeISO(agora?: Date): string;
10
+ /**
11
+ * Copia para `<arquivo>.backup-AAAA-MM-DD[-N]`. Devolve o caminho criado.
12
+ * Se já existe backup do dia, cria um sufixo numérico em vez de sobrescrever —
13
+ * o backup de ontem não pode ser destruído pelo erro de hoje.
14
+ */
15
+ export declare function fazerBackup(arquivo: string, agora?: Date): string;
@@ -0,0 +1,29 @@
1
+ import { copyFileSync, existsSync } from "node:fs";
2
+ /**
3
+ * Cópia datada antes de qualquer escrita em arquivo do usuário.
4
+ *
5
+ * O `settings.json` costuma conter configuração que a pessoa ajustou à mão e
6
+ * que não está em lugar nenhum além dali. Um merge com defeito sem backup é
7
+ * perda de trabalho irrecuperável.
8
+ */
9
+ /** `AAAA-MM-DD` do relógio do sistema. */
10
+ export function hojeISO(agora = new Date()) {
11
+ return agora.toISOString().slice(0, 10);
12
+ }
13
+ /**
14
+ * Copia para `<arquivo>.backup-AAAA-MM-DD[-N]`. Devolve o caminho criado.
15
+ * Se já existe backup do dia, cria um sufixo numérico em vez de sobrescrever —
16
+ * o backup de ontem não pode ser destruído pelo erro de hoje.
17
+ */
18
+ export function fazerBackup(arquivo, agora = new Date()) {
19
+ const base = `${arquivo}.backup-${hojeISO(agora)}`;
20
+ let destino = base;
21
+ let n = 1;
22
+ while (existsSync(destino)) {
23
+ destino = `${base}-${String(n)}`;
24
+ n += 1;
25
+ }
26
+ copyFileSync(arquivo, destino);
27
+ return destino;
28
+ }
29
+ //# sourceMappingURL=backup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup.js","sourceRoot":"","sources":["../../src/harness/backup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAEnD;;;;;;GAMG;AAEH,0CAA0C;AAC1C,MAAM,UAAU,OAAO,CAAC,QAAc,IAAI,IAAI,EAAE;IAC9C,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC1C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,QAAc,IAAI,IAAI,EAAE;IACnE,MAAM,IAAI,GAAG,GAAG,OAAO,WAAW,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;IACnD,IAAI,OAAO,GAAG,IAAI,CAAC;IACnB,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,OAAO,GAAG,GAAG,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACjC,CAAC,IAAI,CAAC,CAAC;IACT,CAAC;IACD,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC/B,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * A instalação de fato do plugin no Claude Code.
3
+ *
4
+ * Declarar o marketplace em `.claude/settings.json` NÃO instala nada: cinco
5
+ * sintaxes foram testadas em execução e nenhuma carregou o plugin (o
6
+ * experimento está em `docs/expx-cli/base/09-validacao-marketplace-local.md`).
7
+ * Quem instala é o próprio `claude`, por estes dois comandos.
8
+ *
9
+ * Consequência para quem clona o repositório: como o `marketplace add` grava um
10
+ * caminho ABSOLUTO no settings do usuário, esse passo não viaja no commit —
11
+ * cada pessoa roda o `expx init` na própria máquina.
12
+ */
13
+ export type ComandoExterno = {
14
+ bin: string;
15
+ args: string[];
16
+ };
17
+ export declare function comandosDeInstalacao(caminhoMarketplace: string): ComandoExterno[];
18
+ /** O que dizer quando o binário `claude` não está disponível. */
19
+ export declare function instrucaoManual(caminhoMarketplace: string): string;
20
+ export type ResultadoInstalacao = {
21
+ ok: boolean;
22
+ executados: string[];
23
+ /** Quando o binário falta, o texto que o usuário precisa ver. */
24
+ instrucao?: string;
25
+ erro?: string;
26
+ };
27
+ /**
28
+ * Roda os dois comandos. Binário ausente NÃO é falha fatal: o `.expx/` já está
29
+ * montado e a instalação pode ser concluída à mão.
30
+ */
31
+ export declare function instalarPlugin(caminhoMarketplace: string): Promise<ResultadoInstalacao>;
@@ -0,0 +1,43 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ const exec = promisify(execFile);
4
+ export function comandosDeInstalacao(caminhoMarketplace) {
5
+ return [
6
+ { bin: "claude", args: ["plugin", "marketplace", "add", caminhoMarketplace] },
7
+ { bin: "claude", args: ["plugin", "install", "expx@expx-local"] },
8
+ ];
9
+ }
10
+ /** O que dizer quando o binário `claude` não está disponível. */
11
+ export function instrucaoManual(caminhoMarketplace) {
12
+ return [
13
+ "O binario `claude` nao foi encontrado no PATH.",
14
+ "O .expx/ foi montado e esta pronto; falta apenas registrar o plugin:",
15
+ "",
16
+ ` claude plugin marketplace add ${caminhoMarketplace}`,
17
+ " claude plugin install expx@expx-local",
18
+ "",
19
+ "Rode os dois comandos acima e o namespace /expx: ficara disponivel.",
20
+ ].join("\n");
21
+ }
22
+ /**
23
+ * Roda os dois comandos. Binário ausente NÃO é falha fatal: o `.expx/` já está
24
+ * montado e a instalação pode ser concluída à mão.
25
+ */
26
+ export async function instalarPlugin(caminhoMarketplace) {
27
+ const executados = [];
28
+ for (const c of comandosDeInstalacao(caminhoMarketplace)) {
29
+ try {
30
+ await exec(c.bin, c.args);
31
+ executados.push(`${c.bin} ${c.args.join(" ")}`);
32
+ }
33
+ catch (e) {
34
+ const msg = String(e);
35
+ if (msg.includes("ENOENT")) {
36
+ return { ok: false, executados, instrucao: instrucaoManual(caminhoMarketplace) };
37
+ }
38
+ return { ok: false, executados, erro: msg };
39
+ }
40
+ }
41
+ return { ok: true, executados };
42
+ }
43
+ //# sourceMappingURL=instalar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instalar.js","sourceRoot":"","sources":["../../src/harness/instalar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAiBjC,MAAM,UAAU,oBAAoB,CAAC,kBAA0B;IAC7D,OAAO;QACL,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,kBAAkB,CAAC,EAAE;QAC7E,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,iBAAiB,CAAC,EAAE;KAClE,CAAC;AACJ,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,eAAe,CAAC,kBAA0B;IACxD,OAAO;QACL,gDAAgD;QAChD,sEAAsE;QACtE,EAAE;QACF,mCAAmC,kBAAkB,EAAE;QACvD,yCAAyC;QACzC,EAAE;QACF,qEAAqE;KACtE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAUD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,kBAA0B;IAC7D,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,KAAK,MAAM,CAAC,IAAI,oBAAoB,CAAC,kBAAkB,CAAC,EAAE,CAAC;QACzD,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;YAC1B,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAClD,CAAC;QAAC,OAAO,CAAU,EAAE,CAAC;YACpB,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACtB,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC3B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,eAAe,CAAC,kBAAkB,CAAC,EAAE,CAAC;YACnF,CAAC;YACD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;QAC9C,CAAC;IACH,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AAClC,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { SkillMontavel } from "../plugin/montagem.js";
2
+ /**
3
+ * Materializa as skills e comandos onde o OpenCode os encontra.
4
+ *
5
+ * O namespace de plugin é do Claude Code; no OpenCode ele não existe. Por isso
6
+ * os comandos vão sem prefixo.
7
+ *
8
+ * As skills vão SOMENTE para `.claude/skills/`, que o OpenCode lê nativamente.
9
+ * Copiar também para `.opencode/skills/` criaria duas cópias do mesmo `name`
10
+ * nas pastas que o OpenCode varre, e a duplicata é resolvida por
11
+ * last-writer-wins com apenas um aviso no log — ou seja, silenciosamente. O
12
+ * `doctor` detecta essa colisão caso ela apareça por outro caminho.
13
+ *
14
+ * `commands/` no plural é a forma documentada. Os repositórios reais divergem
15
+ * entre `.opencode/command/` e `.opencode/commands/`; normalizar aqui evita
16
+ * projetos inconsistentes.
17
+ */
18
+ export declare function materializarOpenCode(raizProjeto: string, skills: readonly SkillMontavel[]): void;
@@ -0,0 +1,32 @@
1
+ import { cpSync, mkdirSync } from "node:fs";
2
+ import { basename, join } from "node:path";
3
+ /**
4
+ * Materializa as skills e comandos onde o OpenCode os encontra.
5
+ *
6
+ * O namespace de plugin é do Claude Code; no OpenCode ele não existe. Por isso
7
+ * os comandos vão sem prefixo.
8
+ *
9
+ * As skills vão SOMENTE para `.claude/skills/`, que o OpenCode lê nativamente.
10
+ * Copiar também para `.opencode/skills/` criaria duas cópias do mesmo `name`
11
+ * nas pastas que o OpenCode varre, e a duplicata é resolvida por
12
+ * last-writer-wins com apenas um aviso no log — ou seja, silenciosamente. O
13
+ * `doctor` detecta essa colisão caso ela apareça por outro caminho.
14
+ *
15
+ * `commands/` no plural é a forma documentada. Os repositórios reais divergem
16
+ * entre `.opencode/command/` e `.opencode/commands/`; normalizar aqui evita
17
+ * projetos inconsistentes.
18
+ */
19
+ export function materializarOpenCode(raizProjeto, skills) {
20
+ const dirSkills = join(raizProjeto, ".claude", "skills");
21
+ const dirComandos = join(raizProjeto, ".opencode", "commands");
22
+ mkdirSync(dirSkills, { recursive: true });
23
+ mkdirSync(dirComandos, { recursive: true });
24
+ for (const s of skills) {
25
+ // O nome da pasta precisa continuar igual ao `name` do frontmatter: o
26
+ // OpenCode exige que os dois batam para descobrir a skill.
27
+ cpSync(s.raizSkill, join(dirSkills, s.nome), { recursive: true });
28
+ for (const c of s.comandos)
29
+ cpSync(c, join(dirComandos, basename(c)));
30
+ }
31
+ }
32
+ //# sourceMappingURL=opencode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode.js","sourceRoot":"","sources":["../../src/harness/opencode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAG3C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,oBAAoB,CAAC,WAAmB,EAAE,MAAgC;IACxF,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IACzD,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;IAC/D,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE5C,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,sEAAsE;QACtE,2DAA2D;QAC3D,MAAM,CAAC,CAAC,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ;YAAE,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC;AACH,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Merge do `.claude/settings.json` — o ponto mais fácil de errar.
3
+ *
4
+ * Regras: faz backup antes de tocar, mescla APENAS as chaves necessárias,
5
+ * preserva todo o resto, e nunca sobrescreve o arquivo inteiro. JSON inválido
6
+ * não é consertado: avisa e sai.
7
+ *
8
+ * Duas formas de `enabledPlugins` circulam: a documentação mostra um array
9
+ * (`["p@m"]`), e o arquivo que o Claude Code realmente escreve usa um objeto
10
+ * (`{"p@m": true}`) — medido em
11
+ * `docs/expx-cli/base/09-validacao-marketplace-local.md`. Ler as duas e
12
+ * escrever a segunda evita corromper o arquivo do usuário.
13
+ *
14
+ * ATENÇÃO: escrever estas chaves NÃO instala o plugin. Foi testado: cinco
15
+ * sintaxes em `settings.json` de projeto e nenhuma carregou. A instalação de
16
+ * fato é feita pelo `claude plugin install` (ver `src/harness/instalar.ts`).
17
+ */
18
+ export type ResultadoMerge = {
19
+ ok: true;
20
+ criado: boolean;
21
+ backup?: string;
22
+ } | {
23
+ ok: false;
24
+ erro: string;
25
+ };
26
+ export type Habilitados = Record<string, boolean>;
27
+ /** Aceita array (documentado) ou objeto (real). Qualquer outra coisa vira `{}`. */
28
+ export declare function lerPluginsHabilitados(valor: unknown): Habilitados;
29
+ export declare function caminhoDoSettings(raizProjeto: string): string;
30
+ /**
31
+ * Acrescenta o marketplace local e habilita o plugin, preservando o resto.
32
+ *
33
+ * `caminhoMarketplace` é absoluto de propósito: é o que o `claude plugin
34
+ * marketplace add` grava, e um caminho relativo não é resolvido aqui.
35
+ */
36
+ export declare function mesclarSettings(raizProjeto: string, caminhoMarketplace: string): ResultadoMerge;
@@ -0,0 +1,66 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { NOME_DO_MARKETPLACE, NOME_DO_PLUGIN } from "../plugin/manifestos.js";
4
+ import { fazerBackup } from "./backup.js";
5
+ /** Aceita array (documentado) ou objeto (real). Qualquer outra coisa vira `{}`. */
6
+ export function lerPluginsHabilitados(valor) {
7
+ if (Array.isArray(valor)) {
8
+ const saida = {};
9
+ for (const item of valor)
10
+ if (typeof item === "string")
11
+ saida[item] = true;
12
+ return saida;
13
+ }
14
+ if (typeof valor === "object" && valor !== null) {
15
+ const saida = {};
16
+ for (const [k, v] of Object.entries(valor)) {
17
+ if (typeof v === "boolean")
18
+ saida[k] = v;
19
+ }
20
+ return saida;
21
+ }
22
+ return {};
23
+ }
24
+ export function caminhoDoSettings(raizProjeto) {
25
+ return join(raizProjeto, ".claude", "settings.json");
26
+ }
27
+ /**
28
+ * Acrescenta o marketplace local e habilita o plugin, preservando o resto.
29
+ *
30
+ * `caminhoMarketplace` é absoluto de propósito: é o que o `claude plugin
31
+ * marketplace add` grava, e um caminho relativo não é resolvido aqui.
32
+ */
33
+ export function mesclarSettings(raizProjeto, caminhoMarketplace) {
34
+ const caminho = caminhoDoSettings(raizProjeto);
35
+ const existe = existsSync(caminho);
36
+ let atual = {};
37
+ let backup;
38
+ if (existe) {
39
+ try {
40
+ atual = JSON.parse(readFileSync(caminho, "utf8"));
41
+ }
42
+ catch (e) {
43
+ return {
44
+ ok: false,
45
+ erro: `${caminho} nao e JSON valido e nao sera alterado: ${String(e)}`,
46
+ };
47
+ }
48
+ if (typeof atual !== "object" || atual === null || Array.isArray(atual)) {
49
+ return { ok: false, erro: `${caminho} nao contem um objeto JSON` };
50
+ }
51
+ backup = fazerBackup(caminho);
52
+ }
53
+ const marketplaces = {
54
+ ...(atual["extraKnownMarketplaces"] ?? {}),
55
+ [NOME_DO_MARKETPLACE]: { source: { source: "directory", path: caminhoMarketplace } },
56
+ };
57
+ const habilitados = {
58
+ ...lerPluginsHabilitados(atual["enabledPlugins"]),
59
+ [`${NOME_DO_PLUGIN}@${NOME_DO_MARKETPLACE}`]: true,
60
+ };
61
+ const novo = { ...atual, extraKnownMarketplaces: marketplaces, enabledPlugins: habilitados };
62
+ mkdirSync(join(caminho, ".."), { recursive: true });
63
+ writeFileSync(caminho, `${JSON.stringify(novo, null, 2)}\n`);
64
+ return backup === undefined ? { ok: true, criado: !existe } : { ok: true, criado: !existe, backup };
65
+ }
66
+ //# sourceMappingURL=settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/harness/settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9E,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AA0B1C,mFAAmF;AACnF,MAAM,UAAU,qBAAqB,CAAC,KAAc;IAClD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,KAAK,GAAgB,EAAE,CAAC;QAC9B,KAAK,MAAM,IAAI,IAAI,KAAK;YAAE,IAAI,OAAO,IAAI,KAAK,QAAQ;gBAAE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QAC3E,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,MAAM,KAAK,GAAgB,EAAE,CAAC;QAC9B,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,EAAE,CAAC;YACtE,IAAI,OAAO,CAAC,KAAK,SAAS;gBAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAC3C,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,WAAmB;IACnD,OAAO,IAAI,CAAC,WAAW,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;AACvD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,WAAmB,EAAE,kBAA0B;IAC7E,MAAM,OAAO,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IAEnC,IAAI,KAAK,GAA4B,EAAE,CAAC;IACxC,IAAI,MAA0B,CAAC;IAE/B,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,CAAC;YACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAA4B,CAAC;QAC/E,CAAC;QAAC,OAAO,CAAU,EAAE,CAAC;YACpB,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,GAAG,OAAO,2CAA2C,MAAM,CAAC,CAAC,CAAC,EAAE;aACvE,CAAC;QACJ,CAAC;QACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACxE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,4BAA4B,EAAE,CAAC;QACrE,CAAC;QACD,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAED,MAAM,YAAY,GAAG;QACnB,GAAG,CAAE,KAAK,CAAC,wBAAwB,CAAyC,IAAI,EAAE,CAAC;QACnF,CAAC,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,kBAAkB,EAAE,EAAE;KACrF,CAAC;IACF,MAAM,WAAW,GAAgB;QAC/B,GAAG,qBAAqB,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;QACjD,CAAC,GAAG,cAAc,IAAI,mBAAmB,EAAE,CAAC,EAAE,IAAI;KACnD,CAAC;IAEF,MAAM,IAAI,GAAG,EAAE,GAAG,KAAK,EAAE,sBAAsB,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC;IAC7F,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,aAAa,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAE7D,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;AACtG,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @expx/painel — ponto de entrada do pacote.
3
+ *
4
+ * O painel é somente leitura: nunca escreve nos arquivos do projeto observado
5
+ * e nunca executa comando algum. Ver docs/expx-panel/ORQUESTRADOR.md.
6
+ */
7
+ /** Versão do contrato expx-schema que este painel sabe ler. */
8
+ export declare const VERSAO_SCHEMA_SUPORTADA = 1;
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @expx/painel — ponto de entrada do pacote.
3
+ *
4
+ * O painel é somente leitura: nunca escreve nos arquivos do projeto observado
5
+ * e nunca executa comando algum. Ver docs/expx-panel/ORQUESTRADOR.md.
6
+ */
7
+ /** Versão do contrato expx-schema que este painel sabe ler. */
8
+ export const VERSAO_SCHEMA_SUPORTADA = 1;
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,+DAA+D;AAC/D,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Traz os arquivos de uma skill para o disco, na referência alvo.
3
+ *
4
+ * Usa o `git` do sistema em vez da API do GitHub: assim a credencial já
5
+ * configurada na máquina atende repositório privado sem que o CLI jamais peça
6
+ * ou armazene token, e não há limite de requisição para esbarrar.
7
+ *
8
+ * Falha de rede ou repositório inacessível NUNCA lança: devolve `ok: false`
9
+ * nomeando a skill, porque uma skill indisponível não pode abortar a
10
+ * instalação das outras quatro.
11
+ */
12
+ export type PedidoBusca = {
13
+ nome: string;
14
+ repositorio: string;
15
+ referencia: string;
16
+ };
17
+ export type ResultadoBusca = {
18
+ ok: true;
19
+ nome: string;
20
+ caminho: string;
21
+ commit: string;
22
+ } | {
23
+ ok: false;
24
+ nome: string;
25
+ erro: string;
26
+ };
27
+ /** Clona só a referência pedida, com profundidade 1. Devolve a pasta temporária. */
28
+ export declare function buscarSkill(p: PedidoBusca): Promise<ResultadoBusca>;
@@ -0,0 +1,29 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ import { mkdtempSync, rmSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ const exec = promisify(execFile);
7
+ /** Clona só a referência pedida, com profundidade 1. Devolve a pasta temporária. */
8
+ export async function buscarSkill(p) {
9
+ const destino = mkdtempSync(join(tmpdir(), `expx-busca-${p.nome}-`));
10
+ try {
11
+ await exec("git", [
12
+ "clone",
13
+ "--quiet",
14
+ "--depth",
15
+ "1",
16
+ "--branch",
17
+ p.referencia,
18
+ p.repositorio,
19
+ destino,
20
+ ]);
21
+ const { stdout } = await exec("git", ["rev-parse", "HEAD"], { cwd: destino });
22
+ return { ok: true, nome: p.nome, caminho: destino, commit: stdout.trim() };
23
+ }
24
+ catch (e) {
25
+ rmSync(destino, { recursive: true, force: true });
26
+ return { ok: false, nome: p.nome, erro: String(e) };
27
+ }
28
+ }
29
+ //# sourceMappingURL=busca.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"busca.js","sourceRoot":"","sources":["../../src/nucleo/busca.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAwBjC,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,CAAc;IAC9C,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,cAAc,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;IACrE,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,KAAK,EAAE;YAChB,OAAO;YACP,SAAS;YACT,SAAS;YACT,GAAG;YACH,UAAU;YACV,CAAC,CAAC,UAAU;YACZ,CAAC,CAAC,WAAW;YACb,OAAO;SACR,CAAC,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;QAC9E,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;IAC7E,CAAC;IAAC,OAAO,CAAU,EAAE,CAAC;QACpB,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAClD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACtD,CAAC;AACH,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Recusa skill que referencie arquivo fora da própria raiz.
3
+ *
4
+ * Isto não é precaução teórica: foi medido. Ao instalar, o Claude Code COPIA o
5
+ * plugin para `~/.claude/plugins/cache/<marketplace>/<plugin>/<versao>/`, e só
6
+ * a pasta do plugin vai junto — qualquer `../` sai da árvore copiada e aponta
7
+ * para o vazio. O experimento está em
8
+ * `docs/expx-cli/base/09-validacao-marketplace-local.md`.
9
+ *
10
+ * Hoje as cinco skills reais estão limpas; a verificação existe para pegar a
11
+ * regressão futura antes que ela chegue no usuário.
12
+ */
13
+ export type Achado = {
14
+ /** Caminho do arquivo, relativo à raiz da skill. */
15
+ arquivo: string;
16
+ /** A referência infratora, como aparece no texto. */
17
+ referencia: string;
18
+ linha: number;
19
+ };
20
+ /** Lista as referências que saem da raiz da skill. Lista vazia = skill sadia. */
21
+ export declare function verificarCaminhos(raizSkill: string): Achado[];
@@ -0,0 +1,64 @@
1
+ import { readdirSync, readFileSync, statSync } from "node:fs";
2
+ import { join, relative } from "node:path";
3
+ const EXTENSOES = new Set([".md", ".json", ".yaml", ".yml", ".txt"]);
4
+ const IGNORADAS = new Set([".git", "node_modules"]);
5
+ /** `../algo`, com ou sem `./` na frente. */
6
+ const PADRAO = /(?:^|[\s`'"(\[])((?:\.\/)?(?:\.\.\/)+[\w./-]+)/g;
7
+ function arquivosDe(raiz) {
8
+ const saida = [];
9
+ const fila = [raiz];
10
+ while (fila.length > 0) {
11
+ const dir = fila.shift();
12
+ if (dir === undefined)
13
+ break;
14
+ let entradas;
15
+ try {
16
+ entradas = readdirSync(dir);
17
+ }
18
+ catch {
19
+ continue;
20
+ }
21
+ for (const e of entradas) {
22
+ if (IGNORADAS.has(e))
23
+ continue;
24
+ const p = join(dir, e);
25
+ try {
26
+ if (statSync(p).isDirectory())
27
+ fila.push(p);
28
+ else if ([...EXTENSOES].some((x) => e.endsWith(x)))
29
+ saida.push(p);
30
+ }
31
+ catch {
32
+ // entrada sumiu entre o readdir e o stat: ignora
33
+ }
34
+ }
35
+ }
36
+ return saida;
37
+ }
38
+ /** Lista as referências que saem da raiz da skill. Lista vazia = skill sadia. */
39
+ export function verificarCaminhos(raizSkill) {
40
+ const achados = [];
41
+ for (const arquivo of arquivosDe(raizSkill)) {
42
+ let texto;
43
+ try {
44
+ texto = readFileSync(arquivo, "utf8");
45
+ }
46
+ catch {
47
+ continue;
48
+ }
49
+ texto.split("\n").forEach((linha, i) => {
50
+ for (const m of linha.matchAll(PADRAO)) {
51
+ const ref = m[1];
52
+ if (ref === undefined)
53
+ continue;
54
+ achados.push({
55
+ arquivo: relative(raizSkill, arquivo),
56
+ referencia: ref,
57
+ linha: i + 1,
58
+ });
59
+ }
60
+ });
61
+ }
62
+ return achados;
63
+ }
64
+ //# sourceMappingURL=caminhos.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"caminhos.js","sourceRoot":"","sources":["../../src/nucleo/caminhos.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAuB3C,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AACrE,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;AAEpD,4CAA4C;AAC5C,MAAM,MAAM,GAAG,iDAAiD,CAAC;AAEjE,SAAS,UAAU,CAAC,IAAY;IAC9B,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;IACpB,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;QACzB,IAAI,GAAG,KAAK,SAAS;YAAE,MAAM;QAC7B,IAAI,QAAkB,CAAC;QACvB,IAAI,CAAC;YACH,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,IAAI,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAE,SAAS;YAC/B,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACvB,IAAI,CAAC;gBACH,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;oBAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;qBACvC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;oBAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACpE,CAAC;YAAC,MAAM,CAAC;gBACP,iDAAiD;YACnD,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,iBAAiB,CAAC,SAAiB;IACjD,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,OAAO,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC5C,IAAI,KAAa,CAAC;QAClB,IAAI,CAAC;YACH,KAAK,GAAG,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;YACrC,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBACvC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;gBACjB,IAAI,GAAG,KAAK,SAAS;oBAAE,SAAS;gBAChC,OAAO,CAAC,IAAI,CAAC;oBACX,OAAO,EAAE,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;oBACrC,UAAU,EAAE,GAAG;oBACf,KAAK,EAAE,CAAC,GAAG,CAAC;iBACb,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}