@luquimbo/bi-superpowers 3.0.1 → 3.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.
@@ -5,14 +5,14 @@
5
5
  "url": "https://github.com/luquimbo"
6
6
  },
7
7
  "metadata": {
8
- "description": "AI-powered skills for Power BI, Microsoft Fabric, and Excel development. 24 skills covering DAX, Power Query, data modeling, report design, governance, and more.",
9
- "version": "3.0.1",
8
+ "description": "AI-powered skills for Power BI Desktop development. Works with Claude Code, GitHub Copilot, Codex, Gemini CLI, and Kilo Code.",
9
+ "version": "3.1.0",
10
10
  "repository": "https://github.com/luquimbo/bi-superpowers"
11
11
  },
12
12
  "plugins": [
13
13
  {
14
14
  "name": "bi-superpowers",
15
- "description": "24 AI skills for Power BI, Fabric & Excel DAX, Power Query, data modeling, star schema design, report design, governance, deployment, and more.",
15
+ "description": "2 AI skills + Power BI Modeling and Microsoft Learn MCP servers for local Power BI Desktop workflows across 5 AI agents.",
16
16
  "source": "./",
17
17
  "strict": false,
18
18
  "skills": [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bi-superpowers",
3
3
  "description": "Claude Code plugin for Power BI, Microsoft Fabric, and semantic model workflows powered by the official Microsoft MCP servers.",
4
- "version": "3.0.1",
4
+ "version": "3.1.0",
5
5
  "author": {
6
6
  "name": "Lucas Sanchez"
7
7
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bi-superpowers",
3
- "version": "3.0.1",
3
+ "version": "3.1.0",
4
4
  "skillCount": 2,
5
5
  "skills": [
6
6
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "spec": "open-plugin-spec@1",
3
3
  "name": "bi-superpowers",
4
- "version": "3.0.1",
4
+ "version": "3.1.0",
5
5
  "description": "Claude Code plugin for Power BI, Microsoft Fabric, and semantic model workflows powered by the official Microsoft MCP servers.",
6
6
  "author": {
7
7
  "name": "Lucas Sanchez"
package/README.md CHANGED
@@ -89,6 +89,25 @@ super install --all --yes
89
89
 
90
90
  ---
91
91
 
92
+ ## Alternativas para instalar los MCPs
93
+
94
+ `super install` escribe automáticamente los 2 MCPs en el config file de cada agente. Pero hay otras formas de instalar los mismos MCPs — cualquiera de estas funciona igual de bien:
95
+
96
+ ### Para Power BI Modeling MCP (local)
97
+
98
+ - **Extensión de VS Code** — Microsoft publica "Power BI Modeling MCP" en el VS Code Marketplace. Si la instalás, VS Code + Copilot lo descubren automáticamente sin tocar archivos de config.
99
+ - **Extensión de Cursor** — misma extensión, Cursor la detecta.
100
+ - **Manual** — descargá el binario `.exe` y configurá `BI_SUPERPOWERS_POWERBI_MODELING_MCP_PATH` apuntando al ejecutable.
101
+
102
+ ### Para Microsoft Learn MCP (HTTP)
103
+
104
+ - **MCP marketplace oficial de Anthropic** — en Claude Code se puede hacer `/plugin marketplace add anthropic/mcp-registry` y después `/plugin install microsoft-learn` (según disponibilidad).
105
+ - **Agregarlo a mano** en el config file del agente con la URL `https://learn.microsoft.com/api/mcp`.
106
+
107
+ Si ya instalaste los MCPs por alguno de estos caminos, no necesitás correr `super install` — o, si lo corrés, los configs se mergean sin conflicto (cada agente tiene merge por clave, no por overwrite).
108
+
109
+ ---
110
+
92
111
  ## Requisitos
93
112
 
94
113
  - **Node.js ≥ 18**
@@ -276,6 +276,44 @@ function performInstall(skillsSourceDir, skillDirs, selectedAgents, baseDir) {
276
276
  return { agentResults, copyFallbacks };
277
277
  }
278
278
 
279
+ /**
280
+ * Configura los 2 MCP servers (powerbi-modeling + microsoft-learn)
281
+ * para cada agente seleccionado escribiendo el config file en el path
282
+ * y formato que cada agente espera.
283
+ *
284
+ * Los errores por agente no interrumpen el flujo: se recolectan en los
285
+ * resultados para mostrarlos al final y que el caller decida qué hacer
286
+ * con el exit code.
287
+ *
288
+ * @param {string[]} selectedAgents - IDs de agentes seleccionados
289
+ * @param {string} packageDir - Absolute path al paquete instalado
290
+ * @param {string} baseDir - Home directory del usuario (para display)
291
+ * @param {Object} chalk - chalk instance para colorear output
292
+ * @returns {Array<{agent: string, success: boolean, configPath?: string, error?: string}>}
293
+ */
294
+ function configureMcpsForAgents(selectedAgents, packageDir, baseDir, chalk) {
295
+ console.log(
296
+ chalk.cyan('\n Configurando MCP servers (Power BI Modeling + Microsoft Learn)...\n')
297
+ );
298
+
299
+ const results = [];
300
+ for (const agentId of selectedAgents) {
301
+ const agent = AGENTS[agentId];
302
+ try {
303
+ const configPath = writeMcpConfigForAgent(agentId, packageDir);
304
+ if (configPath) {
305
+ const relPath = configPath.replace(baseDir, '~');
306
+ console.log(chalk.green(` ✓ ${relPath} — ${agent.name}`));
307
+ results.push({ agent: agent.name, configPath, success: true });
308
+ }
309
+ } catch (err) {
310
+ console.log(chalk.red(` ✗ ${agent.name}: ${err.message}`));
311
+ results.push({ agent: agent.name, success: false, error: err.message });
312
+ }
313
+ }
314
+ return results;
315
+ }
316
+
279
317
  /**
280
318
  * Handler principal del comando install.
281
319
  * @param {string[]} args - Argumentos CLI
@@ -386,24 +424,7 @@ async function installCommand(args, config) {
386
424
  }
387
425
 
388
426
  // Configurar MCPs para cada agente seleccionado
389
- console.log(
390
- chalk.cyan('\n Configurando MCP servers (Power BI Modeling + Microsoft Learn)...\n')
391
- );
392
- const mcpResults = [];
393
- for (const agentId of selectedAgents) {
394
- const agent = AGENTS[agentId];
395
- try {
396
- const configPath = writeMcpConfigForAgent(agentId, packageDir);
397
- if (configPath) {
398
- const relPath = configPath.replace(baseDir, '~');
399
- console.log(chalk.green(` ✓ ${relPath} — ${agent.name}`));
400
- mcpResults.push({ agent: agent.name, configPath, success: true });
401
- }
402
- } catch (err) {
403
- console.log(chalk.red(` ✗ ${agent.name}: ${err.message}`));
404
- mcpResults.push({ agent: agent.name, success: false, error: err.message });
405
- }
406
- }
427
+ const mcpResults = configureMcpsForAgents(selectedAgents, packageDir, baseDir, chalk);
407
428
 
408
429
  // Resumen
409
430
  const totalAgents = agentResults.length + (universalAgents.length > 0 ? 1 : 0);
@@ -193,7 +193,14 @@ async function generate(targetDir, skills, options = {}) {
193
193
  JSON.stringify(pluginManifest, null, 2) + '\n'
194
194
  );
195
195
 
196
- // Generate marketplace.json with all skills listed and synced version
196
+ // Generate marketplace.json with all skills listed and synced version.
197
+ // The descriptions are derived from the actual skill count so they never
198
+ // drift out of sync with the real plugin contents.
199
+ const skillCount = skills.length;
200
+ const skillsPlural = skillCount === 1 ? 'skill' : 'skills';
201
+ const metadataDescription = `AI-powered ${skillsPlural} for Power BI Desktop development. Works with Claude Code, GitHub Copilot, Codex, Gemini CLI, and Kilo Code.`;
202
+ const pluginDescription = `${skillCount} AI ${skillsPlural} + Power BI Modeling and Microsoft Learn MCP servers for local Power BI Desktop workflows across 5 AI agents.`;
203
+
197
204
  const marketplaceManifest = {
198
205
  name: 'bi-superpowers',
199
206
  owner: {
@@ -201,16 +208,14 @@ async function generate(targetDir, skills, options = {}) {
201
208
  url: 'https://github.com/luquimbo',
202
209
  },
203
210
  metadata: {
204
- description:
205
- 'AI-powered skills for Power BI, Microsoft Fabric, and Excel development. 24 skills covering DAX, Power Query, data modeling, report design, governance, and more.',
211
+ description: metadataDescription,
206
212
  version,
207
213
  repository: 'https://github.com/luquimbo/bi-superpowers',
208
214
  },
209
215
  plugins: [
210
216
  {
211
217
  name: 'bi-superpowers',
212
- description:
213
- '24 AI skills for Power BI, Fabric & Excel — DAX, Power Query, data modeling, star schema design, report design, governance, deployment, and more.',
218
+ description: pluginDescription,
214
219
  source: './',
215
220
  strict: false,
216
221
  skills: skills.map((skill) => `./skills/${skill.name}`).sort(),
@@ -111,50 +111,113 @@ function escapeRegex(str) {
111
111
  }
112
112
 
113
113
  // ============================================
114
- // CLAUDE CODE
114
+ // JSON AGENT CONFIGURATIONS
115
115
  // ============================================
116
- // Writes ~/.claude.json, adding to `mcpServers`. Preserves other user-level
117
- // config in that file (projects, settings, etc.).
118
- function writeClaudeCodeConfig(packageDir) {
119
- const configPath = path.join(os.homedir(), '.claude.json');
120
- const existing = readJsonSafe(configPath) || {};
121
- const mcpServers = { ...(existing.mcpServers || {}) };
116
+ // The 4 JSON-based agents (Claude Code, GitHub Copilot, Gemini CLI,
117
+ // Kilo Code) all follow the same pattern: read existing JSON, merge
118
+ // our 2 servers into a wrapper key, write back. Each agent differs
119
+ // only in:
120
+ // - The config file path
121
+ // - The wrapper key (`mcpServers` vs `servers`)
122
+ // - Whether the stdio entry needs an explicit `type` field
123
+ // - The HTTP URL field name (`url` vs `httpUrl`)
124
+ //
125
+ // We describe each agent in a single table and build the writer functions
126
+ // from it so adding a new JSON agent is a one-line change.
122
127
 
123
- mcpServers[MODELING_SERVER_NAME] = {
128
+ const JSON_AGENT_CONFIGS = {
129
+ 'claude-code': {
130
+ // Claude Code user-scope MCP config lives in ~/.claude.json under
131
+ // mcpServers. stdio entries omit `type`, HTTP entries include it.
132
+ configPath: () => path.join(os.homedir(), '.claude.json'),
133
+ wrapperKey: 'mcpServers',
134
+ stdioIncludesType: false,
135
+ httpIncludesType: true,
136
+ httpField: 'url',
137
+ },
138
+ 'github-copilot': {
139
+ // Copilot CLI uses `servers` (NOT mcpServers) and every server needs
140
+ // an explicit `type` field.
141
+ configPath: () => path.join(os.homedir(), '.copilot', 'mcp-config.json'),
142
+ wrapperKey: 'servers',
143
+ stdioIncludesType: true,
144
+ httpIncludesType: true,
145
+ httpField: 'url',
146
+ },
147
+ 'gemini-cli': {
148
+ // Gemini uses `httpUrl` (NOT `url`) for HTTP transports and omits
149
+ // `type` — it's inferred from which key is present.
150
+ configPath: () => path.join(os.homedir(), '.gemini', 'settings.json'),
151
+ wrapperKey: 'mcpServers',
152
+ stdioIncludesType: false,
153
+ httpIncludesType: false,
154
+ httpField: 'httpUrl',
155
+ },
156
+ kilo: {
157
+ // Kilo uses the canonical `mcpServers` with `url` for HTTP.
158
+ configPath: () => path.join(os.homedir(), '.kilocode', 'mcp_settings.json'),
159
+ wrapperKey: 'mcpServers',
160
+ stdioIncludesType: false,
161
+ httpIncludesType: false,
162
+ httpField: 'url',
163
+ },
164
+ };
165
+
166
+ /**
167
+ * Build the two-server JSON payload for a JSON-format agent.
168
+ * Each agent differs in whether it wants `type` fields and which key
169
+ * holds the HTTP URL (`url` vs `httpUrl`).
170
+ *
171
+ * @param {Object} agentConfig - Descriptor from JSON_AGENT_CONFIGS
172
+ * @param {string} packageDir - Absolute path to the installed package
173
+ * @returns {Object} Map of server name → server config
174
+ */
175
+ function buildJsonServers(agentConfig, packageDir) {
176
+ // Modeling (stdio local launcher)
177
+ const modelingEntry = {
124
178
  command: 'node',
125
179
  args: [getLauncherAbsolutePath(packageDir)],
126
180
  };
127
- mcpServers[LEARN_SERVER_NAME] = {
128
- type: 'http',
129
- url: MICROSOFT_LEARN_URL,
130
- };
181
+ if (agentConfig.stdioIncludesType) {
182
+ // Put type first for readability
183
+ const { command, args } = modelingEntry;
184
+ Object.keys(modelingEntry).forEach((k) => delete modelingEntry[k]);
185
+ modelingEntry.type = 'stdio';
186
+ modelingEntry.command = command;
187
+ modelingEntry.args = args;
188
+ }
131
189
 
132
- writeJson(configPath, { ...existing, mcpServers });
133
- return configPath;
190
+ // Microsoft Learn (HTTP)
191
+ const learnEntry = {};
192
+ if (agentConfig.httpIncludesType) {
193
+ learnEntry.type = 'http';
194
+ }
195
+ learnEntry[agentConfig.httpField] = MICROSOFT_LEARN_URL;
196
+
197
+ return {
198
+ [MODELING_SERVER_NAME]: modelingEntry,
199
+ [LEARN_SERVER_NAME]: learnEntry,
200
+ };
134
201
  }
135
202
 
136
- // ============================================
137
- // GITHUB COPILOT CLI
138
- // ============================================
139
- // Writes ~/.copilot/mcp-config.json. Copilot uses `servers` (NOT mcpServers)
140
- // and each server has an explicit `type` field.
141
- function writeCopilotConfig(packageDir) {
142
- const configPath = path.join(os.homedir(), '.copilot', 'mcp-config.json');
143
- const existing = readJsonSafe(configPath) || {};
144
- const servers = { ...(existing.servers || {}) };
203
+ /**
204
+ * Create a writer function for a JSON-format agent.
205
+ * The returned function reads the existing config (if any), merges in
206
+ * our 2 servers, and writes back, preserving all other fields.
207
+ */
208
+ function makeJsonWriter(agentId) {
209
+ const agentConfig = JSON_AGENT_CONFIGS[agentId];
210
+ return function jsonWriter(packageDir) {
211
+ const configPath = agentConfig.configPath();
212
+ const existing = readJsonSafe(configPath) || {};
213
+ const wrapperKey = agentConfig.wrapperKey;
145
214
 
146
- servers[MODELING_SERVER_NAME] = {
147
- type: 'stdio',
148
- command: 'node',
149
- args: [getLauncherAbsolutePath(packageDir)],
150
- };
151
- servers[LEARN_SERVER_NAME] = {
152
- type: 'http',
153
- url: MICROSOFT_LEARN_URL,
154
- };
215
+ const newServers = buildJsonServers(agentConfig, packageDir);
216
+ const mergedServers = { ...(existing[wrapperKey] || {}), ...newServers };
155
217
 
156
- writeJson(configPath, { ...existing, servers });
157
- return configPath;
218
+ writeJson(configPath, { ...existing, [wrapperKey]: mergedServers });
219
+ return configPath;
220
+ };
158
221
  }
159
222
 
160
223
  // ============================================
@@ -201,59 +264,17 @@ function writeCodexConfig(packageDir) {
201
264
  return configPath;
202
265
  }
203
266
 
204
- // ============================================
205
- // GEMINI CLI
206
- // ============================================
207
- // Writes ~/.gemini/settings.json. Gemini uses `mcpServers` and `httpUrl`
208
- // (not `url`) for HTTP transports.
209
- function writeGeminiConfig(packageDir) {
210
- const configPath = path.join(os.homedir(), '.gemini', 'settings.json');
211
- const existing = readJsonSafe(configPath) || {};
212
- const mcpServers = { ...(existing.mcpServers || {}) };
213
-
214
- mcpServers[MODELING_SERVER_NAME] = {
215
- command: 'node',
216
- args: [getLauncherAbsolutePath(packageDir)],
217
- };
218
- mcpServers[LEARN_SERVER_NAME] = {
219
- httpUrl: MICROSOFT_LEARN_URL,
220
- };
221
-
222
- writeJson(configPath, { ...existing, mcpServers });
223
- return configPath;
224
- }
225
-
226
- // ============================================
227
- // KILO CODE
228
- // ============================================
229
- // Writes ~/.kilocode/mcp_settings.json. Kilo uses `mcpServers` and `url`
230
- // for HTTP transports.
231
- function writeKiloConfig(packageDir) {
232
- const configPath = path.join(os.homedir(), '.kilocode', 'mcp_settings.json');
233
- const existing = readJsonSafe(configPath) || {};
234
- const mcpServers = { ...(existing.mcpServers || {}) };
235
-
236
- mcpServers[MODELING_SERVER_NAME] = {
237
- command: 'node',
238
- args: [getLauncherAbsolutePath(packageDir)],
239
- };
240
- mcpServers[LEARN_SERVER_NAME] = {
241
- url: MICROSOFT_LEARN_URL,
242
- };
243
-
244
- writeJson(configPath, { ...existing, mcpServers });
245
- return configPath;
246
- }
247
-
248
267
  /**
249
268
  * Registry mapping agent IDs to their MCP config writers.
269
+ * JSON agents are generated from JSON_AGENT_CONFIGS via makeJsonWriter;
270
+ * Codex is the only TOML-based agent and has its own writer.
250
271
  */
251
272
  const MCP_WRITERS = {
252
- 'claude-code': writeClaudeCodeConfig,
253
- 'github-copilot': writeCopilotConfig,
273
+ 'claude-code': makeJsonWriter('claude-code'),
274
+ 'github-copilot': makeJsonWriter('github-copilot'),
254
275
  codex: writeCodexConfig,
255
- 'gemini-cli': writeGeminiConfig,
256
- kilo: writeKiloConfig,
276
+ 'gemini-cli': makeJsonWriter('gemini-cli'),
277
+ kilo: makeJsonWriter('kilo'),
257
278
  };
258
279
 
259
280
  /**
@@ -204,6 +204,43 @@ describe('MCP writers — JSON agents', () => {
204
204
  });
205
205
  }
206
206
 
207
+ // Cross-agent sanity check: verifies each JSON agent writes the HTTP
208
+ // URL under the correct key per its spec. Catches copy-paste errors
209
+ // where someone might accidentally use `url` for Gemini (which expects
210
+ // `httpUrl`) or vice versa.
211
+ test('each JSON agent uses the correct HTTP field name', () => {
212
+ const expectations = {
213
+ 'claude-code': { key: 'url', wrapperKey: 'mcpServers' },
214
+ 'github-copilot': { key: 'url', wrapperKey: 'servers' },
215
+ 'gemini-cli': { key: 'httpUrl', wrapperKey: 'mcpServers' },
216
+ kilo: { key: 'url', wrapperKey: 'mcpServers' },
217
+ };
218
+
219
+ for (const [agentId, expected] of Object.entries(expectations)) {
220
+ // Reset home for each agent so configs don't leak between writes.
221
+ fs.rmSync(tempHome, { recursive: true, force: true });
222
+ fs.mkdirSync(tempHome, { recursive: true });
223
+
224
+ const configPath = MCP_WRITERS[agentId](fakePkgDir);
225
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
226
+ const learnEntry = config[expected.wrapperKey][LEARN_SERVER_NAME];
227
+
228
+ assert.strictEqual(
229
+ learnEntry[expected.key],
230
+ MICROSOFT_LEARN_URL,
231
+ `${agentId} should use "${expected.key}" for HTTP URL`
232
+ );
233
+
234
+ // And it should NOT use the wrong key (defensive check)
235
+ const wrongKey = expected.key === 'url' ? 'httpUrl' : 'url';
236
+ assert.strictEqual(
237
+ learnEntry[wrongKey],
238
+ undefined,
239
+ `${agentId} should NOT have "${wrongKey}"`
240
+ );
241
+ }
242
+ });
243
+
207
244
  test('writeJson refuses to overwrite symlinked target', () => {
208
245
  const realFile = path.join(tempHome, 'real-claude.json');
209
246
  const linkFile = path.join(tempHome, '.claude.json');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luquimbo/bi-superpowers",
3
- "version": "3.0.1",
3
+ "version": "3.1.0",
4
4
  "description": "Plugin-first Claude Code toolkit for Power BI, Microsoft Fabric, and Excel workflows powered by official Microsoft MCP servers.",
5
5
  "main": "bin/cli.js",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "pbi-connect"
3
3
  description: "Use when the user asks about Power BI MCP Connection Skill, especially phrases like \"connect Power BI\", \"modeling mcp\", \"Power BI Desktop\", \"conectar Power BI\", \"can't connect to Power BI\"."
4
- version: "3.0.1"
4
+ version: "3.1.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/pbi-connect.md instead. -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "project-kickoff"
3
3
  description: "Project Kickoff Skill: Project analysis and planning."
4
- version: "3.0.1"
4
+ version: "3.1.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/project-kickoff.md instead. -->