@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.
- package/dist/core/frontmatter.js +8 -0
- package/dist/core/registry.js +24 -0
- package/dist/core/render.js +6 -0
- package/dist/core/skill.js +22 -0
- package/dist/index.js +63 -0
- package/package.json +39 -0
- package/skills/create-ticket/SKILL.md +30 -0
- package/skills/customer-reply/SKILL.md +37 -0
- package/skills/summarize-meeting/SKILL.md +39 -0
|
@@ -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,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}}
|