@luquimbo/bi-superpowers 4.1.2 → 4.1.3
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/CHANGELOG.md +24 -0
- package/README.md +631 -96
- package/bin/cli.js +24 -45
- package/bin/commands/build-desktop.js +60 -6
- package/bin/commands/diff.js +86 -1
- package/bin/commands/watch.js +50 -5
- package/bin/postinstall.js +1 -1
- package/commands/report-design.md +1 -1
- package/desktop-extension/server.js +43 -10
- package/package.json +3 -4
- package/skills/bi-start/SKILL.md +1 -1
- package/skills/bi-start/scripts/update-check.js +1 -1
- package/skills/pbi-connect/SKILL.md +1 -1
- package/skills/pbi-connect/scripts/update-check.js +1 -1
- package/skills/project-kickoff/SKILL.md +1 -1
- package/skills/project-kickoff/scripts/update-check.js +1 -1
- package/skills/report-design/SKILL.md +2 -2
- package/skills/report-design/references/layouts/finance.md +2 -2
- package/skills/report-design/references/native-visuals.md +2 -2
- package/skills/report-design/references/slicer.md +1 -1
- package/skills/report-design/references/textbox.md +1 -1
- package/skills/report-design/scripts/create-visual.js +65 -1
- package/skills/report-design/scripts/update-check.js +1 -1
- package/skills/report-design/scripts/validate-pbir.js +29 -0
- package/src/content/skills/report-design/SKILL.md +1 -1
- package/src/content/skills/report-design/references/layouts/finance.md +2 -2
- package/src/content/skills/report-design/references/native-visuals.md +2 -2
- package/src/content/skills/report-design/references/slicer.md +1 -1
- package/src/content/skills/report-design/references/textbox.md +1 -1
- package/src/content/skills/report-design/scripts/create-visual.js +65 -1
- package/src/content/skills/report-design/scripts/validate-pbir.js +29 -0
package/bin/cli.js
CHANGED
|
@@ -28,34 +28,6 @@ const path = require('path');
|
|
|
28
28
|
const { execSync } = require('child_process');
|
|
29
29
|
const { loadSkills } = require('./lib/skills');
|
|
30
30
|
|
|
31
|
-
// Background update check. update-notifier spawns a detached child process
|
|
32
|
-
// that fetches the latest version from npm and caches it; the banner that
|
|
33
|
-
// `notify()` schedules is printed at process exit, so this is zero-latency
|
|
34
|
-
// on the command path. Errors are swallowed silently — the CLI must not
|
|
35
|
-
// break because of a failed update check.
|
|
36
|
-
let _updateNotifierNotify = null;
|
|
37
|
-
try {
|
|
38
|
-
const updateNotifier = require('update-notifier');
|
|
39
|
-
const pkg = require('../package.json');
|
|
40
|
-
const notifier = updateNotifier({
|
|
41
|
-
pkg,
|
|
42
|
-
updateCheckInterval: 1000 * 60 * 60 * 24, // 24h
|
|
43
|
-
shouldNotifyInNpmScript: false,
|
|
44
|
-
});
|
|
45
|
-
_updateNotifierNotify = () =>
|
|
46
|
-
notifier.notify({
|
|
47
|
-
defer: true,
|
|
48
|
-
isGlobal: true,
|
|
49
|
-
message:
|
|
50
|
-
'Update available {currentVersion} → {latestVersion}\n' +
|
|
51
|
-
'Run {updateCommand} to update,\n' +
|
|
52
|
-
'then refresh your install path (`/plugin update bi-superpowers`, `super install --yes`, or run `super recharge` inside each repo where you used `super kickoff`).',
|
|
53
|
-
});
|
|
54
|
-
} catch (_) {
|
|
55
|
-
// update-notifier not available during npm install phase, or network
|
|
56
|
-
// probe failed — skip silently.
|
|
57
|
-
}
|
|
58
|
-
|
|
59
31
|
// Optional lib import — may not be available during npm install phase.
|
|
60
32
|
let generators;
|
|
61
33
|
try {
|
|
@@ -160,14 +132,6 @@ function main() {
|
|
|
160
132
|
const args = process.argv.slice(2);
|
|
161
133
|
const command = args[0] || 'help';
|
|
162
134
|
|
|
163
|
-
// Schedule the update-notifier banner to print at process exit. Only fire
|
|
164
|
-
// for "normal" commands — skipping machine-readable and ultra-hot paths so
|
|
165
|
-
// tools that parse super output don't get surprised by the banner.
|
|
166
|
-
const SKIP_NOTIFY_COMMANDS = new Set(['version', 'help', 'about', 'info']);
|
|
167
|
-
if (_updateNotifierNotify && !SKIP_NOTIFY_COMMANDS.has(command)) {
|
|
168
|
-
_updateNotifierNotify();
|
|
169
|
-
}
|
|
170
|
-
|
|
171
135
|
if (commands[command]) {
|
|
172
136
|
commands[command](args.slice(1));
|
|
173
137
|
} else {
|
|
@@ -783,19 +747,32 @@ function runWatch(args) {
|
|
|
783
747
|
process.exit(1);
|
|
784
748
|
}
|
|
785
749
|
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
750
|
+
watchCommand(args, getCommandConfig(), createWatchCliModule());
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* Build the tiny adapter used by `super sentinel`.
|
|
755
|
+
* It intentionally delegates through generateForTool(), the same path as
|
|
756
|
+
* kickoff/recharge, so regenerated plugin files keep the real package version,
|
|
757
|
+
* library prefix, and MCP launcher mode.
|
|
758
|
+
*
|
|
759
|
+
* @param {Object} [deps] - Test seams
|
|
760
|
+
* @param {Function} [deps.getSkillFilesFn] - Skill loader
|
|
761
|
+
* @param {Function} [deps.generateForToolFn] - Tool generator
|
|
762
|
+
* @returns {{syncProjectInternal: Function}} Watch command adapter
|
|
763
|
+
*/
|
|
764
|
+
function createWatchCliModule(deps = {}) {
|
|
765
|
+
const getSkillFilesFn = deps.getSkillFilesFn || getSkillFiles;
|
|
766
|
+
const generateForToolFn = deps.generateForToolFn || generateForTool;
|
|
767
|
+
|
|
768
|
+
return {
|
|
769
|
+
syncProjectInternal: async (targetDir, tools) => {
|
|
770
|
+
const skills = getSkillFilesFn();
|
|
789
771
|
for (const tool of tools) {
|
|
790
|
-
|
|
791
|
-
if (cfg && cfg.generate) {
|
|
792
|
-
cfg.generate(targetDir, skills, { packageDir: PACKAGE_DIR });
|
|
793
|
-
}
|
|
772
|
+
await generateForToolFn(tool, targetDir, skills);
|
|
794
773
|
}
|
|
795
774
|
},
|
|
796
775
|
};
|
|
797
|
-
|
|
798
|
-
watchCommand(args, getCommandConfig(), cliModule);
|
|
799
776
|
}
|
|
800
777
|
|
|
801
778
|
/**
|
|
@@ -823,6 +800,8 @@ module.exports = {
|
|
|
823
800
|
getUpgradeCommand,
|
|
824
801
|
getUpgradeRefreshSteps,
|
|
825
802
|
resetUpdateCheckStateAfterUpgrade,
|
|
803
|
+
createWatchCliModule,
|
|
804
|
+
getGenerationOptions,
|
|
826
805
|
AI_TOOLS,
|
|
827
806
|
SKILLS_DIR,
|
|
828
807
|
VERSION,
|
|
@@ -30,10 +30,10 @@ const DESKTOP_TEMPLATE_DIR = path.join(PACKAGE_DIR, 'desktop-extension');
|
|
|
30
30
|
* Bundle skills into the Desktop extension's skills/ directory.
|
|
31
31
|
*
|
|
32
32
|
* Handles both flat `src/content/skills/<name>.md` and folder-based
|
|
33
|
-
* `src/content/skills/<name>/SKILL.md` layouts by
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
33
|
+
* `src/content/skills/<name>/SKILL.md` layouts by writing each skill as
|
|
34
|
+
* `<name>/SKILL.md`. Folder-based skills also get their runtime
|
|
35
|
+
* `references/` and `scripts/` subtrees copied into the MCPB, so prompts
|
|
36
|
+
* never point Claude Desktop at files missing from the installed extension.
|
|
37
37
|
*
|
|
38
38
|
* @param {string} skillsOutDir - Destination directory (created if missing)
|
|
39
39
|
* @param {string} packageDir - npm package root containing src/content/skills/
|
|
@@ -43,11 +43,63 @@ function bundleSkills(skillsOutDir, packageDir) {
|
|
|
43
43
|
fs.mkdirSync(skillsOutDir, { recursive: true });
|
|
44
44
|
const skills = loadSkills({ packageDir });
|
|
45
45
|
for (const skill of skills) {
|
|
46
|
-
|
|
46
|
+
const skillOutDir = path.join(skillsOutDir, skill.name);
|
|
47
|
+
fs.mkdirSync(skillOutDir, { recursive: true });
|
|
48
|
+
fs.writeFileSync(path.join(skillOutDir, 'SKILL.md'), skill.content);
|
|
49
|
+
|
|
50
|
+
if (skill.bundleDir) {
|
|
51
|
+
copyRuntimeTree(
|
|
52
|
+
path.join(skill.bundleDir, 'references'),
|
|
53
|
+
path.join(skillOutDir, 'references')
|
|
54
|
+
);
|
|
55
|
+
copyRuntimeTree(path.join(skill.bundleDir, 'scripts'), path.join(skillOutDir, 'scripts'));
|
|
56
|
+
}
|
|
47
57
|
}
|
|
48
58
|
return skills.map((s) => s.name);
|
|
49
59
|
}
|
|
50
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Copy runtime skill assets, excluding test/build/editor artifacts.
|
|
63
|
+
*
|
|
64
|
+
* @param {string} source - Source file or directory
|
|
65
|
+
* @param {string} target - Destination file or directory
|
|
66
|
+
*/
|
|
67
|
+
function copyRuntimeTree(source, target) {
|
|
68
|
+
if (!fs.existsSync(source)) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const stat = fs.statSync(source);
|
|
73
|
+
if (stat.isDirectory()) {
|
|
74
|
+
fs.mkdirSync(target, { recursive: true });
|
|
75
|
+
for (const entry of fs.readdirSync(source, { withFileTypes: true })) {
|
|
76
|
+
copyRuntimeTree(path.join(source, entry.name), path.join(target, entry.name));
|
|
77
|
+
}
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!stat.isFile() || shouldSkipRuntimeFile(source)) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
86
|
+
fs.copyFileSync(source, target);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @param {string} filePath - Runtime asset path
|
|
91
|
+
* @returns {boolean} True if this file should not ship in the MCPB
|
|
92
|
+
*/
|
|
93
|
+
function shouldSkipRuntimeFile(filePath) {
|
|
94
|
+
const base = path.basename(filePath);
|
|
95
|
+
return (
|
|
96
|
+
base.endsWith('.test.js') ||
|
|
97
|
+
base.endsWith('.spec.js') ||
|
|
98
|
+
base.endsWith('.bak') ||
|
|
99
|
+
base.endsWith('.tmp')
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
51
103
|
/**
|
|
52
104
|
* Build the .mcpb extension for Claude Desktop.
|
|
53
105
|
*
|
|
@@ -94,7 +146,7 @@ BI Agent Superpowers — Build Desktop Extension
|
|
|
94
146
|
|
|
95
147
|
// Bundle every skill (flat + folder-based) into temp/skills/
|
|
96
148
|
const bundledNames = bundleSkills(path.join(tmpDir, 'skills'), PACKAGE_DIR);
|
|
97
|
-
const skillFiles = bundledNames.map((n) => `${n}.md`);
|
|
149
|
+
const skillFiles = bundledNames.map((n) => `${n}/SKILL.md`);
|
|
98
150
|
console.log(` Bundled ${bundledNames.length} skills`);
|
|
99
151
|
|
|
100
152
|
// Patch template placeholder versions with the real package version
|
|
@@ -185,3 +237,5 @@ BI Agent Superpowers — Build Desktop Extension
|
|
|
185
237
|
|
|
186
238
|
module.exports = buildDesktop;
|
|
187
239
|
module.exports.bundleSkills = bundleSkills;
|
|
240
|
+
module.exports.copyRuntimeTree = copyRuntimeTree;
|
|
241
|
+
module.exports.shouldSkipRuntimeFile = shouldSkipRuntimeFile;
|
package/bin/commands/diff.js
CHANGED
|
@@ -13,6 +13,7 @@ const fs = require('fs');
|
|
|
13
13
|
const path = require('path');
|
|
14
14
|
const tui = require('../utils/tui');
|
|
15
15
|
const { readSkillDirectory, normalizeSkillName } = require('../lib/skills');
|
|
16
|
+
const { buildCommandMarkdown } = require('../lib/generators/claude-plugin');
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Generate a simple diff between two strings
|
|
@@ -113,6 +114,71 @@ function compareFiles(skillPath, generatedPath) {
|
|
|
113
114
|
};
|
|
114
115
|
}
|
|
115
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Compare a loaded skill against the content that should have been generated
|
|
119
|
+
* for a specific tool. This avoids false drift from generator-owned wrappers
|
|
120
|
+
* such as Claude command frontmatter, generated comments, and update-check
|
|
121
|
+
* preambles.
|
|
122
|
+
*
|
|
123
|
+
* @param {{name: string, path: string, content: string}} skill - Loaded source skill
|
|
124
|
+
* @param {string} generatedPath - Path to generated file
|
|
125
|
+
* @param {Object} [options] - Comparison options
|
|
126
|
+
* @param {string} [options.tool] - Tool id
|
|
127
|
+
* @param {string} [options.libraryPrefix] - Library prefix used by the generator
|
|
128
|
+
* @returns {Object} Comparison result
|
|
129
|
+
*/
|
|
130
|
+
function compareSkillToGenerated(skill, generatedPath, options = {}) {
|
|
131
|
+
if (!skill || !fs.existsSync(skill.path)) {
|
|
132
|
+
return {
|
|
133
|
+
skill: skill && skill.name ? skill.name : 'unknown',
|
|
134
|
+
status: 'missing-source',
|
|
135
|
+
message: 'Source skill file not found',
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (!fs.existsSync(generatedPath)) {
|
|
140
|
+
return {
|
|
141
|
+
skill: skill.name,
|
|
142
|
+
status: 'not-generated',
|
|
143
|
+
message: 'Generated file does not exist (will be created on sync)',
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const generatedContent = fs.readFileSync(generatedPath, 'utf8');
|
|
148
|
+
const expectedContent = buildExpectedGeneratedContent(skill, options);
|
|
149
|
+
|
|
150
|
+
if (expectedContent === generatedContent) {
|
|
151
|
+
return {
|
|
152
|
+
skill: skill.name,
|
|
153
|
+
status: 'unchanged',
|
|
154
|
+
message: 'No changes',
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const diff = generateDiff(generatedContent, expectedContent);
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
skill: skill.name,
|
|
162
|
+
status: 'changed',
|
|
163
|
+
diff,
|
|
164
|
+
sourcePath: skill.path,
|
|
165
|
+
generatedPath,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* @param {{name: string, content: string}} skill - Loaded source skill
|
|
171
|
+
* @param {Object} options - Comparison options
|
|
172
|
+
* @returns {string} Expected generated output for the scan target
|
|
173
|
+
*/
|
|
174
|
+
function buildExpectedGeneratedContent(skill, options = {}) {
|
|
175
|
+
if (options.tool === 'claude-code') {
|
|
176
|
+
return buildCommandMarkdown(skill, options.libraryPrefix || 'library');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return skill.content;
|
|
180
|
+
}
|
|
181
|
+
|
|
116
182
|
/**
|
|
117
183
|
* Get generated file path for a skill based on tool
|
|
118
184
|
* @param {string} skillName - Skill name (without .md)
|
|
@@ -291,6 +357,7 @@ function diffCommand(args, config) {
|
|
|
291
357
|
// and `path.basename(...)` would return `SKILL`, breaking the diff.
|
|
292
358
|
const results = skillFiles.map((skillPath) => {
|
|
293
359
|
const skillName = skillNameByPath.get(skillPath) || path.basename(skillPath, '.md');
|
|
360
|
+
const skill = skillsByName.get(skillName);
|
|
294
361
|
const generatedPath = getGeneratedPath(skillName, options.tool, targetDir);
|
|
295
362
|
|
|
296
363
|
if (!generatedPath) {
|
|
@@ -301,7 +368,10 @@ function diffCommand(args, config) {
|
|
|
301
368
|
};
|
|
302
369
|
}
|
|
303
370
|
|
|
304
|
-
return
|
|
371
|
+
return compareSkillToGenerated(skill, generatedPath, {
|
|
372
|
+
tool: options.tool,
|
|
373
|
+
libraryPrefix: getLibraryPrefix(targetDir, config.packageDir),
|
|
374
|
+
});
|
|
305
375
|
});
|
|
306
376
|
|
|
307
377
|
// Display results
|
|
@@ -340,6 +410,21 @@ function diffCommand(args, config) {
|
|
|
340
410
|
module.exports = Object.assign(diffCommand, {
|
|
341
411
|
generateDiff,
|
|
342
412
|
compareFiles,
|
|
413
|
+
compareSkillToGenerated,
|
|
414
|
+
buildExpectedGeneratedContent,
|
|
343
415
|
getGeneratedPath,
|
|
344
416
|
parseArgs,
|
|
345
417
|
});
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Match the Claude plugin generator's library reference behavior.
|
|
421
|
+
*
|
|
422
|
+
* @param {string} targetDir - Target project directory
|
|
423
|
+
* @param {string} packageDir - Package root
|
|
424
|
+
* @returns {string} Library prefix used in generated markdown
|
|
425
|
+
*/
|
|
426
|
+
function getLibraryPrefix(targetDir, packageDir) {
|
|
427
|
+
return fs.existsSync(path.join(targetDir, 'library'))
|
|
428
|
+
? 'library'
|
|
429
|
+
: path.join(packageDir, 'library');
|
|
430
|
+
}
|
package/bin/commands/watch.js
CHANGED
|
@@ -48,6 +48,42 @@ function parseArgs(args) {
|
|
|
48
48
|
return options;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Chokidar v4 no longer supports glob paths. Watch the skill root and filter
|
|
53
|
+
* relevant runtime/source files in-process so folder-based skills are covered.
|
|
54
|
+
*
|
|
55
|
+
* @param {string} skillsDir - Directory containing skill sources
|
|
56
|
+
* @returns {string[]} Watch roots
|
|
57
|
+
*/
|
|
58
|
+
function getWatchRoots(skillsDir) {
|
|
59
|
+
return [skillsDir];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @param {string} changedPath - Changed file path
|
|
64
|
+
* @param {string} skillsDir - Watched skill root
|
|
65
|
+
* @returns {boolean} Whether the changed file should trigger regeneration
|
|
66
|
+
*/
|
|
67
|
+
function isRelevantSkillPath(changedPath, skillsDir) {
|
|
68
|
+
const relative = path.relative(skillsDir, changedPath);
|
|
69
|
+
if (!relative || relative.startsWith('..') || path.isAbsolute(relative)) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const base = path.basename(changedPath);
|
|
74
|
+
if (
|
|
75
|
+
base.endsWith('.test.js') ||
|
|
76
|
+
base.endsWith('.spec.js') ||
|
|
77
|
+
base.endsWith('.bak') ||
|
|
78
|
+
base.endsWith('.tmp') ||
|
|
79
|
+
base.startsWith('.')
|
|
80
|
+
) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return ['.md', '.js', '.json', '.sh'].includes(path.extname(changedPath));
|
|
85
|
+
}
|
|
86
|
+
|
|
51
87
|
/**
|
|
52
88
|
* Main watch command handler
|
|
53
89
|
* @param {string[]} args - Command arguments
|
|
@@ -91,7 +127,7 @@ function watchCommand(args, config, cliModule) {
|
|
|
91
127
|
const pendingChanges = new Set();
|
|
92
128
|
|
|
93
129
|
// Create watcher
|
|
94
|
-
const watcher = chokidar.watch(
|
|
130
|
+
const watcher = chokidar.watch(getWatchRoots(skillsDir), {
|
|
95
131
|
persistent: true,
|
|
96
132
|
ignoreInitial: true,
|
|
97
133
|
awaitWriteFinish: {
|
|
@@ -106,7 +142,11 @@ function watchCommand(args, config, cliModule) {
|
|
|
106
142
|
* @param {string} eventType - Type of event (add, change, unlink)
|
|
107
143
|
*/
|
|
108
144
|
function handleChange(changedPath, _eventType) {
|
|
109
|
-
|
|
145
|
+
if (!isRelevantSkillPath(changedPath, skillsDir)) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const filename = path.relative(skillsDir, changedPath);
|
|
110
150
|
pendingChanges.add(filename);
|
|
111
151
|
|
|
112
152
|
// Clear existing timer
|
|
@@ -157,7 +197,7 @@ function watchCommand(args, config, cliModule) {
|
|
|
157
197
|
* @param {string|null} specificTool - Specific tool or null for all
|
|
158
198
|
* @param {Object} cliModule - CLI module reference
|
|
159
199
|
*/
|
|
160
|
-
function regenerateConfigs(targetDir, specificTool, cliModule) {
|
|
200
|
+
async function regenerateConfigs(targetDir, specificTool, cliModule) {
|
|
161
201
|
const ora = require('ora');
|
|
162
202
|
const spinner = ora('Regenerating configs...').start();
|
|
163
203
|
|
|
@@ -193,7 +233,7 @@ function regenerateConfigs(targetDir, specificTool, cliModule) {
|
|
|
193
233
|
|
|
194
234
|
// Call the sync logic from CLI module
|
|
195
235
|
if (cliModule && typeof cliModule.syncProjectInternal === 'function') {
|
|
196
|
-
cliModule.syncProjectInternal(targetDir, tools);
|
|
236
|
+
await cliModule.syncProjectInternal(targetDir, tools);
|
|
197
237
|
spinner.succeed(`Regenerated configs for: ${tools.join(', ')}`);
|
|
198
238
|
} else {
|
|
199
239
|
// Fallback: just report what would be done
|
|
@@ -205,4 +245,9 @@ function regenerateConfigs(targetDir, specificTool, cliModule) {
|
|
|
205
245
|
}
|
|
206
246
|
}
|
|
207
247
|
|
|
208
|
-
module.exports = watchCommand
|
|
248
|
+
module.exports = Object.assign(watchCommand, {
|
|
249
|
+
parseArgs,
|
|
250
|
+
getWatchRoots,
|
|
251
|
+
isRelevantSkillPath,
|
|
252
|
+
regenerateConfigs,
|
|
253
|
+
});
|
package/bin/postinstall.js
CHANGED
|
@@ -36,7 +36,7 @@ Funciona con los 5 agentes:
|
|
|
36
36
|
/report-design — Generá reportes PBIR para Power BI Desktop (Windows)
|
|
37
37
|
|
|
38
38
|
2 MCP servers:
|
|
39
|
-
powerbi-modeling
|
|
39
|
+
powerbi-modeling-mcp — Conexión local a Power BI Desktop (XMLA)
|
|
40
40
|
microsoft-learn — Docs de Microsoft Learn en contexto
|
|
41
41
|
|
|
42
42
|
Documentation: https://github.com/luquimbo/bi-superpowers
|
|
@@ -293,7 +293,7 @@ Do NOT hand-author `report.json` theme metadata. The canonical PBIR shape (for r
|
|
|
293
293
|
- `resourcePackages` includes a `{name: "RegisteredResources", type: "RegisteredResources"}` package whose `items[]` has `{name: "<file>.json", path: "<file>.json", type: "CustomTheme"}`
|
|
294
294
|
- the theme file itself lives at `<reportPath>/StaticResources/RegisteredResources/<file>.json`
|
|
295
295
|
|
|
296
|
-
**Conditional formatting
|
|
296
|
+
**Conditional formatting note.** Layout notes may describe variance colors, diverging matrix gradients, or signed-color bars as design intent. The current `report-design` flow does not author those formatting rules. Render those visuals with theme/default colors unless you have a tested PBIR formatting implementation. Do not invent `pbi format` calls from this skill.
|
|
297
297
|
|
|
298
298
|
### 4.4 Validate (two layers)
|
|
299
299
|
|
|
@@ -25,25 +25,58 @@ const server = new McpServer({
|
|
|
25
25
|
version: '0.0.0-template',
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
function loadBundledSkills(directory) {
|
|
29
|
+
if (!fs.existsSync(directory)) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const skills = [];
|
|
34
|
+
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
35
|
+
const entryPath = path.join(directory, entry.name);
|
|
36
|
+
|
|
37
|
+
if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
38
|
+
skills.push({
|
|
39
|
+
name: path.basename(entry.name, '.md'),
|
|
40
|
+
bundleDir: directory,
|
|
41
|
+
content: fs.readFileSync(entryPath, 'utf8'),
|
|
42
|
+
});
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (entry.isDirectory()) {
|
|
47
|
+
const skillPath = path.join(entryPath, 'SKILL.md');
|
|
48
|
+
if (fs.existsSync(skillPath)) {
|
|
49
|
+
skills.push({
|
|
50
|
+
name: entry.name,
|
|
51
|
+
bundleDir: entryPath,
|
|
52
|
+
content: fs.readFileSync(skillPath, 'utf8'),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return skills.sort((a, b) => a.name.localeCompare(b.name));
|
|
59
|
+
}
|
|
32
60
|
|
|
33
61
|
// Register each skill as an MCP prompt that Claude Desktop can invoke.
|
|
34
|
-
for (const
|
|
35
|
-
const name =
|
|
36
|
-
const content = fs.readFileSync(path.join(skillsDir, file), 'utf8');
|
|
62
|
+
for (const skill of loadBundledSkills(skillsDir)) {
|
|
63
|
+
const { name, bundleDir, content } = skill;
|
|
37
64
|
|
|
38
65
|
// Use the first non-header line as a short description for the prompt.
|
|
39
66
|
const firstLine = content.split('\n').find((l) => l.trim() && !l.startsWith('#'));
|
|
40
67
|
const description = firstLine ? firstLine.slice(0, 120).trim() : `BI Superpowers: ${name}`;
|
|
68
|
+
const promptText = [
|
|
69
|
+
`Bundled skill directory: ${bundleDir}`,
|
|
70
|
+
'Resolve this skill\'s references/ and scripts/ paths relative to that directory.',
|
|
71
|
+
'',
|
|
72
|
+
content,
|
|
73
|
+
].join('\n');
|
|
41
74
|
|
|
42
75
|
server.prompt(name, { description }, () => ({
|
|
43
76
|
messages: [
|
|
44
77
|
{
|
|
45
78
|
role: 'user',
|
|
46
|
-
content: { type: 'text', text:
|
|
79
|
+
content: { type: 'text', text: promptText },
|
|
47
80
|
},
|
|
48
81
|
],
|
|
49
82
|
}));
|
|
@@ -73,7 +106,7 @@ into Claude Desktop, add the following to your \`claude_desktop_config.json\`
|
|
|
73
106
|
\`\`\`json
|
|
74
107
|
{
|
|
75
108
|
"mcpServers": {
|
|
76
|
-
"powerbi-modeling": {
|
|
109
|
+
"powerbi-modeling-mcp": {
|
|
77
110
|
"command": "node",
|
|
78
111
|
"args": ["<absolute path to powerbi-modeling-launcher.js>"]
|
|
79
112
|
},
|
|
@@ -85,7 +118,7 @@ into Claude Desktop, add the following to your \`claude_desktop_config.json\`
|
|
|
85
118
|
}
|
|
86
119
|
\`\`\`
|
|
87
120
|
|
|
88
|
-
The \`powerbi-modeling\` server requires Power BI Desktop on Windows
|
|
121
|
+
The \`powerbi-modeling-mcp\` server requires Power BI Desktop on Windows
|
|
89
122
|
with a model open. If you prefer automated setup, run
|
|
90
123
|
\`super install\` from the bi-superpowers CLI — it configures both
|
|
91
124
|
servers across all 5 supported AI agents.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@luquimbo/bi-superpowers",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.3",
|
|
4
4
|
"description": "Open-source Power BI Desktop toolkit for Claude Code, GitHub Copilot, Codex, Gemini CLI, and Kilo Code. Ships 4 skills and 2 official Microsoft MCP servers.",
|
|
5
5
|
"main": "bin/cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -24,10 +24,9 @@
|
|
|
24
24
|
"adm-zip": "^0.5.17",
|
|
25
25
|
"boxen": "^5.1.2",
|
|
26
26
|
"chalk": "^4.1.2",
|
|
27
|
-
"chokidar": "^
|
|
27
|
+
"chokidar": "^4.0.3",
|
|
28
28
|
"cli-table3": "^0.6.5",
|
|
29
|
-
"ora": "^5.4.1"
|
|
30
|
-
"update-notifier": "^5.1.0"
|
|
29
|
+
"ora": "^5.4.1"
|
|
31
30
|
},
|
|
32
31
|
"devDependencies": {
|
|
33
32
|
"@eslint/js": "^9.39.2",
|
package/skills/bi-start/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "bi-start"
|
|
3
3
|
description: "Use when the user asks about BI Start Skill, especially phrases like \"bi-start\", \"bi start\", \"/bi-start\", \"empezar\", \"comenzar\", \"arranco\"."
|
|
4
|
-
version: "4.1.
|
|
4
|
+
version: "4.1.3"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- Generated by BI Agent Superpowers. Edit src/content/skills/bi-start.md instead. -->
|
|
@@ -47,7 +47,7 @@ const HTTPS_TIMEOUT_MS = 5000;
|
|
|
47
47
|
// Rewritten at generation time when this helper is copied into
|
|
48
48
|
// `skills/<name>/scripts/update-check.js`. In the canonical source under
|
|
49
49
|
// `bin/commands/`, it stays null and we fall back to package.json.
|
|
50
|
-
const BUNDLED_INSTALLED_VERSION = "4.1.
|
|
50
|
+
const BUNDLED_INSTALLED_VERSION = "4.1.3";
|
|
51
51
|
|
|
52
52
|
// ---------------------------------------------------------------------------
|
|
53
53
|
// Argument parsing
|
|
@@ -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\", \"PBI connection\", \"MCP connection\", \"Power BI MCP\", \"modeling mcp\", \"Power BI Modeling MCP\"."
|
|
4
|
-
version: "4.1.
|
|
4
|
+
version: "4.1.3"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- Generated by BI Agent Superpowers. Edit src/content/skills/pbi-connect.md instead. -->
|
|
@@ -47,7 +47,7 @@ const HTTPS_TIMEOUT_MS = 5000;
|
|
|
47
47
|
// Rewritten at generation time when this helper is copied into
|
|
48
48
|
// `skills/<name>/scripts/update-check.js`. In the canonical source under
|
|
49
49
|
// `bin/commands/`, it stays null and we fall back to package.json.
|
|
50
|
-
const BUNDLED_INSTALLED_VERSION = "4.1.
|
|
50
|
+
const BUNDLED_INSTALLED_VERSION = "4.1.3";
|
|
51
51
|
|
|
52
52
|
// ---------------------------------------------------------------------------
|
|
53
53
|
// Argument parsing
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "project-kickoff"
|
|
3
3
|
description: "Use when the user asks about Project Kickoff Skill, especially phrases like \"I'm starting a brand-new BI project from scratch\", \"analizar proyecto\", \"analyze project\", \"project kickoff\", \"nuevo proyecto\", \"new project\"."
|
|
4
|
-
version: "4.1.
|
|
4
|
+
version: "4.1.3"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- Generated by BI Agent Superpowers. Edit src/content/skills/project-kickoff.md instead. -->
|
|
@@ -47,7 +47,7 @@ const HTTPS_TIMEOUT_MS = 5000;
|
|
|
47
47
|
// Rewritten at generation time when this helper is copied into
|
|
48
48
|
// `skills/<name>/scripts/update-check.js`. In the canonical source under
|
|
49
49
|
// `bin/commands/`, it stays null and we fall back to package.json.
|
|
50
|
-
const BUNDLED_INSTALLED_VERSION = "4.1.
|
|
50
|
+
const BUNDLED_INSTALLED_VERSION = "4.1.3";
|
|
51
51
|
|
|
52
52
|
// ---------------------------------------------------------------------------
|
|
53
53
|
// Argument parsing
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "report-design"
|
|
3
3
|
description: "Use when the user asks about Report Design Skill, especially phrases like \"crear reportes\", \"armar el reporte\", \"diseñar reporte\", \"report design\", \"create reports\", \"build dashboard\"."
|
|
4
|
-
version: "4.1.
|
|
4
|
+
version: "4.1.3"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- Generated by BI Agent Superpowers. Edit src/content/skills/report-design.md instead. -->
|
|
@@ -295,7 +295,7 @@ Do NOT hand-author `report.json` theme metadata. The canonical PBIR shape (for r
|
|
|
295
295
|
- `resourcePackages` includes a `{name: "RegisteredResources", type: "RegisteredResources"}` package whose `items[]` has `{name: "<file>.json", path: "<file>.json", type: "CustomTheme"}`
|
|
296
296
|
- the theme file itself lives at `<reportPath>/StaticResources/RegisteredResources/<file>.json`
|
|
297
297
|
|
|
298
|
-
**Conditional formatting
|
|
298
|
+
**Conditional formatting note.** Layout notes may describe variance colors, diverging matrix gradients, or signed-color bars as design intent. The current `report-design` flow does not author those formatting rules. Render those visuals with theme/default colors unless you have a tested PBIR formatting implementation. Do not invent `pbi format` calls from this skill.
|
|
299
299
|
|
|
300
300
|
### 4.4 Validate (two layers)
|
|
301
301
|
|
|
@@ -28,7 +28,7 @@ Roles used below (to be resolved via the MCP):
|
|
|
28
28
|
| Scenario slicer | slicer (manual JSON — see `references/slicer.md`) | 848, 312, 336, 80 | `Scenario[Scenario]` |
|
|
29
29
|
|
|
30
30
|
**Design notes:**
|
|
31
|
-
- Variance KPI:
|
|
31
|
+
- Variance KPI: green/red conditional coloring is design intent only. Render as a plain card with theme/default colors unless a tested PBIR formatting implementation is available.
|
|
32
32
|
- Year slicer default: current year (single-select).
|
|
33
33
|
|
|
34
34
|
---
|
|
@@ -61,5 +61,5 @@ Roles used below (to be resolved via the MCP):
|
|
|
61
61
|
| Account × Month matrix | matrix | 16, 512, 1168, 144 | rows: `account-dim-column`, cols: `'Date'[Month]`, values: `variance-measure` with diverging gradient |
|
|
62
62
|
|
|
63
63
|
**Design notes:**
|
|
64
|
-
- Variance-sign coloring (diverging palette) is
|
|
64
|
+
- Variance-sign coloring (diverging palette) is design intent only. Render bars/matrix with theme/default colors unless a tested PBIR formatting implementation is available.
|
|
65
65
|
- The bottom matrix is short (144px) so it acts as a "heatmap strip" rather than a scrollable table.
|
|
@@ -311,7 +311,7 @@ Si PBI Desktop introduce un visualType nuevo (o encontrás uno nativo que falta
|
|
|
311
311
|
|
|
312
312
|
## Features de `visual.json` que `create-visual.js` NO soporta todavía
|
|
313
313
|
|
|
314
|
-
Descubiertos al regenerar la smoke-test "Galería de Visuales" end-to-end con el script
|
|
314
|
+
Descubiertos al regenerar la smoke-test "Galería de Visuales" end-to-end con el script. No bloquean el authoring — la shape que emite el script pasa `pbi report validate` y `validate-pbir.js` — pero sí dejan regresiones visuales si el `visual.json` hand-written aprovechaba alguno de estos features:
|
|
315
315
|
|
|
316
316
|
1. **`query.sortDefinition`** — sort explícito por medida/columna con dirección. `create-visual.js` no lo emite, así que un visual "ranking" tipo barChart ordenado descendente por una medida pierde el orden en la regeneración. **Workaround**: ordenar en Desktop manualmente después del `.pbip` abrir, o hand-extender el `visual.json` con `sortDefinition` post-creación. **Fix futuro**: `--sort-by "Role:Field" --sort-dir descending` en `create-visual.js`.
|
|
317
317
|
|
|
@@ -319,7 +319,7 @@ Descubiertos al regenerar la smoke-test "Galería de Visuales" end-to-end con el
|
|
|
319
319
|
|
|
320
320
|
3. **`queryState.<Role>.projections[].active`** — las columnas siempre salen con `active: true`, las medidas nunca. Si un visual hand-written tenía `active: true` en una medida (para indicar que la medida es la "default Y") o `active: false` en una columna (hidden projection), el script normaliza. Rara vez significativo para el render pero cambia el shape JSON.
|
|
321
321
|
|
|
322
|
-
4. **Formato condicional**: colores signados (positivo verde / negativo rojo), gradientes en matriz, data bars en tabla. Ninguno de estos expresa shape via `create-visual.js
|
|
322
|
+
4. **Formato condicional**: colores signados (positivo verde / negativo rojo), gradientes en matriz, data bars en tabla. Ninguno de estos expresa shape via `create-visual.js`; tratarlos como diseño previsto hasta que exista una implementación PBIR testeada.
|
|
323
323
|
|
|
324
324
|
Cualquier cambio en el script que cubra estos gaps debe: (a) agregar el flag a `parseArgs`, (b) cubrirlo con tests en `create-visual.test.js`, (c) documentarlo en este archivo, (d) regenerar la Galería para que ejercite el feature.
|
|
325
325
|
|
|
@@ -86,4 +86,4 @@ cat > "<reportPath>/definition/pages/<pageName>/visuals/<visualName>/visual.json
|
|
|
86
86
|
EOF
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
Prefer `scripts/create-visual.js --type slicer` for new slicers. Keep the heredoc pattern above only as a fallback when the helper script is unavailable.
|