@luquimbo/bi-superpowers 1.0.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 (193) hide show
  1. package/.claude-plugin/plugin.json +8 -0
  2. package/.mcp.json +25 -0
  3. package/AGENTS.md +244 -0
  4. package/CHANGELOG.md +265 -0
  5. package/LICENSE +21 -0
  6. package/README.md +211 -0
  7. package/bin/build-plugin.js +30 -0
  8. package/bin/cli.js +1064 -0
  9. package/bin/commands/add.js +533 -0
  10. package/bin/commands/add.test.js +77 -0
  11. package/bin/commands/build-desktop.js +166 -0
  12. package/bin/commands/changelog.js +443 -0
  13. package/bin/commands/diff.js +325 -0
  14. package/bin/commands/lint.js +419 -0
  15. package/bin/commands/lint.test.js +103 -0
  16. package/bin/commands/mcp-setup.js +246 -0
  17. package/bin/commands/pull.js +287 -0
  18. package/bin/commands/pull.test.js +36 -0
  19. package/bin/commands/push.js +231 -0
  20. package/bin/commands/push.test.js +14 -0
  21. package/bin/commands/search.js +344 -0
  22. package/bin/commands/search.test.js +115 -0
  23. package/bin/commands/setup.js +545 -0
  24. package/bin/commands/setup.test.js +46 -0
  25. package/bin/commands/sync-profile.js +405 -0
  26. package/bin/commands/sync-profile.test.js +14 -0
  27. package/bin/commands/sync-source.js +418 -0
  28. package/bin/commands/sync-source.test.js +14 -0
  29. package/bin/commands/watch.js +206 -0
  30. package/bin/lib/generators/claude-plugin.js +266 -0
  31. package/bin/lib/generators/claude-plugin.test.js +110 -0
  32. package/bin/lib/generators/index.js +116 -0
  33. package/bin/lib/generators/shared.js +282 -0
  34. package/bin/lib/licensing/index.js +35 -0
  35. package/bin/lib/licensing/storage.js +364 -0
  36. package/bin/lib/licensing/storage.test.js +55 -0
  37. package/bin/lib/licensing/validator.js +213 -0
  38. package/bin/lib/licensing/validator.test.js +137 -0
  39. package/bin/lib/microsoft-mcp.js +176 -0
  40. package/bin/lib/microsoft-mcp.test.js +106 -0
  41. package/bin/lib/skills.js +84 -0
  42. package/bin/mcp/powerbi-modeling-launcher.js +38 -0
  43. package/bin/postinstall.js +44 -0
  44. package/bin/utils/errors.js +159 -0
  45. package/bin/utils/git.js +298 -0
  46. package/bin/utils/logger.js +142 -0
  47. package/bin/utils/mcp-detect.js +274 -0
  48. package/bin/utils/mcp-detect.test.js +105 -0
  49. package/bin/utils/pbix.js +305 -0
  50. package/bin/utils/pbix.test.js +37 -0
  51. package/bin/utils/profiles.js +312 -0
  52. package/bin/utils/projects.js +168 -0
  53. package/bin/utils/readline.js +206 -0
  54. package/bin/utils/readline.test.js +47 -0
  55. package/bin/utils/tui.js +314 -0
  56. package/bin/utils/tui.test.js +127 -0
  57. package/commands/contributions.md +265 -0
  58. package/commands/data-model-design.md +468 -0
  59. package/commands/dax-doctor.md +248 -0
  60. package/commands/fabric-scripts.md +452 -0
  61. package/commands/migration-assistant.md +290 -0
  62. package/commands/model-documenter.md +242 -0
  63. package/commands/pbi-connect.md +239 -0
  64. package/commands/project-kickoff.md +905 -0
  65. package/commands/report-layout.md +296 -0
  66. package/commands/rls-design.md +533 -0
  67. package/commands/theme-tweaker.md +624 -0
  68. package/config.example.json +23 -0
  69. package/config.json +23 -0
  70. package/desktop-extension/manifest.json +37 -0
  71. package/desktop-extension/package.json +10 -0
  72. package/desktop-extension/server.js +95 -0
  73. package/docs/openrouter-free-models.md +92 -0
  74. package/library/examples/README.md +151 -0
  75. package/library/examples/finance-reporting/README.md +351 -0
  76. package/library/examples/finance-reporting/data-model.md +267 -0
  77. package/library/examples/finance-reporting/measures.dax +557 -0
  78. package/library/examples/hr-analytics/README.md +371 -0
  79. package/library/examples/hr-analytics/data-model.md +315 -0
  80. package/library/examples/hr-analytics/measures.dax +460 -0
  81. package/library/examples/marketing-analytics/README.md +37 -0
  82. package/library/examples/marketing-analytics/data-model.md +62 -0
  83. package/library/examples/marketing-analytics/measures.dax +110 -0
  84. package/library/examples/retail-analytics/README.md +439 -0
  85. package/library/examples/retail-analytics/data-model.md +288 -0
  86. package/library/examples/retail-analytics/measures.dax +481 -0
  87. package/library/examples/supply-chain/README.md +37 -0
  88. package/library/examples/supply-chain/data-model.md +69 -0
  89. package/library/examples/supply-chain/measures.dax +77 -0
  90. package/library/examples/udf-library/README.md +228 -0
  91. package/library/examples/udf-library/functions.dax +571 -0
  92. package/library/snippets/dax/README.md +292 -0
  93. package/library/snippets/dax/business-domains.md +576 -0
  94. package/library/snippets/dax/calculate-patterns.md +276 -0
  95. package/library/snippets/dax/calculation-groups.md +489 -0
  96. package/library/snippets/dax/error-handling.md +495 -0
  97. package/library/snippets/dax/iterators-and-aggregations.md +474 -0
  98. package/library/snippets/dax/kpis-and-metrics.md +293 -0
  99. package/library/snippets/dax/rankings-and-topn.md +235 -0
  100. package/library/snippets/dax/security-patterns.md +413 -0
  101. package/library/snippets/dax/text-and-formatting.md +316 -0
  102. package/library/snippets/dax/time-intelligence.md +196 -0
  103. package/library/snippets/dax/user-defined-functions.md +477 -0
  104. package/library/snippets/dax/virtual-tables.md +546 -0
  105. package/library/snippets/excel-formulas/README.md +84 -0
  106. package/library/snippets/excel-formulas/aggregations.md +330 -0
  107. package/library/snippets/excel-formulas/dates-and-times.md +361 -0
  108. package/library/snippets/excel-formulas/dynamic-arrays.md +314 -0
  109. package/library/snippets/excel-formulas/lookups.md +169 -0
  110. package/library/snippets/excel-formulas/text-functions.md +363 -0
  111. package/library/snippets/governance/naming-conventions.md +97 -0
  112. package/library/snippets/governance/review-checklists.md +107 -0
  113. package/library/snippets/power-query/README.md +389 -0
  114. package/library/snippets/power-query/api-integration.md +707 -0
  115. package/library/snippets/power-query/connections.md +434 -0
  116. package/library/snippets/power-query/data-cleaning.md +298 -0
  117. package/library/snippets/power-query/error-handling.md +526 -0
  118. package/library/snippets/power-query/parameters.md +350 -0
  119. package/library/snippets/power-query/performance.md +506 -0
  120. package/library/snippets/power-query/transformations.md +330 -0
  121. package/library/snippets/report-design/accessibility.md +78 -0
  122. package/library/snippets/report-design/chart-selection.md +54 -0
  123. package/library/snippets/report-design/layout-patterns.md +87 -0
  124. package/library/templates/data-models/README.md +93 -0
  125. package/library/templates/data-models/finance-model.md +627 -0
  126. package/library/templates/data-models/retail-star-schema.md +473 -0
  127. package/library/templates/excel/README.md +83 -0
  128. package/library/templates/excel/budget-tracker.md +432 -0
  129. package/library/templates/excel/data-entry-form.md +533 -0
  130. package/library/templates/power-bi/README.md +72 -0
  131. package/library/templates/power-bi/finance-report.md +449 -0
  132. package/library/templates/power-bi/kpi-scorecard.md +461 -0
  133. package/library/templates/power-bi/sales-dashboard.md +281 -0
  134. package/library/themes/excel/README.md +436 -0
  135. package/library/themes/power-bi/README.md +271 -0
  136. package/library/themes/power-bi/accessible.json +307 -0
  137. package/library/themes/power-bi/bi-superpowers-default.json +858 -0
  138. package/library/themes/power-bi/corporate-blue.json +291 -0
  139. package/library/themes/power-bi/dark-mode.json +291 -0
  140. package/library/themes/power-bi/minimal.json +292 -0
  141. package/library/themes/power-bi/print-friendly.json +309 -0
  142. package/package.json +93 -0
  143. package/skills/contributions/SKILL.md +267 -0
  144. package/skills/data-model-design/SKILL.md +470 -0
  145. package/skills/data-modeling/SKILL.md +254 -0
  146. package/skills/data-quality/SKILL.md +664 -0
  147. package/skills/dax/SKILL.md +708 -0
  148. package/skills/dax-doctor/SKILL.md +250 -0
  149. package/skills/dax-udf/SKILL.md +489 -0
  150. package/skills/deployment/SKILL.md +320 -0
  151. package/skills/excel-formulas/SKILL.md +463 -0
  152. package/skills/fabric-scripts/SKILL.md +454 -0
  153. package/skills/fast-standard/SKILL.md +509 -0
  154. package/skills/governance/SKILL.md +205 -0
  155. package/skills/migration-assistant/SKILL.md +292 -0
  156. package/skills/model-documenter/SKILL.md +244 -0
  157. package/skills/pbi-connect/SKILL.md +241 -0
  158. package/skills/power-query/SKILL.md +406 -0
  159. package/skills/project-kickoff/SKILL.md +907 -0
  160. package/skills/query-performance/SKILL.md +480 -0
  161. package/skills/report-design/SKILL.md +207 -0
  162. package/skills/report-layout/SKILL.md +298 -0
  163. package/skills/rls-design/SKILL.md +535 -0
  164. package/skills/semantic-model/SKILL.md +237 -0
  165. package/skills/testing-validation/SKILL.md +643 -0
  166. package/skills/theme-tweaker/SKILL.md +626 -0
  167. package/src/content/base.md +237 -0
  168. package/src/content/mcp-requirements.json +69 -0
  169. package/src/content/routing.md +203 -0
  170. package/src/content/skills/contributions.md +259 -0
  171. package/src/content/skills/data-model-design.md +462 -0
  172. package/src/content/skills/data-modeling.md +246 -0
  173. package/src/content/skills/data-quality.md +656 -0
  174. package/src/content/skills/dax-doctor.md +242 -0
  175. package/src/content/skills/dax-udf.md +481 -0
  176. package/src/content/skills/dax.md +700 -0
  177. package/src/content/skills/deployment.md +312 -0
  178. package/src/content/skills/excel-formulas.md +455 -0
  179. package/src/content/skills/fabric-scripts.md +446 -0
  180. package/src/content/skills/fast-standard.md +501 -0
  181. package/src/content/skills/governance.md +197 -0
  182. package/src/content/skills/migration-assistant.md +284 -0
  183. package/src/content/skills/model-documenter.md +236 -0
  184. package/src/content/skills/pbi-connect.md +233 -0
  185. package/src/content/skills/power-query.md +398 -0
  186. package/src/content/skills/project-kickoff.md +899 -0
  187. package/src/content/skills/query-performance.md +472 -0
  188. package/src/content/skills/report-design.md +199 -0
  189. package/src/content/skills/report-layout.md +290 -0
  190. package/src/content/skills/rls-design.md +527 -0
  191. package/src/content/skills/semantic-model.md +229 -0
  192. package/src/content/skills/testing-validation.md +635 -0
  193. package/src/content/skills/theme-tweaker.md +618 -0
