@luquimbo/bi-superpowers 4.1.1 → 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/AGENTS.md +4 -5
- package/CHANGELOG.md +24 -0
- package/README.md +633 -93
- package/bin/cli.js +52 -54
- package/bin/commands/build-desktop.js +60 -6
- package/bin/commands/diff.js +86 -1
- package/bin/commands/watch.js +50 -5
- package/bin/lib/generators/claude-plugin.js +1 -1
- package/bin/postinstall.js +1 -1
- package/commands/bi-start.md +2 -2
- package/commands/pbi-connect.md +1 -1
- package/commands/project-kickoff.md +5 -5
- package/commands/report-design.md +8 -8
- package/desktop-extension/server.js +43 -10
- package/package.json +5 -5
- package/skills/bi-start/SKILL.md +3 -3
- package/skills/bi-start/scripts/update-check.js +1 -1
- package/skills/pbi-connect/SKILL.md +2 -2
- package/skills/pbi-connect/scripts/update-check.js +1 -1
- package/skills/project-kickoff/SKILL.md +6 -6
- package/skills/project-kickoff/scripts/update-check.js +1 -1
- package/skills/report-design/SKILL.md +9 -9
- 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/bi-start.md +2 -2
- package/src/content/skills/project-kickoff.md +4 -4
- package/src/content/skills/report-design/SKILL.md +7 -7
- 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 `super install --yes` to propagate the new skills.',
|
|
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 {
|
|
@@ -619,9 +583,10 @@ function getUpgradeCommand(userAgent) {
|
|
|
619
583
|
/**
|
|
620
584
|
* `super upgrade` — reinstall the package at the latest version.
|
|
621
585
|
*
|
|
622
|
-
* After the reinstall, prints a "next steps" block with
|
|
623
|
-
*
|
|
624
|
-
*
|
|
586
|
+
* After the reinstall, prints a "next steps" block with the refresh paths
|
|
587
|
+
* for marketplace installs, user-profile installs, and project-local
|
|
588
|
+
* plugins generated by `super kickoff`, so users know how to propagate
|
|
589
|
+
* the new skills into their agents. We
|
|
625
590
|
* intentionally don't auto-chain into `super install`:
|
|
626
591
|
* - Claude Code users who installed via the plugin marketplace refresh
|
|
627
592
|
* from inside Claude Code (`/plugin update bi-superpowers`), they don't
|
|
@@ -648,6 +613,24 @@ function resetUpdateCheckStateAfterUpgrade(homeDir) {
|
|
|
648
613
|
}
|
|
649
614
|
}
|
|
650
615
|
|
|
616
|
+
function getUpgradeRefreshSteps() {
|
|
617
|
+
return [
|
|
618
|
+
{
|
|
619
|
+
label: 'Claude Code (installed via marketplace):',
|
|
620
|
+
command: '/plugin update bi-superpowers',
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
label: 'Skills installed in your user profile (npm + super install):',
|
|
624
|
+
command: 'super install --yes',
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
label:
|
|
628
|
+
'Project-local Claude Code plugin (run inside each repo where you used super kickoff):',
|
|
629
|
+
command: 'super recharge',
|
|
630
|
+
},
|
|
631
|
+
];
|
|
632
|
+
}
|
|
633
|
+
|
|
651
634
|
function updatePackage() {
|
|
652
635
|
console.log(`Updating ${PACKAGE_NAME}...\n`);
|
|
653
636
|
|
|
@@ -662,12 +645,11 @@ function updatePackage() {
|
|
|
662
645
|
resetUpdateCheckStateAfterUpgrade(require('os').homedir());
|
|
663
646
|
|
|
664
647
|
console.log('\nNext step — refresh the skills + MCPs in your agents:\n');
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
console.log('');
|
|
648
|
+
for (const step of getUpgradeRefreshSteps()) {
|
|
649
|
+
console.log(` ${step.label}`);
|
|
650
|
+
console.log(` ${step.command}`);
|
|
651
|
+
console.log('');
|
|
652
|
+
}
|
|
671
653
|
console.log(
|
|
672
654
|
'If you only wanted the CLI updated (e.g. developing locally), you can skip the above.'
|
|
673
655
|
);
|
|
@@ -765,19 +747,32 @@ function runWatch(args) {
|
|
|
765
747
|
process.exit(1);
|
|
766
748
|
}
|
|
767
749
|
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
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();
|
|
771
771
|
for (const tool of tools) {
|
|
772
|
-
|
|
773
|
-
if (cfg && cfg.generate) {
|
|
774
|
-
cfg.generate(targetDir, skills, { packageDir: PACKAGE_DIR });
|
|
775
|
-
}
|
|
772
|
+
await generateForToolFn(tool, targetDir, skills);
|
|
776
773
|
}
|
|
777
774
|
},
|
|
778
775
|
};
|
|
779
|
-
|
|
780
|
-
watchCommand(args, getCommandConfig(), cliModule);
|
|
781
776
|
}
|
|
782
777
|
|
|
783
778
|
/**
|
|
@@ -803,7 +798,10 @@ module.exports = {
|
|
|
803
798
|
loadToolConfig,
|
|
804
799
|
saveToolConfig,
|
|
805
800
|
getUpgradeCommand,
|
|
801
|
+
getUpgradeRefreshSteps,
|
|
806
802
|
resetUpdateCheckStateAfterUpgrade,
|
|
803
|
+
createWatchCliModule,
|
|
804
|
+
getGenerationOptions,
|
|
807
805
|
AI_TOOLS,
|
|
808
806
|
SKILLS_DIR,
|
|
809
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
|
+
});
|
|
@@ -150,7 +150,7 @@ node "{skillBundleDir}/scripts/update-check.js" --silent-if-uptodate --silent-if
|
|
|
150
150
|
|
|
151
151
|
- Empty output or \`UPTODATE\` — proceed with the skill silently. No message.
|
|
152
152
|
- \`UPDATE_AVAILABLE <installed> <latest>\` — tell the user exactly once this conversation, before diving into the skill:
|
|
153
|
-
> "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."
|
|
153
|
+
> "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. Si estás usando un plugin local generado con \`super kickoff\`, después corré \`super recharge\` en ese repo."
|
|
154
154
|
|
|
155
155
|
Then continue with the skill below.
|
|
156
156
|
- \`SNOOZED <iso>\` — proceed silently.
|
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
|
package/commands/bi-start.md
CHANGED
|
@@ -55,7 +55,7 @@ Interpret the single-line output:
|
|
|
55
55
|
```bash
|
|
56
56
|
super upgrade
|
|
57
57
|
```
|
|
58
|
-
After it finishes, remind: _"
|
|
58
|
+
After it finishes, remind: _"Si instalaste skills en el perfil del agente, corré `super install --yes`. Si además usás un plugin local generado con `super kickoff`, corré `super recharge` dentro de ese repo."_
|
|
59
59
|
|
|
60
60
|
On `no` — respect it, continue to PHASE 1 silently. The update-state.json already tracks the user's snooze per `update-check.js` semantics.
|
|
61
61
|
|
|
@@ -180,7 +180,7 @@ Stop. Don't hover. The user will tell you what they want next.
|
|
|
180
180
|
- **Project analysis or setup**: that's `/project-kickoff`. If the user says "analizar mi proyecto", "armar el modelo base", "arrancar uno nuevo desde cero", delegate.
|
|
181
181
|
- **MCP wiring details**: that's `/pbi-connect`. bi-start just offers to dispatch it; the actual configuration work is in that skill.
|
|
182
182
|
- **Report authoring**: that's `/report-design`. Same pattern.
|
|
183
|
-
- **Running the update**: bi-start offers + dispatches `super upgrade`; the actual
|
|
183
|
+
- **Running the update**: bi-start offers + dispatches `super upgrade`; the actual refresh path after eso (`/plugin update bi-superpowers`, `super install --yes`, o `super recharge`) is owned by `/bin/cli.js`.
|
|
184
184
|
|
|
185
185
|
## Related Skills
|
|
186
186
|
|
package/commands/pbi-connect.md
CHANGED
|
@@ -15,7 +15,7 @@ node "{skillBundleDir}/scripts/update-check.js" --silent-if-uptodate --silent-if
|
|
|
15
15
|
|
|
16
16
|
- Empty output or `UPTODATE` — proceed with the skill silently. No message.
|
|
17
17
|
- `UPDATE_AVAILABLE <installed> <latest>` — tell the user exactly once this conversation, before diving into the skill:
|
|
18
|
-
> "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."
|
|
18
|
+
> "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. Si estás usando un plugin local generado con `super kickoff`, después corré `super recharge` en ese repo."
|
|
19
19
|
|
|
20
20
|
Then continue with the skill below.
|
|
21
21
|
- `SNOOZED <iso>` — proceed silently.
|
|
@@ -15,7 +15,7 @@ node "{skillBundleDir}/scripts/update-check.js" --silent-if-uptodate --silent-if
|
|
|
15
15
|
|
|
16
16
|
- Empty output or `UPTODATE` — proceed with the skill silently. No message.
|
|
17
17
|
- `UPDATE_AVAILABLE <installed> <latest>` — tell the user exactly once this conversation, before diving into the skill:
|
|
18
|
-
> "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."
|
|
18
|
+
> "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. Si estás usando un plugin local generado con `super kickoff`, después corré `super recharge` en ese repo."
|
|
19
19
|
|
|
20
20
|
Then continue with the skill below.
|
|
21
21
|
- `SNOOZED <iso>` — proceed silently.
|
|
@@ -592,11 +592,11 @@ Once the model has at least 1 fact, 1 dim, and 3 measures in place, propose movi
|
|
|
592
592
|
|
|
593
593
|
El siguiente paso lógico es armar los 3 reportes con `/report-design`. Ese skill va a:
|
|
594
594
|
1. Usar el dominio que ya definimos ({domain})
|
|
595
|
-
2. Inspeccionar tu modelo vía el pbi
|
|
596
|
-
3. Generar 3 páginas con
|
|
595
|
+
2. Inspeccionar tu modelo vía el Modeling MCP (o `pbi-cli-tool` si hace falta) para las medidas/dimensiones exactas
|
|
596
|
+
3. Generar 3 páginas con scripts Node + comandos `pbi report` (card, line, bar, matrix)
|
|
597
597
|
4. Cerrar y reabrir PBI Desktop para que renderice
|
|
598
598
|
|
|
599
|
-
Requisito: necesitás pbi-cli-tool
|
|
599
|
+
Requisito: necesitás Windows + Power BI Desktop + Python 3.10+ + `pipx` + `pbi-cli-tool` (si te falta algo, el skill te guía).
|
|
600
600
|
|
|
601
601
|
¿Arrancamos con /report-design, o preferís agregar más medidas al modelo primero?
|
|
602
602
|
```
|
|
@@ -608,7 +608,7 @@ If the user opts for reports, load `/report-design` and let it run. If they want
|
|
|
608
608
|
## What this skill does NOT do
|
|
609
609
|
|
|
610
610
|
- **No scoring / benchmarking** of an existing model. For that, the user can ask "audit this model" separately.
|
|
611
|
-
- **Report authoring** is delegated to `/report-design` which
|
|
611
|
+
- **Report authoring** is delegated to `/report-design` which uses bundled Node scripts plus the `pbi` CLI runtime flow — don't write `.Report/` files from here.
|
|
612
612
|
|
|
613
613
|
---
|
|
614
614
|
|
|
@@ -15,7 +15,7 @@ node "{skillBundleDir}/scripts/update-check.js" --silent-if-uptodate --silent-if
|
|
|
15
15
|
|
|
16
16
|
- Empty output or `UPTODATE` — proceed with the skill silently. No message.
|
|
17
17
|
- `UPDATE_AVAILABLE <installed> <latest>` — tell the user exactly once this conversation, before diving into the skill:
|
|
18
|
-
> "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."
|
|
18
|
+
> "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. Si estás usando un plugin local generado con `super kickoff`, después corré `super recharge` en ese repo."
|
|
19
19
|
|
|
20
20
|
Then continue with the skill below.
|
|
21
21
|
- `SNOOZED <iso>` — proceed silently.
|
|
@@ -36,13 +36,13 @@ Activate this skill when the user mentions:
|
|
|
36
36
|
- Explicit invocation: `/report-design`
|
|
37
37
|
|
|
38
38
|
## Identity
|
|
39
|
-
You are **BI Report Architect**, an orchestrator that turns a finished semantic model into a Power BI report. You author PBIR via **bundled Node scripts** — not via `pbi-
|
|
39
|
+
You are **BI Report Architect**, an orchestrator that turns a finished semantic model into a Power BI report. You author the PBIR visual/theme layer via **bundled Node scripts** — not via the old `pbi visual` / `pbi report set-theme` path:
|
|
40
40
|
|
|
41
41
|
- `scripts/create-visual.js` — creates any of the 28 native PBI visualTypes with a canonical `visual.json` shape (allowlist-driven, validated). **This replaces `pbi visual add` + `pbi visual bind`.**
|
|
42
42
|
- `scripts/apply-theme.js` — registers a custom theme with the canonical `report.json` shape. **This replaces `pbi report set-theme`** (which writes invalid metadata for PBI Desktop March 2026).
|
|
43
43
|
- `scripts/validate-pbir.js` — allowlist + bind-role validator. **Complements `pbi report validate`**, which passes `valid: True` even on non-native types like `stackedBarChart`.
|
|
44
44
|
|
|
45
|
-
The `pbi-cli-tool` (MIT, by MinaSaad1) is
|
|
45
|
+
The `pbi-cli-tool` (MIT, by MinaSaad1) is still required today for **three parts of the flow**: model introspection via XMLA (`pbi connect`, `pbi measure list`, `pbi table list`, `pbi column list`), report page management (`pbi report add-page`, `pbi report list-pages`), and schema validation (`pbi report validate`). Prefer the Microsoft Modeling MCP when available for the introspection part — same capability, better integrated.
|
|
46
46
|
|
|
47
47
|
Full rationale: `references/native-visuals.md` → section "Lo que este skill YA no hace via CLI".
|
|
48
48
|
|
|
@@ -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
|
|
|
@@ -368,8 +368,8 @@ Cuando guardes desde Desktop, commiteá el .pbip a git para versionar.
|
|
|
368
368
|
**References** (`references/`):
|
|
369
369
|
- `native-visuals.md` — **canonical reference**: 28 native visualTypes, their roles, shape canonical, copy-paste examples (read this before authoring)
|
|
370
370
|
- `pbi-desktop-installation.md` — why the standalone installer build is required (not Microsoft Store), install + uninstall steps
|
|
371
|
-
- `cli-setup.md` — how to install Python + pipx + pbi-cli-tool + pywin32 (
|
|
372
|
-
- `cli-commands.md` — cheatsheet of the `pbi` commands still used by this skill (
|
|
371
|
+
- `cli-setup.md` — how to install Python + pipx + pbi-cli-tool + pywin32 (required because this skill still uses `pbi` for connect, page operations, and validation)
|
|
372
|
+
- `cli-commands.md` — cheatsheet of the `pbi` commands still used by this skill (`pbi connect`, `pbi measure list`, `pbi report add-page`, `pbi report list-pages`, `pbi report validate`)
|
|
373
373
|
- `textbox.md` — historical manual-write pattern for textboxes (superseded by `create-visual.js --type textbox`; kept for reference)
|
|
374
374
|
- `slicer.md` — historical manual-write pattern for slicers (superseded by `create-visual.js --type slicer --slicer-mode ...`; kept for reference)
|
|
375
375
|
- `close-write-open-pattern.md` — why and how to handle the Desktop restart cycle
|
|
@@ -383,7 +383,7 @@ Cuando guardes desde Desktop, commiteá el .pbip a git para versionar.
|
|
|
383
383
|
- `layouts/generic.md` — fallback 3-page composition
|
|
384
384
|
|
|
385
385
|
**Scripts** (`scripts/`):
|
|
386
|
-
- `ensure-pbi-cli.sh` — idempotent installer for the pbi-cli-tool (
|
|
386
|
+
- `ensure-pbi-cli.sh` — idempotent installer for the pbi-cli-tool (required for this skill's current runtime flow)
|
|
387
387
|
- `apply-theme.js` — registers a custom PBIR theme with the canonical shape Desktop accepts (replaces the broken `pbi report set-theme` in PBI Desktop March 2026)
|
|
388
388
|
- `create-visual.js` — creates any of the 28 native PBI visualTypes with a canonical `visual.json` shape (replaces `pbi visual add` + `pbi visual bind`; enforces allowlist)
|
|
389
389
|
- `validate-pbir.js` — allowlist + role-checks validator that catches non-native visualTypes and missing/invalid bindings (complements `pbi report validate`)
|
|
@@ -400,4 +400,4 @@ Cuando guardes desde Desktop, commiteá el .pbip a git para versionar.
|
|
|
400
400
|
|
|
401
401
|
## Credit
|
|
402
402
|
|
|
403
|
-
This skill
|
|
403
|
+
This skill integrates with the [`pbi-cli-tool`](https://github.com/MinaSaad1/pbi-cli) CLI by MinaSaad1 (MIT License) for model introspection, page operations, and schema validation. Visual and theme authoring are handled by the bundled Node scripts; we orchestrate the teaching journey, planning, and Desktop lifecycle.
|