@luquimbo/bi-superpowers 1.1.2 → 1.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.
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Unit tests for the install command
3
+ *
4
+ * Run with: npm test
5
+ */
6
+
7
+ const { test, describe, beforeEach, afterEach } = require('node:test');
8
+ const assert = require('node:assert');
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const os = require('os');
12
+
13
+ const installCommand = require('./install');
14
+ const { parseArgs, detectAgents, copySkillDir, formatFsError, AGENTS, UNIVERSAL_DIR } =
15
+ installCommand;
16
+
17
+ describe('install command - parseArgs', () => {
18
+ test('parses --yes/-y flag', () => {
19
+ assert.strictEqual(parseArgs(['--yes']).isYes, true);
20
+ assert.strictEqual(parseArgs(['-y']).isYes, true);
21
+ assert.strictEqual(parseArgs([]).isYes, false);
22
+ });
23
+
24
+ test('parses --all flag', () => {
25
+ assert.strictEqual(parseArgs(['--all']).isAll, true);
26
+ assert.strictEqual(parseArgs([]).isAll, false);
27
+ });
28
+
29
+ test('parses multiple --agent flags', () => {
30
+ const opts = parseArgs(['--agent', 'claude-code', '--agent', 'codex']);
31
+ assert.deepStrictEqual(opts.agentFlags, ['claude-code', 'codex']);
32
+ });
33
+
34
+ test('parses short -a flag', () => {
35
+ const opts = parseArgs(['-a', 'claude-code', '-a', 'kilo']);
36
+ assert.deepStrictEqual(opts.agentFlags, ['claude-code', 'kilo']);
37
+ });
38
+
39
+ test('ignores --agent without value (end of args)', () => {
40
+ const opts = parseArgs(['--agent']);
41
+ assert.deepStrictEqual(opts.agentFlags, []);
42
+ });
43
+
44
+ test('ignores --agent when next arg is another flag', () => {
45
+ const opts = parseArgs(['--agent', '--yes']);
46
+ assert.deepStrictEqual(opts.agentFlags, []);
47
+ assert.strictEqual(opts.isYes, true);
48
+ });
49
+
50
+ test('combines all flags correctly', () => {
51
+ const opts = parseArgs(['--all', '--yes', '-a', 'claude-code']);
52
+ assert.strictEqual(opts.isAll, true);
53
+ assert.strictEqual(opts.isYes, true);
54
+ assert.deepStrictEqual(opts.agentFlags, ['claude-code']);
55
+ });
56
+ });
57
+
58
+ describe('install command - AGENTS registry', () => {
59
+ test('has exactly 5 supported agents', () => {
60
+ assert.strictEqual(Object.keys(AGENTS).length, 5);
61
+ });
62
+
63
+ test('includes the 5 officially supported agents', () => {
64
+ const ids = Object.keys(AGENTS);
65
+ assert.ok(ids.includes('github-copilot'));
66
+ assert.ok(ids.includes('claude-code'));
67
+ assert.ok(ids.includes('codex'));
68
+ assert.ok(ids.includes('gemini-cli'));
69
+ assert.ok(ids.includes('kilo'));
70
+ });
71
+
72
+ test('every agent has name and dir fields', () => {
73
+ for (const [id, agent] of Object.entries(AGENTS)) {
74
+ assert.ok(agent.name, `${id} missing name`);
75
+ assert.ok(agent.dir, `${id} missing dir`);
76
+ assert.ok(agent.dir.startsWith('.'), `${id} dir should start with .`);
77
+ }
78
+ });
79
+
80
+ test('UNIVERSAL_DIR is .agents/skills', () => {
81
+ assert.strictEqual(UNIVERSAL_DIR, '.agents/skills');
82
+ });
83
+
84
+ test('codex uses UNIVERSAL_DIR', () => {
85
+ assert.strictEqual(AGENTS.codex.dir, UNIVERSAL_DIR);
86
+ });
87
+ });
88
+
89
+ describe('install command - detectAgents', () => {
90
+ let tempDir;
91
+
92
+ beforeEach(() => {
93
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'bi-install-test-'));
94
+ });
95
+
96
+ afterEach(() => {
97
+ fs.rmSync(tempDir, { recursive: true, force: true });
98
+ });
99
+
100
+ test('returns empty array when no agents are installed', () => {
101
+ const detected = detectAgents(tempDir);
102
+ assert.deepStrictEqual(detected, []);
103
+ });
104
+
105
+ test('detects claude-code when .claude exists', () => {
106
+ fs.mkdirSync(path.join(tempDir, '.claude'), { recursive: true });
107
+ const detected = detectAgents(tempDir);
108
+ assert.ok(detected.includes('claude-code'));
109
+ });
110
+
111
+ test('detects multiple agents', () => {
112
+ fs.mkdirSync(path.join(tempDir, '.claude'), { recursive: true });
113
+ fs.mkdirSync(path.join(tempDir, '.github'), { recursive: true });
114
+ fs.mkdirSync(path.join(tempDir, '.gemini'), { recursive: true });
115
+ const detected = detectAgents(tempDir);
116
+ assert.ok(detected.includes('claude-code'));
117
+ assert.ok(detected.includes('github-copilot'));
118
+ assert.ok(detected.includes('gemini-cli'));
119
+ });
120
+ });
121
+
122
+ describe('install command - copySkillDir', () => {
123
+ let srcDir;
124
+ let destDir;
125
+
126
+ beforeEach(() => {
127
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'bi-install-copy-'));
128
+ srcDir = path.join(tempDir, 'src');
129
+ destDir = path.join(tempDir, 'dest');
130
+ fs.mkdirSync(srcDir, { recursive: true });
131
+ });
132
+
133
+ afterEach(() => {
134
+ fs.rmSync(path.dirname(srcDir), { recursive: true, force: true });
135
+ });
136
+
137
+ test('copies a single file', () => {
138
+ fs.writeFileSync(path.join(srcDir, 'SKILL.md'), '# Test');
139
+ copySkillDir(srcDir, destDir);
140
+ assert.strictEqual(fs.readFileSync(path.join(destDir, 'SKILL.md'), 'utf8'), '# Test');
141
+ });
142
+
143
+ test('creates destination directory if missing', () => {
144
+ fs.writeFileSync(path.join(srcDir, 'SKILL.md'), 'content');
145
+ copySkillDir(srcDir, destDir);
146
+ assert.ok(fs.existsSync(destDir));
147
+ });
148
+
149
+ test('copies nested directories recursively', () => {
150
+ fs.writeFileSync(path.join(srcDir, 'SKILL.md'), 'top');
151
+ fs.mkdirSync(path.join(srcDir, 'references'));
152
+ fs.writeFileSync(path.join(srcDir, 'references', 'notes.md'), 'nested');
153
+ fs.mkdirSync(path.join(srcDir, 'scripts'));
154
+ fs.writeFileSync(path.join(srcDir, 'scripts', 'run.sh'), '#!/bin/sh');
155
+
156
+ copySkillDir(srcDir, destDir);
157
+
158
+ assert.strictEqual(fs.readFileSync(path.join(destDir, 'SKILL.md'), 'utf8'), 'top');
159
+ assert.strictEqual(
160
+ fs.readFileSync(path.join(destDir, 'references', 'notes.md'), 'utf8'),
161
+ 'nested'
162
+ );
163
+ assert.strictEqual(
164
+ fs.readFileSync(path.join(destDir, 'scripts', 'run.sh'), 'utf8'),
165
+ '#!/bin/sh'
166
+ );
167
+ });
168
+ });
169
+
170
+ describe('install command - formatFsError', () => {
171
+ test('includes the context and error message', () => {
172
+ const err = new Error('oops');
173
+ err.code = 'UNKNOWN';
174
+ const msg = formatFsError(err, 'Failed');
175
+ assert.ok(msg.includes('Failed'));
176
+ assert.ok(msg.includes('oops'));
177
+ });
178
+
179
+ test('adds hint for EACCES', () => {
180
+ const err = new Error('permission denied');
181
+ err.code = 'EACCES';
182
+ const msg = formatFsError(err, 'Failed');
183
+ assert.ok(msg.toLowerCase().includes('permiso'));
184
+ });
185
+
186
+ test('adds hint for EPERM (Windows admin)', () => {
187
+ const err = new Error('not permitted');
188
+ err.code = 'EPERM';
189
+ const msg = formatFsError(err, 'Failed');
190
+ assert.ok(msg.toLowerCase().includes('admin'));
191
+ });
192
+
193
+ test('adds hint for ENOSPC', () => {
194
+ const err = new Error('no space');
195
+ err.code = 'ENOSPC';
196
+ const msg = formatFsError(err, 'Failed');
197
+ assert.ok(msg.toLowerCase().includes('espacio'));
198
+ });
199
+ });
200
+
201
+ describe('install command - module exports', () => {
202
+ test('module default export is a function', () => {
203
+ assert.strictEqual(typeof installCommand, 'function');
204
+ });
205
+
206
+ test('exposes internal helpers for testing', () => {
207
+ assert.strictEqual(typeof installCommand.parseArgs, 'function');
208
+ assert.strictEqual(typeof installCommand.detectAgents, 'function');
209
+ assert.strictEqual(typeof installCommand.copySkillDir, 'function');
210
+ assert.strictEqual(typeof installCommand.formatFsError, 'function');
211
+ });
212
+ });
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Agent Registry - Supported AI coding agents
3
+ * =============================================
4
+ *
5
+ * Single source of truth for the agents that bi-superpowers officially
6
+ * supports. Used by the install command (skill installation paths) and
7
+ * any future command that needs to know which agents exist.
8
+ *
9
+ * Each agent has:
10
+ * - A stable ID (used as CLI flag value, e.g. --agent claude-code)
11
+ * - A human-readable name (shown in the UI)
12
+ * - A directory relative to the user's home where its skills live
13
+ *
14
+ * UNIVERSAL_DIR is the path most agents read from by convention
15
+ * (.agents/skills/). The installer always writes here first, then
16
+ * symlinks agent-specific directories to it. Agents whose dir === UNIVERSAL_DIR
17
+ * (e.g. Codex, OpenCode) are served directly by the universal install.
18
+ *
19
+ * @module lib/agents
20
+ */
21
+
22
+ /**
23
+ * Supported agents, in the order they appear in the interactive installer.
24
+ * Object insertion order matters — the installer renders the list in this order.
25
+ */
26
+ const AGENTS = {
27
+ 'github-copilot': { name: 'GitHub Copilot', dir: '.github/skills' },
28
+ 'claude-code': { name: 'Claude Code', dir: '.claude/skills' },
29
+ codex: { name: 'Codex (OpenAI)', dir: '.agents/skills' },
30
+ 'gemini-cli': { name: 'Gemini CLI', dir: '.gemini/skills' },
31
+ kilo: { name: 'Kilo Code', dir: '.kilocode/skills' },
32
+ };
33
+
34
+ /**
35
+ * Universal path — most agents read from .agents/skills/ by the Agent Skills spec.
36
+ * The installer copies skills here first and symlinks agent-specific dirs to it.
37
+ */
38
+ const UNIVERSAL_DIR = '.agents/skills';
39
+
40
+ module.exports = {
41
+ AGENTS,
42
+ UNIVERSAL_DIR,
43
+ };
@@ -81,10 +81,19 @@ function rewriteLibraryReferences(content, libraryPrefix) {
81
81
  /**
82
82
  * Build a concise frontmatter description for plugin skills.
83
83
  *
84
- * @param {Object} skill - Skill definition object
84
+ * @param {Object} skill - Skill definition object with at least { name, content }
85
85
  * @returns {string} Description string
86
+ * @throws {Error} If skill is missing required fields
86
87
  */
87
88
  function getPluginDescription(skill) {
89
+ if (!skill || typeof skill !== 'object' || !skill.name || typeof skill.content !== 'string') {
90
+ const hint = skill && skill.path ? ` (path: ${skill.path})` : '';
91
+ throw new Error(
92
+ `Invalid skill object: missing name or content${hint}. ` +
93
+ 'Check the source file in src/content/skills/.'
94
+ );
95
+ }
96
+
88
97
  const metadata = parseSkillMetadata(skill.content);
89
98
 
90
99
  if (metadata.triggers.length > 0) {
@@ -101,7 +110,6 @@ function getPluginDescription(skill) {
101
110
  /**
102
111
  * Serialize values safely for YAML frontmatter.
103
112
  *
104
- /**
105
113
  * @param {string} value - Raw value
106
114
  * @returns {string} JSON-escaped string valid in YAML
107
115
  */
@@ -62,7 +62,14 @@ function loadLicense() {
62
62
  * @param {string} data.contentVersion - Content version
63
63
  */
64
64
  function saveLicense(data) {
65
- fs.writeFileSync(LICENSE_FILE, JSON.stringify(data, null, 2));
65
+ try {
66
+ fs.writeFileSync(LICENSE_FILE, JSON.stringify(data, null, 2));
67
+ } catch (err) {
68
+ throw new Error(
69
+ `No pude guardar la licencia en ${LICENSE_FILE}: ${err.message}. ` +
70
+ 'Revisá los permisos de tu directorio home.'
71
+ );
72
+ }
66
73
  }
67
74
 
68
75
  /**
@@ -239,6 +246,36 @@ function isUnsafeZipEntry(entryName, destRoot) {
239
246
  return relative.startsWith('..') || path.isAbsolute(relative);
240
247
  }
241
248
 
249
+ /**
250
+ * Verifica que el archivo tenga los magic bytes de un ZIP válido.
251
+ * Los ZIP empiezan con la signature PK\x03\x04 (o PK\x05\x06 si está vacío).
252
+ * @param {string} zipPath - Ruta al archivo a verificar
253
+ * @throws {Error} Si el archivo no tiene signature de ZIP
254
+ */
255
+ function verifyZipMagicBytes(zipPath) {
256
+ const header = Buffer.alloc(4);
257
+ const fd = fs.openSync(zipPath, 'r');
258
+ try {
259
+ fs.readSync(fd, header, 0, 4, 0);
260
+ } finally {
261
+ fs.closeSync(fd);
262
+ }
263
+
264
+ // ZIP local file header: PK\x03\x04
265
+ // ZIP empty archive: PK\x05\x06
266
+ const isLocalFile =
267
+ header[0] === 0x50 && header[1] === 0x4b && header[2] === 0x03 && header[3] === 0x04;
268
+ const isEmpty =
269
+ header[0] === 0x50 && header[1] === 0x4b && header[2] === 0x05 && header[3] === 0x06;
270
+
271
+ if (!isLocalFile && !isEmpty) {
272
+ throw new Error(
273
+ `El archivo descargado no es un ZIP válido (magic bytes inesperados: ${header.toString('hex')}). ` +
274
+ 'Puede que el servidor haya retornado HTML u otro formato.'
275
+ );
276
+ }
277
+ }
278
+
242
279
  function extractZip(zipPath, destDir) {
243
280
  return new Promise((resolve, reject) => {
244
281
  try {
@@ -247,6 +284,9 @@ function extractZip(zipPath, destDir) {
247
284
  fs.mkdirSync(destDir, { recursive: true });
248
285
  }
249
286
 
287
+ // Verify magic bytes before handing to AdmZip
288
+ verifyZipMagicBytes(zipPath);
289
+
250
290
  const destRoot = path.resolve(destDir);
251
291
  const zip = new AdmZip(zipPath);
252
292
  const entries = zip.getEntries();
package/bin/lib/skills.js CHANGED
@@ -15,6 +15,11 @@ const path = require('path');
15
15
  /**
16
16
  * Read all markdown skills from a directory.
17
17
  *
18
+ * Robusto a fallos de filesystem:
19
+ * - Si el directorio no existe → retorna []
20
+ * - Si no se puede leer (permisos) → imprime warning y retorna []
21
+ * - Si un archivo individual no se puede leer → warning y se omite
22
+ *
18
23
  * @param {string} directory - Directory containing skill markdown files
19
24
  * @returns {Array<{name: string, path: string, content: string}>}
20
25
  */
@@ -23,18 +28,31 @@ function readSkillDirectory(directory) {
23
28
  return [];
24
29
  }
25
30
 
26
- return fs
27
- .readdirSync(directory)
31
+ let files;
32
+ try {
33
+ files = fs.readdirSync(directory);
34
+ } catch (err) {
35
+ console.warn(`⚠ No pude leer el directorio de skills ${directory}: ${err.message}`);
36
+ return [];
37
+ }
38
+
39
+ return files
28
40
  .filter((file) => file.endsWith('.md'))
29
41
  .sort()
30
42
  .map((file) => {
31
43
  const filePath = path.join(directory, file);
32
- return {
33
- name: file.replace(/\.md$/, ''),
34
- path: filePath,
35
- content: fs.readFileSync(filePath, 'utf8'),
36
- };
37
- });
44
+ try {
45
+ return {
46
+ name: file.replace(/\.md$/, ''),
47
+ path: filePath,
48
+ content: fs.readFileSync(filePath, 'utf8'),
49
+ };
50
+ } catch (err) {
51
+ console.warn(`⚠ No pude leer el skill ${filePath}: ${err.message}`);
52
+ return null;
53
+ }
54
+ })
55
+ .filter(Boolean);
38
56
  }
39
57
 
40
58
  /**
@@ -77,9 +77,10 @@ function getAllProjects(repoPath) {
77
77
  projectPath,
78
78
  });
79
79
  } catch (e) {
80
- // Skip invalid configs
80
+ // Warn user about broken config so they can fix it
81
+ console.warn(`⚠ Config de proyecto inválido en ${configPath}: ${e.message}`);
81
82
  if (process.env.DEBUG === 'true') {
82
- console.error(`[DEBUG] Skipping invalid config: ${configPath}`);
83
+ console.error('[DEBUG] Full error:', e);
83
84
  }
84
85
  }
85
86
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luquimbo/bi-superpowers",
3
- "version": "1.1.2",
3
+ "version": "1.2.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": {
@@ -20,12 +20,12 @@
20
20
  "prepack": "npm run build:plugin && npm run check"
21
21
  },
22
22
  "dependencies": {
23
- "adm-zip": "^0.5.16",
23
+ "adm-zip": "^0.5.17",
24
24
  "boxen": "^5.1.2",
25
25
  "chalk": "^4.1.2",
26
26
  "chokidar": "^3.6.0",
27
27
  "cli-table3": "^0.6.5",
28
- "fuse.js": "^7.0.0",
28
+ "fuse.js": "^7.3.0",
29
29
  "gray-matter": "^4.0.3",
30
30
  "js-yaml": "^4.1.0",
31
31
  "ora": "^5.4.1",
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "contributions"
3
3
  description: "Contributions Validation Skill: Contribution validation."
4
- version: "1.1.2"
4
+ version: "1.2.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/contributions.md instead. -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "data-model-design"
3
3
  description: "Use when the user asks about Data Model Design Skill, especially phrases like \"diseñar modelo de datos\", \"crear modelo Power BI\", \"arquitectura de datos\", \"empezar proyecto BI\", \"nuevo modelo semántico\"."
4
- version: "1.1.2"
4
+ version: "1.2.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/data-model-design.md instead. -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "data-modeling"
3
3
  description: "Use when the user asks about Data Modeling Skill, especially phrases like \"data model\", \"star schema\", \"fact table\", \"relationship\", \"surrogate key\", \"SCD\"."
4
- version: "1.1.2"
4
+ version: "1.2.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/data-modeling.md instead. -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "data-quality"
3
3
  description: "Use when the user asks about Data Quality Skill, especially phrases like \"data quality\", \"check for errors\", \"data profiling\", \"clean data\", \"calidad de datos\"."
4
- version: "1.1.2"
4
+ version: "1.2.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/data-quality.md instead. -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "dax"
3
3
  description: "Use when the user asks about DAX Skill, especially phrases like \"DAX\", \"CALCULATE\", \"time intelligence\", \"SUMX\", \"context transition\", \"Power BI formula\"."
4
- version: "1.1.2"
4
+ version: "1.2.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/dax.md instead. -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "dax-doctor"
3
3
  description: "Use when the user asks about DAX Doctor Skill, especially phrases like \"debug DAX\", \"wrong result\", \"DAX error\", \"slow measure\", \"context issue\", \"depurar DAX\"."
4
- version: "1.1.2"
4
+ version: "1.2.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/dax-doctor.md instead. -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "dax-udf"
3
3
  description: "Use when the user asks about DAX User-Defined Functions (UDFs), especially phrases like \"UDF\", \"DEFINE FUNCTION\", \"DAX Lib\", \"NAMEOF\", \"reusable DAX\", \"VAL parameter\"."
4
- version: "1.1.2"
4
+ version: "1.2.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/dax-udf.md instead. -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "deployment"
3
3
  description: "Use when the user asks about Deployment Skill, especially phrases like \"deploy\", \"CI/CD\", \"ALM\", \"git integration\", \"environment\", \"desplegar\"."
4
- version: "1.1.2"
4
+ version: "1.2.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/deployment.md instead. -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "excel-formulas"
3
3
  description: "Use when the user asks about Excel Formulas Skill, especially phrases like \"Excel formula\", \"XLOOKUP\", \"dynamic array\", \"LET formula\", \"named range\", \"conditional formatting formula\"."
4
- version: "1.1.2"
4
+ version: "1.2.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/excel-formulas.md instead. -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "fabric-scripts"
3
3
  description: "Use when the user asks about Fabric Scripts Skill, especially phrases like \"Fabric scripts\", \"download dataflows\", \"diagnose connection\", \"TMDL sync\", \"scripts de Fabric\"."
4
- version: "1.1.2"
4
+ version: "1.2.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/fabric-scripts.md instead. -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "fast-standard"
3
3
  description: "Use when the user asks about FAST Standard Skill, especially phrases like \"FAST Standard\", \"financial model\", \"spreadsheet modeling best practices\", \"model audit\", \"calculation block\", \"modelo financiero\"."
4
- version: "1.1.2"
4
+ version: "1.2.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/fast-standard.md instead. -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "governance"
3
3
  description: "Use when the user asks about Governance Skill, especially phrases like \"naming convention\", \"governance\", \"documentation standard\", \"display folder\", \"convención de nombres\"."
4
- version: "1.1.2"
4
+ version: "1.2.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/governance.md instead. -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "migration-assistant"
3
3
  description: "Use when the user asks about Migration Assistant Skill, especially phrases like \"migrate\", \"move to Fabric\", \"Desktop to PBIP\", \"deprecated feature\", \"migrar\"."
4
- version: "1.1.2"
4
+ version: "1.2.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/migration-assistant.md instead. -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "model-documenter"
3
3
  description: "Use when the user asks about Model Documenter Skill, especially phrases like \"document model\", \"document measures\", \"generate documentation\", \"describe tables\", \"documentar modelo\"."
4
- version: "1.1.2"
4
+ version: "1.2.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/model-documenter.md instead. -->
@@ -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: "1.1.2"
4
+ version: "1.2.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: "power-query"
3
3
  description: "Use when the user asks about Power Query Skill, especially phrases like \"Power Query\", \"query folding\", \"ETL\", \"source connection\", \"refresh\", \"parameters\"."
4
- version: "1.1.2"
4
+ version: "1.2.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/power-query.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: "1.1.2"
4
+ version: "1.2.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/project-kickoff.md instead. -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "query-performance"
3
3
  description: "Use when the user asks about Query Performance Skill, especially phrases like \"slow\", \"DAX Studio\", \"taking too long\", \"reduce refresh time\", \"rendimiento\"."
4
- version: "1.1.2"
4
+ version: "1.2.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/query-performance.md instead. -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "report-design"
3
3
  description: "Use when the user asks about Report Design Skill, especially phrases like \"report design\", \"chart type\", \"IBCS\", \"accessibility\", \"mobile layout\", \"diseño de reporte\"."
4
- version: "1.1.2"
4
+ version: "1.2.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/report-design.md instead. -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "report-layout"
3
3
  description: "Use when the user asks about Report Layout Skill, especially phrases like \"report layout\", \"visual placement\", \"report wireframe\", \"navigation design\", \"diseño de reporte\"."
4
- version: "1.1.2"
4
+ version: "1.2.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/report-layout.md instead. -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "rls-design"
3
3
  description: "Use when the user asks about RLS Design Skill, especially phrases like \"RLS\", \"user can only see their data\", \"restrict access\", \"security role\", \"seguridad a nivel de fila\"."
4
- version: "1.1.2"
4
+ version: "1.2.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/rls-design.md instead. -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: "semantic-model"
3
3
  description: "Use when the user asks about Semantic Model Skill, especially phrases like \"semantic model\", \"TMDL\", \"DirectLake\", \"calculation group\", \"storage mode\", \"modelo semántico\"."
4
- version: "1.1.2"
4
+ version: "1.2.0"
5
5
  ---
6
6
 
7
7
  <!-- Generated by BI Agent Superpowers. Edit src/content/skills/semantic-model.md instead. -->