@luquimbo/bi-superpowers 1.0.0 → 1.1.2
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 +46 -0
- package/.claude-plugin/plugin.json +1 -1
- package/.mcp.json +4 -0
- package/AGENTS.md +98 -7
- package/README.md +139 -118
- package/bin/cli.js +45 -31
- package/bin/commands/install.js +317 -0
- package/bin/lib/microsoft-mcp.js +8 -0
- package/bin/lib/microsoft-mcp.test.js +5 -0
- package/package.json +1 -1
- package/skills/contributions/SKILL.md +1 -1
- package/skills/data-model-design/SKILL.md +1 -1
- package/skills/data-modeling/SKILL.md +82 -56
- package/skills/data-quality/SKILL.md +1 -1
- package/skills/dax/SKILL.md +74 -36
- package/skills/dax-doctor/SKILL.md +1 -1
- package/skills/dax-udf/SKILL.md +1 -1
- package/skills/deployment/SKILL.md +1 -1
- package/skills/excel-formulas/SKILL.md +1 -1
- package/skills/fabric-scripts/SKILL.md +1 -1
- package/skills/fast-standard/SKILL.md +1 -1
- package/skills/governance/SKILL.md +103 -50
- package/skills/migration-assistant/SKILL.md +1 -1
- package/skills/model-documenter/SKILL.md +1 -1
- package/skills/pbi-connect/SKILL.md +1 -1
- package/skills/power-query/SKILL.md +1 -1
- package/skills/project-kickoff/SKILL.md +1 -1
- package/skills/query-performance/SKILL.md +1 -1
- package/skills/report-design/SKILL.md +1 -1
- package/skills/report-layout/SKILL.md +1 -1
- package/skills/rls-design/SKILL.md +1 -1
- package/skills/semantic-model/SKILL.md +1 -1
- package/skills/testing-validation/SKILL.md +1 -1
- package/skills/theme-tweaker/SKILL.md +1 -1
- package/src/content/mcp-requirements.json +13 -0
- package/src/content/skills/data-modeling.md +81 -55
- package/src/content/skills/dax.md +73 -35
- package/src/content/skills/governance.md +102 -49
package/bin/cli.js
CHANGED
|
@@ -43,7 +43,7 @@ try {
|
|
|
43
43
|
// Wrapped in try-catch because they may not be available during initial npm install
|
|
44
44
|
let searchCommand, lintCommand, diffCommand, watchCommand, mcpSetupCommand, tui;
|
|
45
45
|
let setupCommand, addCommand, pullCommand, pushCommand, syncProfileCommand, syncSourceCommand;
|
|
46
|
-
let changelogCommand, buildDesktopCommand;
|
|
46
|
+
let changelogCommand, buildDesktopCommand, installCommand;
|
|
47
47
|
try {
|
|
48
48
|
searchCommand = require('./commands/search'); // Fuzzy search across library content
|
|
49
49
|
lintCommand = require('./commands/lint'); // Skill file validation
|
|
@@ -61,6 +61,7 @@ try {
|
|
|
61
61
|
syncSourceCommand = require('./commands/sync-source'); // Bidirectional sync
|
|
62
62
|
changelogCommand = require('./commands/changelog'); // Generate changelog from Git
|
|
63
63
|
buildDesktopCommand = require('./commands/build-desktop'); // Build MCPB for Claude Desktop
|
|
64
|
+
installCommand = require('./commands/install'); // Multi-agent skill installer
|
|
64
65
|
} catch (e) {
|
|
65
66
|
// Silent fail - commands may not be available during npm install phase
|
|
66
67
|
// This is expected behavior, not an error condition
|
|
@@ -126,47 +127,29 @@ const AI_TOOLS = generators ? generators.AI_TOOLS : {};
|
|
|
126
127
|
* - Developer: Advanced tools for content management (xray, checkup, scan, sentinel, powers)
|
|
127
128
|
* - Legacy: Old command names maintained for backward compatibility
|
|
128
129
|
*/
|
|
130
|
+
// Commands are registered lazily to avoid TDZ issues with const wrappers.
|
|
131
|
+
// Core commands use hoisted function declarations; developer commands use
|
|
132
|
+
// createCommandWrapper which is defined later. We populate the map in
|
|
133
|
+
// registerCommands() which runs before main().
|
|
129
134
|
const commands = {
|
|
130
|
-
// Core commands - basic info and status
|
|
135
|
+
// Core commands - basic info and status (hoisted functions, safe here)
|
|
131
136
|
help: showHelp,
|
|
132
137
|
version: showVersion,
|
|
133
138
|
about: showInfo,
|
|
134
139
|
status: showLicenseStatus,
|
|
135
140
|
|
|
136
|
-
// Setup & sync - project configuration
|
|
137
|
-
kickoff: initProject,
|
|
138
|
-
recharge: syncProject,
|
|
139
|
-
unlock: activateLicense,
|
|
140
|
-
upgrade: updatePackage,
|
|
141
|
-
|
|
142
|
-
// Developer tools - content management
|
|
143
|
-
xray: runSearch, // Fuzzy search library content
|
|
144
|
-
checkup: runLint, // Validate skill files
|
|
145
|
-
scan: runDiff, // Compare source vs generated
|
|
146
|
-
sentinel: runWatch, // Watch and auto-regenerate
|
|
147
|
-
powers: listAgents, // List available skills
|
|
148
|
-
'mcp-setup': runMcpSetup, // Configure MCP servers for AI tools
|
|
149
|
-
mcp: runMcpSetup, // Alias for mcp-setup
|
|
150
|
-
'build-desktop': runBuildDesktop, // Build MCPB extension for Claude Desktop
|
|
141
|
+
// Setup & sync - project configuration (hoisted functions, safe here)
|
|
142
|
+
kickoff: initProject,
|
|
143
|
+
recharge: syncProject,
|
|
144
|
+
unlock: activateLicense,
|
|
145
|
+
upgrade: updatePackage,
|
|
146
|
+
powers: listAgents,
|
|
151
147
|
|
|
152
|
-
//
|
|
153
|
-
setup: runSetup, // Onboarding wizard - creates bi-repo
|
|
154
|
-
add: runAdd, // Add project to bi-repo
|
|
155
|
-
pull: runPull, // Pull changes from original file to repo
|
|
156
|
-
push: runPush, // Push changes from repo to original file
|
|
157
|
-
'sync-source': runSyncSource, // Bidirectional sync with original file
|
|
158
|
-
'sync-profile': runSyncProfile, // Sync snippets to base profile
|
|
159
|
-
changelog: runChangelog, // Generate changelog from Git history
|
|
160
|
-
|
|
161
|
-
// Legacy aliases (backward compatibility with v2.0.x)
|
|
148
|
+
// Legacy aliases (hoisted functions, safe here)
|
|
162
149
|
init: initProject,
|
|
163
150
|
sync: syncProject,
|
|
164
151
|
activate: activateLicense,
|
|
165
152
|
update: updatePackage,
|
|
166
|
-
search: runSearch,
|
|
167
|
-
lint: runLint,
|
|
168
|
-
diff: runDiff,
|
|
169
|
-
watch: runWatch,
|
|
170
153
|
list: listAgents,
|
|
171
154
|
info: showInfo,
|
|
172
155
|
};
|
|
@@ -199,6 +182,7 @@ Usage:
|
|
|
199
182
|
super <command> [options]
|
|
200
183
|
|
|
201
184
|
Commands:
|
|
185
|
+
install Install skills for your AI agents (Claude, Copilot, Codex...)
|
|
202
186
|
unlock Activate your license key
|
|
203
187
|
kickoff [path] Initialize the Claude Code plugin in your project
|
|
204
188
|
recharge [path] Regenerate the plugin from source skills
|
|
@@ -225,7 +209,15 @@ Repo Multi-Proyecto (v3):
|
|
|
225
209
|
Options:
|
|
226
210
|
--dry-run Preview changes without creating files (kickoff, recharge)
|
|
227
211
|
|
|
212
|
+
Install options:
|
|
213
|
+
--agent, -a Specify agents (e.g. -a claude-code -a codex)
|
|
214
|
+
--all Install for all supported agents
|
|
215
|
+
--yes, -y Skip confirmation prompts
|
|
216
|
+
|
|
228
217
|
Examples:
|
|
218
|
+
super install # Interactive multi-agent installer
|
|
219
|
+
super install -a claude-code # Install for Claude Code only
|
|
220
|
+
super install --all --yes # Install for all agents, no prompts
|
|
229
221
|
super unlock # Activate your license
|
|
230
222
|
super kickoff # Initialize plugin in current directory
|
|
231
223
|
super kickoff ./my-project # Initialize in specific directory
|
|
@@ -1006,6 +998,28 @@ const runSyncSource = createCommandWrapper(syncSourceCommand, 'Sync-source');
|
|
|
1006
998
|
const runSyncProfile = createCommandWrapper(syncProfileCommand, 'Sync-profile');
|
|
1007
999
|
const runChangelog = createCommandWrapper(changelogCommand, 'Changelog');
|
|
1008
1000
|
const runBuildDesktop = createCommandWrapper(buildDesktopCommand, 'Build Desktop');
|
|
1001
|
+
const runInstall = createCommandWrapper(installCommand, 'Install');
|
|
1002
|
+
|
|
1003
|
+
// Register commands that depend on createCommandWrapper (avoids TDZ in object literal)
|
|
1004
|
+
commands.install = runInstall;
|
|
1005
|
+
commands.xray = runSearch;
|
|
1006
|
+
commands.checkup = runLint;
|
|
1007
|
+
commands.scan = runDiff;
|
|
1008
|
+
commands.sentinel = runWatch;
|
|
1009
|
+
commands['mcp-setup'] = runMcpSetup;
|
|
1010
|
+
commands.mcp = runMcpSetup;
|
|
1011
|
+
commands['build-desktop'] = runBuildDesktop;
|
|
1012
|
+
commands.setup = runSetup;
|
|
1013
|
+
commands.add = runAdd;
|
|
1014
|
+
commands.pull = runPull;
|
|
1015
|
+
commands.push = runPush;
|
|
1016
|
+
commands['sync-source'] = runSyncSource;
|
|
1017
|
+
commands['sync-profile'] = runSyncProfile;
|
|
1018
|
+
commands.changelog = runChangelog;
|
|
1019
|
+
commands.search = runSearch;
|
|
1020
|
+
commands.lint = runLint;
|
|
1021
|
+
commands.diff = runDiff;
|
|
1022
|
+
commands.watch = runWatch;
|
|
1009
1023
|
|
|
1010
1024
|
/**
|
|
1011
1025
|
* Run watch command (special case - needs additional context)
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install Command - Multi-agent skill installer
|
|
3
|
+
* ===============================================
|
|
4
|
+
*
|
|
5
|
+
* Installs BI Agent Superpowers skills into the correct directories
|
|
6
|
+
* for each AI coding agent. Inspired by the `npx skills` CLI from Vercel Labs.
|
|
7
|
+
*
|
|
8
|
+
* Skills are always installed at the user level (~/) to protect licensed content.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* npx @luquimbo/bi-superpowers install
|
|
12
|
+
* super install
|
|
13
|
+
* super install --agent claude-code --agent codex
|
|
14
|
+
* super install --all --yes
|
|
15
|
+
*
|
|
16
|
+
* @module commands/install
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
const os = require('os');
|
|
22
|
+
const readline = require('readline');
|
|
23
|
+
|
|
24
|
+
// Agent registry: each agent's skill directory path (relative to home directory)
|
|
25
|
+
// Order matches the interactive installer display order
|
|
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
|
+
// Universal path — most agents read from .agents/skills/
|
|
35
|
+
const UNIVERSAL_DIR = '.agents/skills';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Detect which agents are available by checking for their config directories
|
|
39
|
+
* @param {string} baseDir - Directory to check (project root or home)
|
|
40
|
+
* @returns {string[]} Array of detected agent IDs
|
|
41
|
+
*/
|
|
42
|
+
function detectAgents(baseDir) {
|
|
43
|
+
const detected = [];
|
|
44
|
+
for (const [id, agent] of Object.entries(AGENTS)) {
|
|
45
|
+
const agentRoot = path.dirname(agent.dir);
|
|
46
|
+
const checkPath = path.join(baseDir, agentRoot);
|
|
47
|
+
if (fs.existsSync(checkPath)) {
|
|
48
|
+
detected.push(id);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return detected;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Create readline interface for interactive prompts
|
|
56
|
+
*/
|
|
57
|
+
function createReadline() {
|
|
58
|
+
return readline.createInterface({
|
|
59
|
+
input: process.stdin,
|
|
60
|
+
output: process.stdout,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Prompt user with a question
|
|
66
|
+
*/
|
|
67
|
+
function prompt(rl, question) {
|
|
68
|
+
return new Promise((resolve) => {
|
|
69
|
+
rl.question(question, (answer) => resolve(answer.trim()));
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Display a numbered list and let user pick multiple items
|
|
75
|
+
* @param {readline.Interface} rl
|
|
76
|
+
* @param {Array<{id: string, name: string}>} items
|
|
77
|
+
* @param {string[]} preselected - IDs to preselect
|
|
78
|
+
* @returns {Promise<string[]>} Selected IDs
|
|
79
|
+
*/
|
|
80
|
+
async function selectMultiple(rl, items, preselected = []) {
|
|
81
|
+
items.forEach((item, i) => {
|
|
82
|
+
const marker = preselected.includes(item.id) ? '●' : '○';
|
|
83
|
+
console.log(` ${i + 1}) ${marker} ${item.name}`);
|
|
84
|
+
});
|
|
85
|
+
console.log();
|
|
86
|
+
console.log(' Enter numbers separated by commas (e.g. 1,2,3)');
|
|
87
|
+
console.log(' Press Enter for detected agents, or "a" for all');
|
|
88
|
+
|
|
89
|
+
const answer = await prompt(rl, '\n > ');
|
|
90
|
+
|
|
91
|
+
if (answer.toLowerCase() === 'a') {
|
|
92
|
+
return items.map((item) => item.id);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (answer === '') {
|
|
96
|
+
return preselected.length > 0 ? preselected : items.map((item) => item.id);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const indices = answer
|
|
100
|
+
.split(',')
|
|
101
|
+
.map((s) => parseInt(s.trim(), 10) - 1)
|
|
102
|
+
.filter((i) => i >= 0 && i < items.length);
|
|
103
|
+
|
|
104
|
+
return indices.map((i) => items[i].id);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Copy a skill directory to a target location
|
|
109
|
+
* @param {string} srcDir - Source skill directory (containing SKILL.md)
|
|
110
|
+
* @param {string} destDir - Destination directory
|
|
111
|
+
*/
|
|
112
|
+
function copySkillDir(srcDir, destDir) {
|
|
113
|
+
if (!fs.existsSync(destDir)) {
|
|
114
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const entries = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
118
|
+
for (const entry of entries) {
|
|
119
|
+
const srcPath = path.join(srcDir, entry.name);
|
|
120
|
+
const destPath = path.join(destDir, entry.name);
|
|
121
|
+
|
|
122
|
+
if (entry.isDirectory()) {
|
|
123
|
+
copySkillDir(srcPath, destPath);
|
|
124
|
+
} else {
|
|
125
|
+
fs.copyFileSync(srcPath, destPath);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Main install command handler
|
|
132
|
+
* @param {string[]} args - CLI arguments
|
|
133
|
+
* @param {Object} config - Command config from CLI
|
|
134
|
+
*/
|
|
135
|
+
async function installCommand(args, config) {
|
|
136
|
+
const chalk = require('chalk');
|
|
137
|
+
const boxen = require('boxen');
|
|
138
|
+
|
|
139
|
+
const isYes = args.includes('--yes') || args.includes('-y');
|
|
140
|
+
const isAll = args.includes('--all');
|
|
141
|
+
const agentFlags = [];
|
|
142
|
+
|
|
143
|
+
// Parse --agent flags
|
|
144
|
+
for (let i = 0; i < args.length; i++) {
|
|
145
|
+
if ((args[i] === '--agent' || args[i] === '-a') && args[i + 1]) {
|
|
146
|
+
agentFlags.push(args[i + 1]);
|
|
147
|
+
i++;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Always install at user level (home directory) to protect licensed content
|
|
152
|
+
const baseDir = os.homedir();
|
|
153
|
+
|
|
154
|
+
// Find skills from the package
|
|
155
|
+
const packageDir = config.packageDir || path.dirname(__dirname);
|
|
156
|
+
const skillsSourceDir = path.join(packageDir, 'skills');
|
|
157
|
+
|
|
158
|
+
if (!fs.existsSync(skillsSourceDir)) {
|
|
159
|
+
console.error(chalk.red('Skills directory not found. Try reinstalling the package.'));
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Read available skills
|
|
164
|
+
const skillDirs = fs
|
|
165
|
+
.readdirSync(skillsSourceDir, { withFileTypes: true })
|
|
166
|
+
.filter((d) => d.isDirectory() && fs.existsSync(path.join(skillsSourceDir, d.name, 'SKILL.md')))
|
|
167
|
+
.map((d) => d.name);
|
|
168
|
+
|
|
169
|
+
// Header
|
|
170
|
+
console.log(
|
|
171
|
+
boxen(
|
|
172
|
+
chalk.bold.cyan('BI Agent Superpowers') +
|
|
173
|
+
chalk.gray(` v${config.version}`) +
|
|
174
|
+
'\n' +
|
|
175
|
+
chalk.gray('Multi-agent skill installer'),
|
|
176
|
+
{
|
|
177
|
+
padding: 1,
|
|
178
|
+
borderStyle: 'round',
|
|
179
|
+
borderColor: 'cyan',
|
|
180
|
+
}
|
|
181
|
+
)
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
console.log(chalk.gray(` Install path: ~/${UNIVERSAL_DIR}/`));
|
|
185
|
+
console.log(chalk.gray(` Skills: ${skillDirs.length} available\n`));
|
|
186
|
+
|
|
187
|
+
// Determine which agents to install for
|
|
188
|
+
let selectedAgents;
|
|
189
|
+
|
|
190
|
+
if (isAll) {
|
|
191
|
+
selectedAgents = Object.keys(AGENTS);
|
|
192
|
+
} else if (agentFlags.length > 0) {
|
|
193
|
+
selectedAgents = agentFlags.filter((a) => AGENTS[a]);
|
|
194
|
+
const unknown = agentFlags.filter((a) => !AGENTS[a]);
|
|
195
|
+
if (unknown.length > 0) {
|
|
196
|
+
console.log(chalk.yellow(` Unknown agents: ${unknown.join(', ')}`));
|
|
197
|
+
console.log(chalk.gray(` Available: ${Object.keys(AGENTS).join(', ')}\n`));
|
|
198
|
+
}
|
|
199
|
+
} else if (isYes) {
|
|
200
|
+
selectedAgents = Object.keys(AGENTS);
|
|
201
|
+
} else {
|
|
202
|
+
// Interactive mode: detect and ask
|
|
203
|
+
const detected = detectAgents(baseDir);
|
|
204
|
+
|
|
205
|
+
console.log(chalk.cyan(' Select agents to install for:\n'));
|
|
206
|
+
|
|
207
|
+
const items = Object.entries(AGENTS).map(([id, agent]) => ({
|
|
208
|
+
id,
|
|
209
|
+
name: detected.includes(id) ? `${agent.name} ${chalk.green('(detected)')}` : agent.name,
|
|
210
|
+
}));
|
|
211
|
+
|
|
212
|
+
const rl = createReadline();
|
|
213
|
+
try {
|
|
214
|
+
selectedAgents = await selectMultiple(rl, items, detected);
|
|
215
|
+
} finally {
|
|
216
|
+
rl.close();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (selectedAgents.length === 0) {
|
|
221
|
+
console.log(chalk.yellow('\n No agents selected. Nothing to install.'));
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
console.log(
|
|
226
|
+
chalk.cyan(`\n Installing ${skillDirs.length} skills for ${selectedAgents.length} agents...\n`)
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
// Always install to universal path first
|
|
230
|
+
const universalTarget = path.join(baseDir, UNIVERSAL_DIR);
|
|
231
|
+
let installedCount = 0;
|
|
232
|
+
|
|
233
|
+
for (const skill of skillDirs) {
|
|
234
|
+
const src = path.join(skillsSourceDir, skill);
|
|
235
|
+
const dest = path.join(universalTarget, skill);
|
|
236
|
+
copySkillDir(src, dest);
|
|
237
|
+
installedCount++;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// List agents whose default dir is the universal path (e.g. Codex)
|
|
241
|
+
const universalAgents = selectedAgents
|
|
242
|
+
.filter((id) => AGENTS[id] && AGENTS[id].dir === UNIVERSAL_DIR)
|
|
243
|
+
.map((id) => AGENTS[id].name);
|
|
244
|
+
const universalSuffix = universalAgents.length > 0 ? ` — ${universalAgents.join(', ')}` : '';
|
|
245
|
+
console.log(chalk.green(` ✓ ${UNIVERSAL_DIR}/ (${installedCount} skills)${universalSuffix}`));
|
|
246
|
+
|
|
247
|
+
// Symlink agent-specific directories to universal path
|
|
248
|
+
const agentResults = [];
|
|
249
|
+
for (const agentId of selectedAgents) {
|
|
250
|
+
const agent = AGENTS[agentId];
|
|
251
|
+
if (agent.dir === UNIVERSAL_DIR) continue; // Already handled
|
|
252
|
+
|
|
253
|
+
const agentTarget = path.join(baseDir, agent.dir);
|
|
254
|
+
|
|
255
|
+
// If the directory already exists and is not a symlink, copy instead
|
|
256
|
+
if (fs.existsSync(agentTarget) && !fs.lstatSync(agentTarget).isSymbolicLink()) {
|
|
257
|
+
// Copy skills into existing directory
|
|
258
|
+
for (const skill of skillDirs) {
|
|
259
|
+
copySkillDir(path.join(skillsSourceDir, skill), path.join(agentTarget, skill));
|
|
260
|
+
}
|
|
261
|
+
agentResults.push({ agent: agent.name, method: 'copied', dir: agent.dir });
|
|
262
|
+
} else {
|
|
263
|
+
// Create symlink to universal directory
|
|
264
|
+
const parentDir = path.dirname(agentTarget);
|
|
265
|
+
if (!fs.existsSync(parentDir)) {
|
|
266
|
+
fs.mkdirSync(parentDir, { recursive: true });
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Remove existing symlink if present
|
|
270
|
+
if (fs.existsSync(agentTarget)) {
|
|
271
|
+
fs.unlinkSync(agentTarget);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
try {
|
|
275
|
+
// Relative symlink from agent dir to universal dir
|
|
276
|
+
const relPath = path.relative(parentDir, universalTarget);
|
|
277
|
+
fs.symlinkSync(relPath, agentTarget);
|
|
278
|
+
agentResults.push({ agent: agent.name, method: 'symlinked', dir: agent.dir });
|
|
279
|
+
} catch (_e) {
|
|
280
|
+
// Fallback to copy if symlink fails (e.g. Windows without admin)
|
|
281
|
+
if (!fs.existsSync(agentTarget)) {
|
|
282
|
+
fs.mkdirSync(agentTarget, { recursive: true });
|
|
283
|
+
}
|
|
284
|
+
for (const skill of skillDirs) {
|
|
285
|
+
copySkillDir(path.join(skillsSourceDir, skill), path.join(agentTarget, skill));
|
|
286
|
+
}
|
|
287
|
+
agentResults.push({ agent: agent.name, method: 'copied', dir: agent.dir });
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Print results
|
|
293
|
+
for (const result of agentResults) {
|
|
294
|
+
const icon = result.method === 'symlinked' ? '→' : '✓';
|
|
295
|
+
console.log(chalk.green(` ${icon} ${result.dir}/ (${result.method}) — ${result.agent}`));
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Summary
|
|
299
|
+
const totalAgents = agentResults.length + 1; // +1 for universal
|
|
300
|
+
console.log(
|
|
301
|
+
boxen(
|
|
302
|
+
chalk.green.bold(`Installed ${installedCount} skills for ${totalAgents} agents`) +
|
|
303
|
+
'\n\n' +
|
|
304
|
+
chalk.gray('Skills are ready to use. Open your AI agent and start prompting.') +
|
|
305
|
+
'\n' +
|
|
306
|
+
chalk.gray('Example: "Help me write a DAX measure for YTD sales"'),
|
|
307
|
+
{
|
|
308
|
+
padding: 1,
|
|
309
|
+
margin: { top: 1 },
|
|
310
|
+
borderStyle: 'round',
|
|
311
|
+
borderColor: 'green',
|
|
312
|
+
}
|
|
313
|
+
)
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
module.exports = installCommand;
|
package/bin/lib/microsoft-mcp.js
CHANGED
|
@@ -13,9 +13,11 @@ const path = require('path');
|
|
|
13
13
|
|
|
14
14
|
const REMOTE_POWERBI_URL = 'https://api.fabric.microsoft.com/v1/mcp/powerbi';
|
|
15
15
|
const FABRIC_MCP_PACKAGE = '@microsoft/fabric-mcp@latest';
|
|
16
|
+
const MICROSOFT_LEARN_URL = 'https://learn.microsoft.com/api/mcp';
|
|
16
17
|
const MODELING_SERVER_NAME = 'powerbi-modeling-mcp';
|
|
17
18
|
const REMOTE_SERVER_NAME = 'powerbi-remote';
|
|
18
19
|
const FABRIC_SERVER_NAME = 'fabric-mcp-server';
|
|
20
|
+
const LEARN_SERVER_NAME = 'microsoft-learn';
|
|
19
21
|
const ABSOLUTE_LAUNCHER_MODE = 'absolute';
|
|
20
22
|
const PLUGIN_ROOT_LAUNCHER_MODE = 'plugin-root';
|
|
21
23
|
|
|
@@ -63,6 +65,10 @@ function createPluginMcpConfig(options = {}) {
|
|
|
63
65
|
command: 'node',
|
|
64
66
|
args: [launcherPath],
|
|
65
67
|
},
|
|
68
|
+
[LEARN_SERVER_NAME]: {
|
|
69
|
+
type: 'http',
|
|
70
|
+
url: MICROSOFT_LEARN_URL,
|
|
71
|
+
},
|
|
66
72
|
};
|
|
67
73
|
}
|
|
68
74
|
|
|
@@ -166,9 +172,11 @@ module.exports = {
|
|
|
166
172
|
PLUGIN_ROOT_LAUNCHER_MODE,
|
|
167
173
|
REMOTE_POWERBI_URL,
|
|
168
174
|
FABRIC_MCP_PACKAGE,
|
|
175
|
+
MICROSOFT_LEARN_URL,
|
|
169
176
|
MODELING_SERVER_NAME,
|
|
170
177
|
REMOTE_SERVER_NAME,
|
|
171
178
|
FABRIC_SERVER_NAME,
|
|
179
|
+
LEARN_SERVER_NAME,
|
|
172
180
|
resolveModelingLauncherPath,
|
|
173
181
|
createPluginMcpConfig,
|
|
174
182
|
createMcpConfigForFormat,
|
|
@@ -11,6 +11,7 @@ const {
|
|
|
11
11
|
PLUGIN_ROOT_LAUNCHER_MODE,
|
|
12
12
|
REMOTE_POWERBI_URL,
|
|
13
13
|
FABRIC_MCP_PACKAGE,
|
|
14
|
+
MICROSOFT_LEARN_URL,
|
|
14
15
|
createPluginMcpConfig,
|
|
15
16
|
createMcpConfigForFormat,
|
|
16
17
|
mergeMcpConfig,
|
|
@@ -58,6 +59,10 @@ describe('createPluginMcpConfig', () => {
|
|
|
58
59
|
assert.strictEqual(config['powerbi-modeling-mcp'].type, 'stdio');
|
|
59
60
|
assert.strictEqual(config['powerbi-modeling-mcp'].command, 'node');
|
|
60
61
|
assert.ok(config['powerbi-modeling-mcp'].args[0].endsWith('powerbi-modeling-launcher.js'));
|
|
62
|
+
assert.deepStrictEqual(config['microsoft-learn'], {
|
|
63
|
+
type: 'http',
|
|
64
|
+
url: MICROSOFT_LEARN_URL,
|
|
65
|
+
});
|
|
61
66
|
});
|
|
62
67
|
});
|
|
63
68
|
|
package/package.json
CHANGED
|
@@ -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.
|
|
4
|
+
version: "1.1.2"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- Generated by BI Agent Superpowers. Edit src/content/skills/data-model-design.md instead. -->
|