@sysvv/ai-skill 1.6.1 → 1.7.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 (2) hide show
  1. package/dist/index.js +88 -68
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import { fileURLToPath } from "node:url";
7
7
  import { findSkillFiles } from "./core/registry.js";
8
8
  import { parseSkillFile } from "./core/frontmatter.js";
9
9
  import { loadSkill } from "./core/skill.js";
10
- import { loadMcp, writeMcp, getMcpTargetPath } from "./core/mcp.js";
10
+ import { loadMcp, writeMcp } from "./core/mcp.js";
11
11
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
12
  // ── ANSI ────────────────────────────────────────────────────────────────
13
13
  const ANSI = {
@@ -159,88 +159,116 @@ async function installSkills(agent) {
159
159
  }
160
160
  if (mcpsToInstall.size > 0) {
161
161
  console.log('');
162
+ const envVarsNeeded = new Map();
162
163
  for (const mcpName of mcpsToInstall) {
163
164
  try {
164
165
  const mcp = await loadMcp(mcpName);
165
166
  const mcpTarget = await writeMcp(agent, mcp);
166
167
  console.log(` ${GREEN}✔${ANSI.reset} MCP ${ANSI.bold}${mcpName}${ANSI.reset} ${ANSI.dim}→ ${mcpTarget}${ANSI.reset}`);
168
+ // Coleta env vars do MCP
169
+ if (mcp.env) {
170
+ for (const key of Object.keys(mcp.env)) {
171
+ envVarsNeeded.set(key, mcpName);
172
+ }
173
+ }
167
174
  }
168
175
  catch (err) {
169
176
  console.log(` ${RED}✖${ANSI.reset} MCP ${mcpName}: ${err.message}`);
170
177
  }
171
178
  }
179
+ // Atualiza .env
180
+ if (envVarsNeeded.size > 0) {
181
+ await ensureEnvFile(envVarsNeeded);
182
+ }
172
183
  }
173
184
  console.log(`\n Skills instaladas em ${ANSI.bold}${destBase}/${ANSI.reset}\n`);
174
185
  }
