@luquimbo/bi-superpowers 3.1.0 → 3.2.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 (110) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.claude-plugin/skill-manifest.json +1 -1
  4. package/.plugin/plugin.json +1 -1
  5. package/README.md +2 -2
  6. package/bin/build-plugin.js +6 -6
  7. package/bin/cli.js +169 -310
  8. package/bin/commands/install.js +87 -70
  9. package/bin/commands/install.test.js +2 -2
  10. package/bin/lib/agents.js +21 -2
  11. package/bin/lib/mcp-config.js +27 -5
  12. package/bin/lib/mcp-config.test.js +1 -1
  13. package/desktop-extension/manifest.json +4 -11
  14. package/desktop-extension/server.js +34 -25
  15. package/package.json +3 -9
  16. package/skills/pbi-connect/SKILL.md +1 -1
  17. package/skills/project-kickoff/SKILL.md +1 -1
  18. package/bin/commands/add.js +0 -533
  19. package/bin/commands/add.test.js +0 -77
  20. package/bin/commands/changelog.js +0 -443
  21. package/bin/commands/pull.js +0 -287
  22. package/bin/commands/pull.test.js +0 -36
  23. package/bin/commands/push.js +0 -231
  24. package/bin/commands/push.test.js +0 -14
  25. package/bin/commands/search.js +0 -344
  26. package/bin/commands/search.test.js +0 -115
  27. package/bin/commands/setup.js +0 -545
  28. package/bin/commands/setup.test.js +0 -46
  29. package/bin/commands/sync-profile.js +0 -405
  30. package/bin/commands/sync-profile.test.js +0 -14
  31. package/bin/commands/sync-source.js +0 -418
  32. package/bin/commands/sync-source.test.js +0 -14
  33. package/bin/utils/errors.js +0 -159
  34. package/bin/utils/git.js +0 -298
  35. package/bin/utils/logger.js +0 -142
  36. package/bin/utils/pbix.js +0 -305
  37. package/bin/utils/pbix.test.js +0 -37
  38. package/bin/utils/profiles.js +0 -312
  39. package/bin/utils/projects.js +0 -169
  40. package/bin/utils/readline.js +0 -206
  41. package/bin/utils/readline.test.js +0 -47
  42. package/docs/openrouter-free-models.md +0 -92
  43. package/library/examples/README.md +0 -151
  44. package/library/examples/finance-reporting/README.md +0 -351
  45. package/library/examples/finance-reporting/data-model.md +0 -267
  46. package/library/examples/finance-reporting/measures.dax +0 -557
  47. package/library/examples/hr-analytics/README.md +0 -371
  48. package/library/examples/hr-analytics/data-model.md +0 -315
  49. package/library/examples/hr-analytics/measures.dax +0 -460
  50. package/library/examples/marketing-analytics/README.md +0 -37
  51. package/library/examples/marketing-analytics/data-model.md +0 -62
  52. package/library/examples/marketing-analytics/measures.dax +0 -110
  53. package/library/examples/retail-analytics/README.md +0 -439
  54. package/library/examples/retail-analytics/data-model.md +0 -288
  55. package/library/examples/retail-analytics/measures.dax +0 -481
  56. package/library/examples/supply-chain/README.md +0 -37
  57. package/library/examples/supply-chain/data-model.md +0 -69
  58. package/library/examples/supply-chain/measures.dax +0 -77
  59. package/library/examples/udf-library/README.md +0 -228
  60. package/library/examples/udf-library/functions.dax +0 -571
  61. package/library/snippets/dax/README.md +0 -292
  62. package/library/snippets/dax/business-domains.md +0 -576
  63. package/library/snippets/dax/calculate-patterns.md +0 -276
  64. package/library/snippets/dax/calculation-groups.md +0 -489
  65. package/library/snippets/dax/error-handling.md +0 -495
  66. package/library/snippets/dax/iterators-and-aggregations.md +0 -474
  67. package/library/snippets/dax/kpis-and-metrics.md +0 -293
  68. package/library/snippets/dax/rankings-and-topn.md +0 -235
  69. package/library/snippets/dax/security-patterns.md +0 -413
  70. package/library/snippets/dax/text-and-formatting.md +0 -316
  71. package/library/snippets/dax/time-intelligence.md +0 -196
  72. package/library/snippets/dax/user-defined-functions.md +0 -477
  73. package/library/snippets/dax/virtual-tables.md +0 -546
  74. package/library/snippets/excel-formulas/README.md +0 -84
  75. package/library/snippets/excel-formulas/aggregations.md +0 -330
  76. package/library/snippets/excel-formulas/dates-and-times.md +0 -361
  77. package/library/snippets/excel-formulas/dynamic-arrays.md +0 -314
  78. package/library/snippets/excel-formulas/lookups.md +0 -169
  79. package/library/snippets/excel-formulas/text-functions.md +0 -363
  80. package/library/snippets/governance/naming-conventions.md +0 -97
  81. package/library/snippets/governance/review-checklists.md +0 -107
  82. package/library/snippets/power-query/README.md +0 -389
  83. package/library/snippets/power-query/api-integration.md +0 -707
  84. package/library/snippets/power-query/connections.md +0 -434
  85. package/library/snippets/power-query/data-cleaning.md +0 -298
  86. package/library/snippets/power-query/error-handling.md +0 -526
  87. package/library/snippets/power-query/parameters.md +0 -350
  88. package/library/snippets/power-query/performance.md +0 -506
  89. package/library/snippets/power-query/transformations.md +0 -330
  90. package/library/snippets/report-design/accessibility.md +0 -78
  91. package/library/snippets/report-design/chart-selection.md +0 -54
  92. package/library/snippets/report-design/layout-patterns.md +0 -87
  93. package/library/templates/data-models/README.md +0 -93
  94. package/library/templates/data-models/finance-model.md +0 -627
  95. package/library/templates/data-models/retail-star-schema.md +0 -473
  96. package/library/templates/excel/README.md +0 -83
  97. package/library/templates/excel/budget-tracker.md +0 -432
  98. package/library/templates/excel/data-entry-form.md +0 -533
  99. package/library/templates/power-bi/README.md +0 -72
  100. package/library/templates/power-bi/finance-report.md +0 -449
  101. package/library/templates/power-bi/kpi-scorecard.md +0 -461
  102. package/library/templates/power-bi/sales-dashboard.md +0 -281
  103. package/library/themes/excel/README.md +0 -436
  104. package/library/themes/power-bi/README.md +0 -271
  105. package/library/themes/power-bi/accessible.json +0 -307
  106. package/library/themes/power-bi/bi-superpowers-default.json +0 -858
  107. package/library/themes/power-bi/corporate-blue.json +0 -291
  108. package/library/themes/power-bi/dark-mode.json +0 -291
  109. package/library/themes/power-bi/minimal.json +0 -292
  110. package/library/themes/power-bi/print-friendly.json +0 -309
