@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.claude-plugin/skill-manifest.json +1 -1
- package/.plugin/plugin.json +1 -1
- package/README.md +2 -2
- package/bin/build-plugin.js +6 -6
- package/bin/cli.js +169 -310
- package/bin/commands/install.js +87 -70
- package/bin/commands/install.test.js +2 -2
- package/bin/lib/agents.js +21 -2
- package/bin/lib/mcp-config.js +27 -5
- package/bin/lib/mcp-config.test.js +1 -1
- package/desktop-extension/manifest.json +4 -11
- package/desktop-extension/server.js +34 -25
- package/package.json +3 -9
- package/skills/pbi-connect/SKILL.md +1 -1
- package/skills/project-kickoff/SKILL.md +1 -1
- package/bin/commands/add.js +0 -533
- package/bin/commands/add.test.js +0 -77
- package/bin/commands/changelog.js +0 -443
- package/bin/commands/pull.js +0 -287
- package/bin/commands/pull.test.js +0 -36
- package/bin/commands/push.js +0 -231
- package/bin/commands/push.test.js +0 -14
- package/bin/commands/search.js +0 -344
- package/bin/commands/search.test.js +0 -115
- package/bin/commands/setup.js +0 -545
- package/bin/commands/setup.test.js +0 -46
- package/bin/commands/sync-profile.js +0 -405
- package/bin/commands/sync-profile.test.js +0 -14
- package/bin/commands/sync-source.js +0 -418
- package/bin/commands/sync-source.test.js +0 -14
- package/bin/utils/errors.js +0 -159
- package/bin/utils/git.js +0 -298
- package/bin/utils/logger.js +0 -142
- package/bin/utils/pbix.js +0 -305
- package/bin/utils/pbix.test.js +0 -37
- package/bin/utils/profiles.js +0 -312
- package/bin/utils/projects.js +0 -169
- package/bin/utils/readline.js +0 -206
- package/bin/utils/readline.test.js +0 -47
- package/docs/openrouter-free-models.md +0 -92
- package/library/examples/README.md +0 -151
- package/library/examples/finance-reporting/README.md +0 -351
- package/library/examples/finance-reporting/data-model.md +0 -267
- package/library/examples/finance-reporting/measures.dax +0 -557
- package/library/examples/hr-analytics/README.md +0 -371
- package/library/examples/hr-analytics/data-model.md +0 -315
- package/library/examples/hr-analytics/measures.dax +0 -460
- package/library/examples/marketing-analytics/README.md +0 -37
- package/library/examples/marketing-analytics/data-model.md +0 -62
- package/library/examples/marketing-analytics/measures.dax +0 -110
- package/library/examples/retail-analytics/README.md +0 -439
- package/library/examples/retail-analytics/data-model.md +0 -288
- package/library/examples/retail-analytics/measures.dax +0 -481
- package/library/examples/supply-chain/README.md +0 -37
- package/library/examples/supply-chain/data-model.md +0 -69
- package/library/examples/supply-chain/measures.dax +0 -77
- package/library/examples/udf-library/README.md +0 -228
- package/library/examples/udf-library/functions.dax +0 -571
- package/library/snippets/dax/README.md +0 -292
- package/library/snippets/dax/business-domains.md +0 -576
- package/library/snippets/dax/calculate-patterns.md +0 -276
- package/library/snippets/dax/calculation-groups.md +0 -489
- package/library/snippets/dax/error-handling.md +0 -495
- package/library/snippets/dax/iterators-and-aggregations.md +0 -474
- package/library/snippets/dax/kpis-and-metrics.md +0 -293
- package/library/snippets/dax/rankings-and-topn.md +0 -235
- package/library/snippets/dax/security-patterns.md +0 -413
- package/library/snippets/dax/text-and-formatting.md +0 -316
- package/library/snippets/dax/time-intelligence.md +0 -196
- package/library/snippets/dax/user-defined-functions.md +0 -477
- package/library/snippets/dax/virtual-tables.md +0 -546
- package/library/snippets/excel-formulas/README.md +0 -84
- package/library/snippets/excel-formulas/aggregations.md +0 -330
- package/library/snippets/excel-formulas/dates-and-times.md +0 -361
- package/library/snippets/excel-formulas/dynamic-arrays.md +0 -314
- package/library/snippets/excel-formulas/lookups.md +0 -169
- package/library/snippets/excel-formulas/text-functions.md +0 -363
- package/library/snippets/governance/naming-conventions.md +0 -97
- package/library/snippets/governance/review-checklists.md +0 -107
- package/library/snippets/power-query/README.md +0 -389
- package/library/snippets/power-query/api-integration.md +0 -707
- package/library/snippets/power-query/connections.md +0 -434
- package/library/snippets/power-query/data-cleaning.md +0 -298
- package/library/snippets/power-query/error-handling.md +0 -526
- package/library/snippets/power-query/parameters.md +0 -350
- package/library/snippets/power-query/performance.md +0 -506
- package/library/snippets/power-query/transformations.md +0 -330
- package/library/snippets/report-design/accessibility.md +0 -78
- package/library/snippets/report-design/chart-selection.md +0 -54
- package/library/snippets/report-design/layout-patterns.md +0 -87
- package/library/templates/data-models/README.md +0 -93
- package/library/templates/data-models/finance-model.md +0 -627
- package/library/templates/data-models/retail-star-schema.md +0 -473
- package/library/templates/excel/README.md +0 -83
- package/library/templates/excel/budget-tracker.md +0 -432
- package/library/templates/excel/data-entry-form.md +0 -533
- package/library/templates/power-bi/README.md +0 -72
- package/library/templates/power-bi/finance-report.md +0 -449
- package/library/templates/power-bi/kpi-scorecard.md +0 -461
- package/library/templates/power-bi/sales-dashboard.md +0 -281
- package/library/themes/excel/README.md +0 -436
- package/library/themes/power-bi/README.md +0 -271
- package/library/themes/power-bi/accessible.json +0 -307
- package/library/themes/power-bi/bi-superpowers-default.json +0 -858
- package/library/themes/power-bi/corporate-blue.json +0 -291
- package/library/themes/power-bi/dark-mode.json +0 -291
- package/library/themes/power-bi/minimal.json +0 -292
- package/library/themes/power-bi/print-friendly.json +0 -309
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* BI Agent Superpowers — MCP Server for Claude Desktop
|
|
5
5
|
*
|
|
6
|
-
* Lightweight MCP server that exposes
|
|
7
|
-
* Bundled into a .mcpb extension via
|
|
6
|
+
* Lightweight MCP server that exposes each bi-superpowers skill as a
|
|
7
|
+
* Claude Desktop prompt. Bundled into a .mcpb extension via
|
|
8
|
+
* `super build-desktop`.
|
|
8
9
|
*
|
|
9
|
-
* Skills are loaded from the ./skills/ directory (copied at build time).
|
|
10
|
+
* Skills are loaded from the ./skills/ directory (copied in at build time).
|
|
10
11
|
*/
|
|
11
12
|
|
|
12
13
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
@@ -18,29 +19,25 @@ import { fileURLToPath } from 'url';
|
|
|
18
19
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
19
20
|
const skillsDir = path.join(__dirname, 'skills');
|
|
20
21
|
|
|
21
|
-
// Create the MCP server instance
|
|
22
|
+
// Create the MCP server instance.
|
|
22
23
|
const server = new McpServer({
|
|
23
24
|
name: 'bi-superpowers',
|
|
24
25
|
version: '0.0.0-template',
|
|
25
26
|
});
|
|
26
27
|
|
|
27
|
-
// Load all skill markdown files from the bundled skills/ directory
|
|
28
|
+
// Load all skill markdown files from the bundled skills/ directory.
|
|
28
29
|
const skillFiles = fs.existsSync(skillsDir)
|
|
29
30
|
? fs.readdirSync(skillsDir).filter((f) => f.endsWith('.md'))
|
|
30
31
|
: [];
|
|
31
32
|
|
|
32
|
-
// Register each skill as an MCP prompt that Claude Desktop can invoke
|
|
33
|
+
// Register each skill as an MCP prompt that Claude Desktop can invoke.
|
|
33
34
|
for (const file of skillFiles) {
|
|
34
35
|
const name = path.basename(file, '.md');
|
|
35
36
|
const content = fs.readFileSync(path.join(skillsDir, file), 'utf8');
|
|
36
37
|
|
|
37
|
-
//
|
|
38
|
-
const firstLine = content
|
|
39
|
-
|
|
40
|
-
.find((l) => l.trim() && !l.startsWith('#'));
|
|
41
|
-
const description = firstLine
|
|
42
|
-
? firstLine.slice(0, 120).trim()
|
|
43
|
-
: `BI Superpowers: ${name}`;
|
|
38
|
+
// Use the first non-header line as a short description for the prompt.
|
|
39
|
+
const firstLine = content.split('\n').find((l) => l.trim() && !l.startsWith('#'));
|
|
40
|
+
const description = firstLine ? firstLine.slice(0, 120).trim() : `BI Superpowers: ${name}`;
|
|
44
41
|
|
|
45
42
|
server.prompt(name, { description }, () => ({
|
|
46
43
|
messages: [
|
|
@@ -52,10 +49,15 @@ for (const file of skillFiles) {
|
|
|
52
49
|
}));
|
|
53
50
|
}
|
|
54
51
|
|
|
55
|
-
// Register a setup-mcp helper prompt
|
|
52
|
+
// Register a setup-mcp helper prompt that explains how to wire the
|
|
53
|
+
// official Microsoft MCP servers (Power BI Modeling + Microsoft Learn)
|
|
54
|
+
// into Claude Desktop.
|
|
56
55
|
server.prompt(
|
|
57
56
|
'setup-mcp',
|
|
58
|
-
{
|
|
57
|
+
{
|
|
58
|
+
description:
|
|
59
|
+
'Instructions to configure the Power BI Modeling and Microsoft Learn MCP servers in Claude Desktop',
|
|
60
|
+
},
|
|
59
61
|
() => ({
|
|
60
62
|
messages: [
|
|
61
63
|
{
|
|
@@ -64,25 +66,32 @@ server.prompt(
|
|
|
64
66
|
type: 'text',
|
|
65
67
|
text: `# Configure MCP Servers for Claude Desktop
|
|
66
68
|
|
|
67
|
-
|
|
68
|
-
to your \`claude_desktop_config.json\`
|
|
69
|
+
bi-superpowers ships two official Microsoft MCP servers. To wire them
|
|
70
|
+
into Claude Desktop, add the following to your \`claude_desktop_config.json\`
|
|
71
|
+
(Settings → Developer → Edit Config):
|
|
69
72
|
|
|
70
73
|
\`\`\`json
|
|
71
74
|
{
|
|
72
75
|
"mcpServers": {
|
|
73
|
-
"powerbi-
|
|
74
|
-
"command": "
|
|
75
|
-
"args": ["
|
|
76
|
+
"powerbi-modeling": {
|
|
77
|
+
"command": "node",
|
|
78
|
+
"args": ["<absolute path to powerbi-modeling-launcher.js>"]
|
|
76
79
|
},
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
+
"microsoft-learn": {
|
|
81
|
+
"type": "http",
|
|
82
|
+
"url": "https://learn.microsoft.com/api/mcp"
|
|
80
83
|
}
|
|
81
84
|
}
|
|
82
85
|
}
|
|
83
86
|
\`\`\`
|
|
84
87
|
|
|
85
|
-
|
|
88
|
+
The \`powerbi-modeling\` server requires Power BI Desktop on Windows
|
|
89
|
+
with a model open. If you prefer automated setup, run
|
|
90
|
+
\`super install\` from the bi-superpowers CLI — it configures both
|
|
91
|
+
servers across all 5 supported AI agents.
|
|
92
|
+
|
|
93
|
+
After saving, restart Claude Desktop. The MCP servers will appear in
|
|
94
|
+
your tools.
|
|
86
95
|
`,
|
|
87
96
|
},
|
|
88
97
|
},
|
|
@@ -90,6 +99,6 @@ After saving, restart Claude Desktop. The MCP servers will appear in your tools.
|
|
|
90
99
|
})
|
|
91
100
|
);
|
|
92
101
|
|
|
93
|
-
// Start the server with stdio transport
|
|
102
|
+
// Start the server with stdio transport.
|
|
94
103
|
const transport = new StdioServerTransport();
|
|
95
104
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@luquimbo/bi-superpowers",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.2.0",
|
|
4
|
+
"description": "Open-source Power BI Desktop toolkit for Claude Code, GitHub Copilot, Codex, Gemini CLI, and Kilo Code. Ships 2 skills and 2 official Microsoft MCP servers.",
|
|
5
5
|
"main": "bin/cli.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"bi-superpowers": "bin/cli.js"
|
|
@@ -25,11 +25,7 @@
|
|
|
25
25
|
"chalk": "^4.1.2",
|
|
26
26
|
"chokidar": "^3.6.0",
|
|
27
27
|
"cli-table3": "^0.6.5",
|
|
28
|
-
"
|
|
29
|
-
"gray-matter": "^4.0.3",
|
|
30
|
-
"js-yaml": "^4.1.0",
|
|
31
|
-
"ora": "^5.4.1",
|
|
32
|
-
"simple-git": "^3.22.0"
|
|
28
|
+
"ora": "^5.4.1"
|
|
33
29
|
},
|
|
34
30
|
"devDependencies": {
|
|
35
31
|
"@eslint/js": "^9.39.2",
|
|
@@ -81,9 +77,7 @@
|
|
|
81
77
|
"commands/",
|
|
82
78
|
"skills/",
|
|
83
79
|
"src/content/",
|
|
84
|
-
"library/",
|
|
85
80
|
"desktop-extension/",
|
|
86
|
-
"docs/",
|
|
87
81
|
"config.json",
|
|
88
82
|
"config.example.json",
|
|
89
83
|
"README.md",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "pbi-connect"
|
|
3
3
|
description: "Use when the user asks about Power BI MCP Connection Skill, especially phrases like \"connect Power BI\", \"modeling mcp\", \"Power BI Desktop\", \"conectar Power BI\", \"can't connect to Power BI\"."
|
|
4
|
-
version: "3.
|
|
4
|
+
version: "3.2.0"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- Generated by BI Agent Superpowers. Edit src/content/skills/pbi-connect.md instead. -->
|
package/bin/commands/add.js
DELETED
|
@@ -1,533 +0,0 @@
|
|
|
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;
|