175
- // ── Clear ───────────────────────────────────────────────────────────────
176
- async function clearFlow() {
177
- // Descobre o que existe
178
- const foundSkills = [];
179
- const foundMcps = [];
180
- for (const [agent, dir] of Object.entries(AGENT_DIRS)) {
181
- const skillsFull = path.resolve(dir);
182
- if (await fs.pathExists(skillsFull)) {
183
- foundSkills.push({ agent, dir: skillsFull });
184
- }
185
- const mcpTarget = getMcpTargetPath(agent);
186
- if (mcpTarget) {
187
- const mcpFull = path.resolve(mcpTarget);
188
- if (await fs.pathExists(mcpFull)) {
189
- foundMcps.push({ agent, file: mcpFull });
190
- }
186
+ // ── .env ────────────────────────────────────────────────────────────────
187
+ /** Garante que o .env tenha as variáveis necessárias dos MCPs */
188
+ async function ensureEnvFile(envVars) {
189
+ const envPath = path.resolve(".env");
190
+ // .env existente
191
+ let existing = '';
192
+ if (await fs.pathExists(envPath)) {
193
+ existing = await fs.readFile(envPath, "utf8");
194
+ }
195
+ // Descobre quais chaves já existem
196
+ const existingKeys = new Set();
197
+ for (const line of existing.split('\n')) {
198
+ const match = line.match(/^([A-Z_][A-Z0-9_]*)=/);
199
+ if (match)
200
+ existingKeys.add(match[1]);
201
+ }
202
+ // Filtra só as que faltam
203
+ const missing = [];
204
+ for (const [key, mcp] of envVars) {
205
+ if (!existingKeys.has(key)) {
206
+ missing.push({ key, mcp });
191
207
  }
192
208
  }
193
- const hasSkills = foundSkills.length > 0;
194
- const hasMcps = foundMcps.length > 0;
195
- if (!hasSkills && !hasMcps) {
196
- console.log(" Nenhuma skill ou MCP instalado.\n");
209
+ if (missing.length === 0)
197
210
  return;
211
+ // Monta as linhas novas
212
+ const lines = [];
213
+ if (existing.length > 0 && !existing.endsWith('\n')) {
214
+ lines.push('');
198
215
  }
199
- // Mostra o que encontrou
200
- console.log(` ${RED}⚠${ANSI.reset} Encontrado:\n`);
201
- if (hasSkills) {
202
- console.log(` ${ANSI.bold}Skills${ANSI.reset}`);
203
- for (const s of foundSkills) {
204
- console.log(` ${ANSI.dim}${s.dir}${ANSI.reset}`);
205
- }
216
+ // Agrupa por MCP
217
+ const byMcp = new Map();
218
+ for (const { key, mcp } of missing) {
219
+ if (!byMcp.has(mcp))
220
+ byMcp.set(mcp, []);
221
+ byMcp.get(mcp).push(key);
206
222
  }
207
- if (hasMcps) {
208
- if (hasSkills)
209
- console.log('');
210
- console.log(` ${ANSI.bold}MCPs${ANSI.reset}`);
211
- for (const m of foundMcps) {
212
- console.log(` ${ANSI.dim}${m.file}${ANSI.reset}`);
223
+ for (const [mcp, keys] of byMcp) {
224
+ lines.push(`# ${mcp}`);
225
+ for (const key of keys) {
226
+ lines.push(`${key}=`);
213
227
  }
228
+ lines.push('');
214
229
  }
215
- console.log('');
216
- const choices = [];
217
- if (hasSkills && hasMcps) {
218
- choices.push({ name: `${RED}Tudo${ANSI.reset} ${ANSI.dim}(skills + MCPs)${ANSI.reset}`, value: "all" }, { name: `Apenas Skills`, value: "skills" }, { name: `Apenas MCPs`, value: "mcps" });
230
+ await fs.writeFile(envPath, existing + lines.join('\n'), "utf8");
231
+ console.log(`\n ${GREEN}✔${ANSI.reset} .env ${ANSI.dim}→ ${missing.map(m => m.key).join(', ')}${ANSI.reset}`);
232
+ console.log(` ${YELLOW}!${ANSI.reset} Preencha as variáveis no ${ANSI.bold}.env${ANSI.reset} antes de usar`);
233
+ }
234
+ // ── Clear ───────────────────────────────────────────────────────────────
235
+ /** Todas as pastas/arquivos que o CLI pode criar, por agent */
236
+ const AGENT_ROOTS = {
237
+ claude: [".claude", ".mcp.json"],
238
+ codex: [".codex"],
239
+ gemini: [".gemini"],
240
+ copilot: [".github", ".vscode"],
241
+ };
242
+ async function clearFlow() {
243
+ // Descobre o que existe
244
+ const found = [];
245
+ const seen = new Set();
246
+ for (const [agent, paths] of Object.entries(AGENT_ROOTS)) {
247
+ for (const p of paths) {
248
+ if (seen.has(p))
249
+ continue;
250
+ seen.add(p);
251
+ const full = path.resolve(p);
252
+ if (await fs.pathExists(full)) {
253
+ found.push({ label: `${agent} → ${p}`, fullPath: full });
254
+ }
255
+ }
219
256
  }
220
- else if (hasSkills) {
221
- choices.push({ name: `Apagar Skills`, value: "skills" });
257
+ if (found.length === 0) {
258
+ console.log(" Nenhuma pasta ou arquivo de agent encontrado.\n");
259
+ return;
222
260
  }
223
- else {
224
- choices.push({ name: `Apagar MCPs`, value: "mcps" });
261
+ // Mostra o que vai ser removido
262
+ console.log(` ${RED}⚠${ANSI.reset} Será removido:\n`);
263
+ for (const f of found) {
264
+ console.log(` ${RED}✖${ANSI.reset} ${ANSI.dim}${f.fullPath}${ANSI.reset}`);
225
265
  }
226
- const { action } = await inquirer.prompt([
227
- {
228
- type: "list",
229
- name: "action",
230
- message: "O que deseja remover?",
231
- choices,
232
- theme: sysTheme
233
- }
234
- ]);
235
- const removeSkills = action === "all" || action === "skills";
236
- const removeMcps = action === "all" || action === "mcps";
237
- // Confirmação
238
- const target = action === "all" ? "skills e MCPs" : action === "skills" ? "skills" : "MCPs";
266
+ console.log('');
239
267
  const { confirm } = await inquirer.prompt([
240
268
  {
241
269
  type: "confirm",
242
270
  name: "confirm",
243
- message: `Confirma a remoção de ${target}?`,
271
+ message: "Confirma a remoção de TUDO?",
244
272
  default: false,
245
273
  theme: sysTheme
246
274
  }
@@ -250,19 +278,11 @@ async function clearFlow() {
250
278
  return;
251
279
  }
252
280
  console.log('');
253
- if (removeSkills) {
254
- for (const s of foundSkills) {
255
- await fs.remove(s.dir);
256
- console.log(` ${RED}✖${ANSI.reset} ${ANSI.dim}${s.dir}${ANSI.reset}`);
257
- }
258
- }
259
- if (removeMcps) {
260
- for (const m of foundMcps) {
261
- await fs.remove(m.file);
262
- console.log(` ${RED}✖${ANSI.reset} ${ANSI.dim}${m.file}${ANSI.reset}`);
263
- }
281
+ for (const f of found) {
282
+ await fs.remove(f.fullPath);
283
+ console.log(` ${RED}✖${ANSI.reset} ${ANSI.dim}${f.fullPath}${ANSI.reset} removido`);
264
284
  }
265
- console.log("\n Removido com sucesso.\n");
285
+ console.log("\n Projeto limpo.\n");
266
286
  }
267
287
  // ── Main ────────────────────────────────────────────────────────────────
268
288
  async function main() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sysvv/ai-skill",
3
- "version": "1.6.1",
3
+ "version": "1.7.0",
4
4
  "description": "Instale skills de IA direto no seu projeto. Escolha o agent, escolha as skills.",
5
5
  "type": "module",
6
6
  "bin": {