@@ -1,18 +1,28 @@
1
1
  /**
2
- * Install Command - Multi-agent skill installer
3
- * ===============================================
2
+ * Install Command Multi-agent skill + MCP installer
3
+ * =====================================================
4
4
  *
5
- * Instala los skills de BI Agent Superpowers en los directorios
6
- * correctos para cada agente AI. Inspirado en el CLI `npx skills` de
7
- * Vercel Labs.
5
+ * Installs the bi-superpowers skills and Microsoft MCP servers into
6
+ * every supported AI coding agent:
7
+ * - Claude Code
8
+ * - GitHub Copilot
9
+ * - Codex (OpenAI)
10
+ * - Gemini CLI
11
+ * - Kilo Code
8
12
  *
9
- * Los skills siempre se instalan a nivel de usuario (~/) para no
10
- * contaminar el repo del proyecto. El usuario los obtiene una vez
11
- * y los comparte con todos sus proyectos.
13
+ * Everything is installed at the user level (~/) so it applies across
14
+ * all projects without polluting any specific repo. Skills land in
15
+ * ~/.agents/skills/ (universal path) and each agent's own skill dir is
16
+ * symlinked to that universal copy. MCPs are written to each agent's
17
+ * expected config file in the format that agent requires (JSON for
18
+ * most, TOML for Codex) — see `lib/mcp-config.js` for details.
12
19
  *
13
- * Open source: todo el contenido ship gratis con el paquete npm.
20
+ * Fully open source (MIT). The user-facing messages are in Spanish to
21
+ * match the primary Spanish-speaking audience of the project; code
22
+ * comments and JSDoc stay in English so contributors from any language
23
+ * can work on the source.
14
24
  *
15
- * Uso:
25
+ * Usage:
16
26
  * npx @luquimbo/bi-superpowers install
17
27
  * super install
18
28
  * super install --agent claude-code --agent codex
@@ -29,9 +39,10 @@ const { AGENTS, UNIVERSAL_DIR } = require('../lib/agents');
29
39
  const { writeMcpConfigForAgent } = require('../lib/mcp-config');
30
40
 
31
41
  /**
32
- * Detecta qué agentes están instalados revisando sus directorios de config.
33
- * @param {string} baseDir - Directorio base (home del usuario)
34
- * @returns {string[]} IDs de agentes detectados
42
+ * Detect which agents are installed by checking their config directories.
43
+ * Used in the interactive installer to pre-select the detected agents.
44
+ * @param {string} baseDir - Base directory to scan (typically the user's home)
45
+ * @returns {string[]} IDs of detected agents
35
46
  */
