@sysvv/ai-skill 1.0.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.
@@ -0,0 +1,8 @@
1
+ import matter from "gray-matter";
2
+ export function parseSkillFile(content) {
3
+ const parsed = matter(content);
4
+ return {
5
+ metadata: parsed.data,
6
+ body: parsed.content.trim()
7
+ };
8
+ }
@@ -0,0 +1,24 @@
1
+ import fs from "fs-extra";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
5
+ /** Resolve a pasta skills/ relativa ao pacote, não ao cwd do usuário */
6
+ function getSkillsRoot() {
7
+ return path.resolve(__dirname, "../../skills");
8
+ }
9
+ export async function findSkillFiles() {
10
+ const root = getSkillsRoot();
11
+ const entries = await fs.readdir(root);
12
+ const results = [];
13
+ for (const entry of entries) {
14
+ const full = path.join(root, entry);
15
+ const stat = await fs.stat(full);
16
+ if (stat.isDirectory()) {
17
+ const skillFile = path.join(full, "SKILL.md");
18
+ if (await fs.pathExists(skillFile)) {
19
+ results.push(skillFile);
20
+ }
21
+ }
22
+ }
23
+ return results;
24
+ }
@@ -0,0 +1,6 @@
1
+ export function renderTemplate(text, variables = {}) {
2
+ return text.replace(/\{\{(.*?)\}\}/g, (_, key) => {
3
+ const cleanKey = String(key).trim();
4
+ return variables[cleanKey] ?? "";
5
+ });
6
+ }
@@ -0,0 +1,22 @@
1
+ import fs from "fs-extra";
2
+ import path from "node:path";
3
+ import { parseSkillFile } from "./frontmatter.js";
4
+ import { renderTemplate } from "./render.js";
5
+ export async function loadSkill(skillFilePath, agent) {
6
+ const absolute = path.resolve(skillFilePath);
7
+ const content = await fs.readFile(absolute, "utf8");
8
+ const { metadata, body } = parseSkillFile(content);
9
+ const baseVariables = metadata.variables ?? {};
10
+ const agentOverride = metadata.provider_overrides?.[agent] ?? {};
11
+ const variables = {
12
+ ...baseVariables,
13
+ ...Object.fromEntries(Object.entries(agentOverride).map(([key, value]) => [`provider_${key}`, value]))
14
+ };
15
+ const renderedBody = renderTemplate(body, variables);
16
+ return {
17
+ path: absolute,
18
+ metadata,
19
+ rawBody: body,
20
+ renderedBody
21
+ };
22
+ }
package/dist/index.js ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+ import inquirer from "inquirer";
3
+ import fs from "fs-extra";
4
+ import path from "node:path";
5
+ import { findSkillFiles } from "./core/registry.js";
6
+ import { parseSkillFile } from "./core/frontmatter.js";
7
+ import { loadSkill } from "./core/skill.js";
8
+ const AGENT_DIRS = {
9
+ claude: ".claude/skills",
10
+ codex: ".codex/skills",
11
+ gemini: ".gemini/skills",
12
+ };
13
+ async function main() {
14
+ const { agent } = await inquirer.prompt([
15
+ {
16
+ type: "list",
17
+ name: "agent",
18
+ message: "Qual IA você usa?",
19
+ choices: Object.keys(AGENT_DIRS)
20
+ }
21
+ ]);
22
+ const skillFiles = await findSkillFiles();
23
+ const available = [];
24
+ for (const file of skillFiles) {
25
+ const content = await fs.readFile(file, "utf8");
26
+ const { metadata } = parseSkillFile(content);
27
+ available.push({
28
+ file,
29
+ name: metadata.name,
30
+ title: metadata.title ?? metadata.name,
31
+ description: metadata.description
32
+ });
33
+ }
34
+ if (available.length === 0) {
35
+ console.log("Nenhuma skill encontrada.");
36
+ process.exit(1);
37
+ }
38
+ const { selected } = await inquirer.prompt([
39
+ {
40
+ type: "checkbox",
41
+ name: "selected",
42
+ message: "Escolha as skills que deseja instalar:",
43
+ choices: available.map((s) => ({
44
+ name: `${s.title} — ${s.description}`,
45
+ value: s.file
46
+ })),
47
+ validate: (answer) => answer.length > 0 ? true : "Selecione pelo menos uma skill."
48
+ }
49
+ ]);
50
+ const destBase = path.resolve(AGENT_DIRS[agent]);
51
+ for (const file of selected) {
52
+ const skill = await loadSkill(file, agent);
53
+ const skillDir = path.join(destBase, skill.metadata.name);
54
+ await fs.ensureDir(skillDir);
55
+ await fs.writeFile(path.join(skillDir, "SKILL.md"), skill.renderedBody, "utf8");
56
+ console.log(` ✔ ${skill.metadata.name}`);
57
+ }
58
+ console.log(`\nSkills instaladas em ${destBase}/\n`);
59
+ }
60
+ main().catch((err) => {
61
+ console.error("Erro:", err.message);
62
+ process.exit(1);
63
+ });
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@sysvv/ai-skill",
3
+ "version": "1.0.0",
4
+ "description": "Instale skills de IA direto no seu projeto. Escolha o agent, escolha as skills.",
5
+ "type": "module",
6
+ "bin": {
7
+ "ai-skill": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "skills"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "dev": "tsx src/index.ts",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "keywords": [
19
+ "ai",
20
+ "skills",
21
+ "claude",
22
+ "codex",
23
+ "gemini",
24
+ "cli",
25
+ "prompt"
26
+ ],
27
+ "license": "MIT",
28
+ "dependencies": {
29
+ "fs-extra": "^11.3.0",
30
+ "gray-matter": "^4.0.3",
31
+ "inquirer": "^12.5.2"
32
+ },
33
+ "devDependencies": {
34
+ "@types/fs-extra": "^11.0.4",
35
+ "@types/node": "^22.15.21",
36
+ "tsx": "^4.19.4",
37
+ "typescript": "^5.8.3"
38
+ }
39
+ }
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: create-ticket
3
+ title: Create Ticket
4
+ description: criar tickets técnicos claros a partir de contexto solto.
5
+ version: 1.0.0
6
+ providers:
7
+ - codex
8
+ - gemini
9
+ - claude
10
+ variables:
11
+ language: pt-BR
12
+ provider_overrides:
13
+ codex:
14
+ style: "priorizar campos técnicos e objetividade"
15
+ gemini:
16
+ style: "priorizar clareza didática"
17
+ claude:
18
+ style: "priorizar síntese e bullets"
19
+ ---
20
+
21
+ Transforme a solicitação em ticket com:
22
+
23
+ - título
24
+ - contexto
25
+ - problema
26
+ - critério de aceite
27
+ - próximos passos
28
+
29
+ Idioma: {{language}}
30
+ Estilo do provider: {{provider_style}}
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: customer-reply
3
+ title: Customer Reply
4
+ description: gerar respostas profissionais para clientes a partir de contexto interno.
5
+ version: 1.0.0
6
+ providers:
7
+ - codex
8
+ - gemini
9
+ - claude
10
+ variables:
11
+ language: pt-BR
12
+ tone: profissional
13
+ provider_overrides:
14
+ codex:
15
+ style: "tom direto e corporativo"
16
+ gemini:
17
+ style: "tom empático e didático"
18
+ claude:
19
+ style: "tom claro e conciso"
20
+ ---
21
+
22
+ Você é um assistente especialista em comunicação com clientes.
23
+
24
+ Objetivo:
25
+ Gerar uma resposta profissional ao cliente com base no contexto interno fornecido.
26
+
27
+ Formato de saída:
28
+ - Saudação
29
+ - Resposta ao problema
30
+ - Próximos passos
31
+ - Encerramento
32
+
33
+ Idioma: {{language}}
34
+ Tom: {{tone}}
35
+
36
+ Instrução específica do provider:
37
+ {{provider_style}}
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: summarize-meeting
3
+ title: Summarize Meeting
4
+ description: resumir reuniões em formato executivo com decisões, riscos e próximos passos.
5
+ version: 1.0.0
6
+ providers:
7
+ - codex
8
+ - gemini
9
+ - claude
10
+ variables:
11
+ language: pt-BR
12
+ tone: executivo
13
+ provider_overrides:
14
+ codex:
15
+ style: "retornar estrutura mais operacional e objetiva"
16
+ gemini:
17
+ style: "retornar texto mais didático e organizado"
18
+ claude:
19
+ style: "retornar bullets curtos e alta clareza"
20
+ output:
21
+ format: markdown
22
+ ---
23
+
24
+ Você é um assistente especialista em resumir reuniões.
25
+
26
+ Objetivo:
27
+ Transformar notas, atas ou transcrições em um resumo acionável.
28
+
29
+ Formato de saída:
30
+ - Resumo executivo
31
+ - Decisões tomadas
32
+ - Riscos
33
+ - Próximos passos
34
+
35
+ Idioma: {{language}}
36
+ Tom: {{tone}}
37
+
38
+ Instrução específica do provider:
39
+ {{provider_style}}