@luquimbo/bi-superpowers 3.1.1 → 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 (107) 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/bin/build-plugin.js +6 -6
  6. package/bin/cli.js +169 -310
  7. package/bin/commands/install.js +87 -70
  8. package/bin/lib/agents.js +19 -0
  9. package/bin/lib/mcp-config.js +23 -4
  10. package/desktop-extension/manifest.json +4 -11
  11. package/desktop-extension/server.js +34 -25
  12. package/package.json +3 -9
  13. package/skills/pbi-connect/SKILL.md +1 -1
  14. package/skills/project-kickoff/SKILL.md +1 -1
  15. package/bin/commands/add.js +0 -533
  16. package/bin/commands/add.test.js +0 -77
  17. package/bin/commands/changelog.js +0 -443
  18. package/bin/commands/pull.js +0 -287
  19. package/bin/commands/pull.test.js +0 -36
  20. package/bin/commands/push.js +0 -231
  21. package/bin/commands/push.test.js +0 -14
  22. package/bin/commands/search.js +0 -344
  23. package/bin/commands/search.test.js +0 -115
  24. package/bin/commands/setup.js +0 -545
  25. package/bin/commands/setup.test.js +0 -46
  26. package/bin/commands/sync-profile.js +0 -405
  27. package/bin/commands/sync-profile.test.js +0 -14
  28. package/bin/commands/sync-source.js +0 -418
  29. package/bin/commands/sync-source.test.js +0 -14
  30. package/bin/utils/errors.js +0 -159
  31. package/bin/utils/git.js +0 -298
  32. package/bin/utils/logger.js +0 -142
  33. package/bin/utils/pbix.js +0 -305
  34. package/bin/utils/pbix.test.js +0 -37
  35. package/bin/utils/profiles.js +0 -312
  36. package/bin/utils/projects.js +0 -169
  37. package/bin/utils/readline.js +0 -206
  38. package/bin/utils/readline.test.js +0 -47
  39. package/docs/openrouter-free-models.md +0 -92
  40. package/library/examples/README.md +0 -151
  41. package/library/examples/finance-reporting/README.md +0 -351
  42. package/library/examples/finance-reporting/data-model.md +0 -267
  43. package/library/examples/finance-reporting/measures.dax +0 -557
  44. package/library/examples/hr-analytics/README.md +0 -371
  45. package/library/examples/hr-analytics/data-model.md +0 -315
  46. package/library/examples/hr-analytics/measures.dax +0 -460
  47. package/library/examples/marketing-analytics/README.md +0 -37
  48. package/library/examples/marketing-analytics/data-model.md +0 -62
  49. package/library/examples/marketing-analytics/measures.dax +0 -110
  50. package/library/examples/retail-analytics/README.md +0 -439
  51. package/library/examples/retail-analytics/data-model.md +0 -288
  52. package/library/examples/retail-analytics/measures.dax +0 -481
  53. package/library/examples/supply-chain/README.md +0 -37
  54. package/library/examples/supply-chain/data-model.md +0 -69
  55. package/library/examples/supply-chain/measures.dax +0 -77
  56. package/library/examples/udf-library/README.md +0 -228
  57. package/library/examples/udf-library/functions.dax +0 -571
  58. package/library/snippets/dax/README.md +0 -292
  59. package/library/snippets/dax/business-domains.md +0 -576
  60. package/library/snippets/dax/calculate-patterns.md +0 -276
  61. package/library/snippets/dax/calculation-groups.md +0 -489
  62. package/library/snippets/dax/error-handling.md +0 -495
  63. package/library/snippets/dax/iterators-and-aggregations.md +0 -474
  64. package/library/snippets/dax/kpis-and-metrics.md +0 -293
  65. package/library/snippets/dax/rankings-and-topn.md +0 -235
  66. package/library/snippets/dax/security-patterns.md +0 -413
  67. package/library/snippets/dax/text-and-formatting.md +0 -316
  68. package/library/snippets/dax/time-intelligence.md +0 -196
  69. package/library/snippets/dax/user-defined-functions.md +0 -477
  70. package/library/snippets/dax/virtual-tables.md +0 -546
  71. package/library/snippets/excel-formulas/README.md +0 -84
  72. package/library/snippets/excel-formulas/aggregations.md +0 -330
  73. package/library/snippets/excel-formulas/dates-and-times.md +0 -361
  74. package/library/snippets/excel-formulas/dynamic-arrays.md +0 -314
  75. package/library/snippets/excel-formulas/lookups.md +0 -169
  76. package/library/snippets/excel-formulas/text-functions.md +0 -363
  77. package/library/snippets/governance/naming-conventions.md +0 -97
  78. package/library/snippets/governance/review-checklists.md +0 -107
  79. package/library/snippets/power-query/README.md +0 -389
  80. package/library/snippets/power-query/api-integration.md +0 -707
  81. package/library/snippets/power-query/connections.md +0 -434
  82. package/library/snippets/power-query/data-cleaning.md +0 -298
  83. package/library/snippets/power-query/error-handling.md +0 -526
  84. package/library/snippets/power-query/parameters.md +0 -350
  85. package/library/snippets/power-query/performance.md +0 -506
  86. package/library/snippets/power-query/transformations.md +0 -330
  87. package/library/snippets/report-design/accessibility.md +0 -78
  88. package/library/snippets/report-design/chart-selection.md +0 -54
  89. package/library/snippets/report-design/layout-patterns.md +0 -87
  90. package/library/templates/data-models/README.md +0 -93
  91. package/library/templates/data-models/finance-model.md +0 -627
  92. package/library/templates/data-models/retail-star-schema.md +0 -473
  93. package/library/templates/excel/README.md +0 -83
  94. package/library/templates/excel/budget-tracker.md +0 -432
  95. package/library/templates/excel/data-entry-form.md +0 -533
  96. package/library/templates/power-bi/README.md +0 -72
  97. package/library/templates/power-bi/finance-report.md +0 -449
  98. package/library/templates/power-bi/kpi-scorecard.md +0 -461
  99. package/library/templates/power-bi/sales-dashboard.md +0 -281
  100. package/library/themes/excel/README.md +0 -436
  101. package/library/themes/power-bi/README.md +0 -271
  102. package/library/themes/power-bi/accessible.json +0 -307
  103. package/library/themes/power-bi/bi-superpowers-default.json +0 -858
  104. package/library/themes/power-bi/corporate-blue.json +0 -291
  105. package/library/themes/power-bi/dark-mode.json +0 -291
  106. package/library/themes/power-bi/minimal.json +0 -292
  107. package/library/themes/power-bi/print-friendly.json +0 -309
