@sysvv/ai-skill 1.3.0 → 1.4.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/mcp.js CHANGED
@@ -1,60 +1,178 @@
1
1
  import fs from "fs-extra";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
+ import * as TOML from "smol-toml";
4
5
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
5
- /** Onde cada agent espera o arquivo de MCP */
6
- const MCP_TARGETS = {
7
- claude: ".mcp.json",
8
- codex: ".codex/mcp.json",
9
- gemini: ".gemini/mcp.json",
10
- copilot: ".github/copilot/mcp.json",
6
+ // ── Targets por agent ───────────────────────────────────────────────────
7
+ const AGENT_MCP_TARGETS = {
8
+ // ── Claude ──────────────────────────────────────────────────────────
9
+ // .mcp.json → { mcpServers: { name: { command, args, env } } }
10
+ claude: {
11
+ format: "json",
12
+ file: ".mcp.json",
13
+ serversKey: "mcpServers",
14
+ buildServerConfig: (mcp) => {
15
+ if (mcp.transport === "stdio") {
16
+ const config = { command: mcp.command };
17
+ if (mcp.args?.length)
18
+ config.args = mcp.args;
19
+ if (mcp.env && Object.keys(mcp.env).length)
20
+ config.env = mcp.env;
21
+ return config;
22
+ }
23
+ const config = { url: mcp.url };
24
+ if (mcp.headers && Object.keys(mcp.headers).length)
25
+ config.headers = mcp.headers;
26
+ if (mcp.env && Object.keys(mcp.env).length)
27
+ config.env = mcp.env;
28
+ return config;
29
+ }
30
+ },
31
+ // ── Gemini ──────────────────────────────────────────────────────────
32
+ // .gemini/settings.json → { mcpServers: { name: { command, args, env, cwd, timeout } } }
33
+ // Gemini usa "httpUrl" para streamable-http
34
+ gemini: {
35
+ format: "json",
36
+ file: ".gemini/settings.json",
37
+ serversKey: "mcpServers",
38
+ buildServerConfig: (mcp) => {
39
+ const config = {};
40
+ if (mcp.transport === "stdio") {
41
+ config.command = mcp.command;
42
+ if (mcp.args?.length)
43
+ config.args = mcp.args;
44
+ if (mcp.cwd)
45
+ config.cwd = mcp.cwd;
46
+ }
47
+ else if (mcp.transport === "sse") {
48
+ config.url = mcp.url;
49
+ if (mcp.headers && Object.keys(mcp.headers).length)
50
+ config.headers = mcp.headers;
51
+ }
52
+ else if (mcp.transport === "streamable-http") {
53
+ config.httpUrl = mcp.url;
54
+ if (mcp.headers && Object.keys(mcp.headers).length)
55
+ config.headers = mcp.headers;
56
+ }
57
+ if (mcp.env && Object.keys(mcp.env).length)
58
+ config.env = mcp.env;
59
+ if (mcp.timeout)
60
+ config.timeout = mcp.timeout;
61
+ return config;
62
+ }
63
+ },
64
+ // ── Codex ───────────────────────────────────────────────────────────
65
+ // .codex/config.toml → [mcp_servers.<name>]
66
+ // stdio: command, args, env, cwd
67
+ // streamable-http: url, bearer_token_env_var, http_headers
68
+ // extras: startup_timeout_sec, tool_timeout_sec, enabled
69
+ codex: {
70
+ format: "toml",
71
+ file: ".codex/config.toml",
72
+ buildServerEntry: (mcp) => {
73
+ const entry = {};
74
+ if (mcp.transport === "stdio") {
75
+ entry.command = mcp.command;
76
+ if (mcp.args?.length)
77
+ entry.args = mcp.args;
78
+ if (mcp.cwd)
79
+ entry.cwd = mcp.cwd;
80
+ }
81
+ else {
82
+ // sse e streamable-http → Codex usa "url" para ambos
83
+ entry.url = mcp.url;
84
+ if (mcp.headers && Object.keys(mcp.headers).length) {
85
+ entry.http_headers = mcp.headers;
86
+ }
87
+ }
88
+ if (mcp.env && Object.keys(mcp.env).length)
89
+ entry.env = mcp.env;
90
+ if (mcp.timeout)
91
+ entry.tool_timeout_sec = Math.ceil(mcp.timeout / 1000);
92
+ return entry;
93
+ }
94
+ },
95
+ // ── Copilot ─────────────────────────────────────────────────────────
96
+ // .vscode/mcp.json → { inputs: [], servers: { name: { command, args, env } } }
97
+ copilot: {
98
+ format: "json",
99
+ file: ".vscode/mcp.json",
100
+ serversKey: "servers",
101
+ buildServerConfig: (mcp) => {
102
+ if (mcp.transport === "stdio") {
103
+ const config = { command: mcp.command };
104
+ if (mcp.args?.length)
105
+ config.args = mcp.args;
106
+ if (mcp.env && Object.keys(mcp.env).length)
107
+ config.env = mcp.env;
108
+ return config;
109
+ }
110
+ const config = { url: mcp.url };
111
+ if (mcp.headers && Object.keys(mcp.headers).length)
112
+ config.headers = mcp.headers;
113
+ if (mcp.env && Object.keys(mcp.env).length)
114
+ config.env = mcp.env;
115
+ return config;
116
+ }
117
+ },
11
118
  };
12
- /** Resolve o caminho do .mcp.json dentro de /mcps/ */
13
- function getMcpFilePath(name) {
14
- return path.resolve(__dirname, "../../mcps", `${name}.mcp.json`);
119
+ // ── Writers ─────────────────────────────────────────────────────────────
120
+ async function writeJsonTarget(target, targetPath, mcp) {
121
+ let existing = {};
122
+ if (await fs.pathExists(targetPath)) {
123
+ existing = await fs.readJson(targetPath);
124
+ }
125
+ // Copilot: garante que "inputs" exista
126
+ if (target.serversKey === "servers" && !existing.inputs) {
127
+ existing.inputs = [];
128
+ }
129
+ const servers = existing[target.serversKey] ?? {};
130
+ servers[mcp.name] = target.buildServerConfig(mcp);
131
+ existing[target.serversKey] = servers;
132
+ await fs.ensureDir(path.dirname(targetPath));
133
+ await fs.writeJson(targetPath, existing, { spaces: 2 });
134
+ }
135
+ async function writeTomlTarget(target, targetPath, mcp) {
136
+ let existing = {};
137
+ if (await fs.pathExists(targetPath)) {
138
+ const content = await fs.readFile(targetPath, "utf8");
139
+ if (content.trim()) {
140
+ existing = TOML.parse(content);
141
+ }
142
+ }
143
+ const mcpServers = existing.mcp_servers ?? {};
144
+ mcpServers[mcp.name] = target.buildServerEntry(mcp);
145
+ existing.mcp_servers = mcpServers;
146
+ await fs.ensureDir(path.dirname(targetPath));
147
+ await fs.writeFile(targetPath, TOML.stringify(existing), "utf8");
15
148
  }
149
+ // ── API pública ─────────────────────────────────────────────────────────
16
150
  /** Carrega a definição de um MCP pelo nome */
17
151
  export async function loadMcp(name) {
18
- const mcpFile = getMcpFilePath(name);
152
+ const mcpFile = path.resolve(__dirname, "../../mcps", `${name}.mcp.json`);
19
153
  if (!(await fs.pathExists(mcpFile))) {
20
154
  throw new Error(`MCP "${name}" não encontrado em mcps/${name}.mcp.json`);
21
155
  }
22
156
  return fs.readJson(mcpFile);
23
157
  }
24
- /** Extrai apenas os campos de server config (sem metadata) */
25
- function extractServerConfig(mcp) {
26
- if (mcp.transport === "stdio") {
27
- const config = { command: mcp.command };
28
- if (mcp.args?.length)
29
- config.args = mcp.args;
30
- if (mcp.env && Object.keys(mcp.env).length)
31
- config.env = mcp.env;
32
- return config;
33
- }
34
- // sse | streamable-http
35
- const config = { url: mcp.url };
36
- if (mcp.headers && Object.keys(mcp.headers).length)
37
- config.headers = mcp.headers;
38
- if (mcp.env && Object.keys(mcp.env).length)
39
- config.env = mcp.env;
40
- return config;
41
- }
42
158
  /** Escreve o MCP no arquivo alvo do agent, fazendo merge com configs existentes */
43
159
  export async function writeMcp(agent, mcp) {
44
- const targetFile = MCP_TARGETS[agent];
45
- if (!targetFile)
160
+ const target = AGENT_MCP_TARGETS[agent];
161
+ if (!target)
46
162
  throw new Error(`Agent "${agent}" não tem target de MCP configurado.`);
47
- const targetPath = path.resolve(targetFile);
48
- let existing = {};
49
- if (await fs.pathExists(targetPath)) {
50
- existing = await fs.readJson(targetPath);
163
+ const targetPath = path.resolve(target.file);
164
+ if (target.format === "json") {
165
+ await writeJsonTarget(target, targetPath, mcp);
166
+ }
167
+ else {
168
+ await writeTomlTarget(target, targetPath, mcp);
51
169
  }
52
- const mcpServers = existing.mcpServers ?? {};
53
- mcpServers[mcp.name] = extractServerConfig(mcp);
54
- await fs.ensureDir(path.dirname(targetPath));
55
- await fs.writeJson(targetPath, { ...existing, mcpServers }, { spaces: 2 });
56
170
  return targetPath;
57
171
  }
172
+ /** Retorna o path do arquivo MCP de um agent */
173
+ export function getMcpTargetPath(agent) {
174
+ return AGENT_MCP_TARGETS[agent]?.file;
175
+ }
58
176
  /** Lista todos os MCPs disponíveis */
59
177
  export async function listMcps() {
60
178
  const mcpsDir = path.resolve(__dirname, "../../mcps");
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 } from "./core/mcp.js";
10
+ import { loadMcp, writeMcp, getMcpTargetPath } from "./core/mcp.js";
11
11
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
12
  // ── ANSI ────────────────────────────────────────────────────────────────
13
13
  const ANSI = {
@@ -178,18 +178,13 @@ async function installSkills(agent) {
178
178
  }
179
179
  // ── Clear all skills + MCPs ─────────────────────────────────────────────
180
180
  async function clearSkills() {
181
- const MCP_TARGETS = {
182
- claude: ".mcp.json",
183
- codex: ".codex/mcp.json",
184
- gemini: ".gemini/mcp.json",
185
- copilot: ".github/copilot/mcp.json",
186
- };
187
181
  const found = [];
188
182
  for (const [agent, dir] of Object.entries(AGENT_DIRS)) {
189
183
  const skillsFull = path.resolve(dir);
190
- const mcpFull = path.resolve(MCP_TARGETS[agent]);
184
+ const mcpTarget = getMcpTargetPath(agent);
185
+ const mcpFull = mcpTarget ? path.resolve(mcpTarget) : "";
191
186
  const hasSkills = await fs.pathExists(skillsFull);
192
- const hasMcp = await fs.pathExists(mcpFull);
187
+ const hasMcp = mcpFull ? await fs.pathExists(mcpFull) : false;
193
188
  if (hasSkills || hasMcp) {
194
189
  found.push({ agent, skillsDir: skillsFull, mcpFile: mcpFull, hasSkills, hasMcp });
195
190
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sysvv/ai-skill",
3
- "version": "1.3.0",
3
+ "version": "1.4.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": {
@@ -30,7 +30,8 @@
30
30
  "dependencies": {
31
31
  "fs-extra": "^11.3.0",
32
32
  "gray-matter": "^4.0.3",
33
- "inquirer": "^12.5.2"
33
+ "inquirer": "^12.5.2",
34
+ "smol-toml": "^1.6.0"
34
35
  },
35
36
  "devDependencies": {
36
37
  "@types/fs-extra": "^11.0.4",
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://sysvv.dev/schemas/mcp.schema.json",
4
4
  "title": "MCP Server Configuration",
5
- "description": "Schema para configuração de servidores MCP (Model Context Protocol). Suporta os transportes stdio, sse e streamable-http.",
5
+ "description": "Schema para configuração de servidores MCP (Model Context Protocol). Suporta os transportes stdio, sse e streamable-http. Campos opcionais como timeout e cwd são aplicados conforme o suporte de cada agent.",
6
6
  "type": "object",
7
7
  "properties": {
8
8
  "$schema": {
@@ -32,6 +32,10 @@
32
32
  "items": { "type": "string" },
33
33
  "description": "[stdio] Argumentos passados ao comando."
34
34
  },
35
+ "cwd": {
36
+ "type": "string",
37
+ "description": "[stdio] Diretório de trabalho para o processo do servidor."
38
+ },
35
39
  "url": {
36
40
  "type": "string",
37
41
  "format": "uri",
@@ -46,6 +50,10 @@
46
50
  "type": "object",
47
51
  "additionalProperties": { "type": "string" },
48
52
  "description": "Variáveis de ambiente. Use ${VAR} para referências dinâmicas."
53
+ },
54
+ "timeout": {
55
+ "type": "number",
56
+ "description": "Timeout da requisição em milissegundos. Padrão varia por agent."
49
57
  }
50
58
  },
51
59
  "required": ["name", "description", "transport"],
@@ -56,11 +64,7 @@
56
64
  "required": ["transport"]
57
65
  },
58
66
  "then": {
59
- "required": ["command"],
60
- "properties": {
61
- "url": false,
62
- "headers": false
63
- }
67
+ "required": ["command"]
64
68
  }
65
69
  },
66
70
  {
@@ -69,11 +73,7 @@
69
73
  "required": ["transport"]
70
74
  },
71
75
  "then": {
72
- "required": ["url"],
73
- "properties": {
74
- "command": false,
75
- "args": false
76
- }
76
+ "required": ["url"]
77
77
  }
78
78
  },
79
79
  {
@@ -82,11 +82,7 @@
82
82
  "required": ["transport"]
83
83
  },
84
84
  "then": {
85
- "required": ["url"],
86
- "properties": {
87
- "command": false,
88
- "args": false
89
- }
85
+ "required": ["url"]
90
86
  }
91
87
  }
92
88
  ]
@@ -0,0 +1,163 @@
1
+ ---
2
+ name: ado-workflow
3
+ title: ADO Workflow
4
+ description: gerenciar work items do Azure DevOps integrado com Git local. Sincroniza estados, vincula commits e mantém ADO atualizado.
5
+ version: 1.0.0
6
+ providers:
7
+ - claude
8
+ - codex
9
+ - gemini
10
+ - copilot
11
+ mcps:
12
+ - azure-devops
13
+ variables:
14
+ language: pt-BR
15
+ ---
16
+
17
+ # ADO Workflow Skill
18
+
19
+ Reusable skill that integrates Azure DevOps work items with local Git operations. Manages work item states, links commits to tasks, and keeps ADO in sync with development progress. Uses MCP tools as primary interface with CLI fallback.
20
+
21
+ ## Setup
22
+
23
+ 1. Verify MCP server "azure-devops" is available (check `mcp__azure-devops__*` tools)
24
+ 2. If MCP is NOT available: fall back to `az` CLI commands for all operations
25
+ 3. Verify `git` is installed and repository is initialized
26
+ 4. **NEVER** print or display the value of `AZURE_DEVOPS_PAT`
27
+
28
+ ## Rules
29
+
30
+ 1. ALWAYS reference ADO work item IDs in commit messages (e.g., `feat: implement login #123`)
31
+ 2. NEVER update work item state without verifying current state first
32
+ 3. NEVER print or display PAT/token values
33
+ 4. Make atomic commits — one logical change per commit
34
+ 5. Use Conventional Commits format (`feat:`, `fix:`, `docs:`, `refactor:`, `chore:`, etc.)
35
+ 6. Keep ADO work item state in sync with actual progress
36
+ 7. ALWAYS confirm with the user before changing work item states
37
+ 8. Organization, project, area paths, and states are project-specific — do not hardcode them
38
+ 9. Work item states may use custom/localized names — always verify before updating
39
+ 10. When creating Tasks: do NOT assign, ALWAYS ask Area Path, activity requires user selection
40
+
41
+ ## Operations
42
+
43
+ ### list
44
+
45
+ List work items assigned to the current user.
46
+
47
+ **MCP:** `mcp__azure-devops__wit_my_work_items` with `project: {project}`, `type: "assignedtome"`, `includeCompleted: false`
48
+
49
+ **Fallback:**
50
+ ```bash
51
+ AZURE_DEVOPS_EXT_PAT=$AZURE_DEVOPS_PAT az boards query \
52
+ --wiql "SELECT [System.Id], [System.Title], [System.State], [System.WorkItemType] FROM WorkItems WHERE [System.AssignedTo] = @Me AND [System.State] <> 'Closed'" \
53
+ --project {project} --organization {organization} --output table
54
+ ```
55
+
56
+ **Output:** Display ID, Title, State, Assigned To, Work Item Type as formatted table.
57
+
58
+ ### show
59
+
60
+ Show full details of a work item by ID.
61
+
62
+ **Params:** `id` (required)
63
+
64
+ **MCP:** `mcp__azure-devops__wit_get_work_item` with `id: {id}`, `project: {project}`, `expand: "relations"`
65
+
66
+ **Fallback:**
67
+ ```bash
68
+ AZURE_DEVOPS_EXT_PAT=$AZURE_DEVOPS_PAT az boards work-item show --id {id} --organization {organization} --output json
69
+ ```
70
+
71
+ **Output:** Parse and display id, type, title, state, description, acceptance criteria, assignedTo, iterationPath, areaPath, priority, storyPoints, relations.
72
+
73
+ ### start
74
+
75
+ Start working on a task — set state to active and begin implementation.
76
+
77
+ **Params:** `id` (required)
78
+
79
+ **Process:**
80
+ 1. Fetch work item details using "show" operation
81
+ 2. Display title, description, and acceptance criteria to user
82
+ 3. Update state to the active state in ADO
83
+ 4. Create implementation plan based on requirements
84
+ 5. Ask user for confirmation before proceeding
85
+
86
+ **MCP:** `mcp__azure-devops__wit_update_work_item` with `id: {id}`, `updates: [{"path": "/fields/System.State", "value": "{active_state}"}]`
87
+
88
+ **Fallback:**
89
+ ```bash
90
+ AZURE_DEVOPS_EXT_PAT=$AZURE_DEVOPS_PAT az boards work-item update --id {id} --state "{active_state}" --organization {organization} --output json
91
+ ```
92
+
93
+ **Output:** Confirm state change. Show implementation plan. Wait for user approval.
94
+
95
+ ### complete
96
+
97
+ Complete a task — verify criteria, final commit, set state to closed.
98
+
99
+ **Params:** `id` (required)
100
+
101
+ **Process:**
102
+ 1. Fetch work item details — verify acceptance criteria are met
103
+ 2. Run final tests if applicable
104
+ 3. Make final commit referencing work item ID
105
+ 4. Update state to "Closed" in ADO
106
+ 5. Add completion comment with commit SHA
107
+
108
+ **MCP:** `mcp__azure-devops__wit_update_work_item` with `id: {id}`, `updates: [{"path": "/fields/System.State", "value": "Closed"}]`
109
+
110
+ **Fallback:**
111
+ ```bash
112
+ AZURE_DEVOPS_EXT_PAT=$AZURE_DEVOPS_PAT az boards work-item update --id {id} --state "Closed" --organization {organization} --output json
113
+ ```
114
+
115
+ **Output:** Confirm completion. Show final commit SHA, updated state, summary.
116
+
117
+ ### commit
118
+
119
+ Make an incremental commit linked to a work item.
120
+
121
+ **Params:** `message` (optional), `work-item-id` (optional)
122
+
123
+ **Process:**
124
+ 1. Delegate to git-cli skill `/git-commit`
125
+ 2. Append work item reference to commit message (e.g., `#123`)
126
+
127
+ **Output:** Commit hash, message, files changed. Tip: use `/git-push` to push.
128
+
129
+ **Notes:** This operation delegates to the git-cli skill for actual Git operations.
130
+
131
+ ### create
132
+
133
+ Create a new work item in ADO.
134
+
135
+ **Params:** `type` (required), `title` (required), `description` (optional), `area-path` (optional — ALWAYS ask user), `activity` (optional — present types to user)
136
+
137
+ **MCP:** `mcp__azure-devops__wit_create_work_item` with `project: {project}`, `type: "{type}"`, `title: "{title}"`, `additionalFields: {"System.Description": "{description}", "System.AreaPath": "{area-path}", "Microsoft.VSTS.Common.Activity": "{activity}", "Microsoft.VSTS.Scheduling.OriginalEstimate": 0, "Microsoft.VSTS.Scheduling.RemainingWork": 0, "Microsoft.VSTS.Scheduling.CompletedWork": 0}`
138
+
139
+ **Fallback:**
140
+ ```bash
141
+ AZURE_DEVOPS_EXT_PAT=$AZURE_DEVOPS_PAT az boards work-item create \
142
+ --type "{type}" --title "{title}" --project {project} \
143
+ --organization {organization} --area "{area-path}" \
144
+ --fields "Microsoft.VSTS.Common.Activity={activity}" \
145
+ --output json
146
+ ```
147
+
148
+ **Output:** Parse response: extract id, title, state, url.
149
+
150
+ **Notes:**
151
+ - Omit additionalFields entries for params not provided
152
+ - Do NOT assign unless explicitly requested
153
+ - ALWAYS ask user for Area Path before executing
154
+
155
+ ## Error Handling
156
+
157
+ | Error | Cause | Resolution |
158
+ |-------|-------|------------|
159
+ | MCP server not available | Not configured or not running | Check `.mcp.json`, verify PAT in `.env`, restart AI tool |
160
+ | 401 / 403 | Invalid or expired PAT | Check PAT scopes: Work Items (Read, Write, Manage), Project (Read) |
161
+ | Work item type not found | Process template mismatch | Agile = "User Story", Scrum = "Product Backlog Item" |
162
+ | State transition not allowed | Invalid state change | Verify allowed transitions for the work item type |
163
+ | MCP tool call fails | Various | Fall back to `az` CLI equivalent command |
@@ -1,37 +0,0 @@
1
- ---
2
- name: azure-devops
3
- title: Azure DevOps
4
- description: gerenciar work items, repos e pipelines no Azure DevOps.
5
- version: 1.0.0
6
- providers:
7
- - claude
8
- - codex
9
- - gemini
10
- mcps:
11
- - azure-devops
12
- variables:
13
- language: pt-BR
14
- provider_overrides:
15
- claude:
16
- style: "retornar bullets curtos e objetivos"
17
- codex:
18
- style: "retornar estrutura operacional"
19
- gemini:
20
- style: "retornar texto claro e organizado"
21
- ---
22
-
23
- Você é um assistente especialista em Azure DevOps.
24
-
25
- Objetivo:
26
- Ajudar a gerenciar work items, repositórios e pipelines usando a integração com Azure DevOps.
27
-
28
- Capacidades:
29
- - Criar e atualizar work items
30
- - Consultar status de pipelines
31
- - Listar repositórios e branches
32
- - Buscar PRs e reviews
33
-
34
- Idioma: {{language}}
35
-
36
- Instrução específica do provider:
37
- {{provider_style}}