@@ -0,0 +1,533 @@
1
+ /**
2
+ * Add Command - Add Project to Repository
3
+ * =========================================
4
+ *
5
+ * Adds a Power BI or Excel project to the bi-repo.
6
+ * Extracts versionable content (TMDL, queries) and creates project config.
7
+ *
8
+ * Usage:
9
+ * super add "C:/path/to/file.pbix"
10
+ * super add "C:/path/to/file.pbix" --profile finance
11
+ * super add "C:/path/to/file.xlsx" --name budget-2026
12
+ *
13
+ * @module commands/add
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+
19
+ const git = require('../utils/git');
20
+ const profiles = require('../utils/profiles');
21
+ const pbix = require('../utils/pbix');
22
+ const rl = require('../utils/readline');
23
+
24
+ // Using shared readline utilities
25
+ const { createReadline, prompt } = rl;
26
+
27
+ /**
28
+ * Parse command line arguments
29
+ * @param {string[]} args - CLI arguments
30
+ * @returns {Object} Parsed options
31
+ */
32
+ function parseArgs(args) {
33
+ const options = {
34
+ filePath: null,
35
+ name: null,
36
+ profile: null,
37
+ help: false,
38
+ };
39
+
40
+ for (let i = 0; i < args.length; i++) {
41
+ const arg = args[i];
42
+
43
+ if (arg === '--name' || arg === '-n') {
44
+ options.name = args[++i];
45
+ } else if (arg === '--profile' || arg === '-p') {
46
+ options.profile = args[++i];
47
+ } else if (arg === '--help' || arg === '-h') {
48
+ options.help = true;
49
+ } else if (!arg.startsWith('-') && !options.filePath) {
50
+ options.filePath = arg;
51
+ }
52
+ }
53
+
54
+ return options;
55
+ }
56
+
57
+ // Readline functions imported from ../utils/readline.js
58
+
59
+ /**
60
+ * Show help message
61
+ */
62
+ function showHelp() {
63
+ console.log(`
64
+ super add - Añadir un proyecto BI al repositorio
65
+
66
+ Uso:
67
+ super add <archivo> Añadir un proyecto
68
+ super add <archivo> --profile <nombre> Añadir con perfil específico
69
+ super add <archivo> --name <nombre> Especificar nombre del proyecto
70
+
71
+ Argumentos:
72
+ <archivo> Ruta al archivo .pbix, .pbip o .xlsx
73
+
74
+ Opciones:
75
+ --name, -n <nombre> Nombre personalizado del proyecto (slug)
76
+ --profile, -p <nombre> Perfil a usar (default, finance, retail, etc.)
77
+ --help, -h Mostrar esta ayuda
78
+
79
+ Ejemplos:
80
+ super add "C:/Users/Juan/Documents/Sales.pbix"
81
+ super add "./Dashboard.pbix" --profile finance
82
+ super add "Budget.xlsx" --name budget-2026 --profile finance
83
+ `);
84
+ }
85
+
86
+ /**
87
+ * Get or create project name
88
+ */
89
+ async function getProjectName(rl, fileInfo, customName) {
90
+ if (customName) {
91
+ return pbix.generateSlug(customName);
92
+ }
93
+
94
+ const suggested = pbix.generateSlug(fileInfo.name);
95
+ console.log(`\nNombre detectado: ${fileInfo.name}`);
96
+ const input = await prompt(rl, `Nombre del proyecto [${suggested}]: `);
97
+
98
+ return input ? pbix.generateSlug(input) : suggested;
99
+ }
100
+
101
+ /**
102
+ * Select profile for the project
103
+ */
104
+ async function selectProfile(rl, customProfile) {
105
+ if (customProfile) {
106
+ // Validate profile exists or create it
107
+ if (!profiles.profileExists(customProfile)) {
108
+ console.log(`\nPerfil "${customProfile}" no existe. Creándolo...`);
109
+ profiles.createProfile(customProfile, { inheritsFrom: 'default' });
110
+ }
111
+ return customProfile;
112
+ }
113
+
114
+ const availableProfiles = profiles.listProfiles();
115
+
116
+ console.log('\nPerfil a usar:');
117
+ availableProfiles.forEach((p, i) => {
118
+ console.log(` ${i + 1}. ${p}`);
119
+ });
120
+
121
+ const choice = await prompt(rl, `\nSelecciona (1-${availableProfiles.length}): `);
122
+ const choiceNum = parseInt(choice, 10);
123
+
124
+ if (choiceNum >= 1 && choiceNum <= availableProfiles.length) {
125
+ return availableProfiles[choiceNum - 1];
126
+ }
127
+
128
+ return 'default';
129
+ }
130
+
131
+ /**
132
+ * Create project directory in repo
133
+ */
134
+ function createProjectDirectory(repoPath, projectName) {
135
+ const projectDir = path.join(repoPath, 'projects', projectName);
136
+
137
+ if (fs.existsSync(projectDir)) {
138
+ return { exists: true, path: projectDir };
139
+ }
140
+
141
+ fs.mkdirSync(projectDir, { recursive: true });
142
+ fs.mkdirSync(path.join(projectDir, 'definition'), { recursive: true });
143
+ fs.mkdirSync(path.join(projectDir, 'queries'), { recursive: true });
144
+
145
+ return { exists: false, path: projectDir };
146
+ }
147
+
148
+ /**
149
+ * Extract content based on file type
150
+ */
151
+ function extractContent(fileInfo, projectDir) {
152
+ const result = {
153
+ success: false,
154
+ files: [],
155
+ message: '',
156
+ };
157
+
158
+ if (fileInfo.type === 'power-bi-project') {
159
+ // PBIP - extract TMDL directly
160
+ const extraction = pbix.extractPbipToRepo(fileInfo.path, projectDir);
161
+ if (extraction.success) {
162
+ result.success = true;
163
+ result.files = extraction.files;
164
+ result.message = `Extraídos ${extraction.files.length} archivos TMDL`;
165
+ } else {
166
+ result.message = extraction.error;
167
+ }
168
+ } else if (fileInfo.type === 'power-bi') {
169
+ // PBIX - binary file, cannot extract directly
170
+ // We'll create a placeholder and instruct user to save as PBIP
171
+ result.success = true;
172
+ result.message = `
173
+ NOTA: Los archivos .pbix son binarios y no se pueden versionar directamente.
174
+
175
+ Para versionar tu modelo, tienes dos opciones:
176
+
177
+ 1. RECOMENDADO: Guarda como proyecto PBIP
178
+ - En Power BI Desktop: File → Save as → Power BI Project (.pbip)
179
+ - Luego ejecuta: super add "ruta/al/proyecto.pbip"
180
+
181
+ 2. Usar el formato actual:
182
+ - El proyecto se añadirá con referencia al .pbix
183
+ - Los cambios se detectarán por fecha de modificación
184
+ - No podrás ver diferencias detalladas en Git`;
185
+
186
+ // Create a note file
187
+ const noteContent = `# ${fileInfo.name}
188
+
189
+ Este proyecto está vinculado a un archivo .pbix binario.
190
+
191
+ ## Archivo Original
192
+ - Ruta: ${fileInfo.path}
193
+ - Tipo: Power BI Desktop (.pbix)
194
+
195
+ ## Para mejor versionado
196
+
197
+ Considera guardar como Power BI Project (.pbip):
198
+ 1. Abre el archivo en Power BI Desktop
199
+ 2. File → Save as → Power BI Project (.pbip)
200
+ 3. Ejecuta: super add "ruta/al/proyecto.pbip"
201
+
202
+ Los archivos .pbip contienen TMDL (texto) que Git puede versionar correctamente.
203
+ `;
204
+
205
+ fs.writeFileSync(path.join(projectDir, 'README.md'), noteContent);
206
+ result.files = ['README.md'];
207
+ } else if (fileInfo.type === 'excel' || fileInfo.type === 'excel-macro') {
208
+ // Excel - create documentation placeholder
209
+ result.success = true;
210
+
211
+ const workbookDir = path.join(projectDir, 'workbook');
212
+ if (!fs.existsSync(workbookDir)) {
213
+ fs.mkdirSync(workbookDir, { recursive: true });
214
+ }
215
+
216
+ const structureContent = `# ${fileInfo.name}
217
+
218
+ ## Estructura del Workbook
219
+
220
+ Documenta aquí la estructura de tu workbook:
221
+
222
+ ### Hojas
223
+ -
224
+
225
+ ### Tablas
226
+ -
227
+
228
+ ### Rangos Nombrados
229
+ -
230
+
231
+ ### Conexiones de Datos
232
+ -
233
+ `;
234
+
235
+ fs.writeFileSync(path.join(workbookDir, 'structure.md'), structureContent);
236
+
237
+ const formulasContent = `# Fórmulas Importantes
238
+
239
+ Documenta aquí las fórmulas clave de tu workbook:
240
+
241
+ ## Cálculos Principales
242
+
243
+ \`\`\`excel
244
+ =EXAMPLE_FORMULA()
245
+ \`\`\`
246
+
247
+ ## Lookups
248
+
249
+ ## Agregaciones
250
+ `;
251
+
252
+ fs.writeFileSync(path.join(workbookDir, 'formulas.md'), formulasContent);
253
+
254
+ result.files = ['workbook/structure.md', 'workbook/formulas.md'];
255
+ result.message = 'Creada estructura para documentar el workbook Excel';
256
+ } else {
257
+ result.message = 'Tipo de archivo no soportado';
258
+ }
259
+
260
+ return result;
261
+ }
262
+
263
+ /**
264
+ * Update repo config with new project
265
+ */
266
+ function updateRepoConfig(repoPath, projectName, projectConfig) {
267
+ const configPath = path.join(repoPath, '.bi-superpowers.json');
268
+
269
+ let config = { version: '3.0', projects: [] };
270
+ if (fs.existsSync(configPath)) {
271
+ try {
272
+ config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
273
+ } catch (e) {
274
+ // Use default
275
+ }
276
+ }
277
+
278
+ // Add or update project
279
+ const existingIndex = config.projects.findIndex((p) => p.name === projectName);
280
+ if (existingIndex >= 0) {
281
+ config.projects[existingIndex] = projectConfig;
282
+ } else {
283
+ config.projects.push(projectConfig);
284
+ }
285
+
286
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
287
+ }
288
+
289
+ /**
290
+ * Main add command handler
291
+ */
292
+ async function addCommand(args, _config) {
293
+ const options = parseArgs(args);
294
+
295
+ if (options.help) {
296
+ showHelp();
297
+ return;
298
+ }
299
+
300
+ // Check if repo exists
301
+ const repoPath = profiles.getRepoPath();
302
+ if (!repoPath || !fs.existsSync(repoPath)) {
303
+ console.log(`
304
+ No se encontró el repositorio de BI.
305
+
306
+ Ejecuta primero:
307
+ super setup
308
+
309
+ Para crear tu repositorio de proyectos.
310
+ `);
311
+ process.exit(1);
312
+ }
313
+
314
+ // Check if file path provided
315
+ if (!options.filePath) {
316
+ console.log(`
317
+ Uso: super add <archivo>
318
+
319
+ Ejemplo:
320
+ super add "C:/Users/Juan/Documents/Sales.pbix"
321
+ super add ./Dashboard.pbip --profile finance
322
+ `);
323
+ process.exit(1);
324
+ }
325
+
326
+ // Resolve and validate file path
327
+ const filePath = path.resolve(options.filePath);
328
+
329
+ // Security: Validate path to prevent path traversal attacks
330
+ // Ensure the resolved path is within expected locations (user's folders, not system paths)
331
+ const normalizedPath = path.normalize(filePath);
332
+ const systemPaths = [
333
+ path.normalize('C:\\Windows'),
334
+ path.normalize('C:\\Program Files'),
335
+ path.normalize('C:\\Program Files (x86)'),
336
+ '/etc',
337
+ '/usr',
338
+ '/bin',
339
+ '/sbin',
340
+ '/var',
341
+ '/root',
342
+ ];
343
+
344
+ const isSystemPath = systemPaths.some((sysPath) =>
345
+ normalizedPath.toLowerCase().startsWith(sysPath.toLowerCase())
346
+ );
347
+
348
+ if (isSystemPath) {
349
+ console.log('\n✗ Access denied: Cannot add files from system directories');
350
+ console.log(' Please use files from your user directories.');
351
+ process.exit(1);
352
+ }
353
+
354
+ const fileInfo = pbix.detectFileType(filePath);
355
+
356
+ if (!fileInfo.exists) {
357
+ console.log(`\n✗ Archivo no encontrado: ${filePath}`);
358
+ process.exit(1);
359
+ }
360
+
361
+ if (fileInfo.type === 'unknown') {
362
+ console.log(`\n✗ Tipo de archivo no soportado: ${fileInfo.extension}`);
363
+ console.log(' Formatos soportados: .pbix, .pbip, .xlsx, .xlsm');
364
+ process.exit(1);
365
+ }
366
+
367
+ console.log(`
368
+ ════════════════════════════════════════════════════════════════
369
+ Añadir Proyecto
370
+ ════════════════════════════════════════════════════════════════
371
+
372
+ Archivo: ${fileInfo.name}${fileInfo.extension}
373
+ Tipo: ${fileInfo.type}
374
+ Ruta: ${filePath}
375
+ `);
376
+
377
+ const rl = createReadline();
378
+
379
+ try {
380
+ // Get project name
381
+ const projectName = await getProjectName(rl, fileInfo, options.name);
382
+
383
+ // Select profile
384
+ const profile = await selectProfile(rl, options.profile);
385
+
386
+ // Create project directory
387
+ const projectDir = createProjectDirectory(repoPath, projectName);
388
+
389
+ if (projectDir.exists) {
390
+ const overwrite = await prompt(
391
+ rl,
392
+ `\nEl proyecto "${projectName}" ya existe. ¿Sobrescribir? (s/n): `
393
+ );
394
+ if (overwrite.toLowerCase() !== 's' && overwrite.toLowerCase() !== 'y') {
395
+ console.log('\nCancelado.');
396
+ return;
397
+ }
398
+ }
399
+
400
+ console.log(`\nCreando proyecto: ${projectName}`);
401
+ console.log(` Directorio: ${projectDir.path}`);
402
+ console.log(` Perfil: ${profile}`);
403
+
404
+ // Extract content
405
+ const extraction = extractContent(fileInfo, projectDir.path);
406
+
407
+ if (extraction.success) {
408
+ console.log(`\n ✓ ${extraction.message}`);
409
+
410
+ if (extraction.files.length > 0) {
411
+ console.log(' Archivos:');
412
+ extraction.files.slice(0, 5).forEach((f) => {
413
+ console.log(` - ${f}`);
414
+ });
415
+ if (extraction.files.length > 5) {
416
+ console.log(` ... y ${extraction.files.length - 5} más`);
417
+ }
418
+ }
419
+ } else if (extraction.message) {
420
+ console.log(extraction.message);
421
+ }
422
+
423
+ // Create project.json (legacy, for backward compatibility)
424
+ const projectConfig = pbix.createProjectConfig({
425
+ name: projectName,
426
+ displayName: fileInfo.name,
427
+ type: fileInfo.type,
428
+ profile: profile,
429
+ sourcePath: filePath,
430
+ });
431
+
432
+ fs.writeFileSync(
433
+ path.join(projectDir.path, 'project.json'),
434
+ JSON.stringify(projectConfig, null, 2)
435
+ );
436
+ console.log(' ✓ Creado: project.json');
437
+
438
+ // Create bi-project.json (versioned project config - NEW in v2.4)
439
+ const biProjectConfig = {
440
+ version: '1.0',
441
+ name: projectName,
442
+ displayName: fileInfo.name,
443
+ type: fileInfo.type,
444
+ profile: profile,
445
+ sourcePath: filePath,
446
+ created: new Date().toISOString(),
447
+ mcpServers: {
448
+ 'powerbi-modeling-mcp': {
449
+ enabled: true,
450
+ launcher: 'official-microsoft',
451
+ },
452
+ 'powerbi-remote': {
453
+ enabled: false,
454
+ semanticModelId: null,
455
+ },
456
+ 'fabric-mcp-server': {
457
+ enabled: true,
458
+ mode: 'all',
459
+ },
460
+ },
461
+ changelog: {
462
+ enabled: true,
463
+ path: 'CHANGELOG.md',
464
+ includeInAgentContext: true,
465
+ recentChangesCount: 5,
466
+ },
467
+ agentContext: {
468
+ includeRecentChanges: true,
469
+ customInstructions: null,
470
+ },
471
+ };
472
+
473
+ fs.writeFileSync(
474
+ path.join(projectDir.path, 'bi-project.json'),
475
+ JSON.stringify(biProjectConfig, null, 2)
476
+ );
477
+ console.log(' ✓ Creado: bi-project.json (configuración versionada)');
478
+
479
+ // Create initial CHANGELOG.md
480
+ const changelogContent = `# Changelog - ${fileInfo.name}
481
+
482
+ All notable changes to this project will be documented in this file.
483
+
484
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
485
+
486
+ ## [Unreleased]
487
+
488
+ ### Added
489
+ - Initial project setup with BI Agent Superpowers
490
+
491
+ ---
492
+ *Generated by [BI Agent Superpowers](https://github.com/luquimbo/bi-superpowers)*
493
+ `;
494
+
495
+ fs.writeFileSync(path.join(projectDir.path, 'CHANGELOG.md'), changelogContent);
496
+ console.log(' ✓ Creado: CHANGELOG.md');
497
+
498
+ // Update repo config
499
+ updateRepoConfig(repoPath, projectName, {
500
+ name: projectName,
501
+ type: fileInfo.type,
502
+ profile: profile,
503
+ });
504
+
505
+ // Git commit
506
+ if (git.isGitRepo(repoPath)) {
507
+ git.stageFiles(repoPath, '.');
508
+ git.commit(repoPath, `Add project: ${projectName}`);
509
+ console.log(` ✓ Commit: "Add project: ${projectName}"`);
510
+ }
511
+
512
+ console.log(`
513
+ ════════════════════════════════════════════════════════════════
514
+ ¡Proyecto añadido!
515
+ ════════════════════════════════════════════════════════════════
516
+
517
+ Los archivos originales permanecen en:
518
+ ${filePath}
519
+
520
+ Para traer cambios del original al repo:
521
+ super pull ${projectName}
522
+
523
+ Para ver el historial:
524
+ cd "${repoPath}" && git log --oneline
525
+
526
+ ════════════════════════════════════════════════════════════════
527
+ `);
528
+ } finally {
529
+ rl.close();
530
+ }
531
+ }
532
+
533
+ module.exports = addCommand;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Tests for Add Command
3
+ * @module commands/add.test
4
+ */
5
+
6
+ const { test, describe } = require('node:test');
7
+ const assert = require('node:assert');
8
+ const path = require('path');
9
+
10
+ describe('Add Command', () => {
11
+ test('module exports a function', () => {
12
+ const addCommand = require('./add');
13
+ assert.strictEqual(typeof addCommand, 'function');
14
+ });
15
+ });
16
+
17
+ describe('Add Command - File Type Detection', () => {
18
+ test('detects .pbix files', () => {
19
+ const pbix = require('../utils/pbix');
20
+ const result = pbix.detectFileType('/path/to/file.pbix');
21
+ assert.strictEqual(result.type, 'power-bi');
22
+ assert.strictEqual(result.extension, '.pbix');
23
+ });
24
+
25
+ test('detects .pbip files', () => {
26
+ const pbix = require('../utils/pbix');
27
+ const result = pbix.detectFileType('/path/to/file.pbip');
28
+ assert.strictEqual(result.type, 'power-bi-project');
29
+ assert.strictEqual(result.extension, '.pbip');
30
+ });
31
+
32
+ test('detects .xlsx files', () => {
33
+ const pbix = require('../utils/pbix');
34
+ const result = pbix.detectFileType('/path/to/file.xlsx');
35
+ assert.strictEqual(result.type, 'excel');
36
+ assert.strictEqual(result.extension, '.xlsx');
37
+ });
38
+
39
+ test('detects .xlsm files', () => {
40
+ const pbix = require('../utils/pbix');
41
+ const result = pbix.detectFileType('/path/to/file.xlsm');
42
+ assert.strictEqual(result.type, 'excel-macro');
43
+ assert.strictEqual(result.extension, '.xlsm');
44
+ });
45
+
46
+ test('handles unknown file types', () => {
47
+ const pbix = require('../utils/pbix');
48
+ const result = pbix.detectFileType('/path/to/file.txt');
49
+ assert.strictEqual(result.type, 'unknown');
50
+ });
51
+ });
52
+
53
+ describe('Add Command - Slug Generation', () => {
54
+ test('generates slug from name', () => {
55
+ const pbix = require('../utils/pbix');
56
+
57
+ assert.strictEqual(pbix.generateSlug('Sales Dashboard Q4'), 'sales-dashboard-q4');
58
+ assert.strictEqual(pbix.generateSlug('My Report!'), 'my-report');
59
+ assert.strictEqual(pbix.generateSlug('Budget 2026'), 'budget-2026');
60
+ });
61
+
62
+ test('handles special characters', () => {
63
+ const pbix = require('../utils/pbix');
64
+
65
+ assert.strictEqual(pbix.generateSlug('Report (Final)'), 'report-final');
66
+ assert.strictEqual(pbix.generateSlug('Test & Demo'), 'test-demo');
67
+ });
68
+
69
+ test('limits slug length', () => {
70
+ const pbix = require('../utils/pbix');
71
+ const longName =
72
+ 'This is a very long project name that should be truncated to a reasonable length';
73
+ const slug = pbix.generateSlug(longName);
74
+
75
+ assert.ok(slug.length <= 50);
76
+ });
77
+ });