@@ -1,405 +0,0 @@
1
- /**
2
- * Sync Profile Command
3
- * =====================
4
- *
5
- * Syncs snippets/standards from a project to a base profile.
6
- * Allows users to save their custom patterns for reuse across projects.
7
- *
8
- * Usage:
9
- * super sync-profile Interactive mode
10
- * super sync-profile --profile finance Sync to specific profile
11
- * super sync-profile --new healthcare Create new profile and sync
12
- * super sync-profile --all Sync all snippets
13
- *
14
- * @module commands/sync-profile
15
- */
16
-
17
- const fs = require('fs');
18
- const path = require('path');
19
-
20
- const profiles = require('../utils/profiles');
21
- const rl = require('../utils/readline');
22
-
23
- // Using shared readline utilities
24
- const { createReadline, prompt } = rl;
25
-
26
- /**
27
- * Parse command line arguments
28
- * @param {string[]} args - CLI arguments
29
- * @returns {Object} Parsed options
30
- */
31
- function parseArgs(args) {
32
- const options = {
33
- profile: null,
34
- newProfile: null,
35
- all: false,
36
- overwrite: false,
37
- help: false,
38
- };
39
-
40
- for (let i = 0; i < args.length; i++) {
41
- const arg = args[i];
42
-
43
- if (arg === '--profile' || arg === '-p') {
44
- options.profile = args[++i];
45
- } else if (arg === '--new' || arg === '-n') {
46
- options.newProfile = args[++i];
47
- } else if (arg === '--all' || arg === '-a') {
48
- options.all = true;
49
- } else if (arg === '--overwrite' || arg === '-o') {
50
- options.overwrite = true;
51
- } else if (arg === '--help' || arg === '-h') {
52
- options.help = true;
53
- }
54
- }
55
-
56
- return options;
57
- }
58
-
59
- // Readline functions imported from ../utils/readline.js
60
-
61
- /**
62
- * Show help message
63
- */
64
- function showHelp() {
65
- console.log(`
66
- super sync-profile - Sync snippets to a base profile
67
-
68
- Usage:
69
- super sync-profile Interactive mode
70
- super sync-profile --profile <name> Sync to specific profile
71
- super sync-profile --new <name> Create new profile and sync
72
-
73
- Options:
74
- --profile, -p <name> Target profile name
75
- --new, -n <name> Create a new profile
76
- --all, -a Sync all snippets without selecting
77
- --overwrite, -o Overwrite existing files without prompting
78
- --help, -h Show this help message
79
-
80
- Examples:
81
- super sync-profile
82
- super sync-profile --profile finance
83
- super sync-profile --new healthcare --all
84
- `);
85
- }
86
-
87
- /**
88
- * Get snippets from the current repo
89
- * @param {string} repoPath - Path to bi-repo
90
- * @returns {Object[]} Array of snippet objects
91
- */
92
- function getRepoSnippets(repoPath) {
93
- const snippetsDir = path.join(repoPath, 'snippets');
94
- const standardsDir = path.join(repoPath, 'standards');
95
-
96
- const snippets = [];
97
-
98
- // Get snippets
99
- if (fs.existsSync(snippetsDir)) {
100
- const files = profiles.findMdFiles(snippetsDir);
101
- for (const file of files) {
102
- snippets.push({
103
- path: file,
104
- relativePath: path.relative(snippetsDir, file),
105
- type: 'snippet',
106
- });
107
- }
108
- }
109
-
110
- // Get standards
111
- if (fs.existsSync(standardsDir)) {
112
- const files = profiles.findMdFiles(standardsDir);
113
- for (const file of files) {
114
- snippets.push({
115
- path: file,
116
- relativePath: path.relative(standardsDir, file),
117
- type: 'standard',
118
- });
119
- }
120
- }
121
-
122
- return snippets;
123
- }
124
-
125
- /**
126
- * Select target profile interactively
127
- */
128
- async function selectProfile(rl, options) {
129
- // If new profile specified, create it
130
- if (options.newProfile) {
131
- const name = options.newProfile;
132
-
133
- if (profiles.profileExists(name)) {
134
- console.log(`\n⚠ El perfil "${name}" ya existe.`);
135
- const useExisting = await prompt(rl, '¿Usar perfil existente? (s/n): ');
136
- if (useExisting.toLowerCase() !== 's' && useExisting.toLowerCase() !== 'y') {
137
- return null;
138
- }
139
- } else {
140
- console.log(`\nCreando perfil: ${name}`);
141
- profiles.createProfile(name, { inheritsFrom: 'default' });
142
- console.log(' ✓ Perfil creado');
143
- }
144
-
145
- return name;
146
- }
147
-
148
- // If profile specified, validate it
149
- if (options.profile) {
150
- if (!profiles.profileExists(options.profile)) {
151
- console.log(`\n⚠ Perfil "${options.profile}" no existe.`);
152
- const create = await prompt(rl, '¿Crear perfil? (s/n): ');
153
- if (create.toLowerCase() === 's' || create.toLowerCase() === 'y') {
154
- profiles.createProfile(options.profile, { inheritsFrom: 'default' });
155
- console.log(' ✓ Perfil creado');
156
- } else {
157
- return null;
158
- }
159
- }
160
- return options.profile;
161
- }
162
-
163
- // Interactive selection
164
- const availableProfiles = profiles.listProfiles();
165
-
166
- console.log('\nPerfiles disponibles:');
167
- availableProfiles.forEach((p, i) => {
168
- console.log(` ${i + 1}. ${p}`);
169
- });
170
- console.log(` ${availableProfiles.length + 1}. [Crear nuevo perfil]`);
171
-
172
- const choice = await prompt(rl, `\nSelecciona (1-${availableProfiles.length + 1}): `);
173
- const choiceNum = parseInt(choice, 10);
174
-
175
- if (choiceNum === availableProfiles.length + 1) {
176
- // Create new profile
177
- const name = await prompt(rl, 'Nombre del nuevo perfil: ');
178
- if (!name) {
179
- console.log('Nombre requerido.');
180
- return null;
181
- }
182
-
183
- profiles.createProfile(name, { inheritsFrom: 'default' });
184
- console.log(' ✓ Perfil creado');
185
- return name;
186
- } else if (choiceNum >= 1 && choiceNum <= availableProfiles.length) {
187
- return availableProfiles[choiceNum - 1];
188
- }
189
-
190
- return 'default';
191
- }
192
-
193
- /**
194
- * Select snippets to sync
195
- */
196
- async function selectSnippets(rl, snippets, options) {
197
- if (options.all) {
198
- return snippets;
199
- }
200
-
201
- if (snippets.length === 0) {
202
- return [];
203
- }
204
-
205
- console.log('\nArchivos disponibles para sincronizar:\n');
206
-
207
- const selected = new Array(snippets.length).fill(true);
208
-
209
- snippets.forEach((s, i) => {
210
- const type = s.type === 'snippet' ? '📝' : '📋';
211
- console.log(` ${i + 1}. [x] ${type} ${s.relativePath}`);
212
- });
213
-
214
- console.log(`
215
- Opciones:
216
- - Número para toggle (ej: 1)
217
- - 'a' para seleccionar todos
218
- - 'n' para deseleccionar todos
219
- - 'd' para continuar
220
- `);
221
-
222
- while (true) {
223
- const input = await prompt(rl, 'Toggle (1-N), a, n, o d: ');
224
-
225
- if (input.toLowerCase() === 'd') {
226
- break;
227
- } else if (input.toLowerCase() === 'a') {
228
- selected.fill(true);
229
- console.log(' ✓ Todos seleccionados');
230
- } else if (input.toLowerCase() === 'n') {
231
- selected.fill(false);
232
- console.log(' ✓ Ninguno seleccionado');
233
- } else {
234
- const num = parseInt(input, 10);
235
- if (num >= 1 && num <= snippets.length) {
236
- selected[num - 1] = !selected[num - 1];
237
- const status = selected[num - 1] ? '✓' : '✗';
238
- console.log(` ${status} ${snippets[num - 1].relativePath}`);
239
- }
240
- }
241
- }
242
-
243
- return snippets.filter((_, i) => selected[i]);
244
- }
245
-
246
- /**
247
- * Sync snippets to profile
248
- */
249
- function syncToProfile(snippets, profileName, overwrite) {
250
- const results = {
251
- copied: 0,
252
- skipped: 0,
253
- errors: [],
254
- };
255
-
256
- const profileSnippetsDir = path.join(profiles.PROFILES_DIR, profileName, 'snippets');
257
-
258
- for (const snippet of snippets) {
259
- const targetPath = path.join(profileSnippetsDir, snippet.relativePath);
260
- const targetDir = path.dirname(targetPath);
261
-
262
- // Check if file exists
263
- if (fs.existsSync(targetPath) && !overwrite) {
264
- results.skipped++;
265
- continue;
266
- }
267
-
268
- try {
269
- // Create directory if needed
270
- if (!fs.existsSync(targetDir)) {
271
- fs.mkdirSync(targetDir, { recursive: true });
272
- }
273
-
274
- // Copy file
275
- fs.copyFileSync(snippet.path, targetPath);
276
- results.copied++;
277
- } catch (e) {
278
- results.errors.push({
279
- file: snippet.relativePath,
280
- error: e.message,
281
- });
282
- }
283
- }
284
-
285
- return results;
286
- }
287
-
288
- /**
289
- * Main sync-profile command handler
290
- */
291
- async function syncProfileCommand(args, _config) {
292
- const options = parseArgs(args);
293
-
294
- if (options.help) {
295
- showHelp();
296
- return;
297
- }
298
-
299
- // Check if repo exists
300
- const repoPath = profiles.getRepoPath();
301
- if (!repoPath || !fs.existsSync(repoPath)) {
302
- console.log(`
303
- No se encontró el repositorio de BI.
304
-
305
- Ejecuta primero:
306
- super setup
307
- `);
308
- process.exit(1);
309
- }
310
-
311
- console.log(`
312
- ════════════════════════════════════════════════════════════════
313
- Sync Profile - Guardar snippets al perfil base
314
- ════════════════════════════════════════════════════════════════
315
- `);
316
-
317
- // Get snippets from repo
318
- const snippets = getRepoSnippets(repoPath);
319
-
320
- if (snippets.length === 0) {
321
- console.log('No hay snippets o standards para sincronizar.');
322
- console.log('\nCrea archivos .md en:');
323
- console.log(` ${path.join(repoPath, 'snippets')}/`);
324
- console.log(` ${path.join(repoPath, 'standards')}/`);
325
- return;
326
- }
327
-
328
- console.log(`Encontrados: ${snippets.length} archivos\n`);
329
-
330
- const rl = createReadline();
331
-
332
- try {
333
- // Select profile
334
- const targetProfile = await selectProfile(rl, options);
335
-
336
- if (!targetProfile) {
337
- console.log('\nCancelado.');
338
- return;
339
- }
340
-
341
- console.log(`\nPerfil destino: ${targetProfile}`);
342
-
343
- // Select snippets
344
- const selectedSnippets = await selectSnippets(rl, snippets, options);
345
-
346
- if (selectedSnippets.length === 0) {
347
- console.log('\nNo hay archivos seleccionados.');
348
- return;
349
- }
350
-
351
- console.log(`\nSincronizando ${selectedSnippets.length} archivos...`);
352
-
353
- // Check for conflicts if not overwrite mode
354
- if (!options.overwrite) {
355
- const profileSnippetsDir = path.join(profiles.PROFILES_DIR, targetProfile, 'snippets');
356
- const conflicts = selectedSnippets.filter((s) =>
357
- fs.existsSync(path.join(profileSnippetsDir, s.relativePath))
358
- );
359
-
360
- if (conflicts.length > 0) {
361
- console.log(`\n⚠ ${conflicts.length} archivo(s) ya existen en el perfil:`);
362
- conflicts.slice(0, 5).forEach((c) => {
363
- console.log(` - ${c.relativePath}`);
364
- });
365
- if (conflicts.length > 5) {
366
- console.log(` ... y ${conflicts.length - 5} más`);
367
- }
368
-
369
- const overwriteChoice = await prompt(rl, '\n¿Sobrescribir existentes? (s/n): ');
370
- options.overwrite =
371
- overwriteChoice.toLowerCase() === 's' || overwriteChoice.toLowerCase() === 'y';
372
- }
373
- }
374
-
375
- // Sync
376
- const results = syncToProfile(selectedSnippets, targetProfile, options.overwrite);
377
-
378
- console.log(`
379
- ════════════════════════════════════════════════════════════════
380
- Resumen
381
- ════════════════════════════════════════════════════════════════
382
-
383
- Perfil: ${targetProfile}
384
- Copiados: ${results.copied}
385
- Omitidos: ${results.skipped}
386
- Errores: ${results.errors.length}
387
-
388
- Los snippets estarán disponibles en proyectos que usen
389
- el perfil "${targetProfile}".
390
-
391
- ════════════════════════════════════════════════════════════════
392
- `);
393
-
394
- if (results.errors.length > 0) {
395
- console.log('Errores:');
396
- results.errors.forEach((e) => {
397
- console.log(` ✗ ${e.file}: ${e.error}`);
398
- });
399
- }
400
- } finally {
401
- rl.close();
402
- }
403
- }
404
-
405
- module.exports = syncProfileCommand;
@@ -1,14 +0,0 @@
1
- /**
2
- * Tests for Sync Profile Command
3
- * @module commands/sync-profile.test
4
- */
5
-
6
- const { test, describe } = require('node:test');
7
- const assert = require('node:assert');
8
-
9
- describe('Sync Profile Command', () => {
10
- test('module exports a function', () => {
11
- const syncProfileCommand = require('./sync-profile');
12
- assert.strictEqual(typeof syncProfileCommand, 'function');
13
- });
14
- });