@luquimbo/bi-superpowers 3.2.0 → 4.1.1
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 +5 -3
- package/.claude-plugin/plugin.json +28 -2
- package/.claude-plugin/skill-manifest.json +22 -6
- package/.plugin/plugin.json +1 -1
- package/AGENTS.md +53 -36
- package/CHANGELOG.md +310 -0
- package/README.md +77 -26
- package/bin/build-plugin.js +11 -4
- package/bin/cli.js +113 -16
- package/bin/commands/build-desktop.js +35 -16
- package/bin/commands/diff.js +31 -13
- package/bin/commands/install.js +7 -3
- package/bin/commands/lint.js +40 -26
- package/bin/commands/mcp-setup.js +3 -10
- package/bin/commands/update-check.js +403 -0
- package/bin/lib/generators/claude-plugin.js +162 -6
- package/bin/lib/generators/shared.js +29 -33
- package/bin/lib/mcp-config.js +168 -12
- package/bin/lib/skills.js +115 -27
- package/bin/postinstall.js +4 -2
- package/bin/utils/mcp-detect.js +2 -2
- package/commands/bi-start.md +197 -0
- package/commands/pbi-connect.md +43 -65
- package/commands/project-kickoff.md +393 -673
- package/commands/report-design.md +403 -0
- package/desktop-extension/manifest.json +3 -3
- package/package.json +7 -5
- package/skills/bi-start/SKILL.md +199 -0
- package/skills/bi-start/scripts/update-check.js +403 -0
- package/skills/pbi-connect/SKILL.md +45 -67
- package/skills/pbi-connect/scripts/update-check.js +403 -0
- package/skills/project-kickoff/SKILL.md +395 -675
- package/skills/project-kickoff/scripts/update-check.js +403 -0
- package/skills/report-design/SKILL.md +405 -0
- package/skills/report-design/references/cli-commands.md +184 -0
- package/skills/report-design/references/cli-setup.md +101 -0
- package/skills/report-design/references/close-write-open-pattern.md +80 -0
- package/skills/report-design/references/layouts/finance.md +65 -0
- package/skills/report-design/references/layouts/generic.md +46 -0
- package/skills/report-design/references/layouts/hr.md +48 -0
- package/skills/report-design/references/layouts/marketing.md +45 -0
- package/skills/report-design/references/layouts/operations.md +44 -0
- package/skills/report-design/references/layouts/sales.md +50 -0
- package/skills/report-design/references/native-visuals.md +341 -0
- package/skills/report-design/references/pbi-desktop-installation.md +87 -0
- package/skills/report-design/references/pbir-preview-activation.md +40 -0
- package/skills/report-design/references/slicer.md +89 -0
- package/skills/report-design/references/textbox.md +101 -0
- package/skills/report-design/references/themes/BISuperpowers.json +915 -0
- package/skills/report-design/references/troubleshooting.md +135 -0
- package/skills/report-design/references/visual-types.md +78 -0
- package/skills/report-design/scripts/apply-theme.js +243 -0
- package/skills/report-design/scripts/create-visual.js +878 -0
- package/skills/report-design/scripts/ensure-pbi-cli.sh +41 -0
- package/skills/report-design/scripts/update-check.js +403 -0
- package/skills/report-design/scripts/validate-pbir.js +322 -0
- package/src/content/base.md +12 -68
- package/src/content/mcp-requirements.json +0 -25
- package/src/content/routing.md +19 -74
- package/src/content/skills/bi-start.md +191 -0
- package/src/content/skills/pbi-connect.md +22 -65
- package/src/content/skills/project-kickoff.md +372 -673
- package/src/content/skills/report-design/SKILL.md +376 -0
- package/src/content/skills/report-design/references/cli-commands.md +184 -0
- package/src/content/skills/report-design/references/cli-setup.md +101 -0
- package/src/content/skills/report-design/references/close-write-open-pattern.md +80 -0
- package/src/content/skills/report-design/references/layouts/finance.md +65 -0
- package/src/content/skills/report-design/references/layouts/generic.md +46 -0
- package/src/content/skills/report-design/references/layouts/hr.md +48 -0
- package/src/content/skills/report-design/references/layouts/marketing.md +45 -0
- package/src/content/skills/report-design/references/layouts/operations.md +44 -0
- package/src/content/skills/report-design/references/layouts/sales.md +50 -0
- package/src/content/skills/report-design/references/native-visuals.md +341 -0
- package/src/content/skills/report-design/references/pbi-desktop-installation.md +87 -0
- package/src/content/skills/report-design/references/pbir-preview-activation.md +40 -0
- package/src/content/skills/report-design/references/slicer.md +89 -0
- package/src/content/skills/report-design/references/textbox.md +101 -0
- package/src/content/skills/report-design/references/themes/BISuperpowers.json +915 -0
- package/src/content/skills/report-design/references/troubleshooting.md +135 -0
- package/src/content/skills/report-design/references/visual-types.md +78 -0
- package/src/content/skills/report-design/scripts/apply-theme.js +243 -0
- package/src/content/skills/report-design/scripts/create-visual.js +878 -0
- package/src/content/skills/report-design/scripts/ensure-pbi-cli.sh +41 -0
- package/src/content/skills/report-design/scripts/validate-pbir.js +322 -0
- package/bin/commands/install.test.js +0 -289
- package/bin/commands/lint.test.js +0 -103
- package/bin/lib/generators/claude-plugin.test.js +0 -111
- package/bin/lib/mcp-config.test.js +0 -310
- package/bin/lib/microsoft-mcp.test.js +0 -115
- package/bin/utils/mcp-detect.test.js +0 -81
- package/bin/utils/tui.test.js +0 -127
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* update-check — cross-agent version-check helper for bi-superpowers.
|
|
4
|
+
*
|
|
5
|
+
* The SKILL.md preamble (see lib/generators/claude-plugin.js) invokes this
|
|
6
|
+
* script at the start of every skill so the agent can surface an update
|
|
7
|
+
* notice to the user when a newer version is on npm — without hitting the
|
|
8
|
+
* network on every invocation. Cache TTL is 24h; repeated calls inside
|
|
9
|
+
* that window are served from `~/.bi-superpowers/update-state.json`.
|
|
10
|
+
*
|
|
11
|
+
* Output (stdout, one line):
|
|
12
|
+
* UPTODATE when installed >= latest
|
|
13
|
+
* UPDATE_AVAILABLE <installed> <latest> when installed < latest
|
|
14
|
+
* SNOOZED <iso> when user deferred the notice
|
|
15
|
+
*
|
|
16
|
+
* Flags:
|
|
17
|
+
* --force bypass cache (re-fetch npm, ignore snooze TTL)
|
|
18
|
+
* --silent-if-uptodate suppress UPTODATE line (used by the preamble)
|
|
19
|
+
* --silent-if-snoozed suppress SNOOZED line (used by the preamble)
|
|
20
|
+
* --json emit JSON instead of text
|
|
21
|
+
* --snooze 24h|48h|7d|clear set (or clear) the snooze state and exit
|
|
22
|
+
* --reset delete the state file and exit (used post-upgrade)
|
|
23
|
+
* --state-dir <path> override ~/.bi-superpowers/ (for tests)
|
|
24
|
+
* --package-name <name> override the package name (for tests)
|
|
25
|
+
* -h, --help show this help
|
|
26
|
+
*
|
|
27
|
+
* Exit code is always 0 when the script itself ran — errors during the
|
|
28
|
+
* network fetch degrade to "no output" so the caller never blocks. A
|
|
29
|
+
* non-zero exit means a user error (bad flags).
|
|
30
|
+
*
|
|
31
|
+
* Pure helpers (compareVersions, isCacheFresh, isSnoozed,
|
|
32
|
+
* computeNextSnoozeUntil, readState, writeState, fetchLatestVersion) are
|
|
33
|
+
* exported so unit tests can exercise them without spawning child
|
|
34
|
+
* processes or hitting the network.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
'use strict';
|
|
38
|
+
|
|
39
|
+
const fs = require('fs');
|
|
40
|
+
const os = require('os');
|
|
41
|
+
const path = require('path');
|
|
42
|
+
const https = require('https');
|
|
43
|
+
|
|
44
|
+
const PACKAGE_NAME = '@luquimbo/bi-superpowers';
|
|
45
|
+
const CACHE_TTL_MS = 1000 * 60 * 60 * 24; // 24 hours
|
|
46
|
+
const HTTPS_TIMEOUT_MS = 5000;
|
|
47
|
+
// Rewritten at generation time when this helper is copied into
|
|
48
|
+
// `skills/<name>/scripts/update-check.js`. In the canonical source under
|
|
49
|
+
// `bin/commands/`, it stays null and we fall back to package.json.
|
|
50
|
+
const BUNDLED_INSTALLED_VERSION = "4.1.1";
|
|
51
|
+
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Argument parsing
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
function parseArgs(argv) {
|
|
57
|
+
const out = {
|
|
58
|
+
force: false,
|
|
59
|
+
silentIfUptodate: false,
|
|
60
|
+
silentIfSnoozed: false,
|
|
61
|
+
json: false,
|
|
62
|
+
snooze: null,
|
|
63
|
+
reset: false,
|
|
64
|
+
help: false,
|
|
65
|
+
stateDir: null,
|
|
66
|
+
packageName: null,
|
|
67
|
+
installedVersion: null,
|
|
68
|
+
};
|
|
69
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
70
|
+
const a = argv[i];
|
|
71
|
+
if (a === '--force') out.force = true;
|
|
72
|
+
else if (a === '--silent-if-uptodate') out.silentIfUptodate = true;
|
|
73
|
+
else if (a === '--silent-if-snoozed') out.silentIfSnoozed = true;
|
|
74
|
+
else if (a === '--json') out.json = true;
|
|
75
|
+
else if (a === '--snooze') out.snooze = argv[++i];
|
|
76
|
+
else if (a === '--reset') out.reset = true;
|
|
77
|
+
else if (a === '--state-dir') out.stateDir = argv[++i];
|
|
78
|
+
else if (a === '--package-name') out.packageName = argv[++i];
|
|
79
|
+
else if (a === '--installed-version') out.installedVersion = argv[++i];
|
|
80
|
+
else if (a === '-h' || a === '--help') out.help = true;
|
|
81
|
+
else {
|
|
82
|
+
process.stderr.write(`update-check: unknown flag: ${a}\n`);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function help() {
|
|
90
|
+
process.stdout.write(
|
|
91
|
+
[
|
|
92
|
+
'Usage: update-check [options]',
|
|
93
|
+
'',
|
|
94
|
+
'Prints one of: UPTODATE, UPDATE_AVAILABLE <installed> <latest>, SNOOZED <iso>.',
|
|
95
|
+
'',
|
|
96
|
+
'Options:',
|
|
97
|
+
' --force Bypass cache and snooze TTL',
|
|
98
|
+
' --silent-if-uptodate Skip the UPTODATE line',
|
|
99
|
+
' --silent-if-snoozed Skip the SNOOZED line',
|
|
100
|
+
' --json Emit JSON',
|
|
101
|
+
' --snooze <dur> Set snooze state (24h|48h|7d) or "clear" to reset snooze',
|
|
102
|
+
' --reset Delete the state file (used after a successful upgrade)',
|
|
103
|
+
' --state-dir <path> Override ~/.bi-superpowers/ (tests)',
|
|
104
|
+
' --package-name <name> Override the package name (tests)',
|
|
105
|
+
' --installed-version <v> Override the installed version (generated skill bundles)',
|
|
106
|
+
' -h, --help Show this help',
|
|
107
|
+
'',
|
|
108
|
+
].join('\n')
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// Version comparison (semver-ish: MAJOR.MINOR.PATCH with optional -prerelease)
|
|
114
|
+
// No deps; handles the shapes @luquimbo/bi-superpowers uses today.
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Compare two semver strings.
|
|
119
|
+
* Returns -1 if a < b, 0 if equal, 1 if a > b.
|
|
120
|
+
* Pre-release tags (`-alpha.1`) sort before the release per semver.
|
|
121
|
+
*/
|
|
122
|
+
function compareVersions(a, b) {
|
|
123
|
+
const parse = (v) => {
|
|
124
|
+
const [main, pre] = String(v).split('-');
|
|
125
|
+
const parts = main.split('.').map((n) => parseInt(n, 10) || 0);
|
|
126
|
+
while (parts.length < 3) parts.push(0);
|
|
127
|
+
return { parts, pre: pre || null };
|
|
128
|
+
};
|
|
129
|
+
const va = parse(a);
|
|
130
|
+
const vb = parse(b);
|
|
131
|
+
for (let i = 0; i < 3; i += 1) {
|
|
132
|
+
if (va.parts[i] !== vb.parts[i]) return va.parts[i] < vb.parts[i] ? -1 : 1;
|
|
133
|
+
}
|
|
134
|
+
// Main equal — pre-release < release.
|
|
135
|
+
if (va.pre && !vb.pre) return -1;
|
|
136
|
+
if (!va.pre && vb.pre) return 1;
|
|
137
|
+
if (va.pre && vb.pre) {
|
|
138
|
+
if (va.pre < vb.pre) return -1;
|
|
139
|
+
if (va.pre > vb.pre) return 1;
|
|
140
|
+
}
|
|
141
|
+
return 0;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
// Cache + snooze state
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
|
|
148
|
+
function defaultStateDir() {
|
|
149
|
+
return path.join(os.homedir(), '.bi-superpowers');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function stateFilePath(stateDir) {
|
|
153
|
+
return path.join(stateDir, 'update-state.json');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function readState(stateDir) {
|
|
157
|
+
const filePath = stateFilePath(stateDir);
|
|
158
|
+
if (!fs.existsSync(filePath)) return null;
|
|
159
|
+
try {
|
|
160
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
161
|
+
} catch (_) {
|
|
162
|
+
// Malformed → treat as no cache.
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function writeState(stateDir, state) {
|
|
168
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
169
|
+
fs.writeFileSync(stateFilePath(stateDir), JSON.stringify(state, null, 2) + '\n');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function resetState(stateDir) {
|
|
173
|
+
const filePath = stateFilePath(stateDir);
|
|
174
|
+
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function isCacheFresh(state, now, ttlMs) {
|
|
178
|
+
if (!state || !state.checkedAt) return false;
|
|
179
|
+
const checkedAt = Date.parse(state.checkedAt);
|
|
180
|
+
if (!Number.isFinite(checkedAt)) return false;
|
|
181
|
+
return now - checkedAt < ttlMs;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function isSnoozed(state, now) {
|
|
185
|
+
if (!state || !state.snoozeUntil) return false;
|
|
186
|
+
const until = Date.parse(state.snoozeUntil);
|
|
187
|
+
if (!Number.isFinite(until)) return false;
|
|
188
|
+
return until > now;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Snooze escalation: 24h → 48h → 7d (capped).
|
|
192
|
+
function computeNextSnoozeUntil(currentLevel, now) {
|
|
193
|
+
const levels = [
|
|
194
|
+
1000 * 60 * 60 * 24, // 24h
|
|
195
|
+
1000 * 60 * 60 * 48, // 48h
|
|
196
|
+
1000 * 60 * 60 * 24 * 7, // 7d
|
|
197
|
+
];
|
|
198
|
+
const idx = Math.min(Math.max(currentLevel, 0), levels.length - 1);
|
|
199
|
+
return new Date(now + levels[idx]).toISOString();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function parseSnoozeArg(arg, now, currentLevel) {
|
|
203
|
+
if (arg === 'clear') return { clear: true };
|
|
204
|
+
if (arg === '24h') return { until: new Date(now + 1000 * 60 * 60 * 24).toISOString(), level: 0 };
|
|
205
|
+
if (arg === '48h') return { until: new Date(now + 1000 * 60 * 60 * 48).toISOString(), level: 1 };
|
|
206
|
+
if (arg === '7d')
|
|
207
|
+
return { until: new Date(now + 1000 * 60 * 60 * 24 * 7).toISOString(), level: 2 };
|
|
208
|
+
if (arg === 'auto')
|
|
209
|
+
return {
|
|
210
|
+
until: computeNextSnoozeUntil(currentLevel, now),
|
|
211
|
+
level: Math.min(currentLevel + 1, 2),
|
|
212
|
+
};
|
|
213
|
+
throw new Error(`invalid --snooze value: ${arg}. Expected 24h|48h|7d|auto|clear.`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ---------------------------------------------------------------------------
|
|
217
|
+
// npm registry fetch
|
|
218
|
+
// ---------------------------------------------------------------------------
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Fetch the latest published version of a package from the npm registry.
|
|
222
|
+
* Never rejects with a network error — resolves null on timeout / failure
|
|
223
|
+
* so callers always degrade gracefully.
|
|
224
|
+
*
|
|
225
|
+
* @param {string} packageName - e.g. "@luquimbo/bi-superpowers"
|
|
226
|
+
* @returns {Promise<string|null>}
|
|
227
|
+
*/
|
|
228
|
+
function fetchLatestVersion(packageName) {
|
|
229
|
+
return new Promise((resolve) => {
|
|
230
|
+
const encoded = packageName.replace('/', '%2F');
|
|
231
|
+
const url = `https://registry.npmjs.org/${encoded}/latest`;
|
|
232
|
+
|
|
233
|
+
const req = https.get(
|
|
234
|
+
url,
|
|
235
|
+
{ headers: { Accept: 'application/vnd.npm.install-v1+json' } },
|
|
236
|
+
(res) => {
|
|
237
|
+
if (res.statusCode !== 200) {
|
|
238
|
+
res.resume();
|
|
239
|
+
resolve(null);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
let body = '';
|
|
243
|
+
res.setEncoding('utf8');
|
|
244
|
+
res.on('data', (chunk) => (body += chunk));
|
|
245
|
+
res.on('end', () => {
|
|
246
|
+
try {
|
|
247
|
+
const json = JSON.parse(body);
|
|
248
|
+
resolve(typeof json.version === 'string' ? json.version : null);
|
|
249
|
+
} catch (_) {
|
|
250
|
+
resolve(null);
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
);
|
|
255
|
+
req.on('error', () => resolve(null));
|
|
256
|
+
req.setTimeout(HTTPS_TIMEOUT_MS, () => {
|
|
257
|
+
req.destroy();
|
|
258
|
+
resolve(null);
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ---------------------------------------------------------------------------
|
|
264
|
+
// Installed version — read from our own package.json
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
|
|
267
|
+
function readInstalledVersion(explicitVersion = null) {
|
|
268
|
+
if (explicitVersion) {
|
|
269
|
+
return String(explicitVersion);
|
|
270
|
+
}
|
|
271
|
+
if (BUNDLED_INSTALLED_VERSION) {
|
|
272
|
+
return String(BUNDLED_INSTALLED_VERSION);
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
return require(path.join(__dirname, '..', '..', 'package.json')).version;
|
|
276
|
+
} catch (_) {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ---------------------------------------------------------------------------
|
|
282
|
+
// Emit helpers
|
|
283
|
+
// ---------------------------------------------------------------------------
|
|
284
|
+
|
|
285
|
+
function emit(args, kind, payload) {
|
|
286
|
+
if (args.json) {
|
|
287
|
+
process.stdout.write(JSON.stringify({ status: kind, ...payload }) + '\n');
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
if (kind === 'UPTODATE' && args.silentIfUptodate) return;
|
|
291
|
+
if (kind === 'SNOOZED' && args.silentIfSnoozed) return;
|
|
292
|
+
|
|
293
|
+
if (kind === 'UPTODATE') process.stdout.write('UPTODATE\n');
|
|
294
|
+
else if (kind === 'UPDATE_AVAILABLE')
|
|
295
|
+
process.stdout.write(`UPDATE_AVAILABLE ${payload.installed} ${payload.latest}\n`);
|
|
296
|
+
else if (kind === 'SNOOZED') process.stdout.write(`SNOOZED ${payload.until}\n`);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// ---------------------------------------------------------------------------
|
|
300
|
+
// main
|
|
301
|
+
// ---------------------------------------------------------------------------
|
|
302
|
+
|
|
303
|
+
async function main() {
|
|
304
|
+
const args = parseArgs(process.argv.slice(2));
|
|
305
|
+
if (args.help) {
|
|
306
|
+
help();
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const stateDir = args.stateDir || defaultStateDir();
|
|
311
|
+
const packageName = args.packageName || PACKAGE_NAME;
|
|
312
|
+
|
|
313
|
+
if (args.reset) {
|
|
314
|
+
resetState(stateDir);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (args.snooze) {
|
|
319
|
+
const now = Date.now();
|
|
320
|
+
const prior = readState(stateDir) || {};
|
|
321
|
+
const parsed = parseSnoozeArg(args.snooze, now, prior.snoozeLevel || 0);
|
|
322
|
+
if (parsed.clear) {
|
|
323
|
+
writeState(stateDir, { ...prior, snoozeUntil: null, snoozeLevel: 0 });
|
|
324
|
+
} else {
|
|
325
|
+
writeState(stateDir, {
|
|
326
|
+
...prior,
|
|
327
|
+
snoozeUntil: parsed.until,
|
|
328
|
+
snoozeLevel: parsed.level,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const installed = readInstalledVersion(args.installedVersion);
|
|
335
|
+
if (!installed) {
|
|
336
|
+
// Installed version undetermined — nothing useful to report.
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const now = Date.now();
|
|
341
|
+
let state = readState(stateDir);
|
|
342
|
+
|
|
343
|
+
// Snooze short-circuits everything except --force.
|
|
344
|
+
if (!args.force && isSnoozed(state, now)) {
|
|
345
|
+
emit(args, 'SNOOZED', { until: state.snoozeUntil });
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Use cached `latest` when the cache is fresh (unless --force).
|
|
350
|
+
let latest = state && state.latest;
|
|
351
|
+
if (args.force || !isCacheFresh(state, now, CACHE_TTL_MS)) {
|
|
352
|
+
const fetched = await fetchLatestVersion(packageName);
|
|
353
|
+
if (fetched) {
|
|
354
|
+
latest = fetched;
|
|
355
|
+
const nextState = {
|
|
356
|
+
installed,
|
|
357
|
+
latest,
|
|
358
|
+
checkedAt: new Date(now).toISOString(),
|
|
359
|
+
snoozeUntil: (state && state.snoozeUntil) || null,
|
|
360
|
+
snoozeLevel: (state && state.snoozeLevel) || 0,
|
|
361
|
+
};
|
|
362
|
+
writeState(stateDir, nextState);
|
|
363
|
+
state = nextState;
|
|
364
|
+
}
|
|
365
|
+
// If fetched is null (network fail), we keep using the previous cache
|
|
366
|
+
// — or emit nothing if there's no cache at all.
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (!latest) {
|
|
370
|
+
// No cached value and no fetch — nothing to say.
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (compareVersions(installed, latest) < 0) {
|
|
375
|
+
emit(args, 'UPDATE_AVAILABLE', { installed, latest });
|
|
376
|
+
} else {
|
|
377
|
+
emit(args, 'UPTODATE', { installed, latest });
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
module.exports = {
|
|
382
|
+
parseArgs,
|
|
383
|
+
compareVersions,
|
|
384
|
+
isCacheFresh,
|
|
385
|
+
isSnoozed,
|
|
386
|
+
computeNextSnoozeUntil,
|
|
387
|
+
parseSnoozeArg,
|
|
388
|
+
readState,
|
|
389
|
+
writeState,
|
|
390
|
+
resetState,
|
|
391
|
+
fetchLatestVersion,
|
|
392
|
+
readInstalledVersion,
|
|
393
|
+
CACHE_TTL_MS,
|
|
394
|
+
PACKAGE_NAME,
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
if (require.main === module) {
|
|
398
|
+
main().catch((err) => {
|
|
399
|
+
// Never throw out of the CLI — the preamble must not break skill invocation.
|
|
400
|
+
process.stderr.write(`update-check: ${err.message}\n`);
|
|
401
|
+
process.exit(0);
|
|
402
|
+
});
|
|
403
|
+
}
|
|
@@ -1,27 +1,48 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "pbi-connect"
|
|
3
|
-
description: "Use when the user asks about Power BI MCP Connection Skill, especially phrases like \"connect Power BI\", \"
|
|
4
|
-
version: "
|
|
3
|
+
description: "Use when the user asks about Power BI MCP Connection Skill, especially phrases like \"connect Power BI\", \"PBI connection\", \"MCP connection\", \"Power BI MCP\", \"modeling mcp\", \"Power BI Modeling MCP\"."
|
|
4
|
+
version: "4.1.1"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- Generated by BI Agent Superpowers. Edit src/content/skills/pbi-connect.md instead. -->
|
|
8
8
|
|
|
9
|
+
<!-- bi-superpowers update-check preamble (auto-generated, do not edit) -->
|
|
10
|
+
## ⬆ Update check
|
|
11
|
+
|
|
12
|
+
Before handling the user's request below, run this command **once per conversation** and interpret the output:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
node "{skillBundleDir}/scripts/update-check.js" --silent-if-uptodate --silent-if-snoozed
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
- Empty output or `UPTODATE` — proceed with the skill silently. No message.
|
|
19
|
+
- `UPDATE_AVAILABLE <installed> <latest>` — tell the user exactly once this conversation, before diving into the skill:
|
|
20
|
+
> "Hay **bi-superpowers v{latest}** disponible (estás en v{installed}). Actualizá con `super upgrade` (o `/plugin update bi-superpowers` en Claude Code) cuando te venga bien."
|
|
21
|
+
|
|
22
|
+
Then continue with the skill below.
|
|
23
|
+
- `SNOOZED <iso>` — proceed silently.
|
|
24
|
+
|
|
25
|
+
If the command fails (missing binary, permissions, offline), ignore the error and proceed with the skill. The update check must never block the user's request.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
<!-- /bi-superpowers update-check preamble -->
|
|
29
|
+
|
|
9
30
|
# Power BI MCP Connection Skill
|
|
10
31
|
|
|
11
32
|
## Trigger
|
|
12
33
|
Activate this skill when user mentions:
|
|
13
34
|
- "connect Power BI", "PBI connection", "MCP connection", "Power BI MCP"
|
|
14
|
-
- "modeling mcp", "
|
|
15
|
-
- "Power BI Desktop", "PBIP", "semantic model"
|
|
16
|
-
- "conectar Power BI", "MCP de Power BI", "
|
|
35
|
+
- "modeling mcp", "Power BI Modeling MCP"
|
|
36
|
+
- "Power BI Desktop", "PBIP", "semantic model"
|
|
37
|
+
- "conectar Power BI", "MCP de Power BI", "modeling mcp"
|
|
17
38
|
- "can't connect to Power BI", "connection error", "MCP not working"
|
|
18
39
|
|
|
19
40
|
## Identity
|
|
20
|
-
You are a **Power BI MCP Connection Specialist**. Your job is to help the user connect
|
|
41
|
+
You are a **Power BI MCP Connection Specialist**. Your job is to help the user connect their AI agent to Power BI Desktop using the official Microsoft MCP servers shipped with bi-superpowers, with a plugin-first workflow.
|
|
21
42
|
|
|
22
43
|
## MANDATORY RULES
|
|
23
44
|
1. **PLUGIN-FIRST.** Prefer `.mcp.json` in the Claude Code plugin root.
|
|
24
|
-
2. **OFFICIAL SERVERS ONLY.**
|
|
45
|
+
2. **OFFICIAL SERVERS ONLY.** Use `powerbi-modeling-mcp` (local) and `microsoft-learn` (HTTP). Do not invent or recommend unofficial MCPs.
|
|
25
46
|
3. **WINDOWS LIMITATION.** Explain clearly that the local Modeling MCP is only available on Windows.
|
|
26
47
|
4. **NO PORT INVENTION.** Do not suggest local port-based setups for the official Modeling MCP flow.
|
|
27
48
|
5. **ONE QUESTION AT A TIME.** Follow the wizard pattern.
|
|
@@ -36,13 +57,12 @@ Start with:
|
|
|
36
57
|
POWER BI MCP CONNECTION
|
|
37
58
|
=======================
|
|
38
59
|
|
|
39
|
-
I'll help you connect
|
|
60
|
+
I'll help you connect your AI agent using the official Microsoft MCP servers.
|
|
40
61
|
|
|
41
62
|
What do you need?
|
|
42
63
|
|
|
43
|
-
1. Connect to Power BI Desktop
|
|
44
|
-
2.
|
|
45
|
-
3. Verify that my plugin `.mcp.json` is configured correctly
|
|
64
|
+
1. Connect to Power BI Desktop on this machine (Windows)
|
|
65
|
+
2. Verify that my plugin `.mcp.json` is configured correctly
|
|
46
66
|
```
|
|
47
67
|
|
|
48
68
|
---
|
|
@@ -83,19 +103,14 @@ If the user wants a config example, show:
|
|
|
83
103
|
|
|
84
104
|
```json
|
|
85
105
|
{
|
|
86
|
-
"powerbi-remote": {
|
|
87
|
-
"type": "http",
|
|
88
|
-
"url": "https://api.fabric.microsoft.com/v1/mcp/powerbi"
|
|
89
|
-
},
|
|
90
|
-
"fabric-mcp-server": {
|
|
91
|
-
"type": "stdio",
|
|
92
|
-
"command": "npx",
|
|
93
|
-
"args": ["-y", "@microsoft/fabric-mcp@latest", "server", "start", "--mode", "all"]
|
|
94
|
-
},
|
|
95
106
|
"powerbi-modeling-mcp": {
|
|
96
107
|
"type": "stdio",
|
|
97
108
|
"command": "node",
|
|
98
109
|
"args": ["${CLAUDE_PLUGIN_ROOT}/bin/mcp/powerbi-modeling-launcher.js"]
|
|
110
|
+
},
|
|
111
|
+
"microsoft-learn": {
|
|
112
|
+
"type": "http",
|
|
113
|
+
"url": "https://learn.microsoft.com/api/mcp"
|
|
99
114
|
}
|
|
100
115
|
}
|
|
101
116
|
```
|
|
@@ -120,51 +135,18 @@ Say:
|
|
|
120
135
|
|
|
121
136
|
```text
|
|
122
137
|
The official local Power BI Modeling MCP is only available on Windows.
|
|
123
|
-
You can still work with:
|
|
124
|
-
|
|
125
|
-
- powerbi-remote
|
|
126
|
-
- fabric-mcp-server
|
|
127
138
|
|
|
128
|
-
|
|
139
|
+
You still have `microsoft-learn` (HTTP) available on every platform for
|
|
140
|
+
docs, and you can work with Power BI files using the skills library.
|
|
141
|
+
For live editing of a local semantic model, you need a Windows environment.
|
|
129
142
|
```
|
|
130
143
|
|
|
131
144
|
---
|
|
132
145
|
|
|
133
|
-
## PHASE 2:
|
|
146
|
+
## PHASE 2: Verify Plugin Config
|
|
134
147
|
|
|
135
148
|
If the user chooses option 2:
|
|
136
149
|
|
|
137
|
-
```text
|
|
138
|
-
REMOTE / FABRIC MCP
|
|
139
|
-
===================
|
|
140
|
-
|
|
141
|
-
This path uses the Microsoft-hosted and Microsoft-published MCP servers.
|
|
142
|
-
|
|
143
|
-
Default servers:
|
|
144
|
-
- powerbi-remote
|
|
145
|
-
- fabric-mcp-server
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
Guide them to:
|
|
149
|
-
|
|
150
|
-
1. Run `bi-superpowers mcp-setup`
|
|
151
|
-
2. Verify `.mcp.json` contains the official endpoint and Fabric package
|
|
152
|
-
3. Authenticate in the environment required by their MCP client
|
|
153
|
-
4. Restart or refresh Claude Code
|
|
154
|
-
|
|
155
|
-
If they ask what gets configured, show:
|
|
156
|
-
|
|
157
|
-
```text
|
|
158
|
-
powerbi-remote -> https://api.fabric.microsoft.com/v1/mcp/powerbi
|
|
159
|
-
fabric-mcp-server -> npx -y @microsoft/fabric-mcp@latest server start --mode all
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
---
|
|
163
|
-
|
|
164
|
-
## PHASE 3: Verify Plugin Config
|
|
165
|
-
|
|
166
|
-
If the user chooses option 3:
|
|
167
|
-
|
|
168
150
|
Check these files in order:
|
|
169
151
|
|
|
170
152
|
1. `.claude-plugin/plugin.json`
|
|
@@ -174,9 +156,8 @@ Check these files in order:
|
|
|
174
156
|
Confirm:
|
|
175
157
|
|
|
176
158
|
- plugin name is `bi-superpowers`
|
|
177
|
-
- `.mcp.json` includes `powerbi-remote`
|
|
178
|
-
- `.mcp.json` includes `fabric-mcp-server`
|
|
179
159
|
- `.mcp.json` includes `powerbi-modeling-mcp`
|
|
160
|
+
- `.mcp.json` includes `microsoft-learn`
|
|
180
161
|
|
|
181
162
|
If anything is missing, recommend:
|
|
182
163
|
|
|
@@ -200,7 +181,7 @@ claude --plugin-dir .
|
|
|
200
181
|
| Modeling MCP missing on Windows | Install the Microsoft extension in VS Code or Cursor |
|
|
201
182
|
| Modeling MCP installed manually | Set `BI_SUPERPOWERS_POWERBI_MODELING_MCP_PATH` |
|
|
202
183
|
| Plugin not loading MCPs | Re-run `bi-superpowers mcp-setup` and restart Claude Code |
|
|
203
|
-
| macOS/Linux local modeling request |
|
|
184
|
+
| macOS/Linux local modeling request | Use `microsoft-learn` for docs; live editing requires Windows |
|
|
204
185
|
| User asks about Excel MCP | Explain Excel remains supported through skills and library content, not a default MCP |
|
|
205
186
|
|
|
206
187
|
---
|
|
@@ -212,7 +193,7 @@ claude --plugin-dir .
|
|
|
212
193
|
| Recommend `uvx` for Modeling MCP | Not the official Microsoft installation path | Use the official executable via the local launcher |
|
|
213
194
|
| Ask the user to find a localhost port | Not required in the new flow | Use the official Modeling MCP launcher |
|
|
214
195
|
| Put plugin MCP config in `.claude/settings.json` first | Plugin-first flow uses `.mcp.json` | Prefer `.mcp.json` at the plugin root |
|
|
215
|
-
|
|
|
196
|
+
| Invent unofficial MCPs (remote, fabric, etc.) | This plugin only ships 2 official MCPs | Only use the 2 official MCPs we ship (`powerbi-modeling-mcp` and `microsoft-learn`) |
|
|
216
197
|
|
|
217
198
|
---
|
|
218
199
|
|
|
@@ -227,15 +208,12 @@ Adjust depth based on `config.json → experienceLevel`:
|
|
|
227
208
|
|
|
228
209
|
## Related Skills
|
|
229
210
|
|
|
230
|
-
- `/
|
|
231
|
-
- `/model-documenter` — Document the connected model
|
|
232
|
-
- `/fabric-scripts` — Fabric automation via MCP
|
|
211
|
+
- `/project-kickoff` — Analyze a BI project and plan next steps
|
|
233
212
|
|
|
234
213
|
---
|
|
235
214
|
|
|
236
215
|
## RELATED RESOURCES
|
|
237
216
|
|
|
238
217
|
- [Power BI MCP overview](https://learn.microsoft.com/en-us/power-bi/developer/mcp/mcp-servers-overview)
|
|
239
|
-
- [
|
|
240
|
-
- [
|
|
241
|
-
- [Microsoft Fabric MCP Server](https://github.com/microsoft/mcp/tree/main/servers/Fabric.Mcp.Server)
|
|
218
|
+
- [Power BI Modeling MCP on GitHub](https://github.com/microsoft/powerbi-modeling-mcp)
|
|
219
|
+
- [Microsoft Learn MCP](https://learn.microsoft.com/en-us/training/support/mcp)
|