36
47
  function detectAgents(baseDir) {
37
48
  const detected = [];
@@ -46,7 +57,7 @@ function detectAgents(baseDir) {
46
57
  }
47
58
 
48
59
  /**
49
- * Crea una interface de readline para prompts interactivos.
60
+ * Create a readline interface for interactive prompts.
50
61
  */
51
62
  function createReadline() {
52
63
  return readline.createInterface({
@@ -56,7 +67,7 @@ function createReadline() {
56
67
  }
57
68
 
58
69
  /**
59
- * Envuelve una pregunta de readline en una Promise.
70
+ * Promise-wrapped readline question.
60
71
  */
61
72
  function prompt(rl, question) {
62
73
  return new Promise((resolve) => {
@@ -65,11 +76,11 @@ function prompt(rl, question) {
65
76
  }
66
77
 
67
78
  /**
68
- * Muestra una lista numerada y deja al usuario elegir múltiples items.
79
+ * Render a numbered multi-select list and wait for the user's choice.
69
80
  * @param {readline.Interface} rl
70
81
  * @param {Array<{id: string, name: string}>} items
71
- * @param {string[]} preselected - IDs preseleccionados
72
- * @returns {Promise<string[]>} IDs seleccionados
82
+ * @param {string[]} preselected - IDs that should default to selected
83
+ * @returns {Promise<string[]>} Selected IDs
73
84
  */
74
85
  async function selectMultiple(rl, items, preselected = []) {
75
86
  items.forEach((item, i) => {
@@ -77,8 +88,8 @@ async function selectMultiple(rl, items, preselected = []) {
77
88
  console.log(` ${i + 1}) ${marker} ${item.name}`);
78
89
  });
79
90
  console.log();
80
- console.log(' Ingresa números separados por comas (ej: 1,2,3)');
81
- console.log(' Presiona Enter para los detectados, o "a" para todos');
91
+ console.log(' Ingresá números separados por comas (ej: 1,2,3)');
92
+ console.log(' Presioná Enter para los detectados, o "a" para todos');
82
93
 
83
94
  const answer = await prompt(rl, '\n > ');
84
95
 
@@ -99,10 +110,10 @@ async function selectMultiple(rl, items, preselected = []) {
99
110
  }
100
111
 
101
112
  /**
102
- * Copia un directorio de skill recursivamente.
103
- * Los errores de fs se propagan al caller para manejo centralizado.
104
- * @param {string} srcDir - Directorio de skill fuente (contiene SKILL.md)
105
- * @param {string} destDir - Directorio destino
113
+ * Recursively copy a skill directory (SKILL.md + references/ + scripts/).
114
+ * Filesystem errors propagate to the caller for centralized handling.
115
+ * @param {string} srcDir - Source skill directory
116
+ * @param {string} destDir - Destination directory
106
117
  */
107
118
  function copySkillDir(srcDir, destDir) {
108
119
  if (!fs.existsSync(destDir)) {
@@ -123,11 +134,12 @@ function copySkillDir(srcDir, destDir) {
123
134
  }
124
135
 
125
136
  /**
126
- * Formatea un error de filesystem con hint útil según el código.
137
+ * Format a filesystem error with a user-friendly hint based on the code.
138
+ * Keeps the underlying error message so debugging is still possible.
127
139
  */
128
140
  function formatFsError(err, context) {
129
141
  const codeHints = {
130
- EACCES: 'Permiso denegado. Revisa los permisos del directorio.',
142
+ EACCES: 'Permiso denegado. Revisá los permisos del directorio.',
131
143
  EPERM: 'Operación no permitida. En Windows, probá ejecutar como Administrador.',
132
144
  ENOSPC: 'No hay espacio en disco.',
133
145
  ENOENT: 'Archivo o directorio no existe.',
@@ -138,8 +150,9 @@ function formatFsError(err, context) {
138
150
  }
139
151
 
140
152
  /**
141
- * Parsea las flags de CLI y devuelve un objeto de opciones.
142
- * Valida que cada --agent/-a tenga un valor asociado.
153
+ * Parse CLI arguments into an options object.
154
+ * Validates that each --agent / -a flag is followed by a real value
155
+ * so we don't silently accept dangling flags.
143
156
  */
144
157
  function parseArgs(args) {
145
158
  const opts = {
@@ -152,7 +165,7 @@ function parseArgs(args) {
152
165
  if (args[i] === '--agent' || args[i] === '-a') {
153
166
  const next = args[i + 1];
154
167
  if (next === undefined || next.startsWith('-')) {
155
- // Falta valor para --agent; avisamos y seguimos sin ese flag
168
+ // Missing value warn and skip this flag instead of crashing.
156
169
  console.warn(`⚠ Flag ${args[i]} sin valor. Uso: ${args[i]} <agente-id>. Ignorando.`);
157
170
  continue;
158
171
  }
@@ -165,8 +178,8 @@ function parseArgs(args) {
165
178
  }
166
179
 
167
180
  /**
168
- * Resuelve qué agentes instalar según las flags y el modo interactivo.
169
- * @returns {Promise<string[]>} IDs de agentes seleccionados
181
+ * Resolve which agents to install based on flags or interactive prompt.
182
+ * @returns {Promise<string[]>} IDs of the selected agents
170
183
  */
171
184
  async function resolveSelectedAgents(opts, baseDir, chalk) {
172
185
  if (opts.isAll) {
@@ -187,7 +200,7 @@ async function resolveSelectedAgents(opts, baseDir, chalk) {
187
200
  return Object.keys(AGENTS);
188
201
  }
189
202
 
190
- // Modo interactivo
203
+ // Interactive mode — detect installed agents and prompt the user.
191
204
  const detected = detectAgents(baseDir);
192
205
  console.log(chalk.cyan(' Seleccioná los agentes donde querés instalar:\n'));
193
206
 
@@ -205,11 +218,13 @@ async function resolveSelectedAgents(opts, baseDir, chalk) {
205
218
  }
206
219
 
207
220
  /**
208
- * Instala los skills en los directorios seleccionados.
221
+ * Copy skills into the universal path first, then either symlink or copy
222
+ * each selected agent's skill directory to point at the universal copy.
209
223
  * @returns {{agentResults: Array, copyFallbacks: number}}
210
224
  */
211
225
  function performInstall(skillsSourceDir, skillDirs, selectedAgents, baseDir) {
212
- // Siempre instalamos primero en el path universal
226
+ // Always install to the universal path first. This is the source of
227
+ // truth that all other agent dirs symlink to.
213
228
  const universalTarget = path.join(baseDir, UNIVERSAL_DIR);
214
229
  for (const skill of skillDirs) {
215
230
  const src = path.join(skillsSourceDir, skill);
@@ -217,17 +232,17 @@ function performInstall(skillsSourceDir, skillDirs, selectedAgents, baseDir) {
217
232
  copySkillDir(src, dest);
218
233
  }
219
234
 
220
- // Symlinks o copias para los directorios específicos de agentes
221
235
  const agentResults = [];
222
236
  let copyFallbacks = 0;
223
237
 
224
238
  for (const agentId of selectedAgents) {
225
239
  const agent = AGENTS[agentId];
226
- if (agent.dir === UNIVERSAL_DIR) continue; // Ya manejado por el universal
240
+ if (agent.dir === UNIVERSAL_DIR) continue; // Already handled above.
227
241
 
228
242
  const agentTarget = path.join(baseDir, agent.dir);
229
243
 
230
- // Si ya existe y no es symlink, copiamos ahí directamente
244
+ // If a real directory already exists (not a symlink), copy into it
245
+ // directly so we don't destroy user data.
231
246
  if (fs.existsSync(agentTarget) && !fs.lstatSync(agentTarget).isSymbolicLink()) {
232
247
  for (const skill of skillDirs) {
233
248
  copySkillDir(path.join(skillsSourceDir, skill), path.join(agentTarget, skill));
@@ -241,22 +256,23 @@ function performInstall(skillsSourceDir, skillDirs, selectedAgents, baseDir) {
241
256
  fs.mkdirSync(parentDir, { recursive: true });
242
257
  }
243
258
 
244
- // Eliminar symlink existente si hay uno (para poder recrearlo)
259
+ // Remove any pre-existing symlink so we can recreate it cleanly.
245
260
  if (fs.existsSync(agentTarget) || fs.lstatSync(agentTarget, { throwIfNoEntry: false })) {
246
261
  try {
247
262
  fs.unlinkSync(agentTarget);
248
263
  } catch (_) {
249
- /* no existe, seguimos */
264
+ /* target didn't exist after all — that's fine */
250
265
  }
251
266
  }
252
267
 
253
268
  try {
254
- // Symlink relativo del dir del agente al dir universal
269
+ // Relative symlink from the agent's dir to the universal dir.
255
270
  const relPath = path.relative(parentDir, universalTarget);
256
271
  fs.symlinkSync(relPath, agentTarget);
257
272
  agentResults.push({ agent: agent.name, method: 'symlinked', dir: agent.dir });
258
273
  } catch (symlinkErr) {
259
- // Fallback a copia si el symlink falla (ej: Windows sin permisos)
274
+ // Windows without admin can't create symlinks. Fall back to copy
275
+ // and remember so we can warn the user at the end.
260
276
  copyFallbacks++;
261
277
  if (!fs.existsSync(agentTarget)) {
262
278
  fs.mkdirSync(agentTarget, { recursive: true });
@@ -277,18 +293,18 @@ function performInstall(skillsSourceDir, skillDirs, selectedAgents, baseDir) {
277
293
  }
278
294
 
279
295
  /**
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.
296
+ * Configure the 2 MCP servers (powerbi-modeling + microsoft-learn)
297
+ * for each selected agent by writing the config file in the path and
298
+ * format that agent expects.
283
299
  *
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.
300
+ * Per-agent errors don't abort the whole flow: they're collected in the
301
+ * results array so the caller can display them and decide on the exit
302
+ * code.
287
303
  *
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
304
+ * @param {string[]} selectedAgents - Agent IDs to configure
305
+ * @param {string} packageDir - Absolute path to the installed package
306
+ * @param {string} baseDir - User's home directory (for display)
307
+ * @param {Object} chalk - chalk instance for colored output
292
308
  * @returns {Array<{agent: string, success: boolean, configPath?: string, error?: string}>}
293
309
  */
294
310
  function configureMcpsForAgents(selectedAgents, packageDir, baseDir, chalk) {
@@ -315,9 +331,9 @@ function configureMcpsForAgents(selectedAgents, packageDir, baseDir, chalk) {
315
331
  }
316
332
 
317
333
  /**
318
- * Handler principal del comando install.
319
- * @param {string[]} args - Argumentos CLI
320
- * @param {Object} config - Config del comando desde el CLI
334
+ * Main handler for the `super install` command.
335
+ * @param {string[]} args - CLI arguments
336
+ * @param {Object} config - Command config from the CLI (packageDir, version)
321
337
  */
322
338
  async function installCommand(args, config) {
323
339
  const chalk = require('chalk');
@@ -325,10 +341,11 @@ async function installCommand(args, config) {
325
341
 
326
342
  const opts = parseArgs(args);
327
343
 
328
- // Siempre a nivel usuario (home) para proteger contenido licenciado
344
+ // Always install at the user level (home directory) skills and MCPs
345
+ // apply across all projects without polluting any specific repo.
329
346
  const baseDir = os.homedir();
330
347
 
331
- // Localizar los skills del paquete
348
+ // Locate the skills inside the installed package.
332
349
  const packageDir = config.packageDir || path.dirname(path.dirname(__dirname));
333
350
  const skillsSourceDir = path.join(packageDir, 'skills');
334
351
 
@@ -341,7 +358,7 @@ async function installCommand(args, config) {
341
358
  process.exit(1);
342
359
  }
343
360
 
344
- // Leer skills disponibles
361
+ // Read the available skill directories from the package.
345
362
  let skillDirs;
346
363
  try {
347
364
  skillDirs = fs
@@ -355,7 +372,7 @@ async function installCommand(args, config) {
355
372
  process.exit(1);
356
373
  }
357
374
 
358
- // Header
375
+ // Header box shown at the top of every install run.
359
376
  console.log(
360
377
  boxen(
361
378
  chalk.bold.cyan('BI Agent Superpowers') +
@@ -373,7 +390,7 @@ async function installCommand(args, config) {
373
390
  console.log(chalk.gray(` Ruta de instalación: ~/${UNIVERSAL_DIR}/`));
374
391
  console.log(chalk.gray(` Skills: ${skillDirs.length} disponibles\n`));
375
392
 
376
- // Resolver qué agentes instalar
393
+ // Resolve which agents to configure.
377
394
  const selectedAgents = await resolveSelectedAgents(opts, baseDir, chalk);
378
395
 
379
396
  if (selectedAgents.length === 0) {
@@ -387,7 +404,7 @@ async function installCommand(args, config) {
387
404
  )
388
405
  );
389
406
 
390
- // Instalar skills
407
+ // Phase 1: copy skills and create symlinks per agent.
391
408
  let agentResults;
392
409
  let copyFallbacks;
393
410
  try {
@@ -399,20 +416,20 @@ async function installCommand(args, config) {
399
416
  process.exit(1);
400
417
  }
401
418
 
402
- // Universal skills path
419
+ // Report on the universal skills path.
403
420
  const universalAgents = selectedAgents
404
421
  .filter((id) => AGENTS[id] && AGENTS[id].dir === UNIVERSAL_DIR)
405
422
  .map((id) => AGENTS[id].name);
406
423
  const universalSuffix = universalAgents.length > 0 ? ` — ${universalAgents.join(', ')}` : '';
407
424
  console.log(chalk.green(` ✓ ${UNIVERSAL_DIR}/ (${skillDirs.length} skills)${universalSuffix}`));
408
425
 
409
- // Skills por agente
426
+ // Report per agent.
410
427
  for (const result of agentResults) {
411
428
  const icon = result.method === 'symlinked' ? '→' : '✓';
412
429
  console.log(chalk.green(` ${icon} ${result.dir}/ (${result.method}) — ${result.agent}`));
413
430
  }
414
431
 
415
- // Aviso si hubo fallbacks de symlink a copia
432
+ // Warn if any agent fell back from symlink to copy.
416
433
  if (copyFallbacks > 0) {
417
434
  console.log(
418
435
  chalk.yellow(
@@ -423,10 +440,10 @@ async function installCommand(args, config) {
423
440
  );
424
441
  }
425
442
 
426
- // Configurar MCPs para cada agente seleccionado
443
+ // Phase 2: write the 2 MCP configs per agent.
427
444
  const mcpResults = configureMcpsForAgents(selectedAgents, packageDir, baseDir, chalk);
428
445
 
429
- // Resumen
446
+ // Build the final summary box.
430
447
  const totalAgents = agentResults.length + (universalAgents.length > 0 ? 1 : 0);
431
448
  const mcpSuccess = mcpResults.filter((r) => r.success).length;
432
449
  const mcpFailures = mcpResults.filter((r) => !r.success);
@@ -457,7 +474,7 @@ async function installCommand(args, config) {
457
474
  '\n' +
458
475
  chalk.gray(' /project-kickoff — Analizá tu proyecto BI') +
459
476
  '\n' +
460
- chalk.gray(' /pbi-connect — Conectá Claude a Power BI Desktop'),
477
+ chalk.gray(' /pbi-connect — Conectá tu agente a Power BI Desktop'),
461
478
  {
462
479
  padding: 1,
463
480
  margin: { top: 1 },
@@ -468,14 +485,14 @@ async function installCommand(args, config) {
468
485
  );
469
486
 
470
487
  if (hasFailures) {
471
- // Non-zero exit so CI/scripts know something went wrong, but skills
472
- // still got installed we use exit code 2 to distinguish from total
473
- // failure (exit 1).
488
+ // Non-zero exit so CI/scripts know something went wrong. Exit code 2
489
+ // distinguishes partial failure (skills ok, some MCPs failed) from
490
+ // total failure (exit code 1).
474
491
  process.exitCode = 2;
475
492
  }
476
493
  }
477
494
 
478
- // Exports internos para testing
495
+ // Internal exports for testing.
479
496
  module.exports = installCommand;
480
497
  module.exports.parseArgs = parseArgs;
481
498
  module.exports.detectAgents = detectAgents;
@@ -110,7 +110,7 @@ describe('install command - detectAgents', () => {
110
110
 
111
111
  test('detects multiple agents', () => {
112
112
  fs.mkdirSync(path.join(tempDir, '.claude'), { recursive: true });
113
- fs.mkdirSync(path.join(tempDir, '.github'), { recursive: true });
113
+ fs.mkdirSync(path.join(tempDir, '.copilot'), { recursive: true });
114
114
  fs.mkdirSync(path.join(tempDir, '.gemini'), { recursive: true });
115
115
  const detected = detectAgents(tempDir);
116
116
  assert.ok(detected.includes('claude-code'));
@@ -270,7 +270,7 @@ describe('install command - integration: --all --yes', () => {
270
270
  path.join(tempHome, '.copilot', 'mcp-config.json'),
271
271
  path.join(tempHome, '.codex', 'config.toml'),
272
272
  path.join(tempHome, '.gemini', 'settings.json'),
273
- path.join(tempHome, '.kilocode', 'mcp_settings.json'),
273
+ path.join(tempHome, '.kilo', 'mcp_settings.json'),
274
274
  ];
275
275
  for (const filePath of expectedMcpFiles) {
276
276
  assert.ok(fs.existsSync(filePath), `expected MCP config at ${filePath}`);
package/bin/lib/agents.js CHANGED
@@ -22,13 +22,32 @@
22
22
  /**
23
23
  * Supported agents, in the order they appear in the interactive installer.
24
24
  * Object insertion order matters — the installer renders the list in this order.
25
+ *
26
+ * IMPORTANT: the `dir` values are the USER-LEVEL skill directories for each
27
+ * agent. They're relative to the user's home directory (installed with
28
+ * `super install` at ~/). Each path comes from official docs — if you
29
+ * change one, verify it against the linked source first.
30
+ *
31
+ * Source docs:
32
+ * - GitHub Copilot: https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/create-skills
33
+ * "For personal skills, shared across projects, use ~/.copilot/skills"
34
+ * (NOT ~/.github/skills — that's project-level for the cloud agent.)
35
+ * - Claude Code: https://code.claude.com/docs/en/skills
36
+ * "Personal skills go in ~/.claude/skills/"
37
+ * - Codex: https://developers.openai.com/codex/skills
38
+ * "Personal skills go in ~/.codex/skills or ~/.agents/skills"
39
+ * We use the universal ~/.agents/skills path.
40
+ * - Gemini CLI: https://geminicli.com/docs/cli/skills/
41
+ * "User skills in $HOME/.gemini/skills"
42
+ * - Kilo Code: https://kilo.ai/docs/customize/skills
43
+ * "Global skills in ~/.kilo/skills/" (NOT ~/.kilocode/)
25
44
  */
26
45
  const AGENTS = {
27
- 'github-copilot': { name: 'GitHub Copilot', dir: '.github/skills' },
46
+ 'github-copilot': { name: 'GitHub Copilot', dir: '.copilot/skills' },
28
47
  'claude-code': { name: 'Claude Code', dir: '.claude/skills' },
29
48
  codex: { name: 'Codex (OpenAI)', dir: '.agents/skills' },
30
49
  'gemini-cli': { name: 'Gemini CLI', dir: '.gemini/skills' },
31
- kilo: { name: 'Kilo Code', dir: '.kilocode/skills' },
50
+ kilo: { name: 'Kilo Code', dir: '.kilo/skills' },
32
51
  };
33
52
 
34
53
  /**
@@ -124,11 +124,19 @@ function escapeRegex(str) {
124
124
  //
125
125
  // We describe each agent in a single table and build the writer functions
126
126
  // from it so adding a new JSON agent is a one-line change.
127
+ //
128
+ // IMPORTANT: every entry below has a source URL pointing at the official
129
+ // docs where the path/format comes from. If you change any of these,
130
+ // verify the new value against the linked source first. Silent drift is
131
+ // the hardest class of bug in this file because the writes succeed even
132
+ // when the agent never reads the file.
127
133
 
128
134
  const JSON_AGENT_CONFIGS = {
129
135
  'claude-code': {
130
- // Claude Code user-scope MCP config lives in ~/.claude.json under
131
- // mcpServers. stdio entries omit `type`, HTTP entries include it.
136
+ // Source: https://code.claude.com/docs/en/mcp
137
+ // "User-scoped servers are stored in ~/.claude.json"
138
+ // Note: ~/.claude/settings.json does NOT work for MCPs (known docs bug).
139
+ // stdio entries omit `type` here; HTTP entries include it.
132
140
  configPath: () => path.join(os.homedir(), '.claude.json'),
133
141
  wrapperKey: 'mcpServers',
134
142
  stdioIncludesType: false,
@@ -136,7 +144,9 @@ const JSON_AGENT_CONFIGS = {
136
144
  httpField: 'url',
137
145
  },
138
146
  'github-copilot': {
139
- // Copilot CLI uses `servers` (NOT mcpServers) and every server needs
147
+ // Source: https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers
148
+ // "MCP servers are saved to ~/.copilot/mcp-config.json"
149
+ // Copilot uses `servers` (NOT mcpServers) and every server needs
140
150
  // an explicit `type` field.
141
151
  configPath: () => path.join(os.homedir(), '.copilot', 'mcp-config.json'),
142
152
  wrapperKey: 'servers',
@@ -145,6 +155,8 @@ const JSON_AGENT_CONFIGS = {
145
155
  httpField: 'url',
146
156
  },
147
157
  'gemini-cli': {
158
+ // Source: https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md
159
+ // "MCP config goes in ~/.gemini/settings.json under mcpServers"
148
160
  // Gemini uses `httpUrl` (NOT `url`) for HTTP transports and omits
149
161
  // `type` — it's inferred from which key is present.
150
162
  configPath: () => path.join(os.homedir(), '.gemini', 'settings.json'),
@@ -154,8 +166,13 @@ const JSON_AGENT_CONFIGS = {
154
166
  httpField: 'httpUrl',
155
167
  },
156
168
  kilo: {
157
- // Kilo uses the canonical `mcpServers` with `url` for HTTP.
158
- configPath: () => path.join(os.homedir(), '.kilocode', 'mcp_settings.json'),
169
+ // Source: https://kilo.ai/docs/automate/mcp/using-in-kilo-code
170
+ // Kilo Code uses ~/.kilo/ as the user config root (consistent with
171
+ // ~/.kilo/skills/). The global MCP settings live at the natural
172
+ // neighbor path — mcp_settings.json — which is what the Kilo VS Code
173
+ // extension and CLI both read. (NOT ~/.kilocode/ — that path is
174
+ // project-level only.)
175
+ configPath: () => path.join(os.homedir(), '.kilo', 'mcp_settings.json'),
159
176
  wrapperKey: 'mcpServers',
160
177
  stdioIncludesType: false,
161
178
  httpIncludesType: false,
@@ -223,6 +240,11 @@ function makeJsonWriter(agentId) {
223
240
  // ============================================
224
241
  // CODEX (OpenAI) — TOML format
225
242
  // ============================================
243
+ // Source: https://developers.openai.com/codex/mcp
244
+ // Also: https://github.com/openai/codex/blob/main/docs/config.md
245
+ // "Codex CLI reads MCP server config from ~/.codex/config.toml, where
246
+ // each server gets its own section with format [mcp_servers.my-server]"
247
+ //
226
248
  // Writes ~/.codex/config.toml, appending [mcp_servers.*] sections.
227
249
  // Preserves existing content by removing only our own sections before
228
250
  // appending the fresh ones.
@@ -154,7 +154,7 @@ describe('MCP writers — JSON agents', () => {
154
154
  test('kilo writer uses url (standard)', () => {
155
155
  const configPath = MCP_WRITERS.kilo(fakePkgDir);
156
156
 
157
- assert.strictEqual(configPath, path.join(tempHome, '.kilocode', 'mcp_settings.json'));
157
+ assert.strictEqual(configPath, path.join(tempHome, '.kilo', 'mcp_settings.json'));
158
158
  const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
159
159
  assert.ok(config.mcpServers);
160
160
  assert.strictEqual(config.mcpServers[LEARN_SERVER_NAME].url, MICROSOFT_LEARN_URL);
@@ -3,8 +3,8 @@
3
3
  "name": "bi-superpowers",
4
4
  "display_name": "BI Agent Superpowers",
5
5
  "version": "0.0.0-template",
6
- "description": "AI-powered toolkit for Power BI, Microsoft Fabric & Excel development. 24 skills covering DAX, Power Query, data modeling, and more.",
7
- "long_description": "BI Agent Superpowers brings 24 specialized skills to Claude Desktop, covering DAX writing, Power Query transformations, data modeling best practices, performance optimization, Excel formulas, report design, and more. Built for Power BI developers, data analysts, and BI professionals.",
6
+ "description": "Open-source Power BI Desktop toolkit for Claude Desktop 2 skills and 2 official Microsoft MCP servers.",
7
+ "long_description": "BI Agent Superpowers brings focused Power BI Desktop skills to Claude Desktop: project kickoff analysis, direct Power BI Desktop connection, and official Microsoft MCP servers (Power BI Modeling + Microsoft Learn). Built for Power BI developers and data analysts.",
8
8
  "author": {
9
9
  "name": "Lucas Sanchez",
10
10
  "url": "https://github.com/luquimbo"
@@ -13,16 +13,9 @@
13
13
  "type": "git",
14
14
  "url": "https://github.com/luquimbo/bi-superpowers"
15
15
  },
16
- "homepage": "https://acadevor.com/bi-superpowers",
16
+ "homepage": "https://github.com/luquimbo/bi-superpowers",
17
17
  "license": "MIT",
18
- "keywords": [
19
- "power-bi",
20
- "dax",
21
- "fabric",
22
- "excel",
23
- "data-modeling",
24
- "business-intelligence"
25
- ],
18
+ "keywords": ["power-bi", "power-bi-desktop", "mcp", "claude", "business-intelligence"],
26
19
  "server": {
27
20
  "type": "node",
28
21
  "entry_point": "server.js"