@luquimbo/bi-superpowers 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +8 -0
- package/.mcp.json +25 -0
- package/AGENTS.md +244 -0
- package/CHANGELOG.md +265 -0
- package/LICENSE +21 -0
- package/README.md +211 -0
- package/bin/build-plugin.js +30 -0
- package/bin/cli.js +1064 -0
- package/bin/commands/add.js +533 -0
- package/bin/commands/add.test.js +77 -0
- package/bin/commands/build-desktop.js +166 -0
- package/bin/commands/changelog.js +443 -0
- package/bin/commands/diff.js +325 -0
- package/bin/commands/lint.js +419 -0
- package/bin/commands/lint.test.js +103 -0
- package/bin/commands/mcp-setup.js +246 -0
- package/bin/commands/pull.js +287 -0
- package/bin/commands/pull.test.js +36 -0
- package/bin/commands/push.js +231 -0
- package/bin/commands/push.test.js +14 -0
- package/bin/commands/search.js +344 -0
- package/bin/commands/search.test.js +115 -0
- package/bin/commands/setup.js +545 -0
- package/bin/commands/setup.test.js +46 -0
- package/bin/commands/sync-profile.js +405 -0
- package/bin/commands/sync-profile.test.js +14 -0
- package/bin/commands/sync-source.js +418 -0
- package/bin/commands/sync-source.test.js +14 -0
- package/bin/commands/watch.js +206 -0
- package/bin/lib/generators/claude-plugin.js +266 -0
- package/bin/lib/generators/claude-plugin.test.js +110 -0
- package/bin/lib/generators/index.js +116 -0
- package/bin/lib/generators/shared.js +282 -0
- package/bin/lib/licensing/index.js +35 -0
- package/bin/lib/licensing/storage.js +364 -0
- package/bin/lib/licensing/storage.test.js +55 -0
- package/bin/lib/licensing/validator.js +213 -0
- package/bin/lib/licensing/validator.test.js +137 -0
- package/bin/lib/microsoft-mcp.js +176 -0
- package/bin/lib/microsoft-mcp.test.js +106 -0
- package/bin/lib/skills.js +84 -0
- package/bin/mcp/powerbi-modeling-launcher.js +38 -0
- package/bin/postinstall.js +44 -0
- package/bin/utils/errors.js +159 -0
- package/bin/utils/git.js +298 -0
- package/bin/utils/logger.js +142 -0
- package/bin/utils/mcp-detect.js +274 -0
- package/bin/utils/mcp-detect.test.js +105 -0
- package/bin/utils/pbix.js +305 -0
- package/bin/utils/pbix.test.js +37 -0
- package/bin/utils/profiles.js +312 -0
- package/bin/utils/projects.js +168 -0
- package/bin/utils/readline.js +206 -0
- package/bin/utils/readline.test.js +47 -0
- package/bin/utils/tui.js +314 -0
- package/bin/utils/tui.test.js +127 -0
- package/commands/contributions.md +265 -0
- package/commands/data-model-design.md +468 -0
- package/commands/dax-doctor.md +248 -0
- package/commands/fabric-scripts.md +452 -0
- package/commands/migration-assistant.md +290 -0
- package/commands/model-documenter.md +242 -0
- package/commands/pbi-connect.md +239 -0
- package/commands/project-kickoff.md +905 -0
- package/commands/report-layout.md +296 -0
- package/commands/rls-design.md +533 -0
- package/commands/theme-tweaker.md +624 -0
- package/config.example.json +23 -0
- package/config.json +23 -0
- package/desktop-extension/manifest.json +37 -0
- package/desktop-extension/package.json +10 -0
- package/desktop-extension/server.js +95 -0
- package/docs/openrouter-free-models.md +92 -0
- package/library/examples/README.md +151 -0
- package/library/examples/finance-reporting/README.md +351 -0
- package/library/examples/finance-reporting/data-model.md +267 -0
- package/library/examples/finance-reporting/measures.dax +557 -0
- package/library/examples/hr-analytics/README.md +371 -0
- package/library/examples/hr-analytics/data-model.md +315 -0
- package/library/examples/hr-analytics/measures.dax +460 -0
- package/library/examples/marketing-analytics/README.md +37 -0
- package/library/examples/marketing-analytics/data-model.md +62 -0
- package/library/examples/marketing-analytics/measures.dax +110 -0
- package/library/examples/retail-analytics/README.md +439 -0
- package/library/examples/retail-analytics/data-model.md +288 -0
- package/library/examples/retail-analytics/measures.dax +481 -0
- package/library/examples/supply-chain/README.md +37 -0
- package/library/examples/supply-chain/data-model.md +69 -0
- package/library/examples/supply-chain/measures.dax +77 -0
- package/library/examples/udf-library/README.md +228 -0
- package/library/examples/udf-library/functions.dax +571 -0
- package/library/snippets/dax/README.md +292 -0
- package/library/snippets/dax/business-domains.md +576 -0
- package/library/snippets/dax/calculate-patterns.md +276 -0
- package/library/snippets/dax/calculation-groups.md +489 -0
- package/library/snippets/dax/error-handling.md +495 -0
- package/library/snippets/dax/iterators-and-aggregations.md +474 -0
- package/library/snippets/dax/kpis-and-metrics.md +293 -0
- package/library/snippets/dax/rankings-and-topn.md +235 -0
- package/library/snippets/dax/security-patterns.md +413 -0
- package/library/snippets/dax/text-and-formatting.md +316 -0
- package/library/snippets/dax/time-intelligence.md +196 -0
- package/library/snippets/dax/user-defined-functions.md +477 -0
- package/library/snippets/dax/virtual-tables.md +546 -0
- package/library/snippets/excel-formulas/README.md +84 -0
- package/library/snippets/excel-formulas/aggregations.md +330 -0
- package/library/snippets/excel-formulas/dates-and-times.md +361 -0
- package/library/snippets/excel-formulas/dynamic-arrays.md +314 -0
- package/library/snippets/excel-formulas/lookups.md +169 -0
- package/library/snippets/excel-formulas/text-functions.md +363 -0
- package/library/snippets/governance/naming-conventions.md +97 -0
- package/library/snippets/governance/review-checklists.md +107 -0
- package/library/snippets/power-query/README.md +389 -0
- package/library/snippets/power-query/api-integration.md +707 -0
- package/library/snippets/power-query/connections.md +434 -0
- package/library/snippets/power-query/data-cleaning.md +298 -0
- package/library/snippets/power-query/error-handling.md +526 -0
- package/library/snippets/power-query/parameters.md +350 -0
- package/library/snippets/power-query/performance.md +506 -0
- package/library/snippets/power-query/transformations.md +330 -0
- package/library/snippets/report-design/accessibility.md +78 -0
- package/library/snippets/report-design/chart-selection.md +54 -0
- package/library/snippets/report-design/layout-patterns.md +87 -0
- package/library/templates/data-models/README.md +93 -0
- package/library/templates/data-models/finance-model.md +627 -0
- package/library/templates/data-models/retail-star-schema.md +473 -0
- package/library/templates/excel/README.md +83 -0
- package/library/templates/excel/budget-tracker.md +432 -0
- package/library/templates/excel/data-entry-form.md +533 -0
- package/library/templates/power-bi/README.md +72 -0
- package/library/templates/power-bi/finance-report.md +449 -0
- package/library/templates/power-bi/kpi-scorecard.md +461 -0
- package/library/templates/power-bi/sales-dashboard.md +281 -0
- package/library/themes/excel/README.md +436 -0
- package/library/themes/power-bi/README.md +271 -0
- package/library/themes/power-bi/accessible.json +307 -0
- package/library/themes/power-bi/bi-superpowers-default.json +858 -0
- package/library/themes/power-bi/corporate-blue.json +291 -0
- package/library/themes/power-bi/dark-mode.json +291 -0
- package/library/themes/power-bi/minimal.json +292 -0
- package/library/themes/power-bi/print-friendly.json +309 -0
- package/package.json +93 -0
- package/skills/contributions/SKILL.md +267 -0
- package/skills/data-model-design/SKILL.md +470 -0
- package/skills/data-modeling/SKILL.md +254 -0
- package/skills/data-quality/SKILL.md +664 -0
- package/skills/dax/SKILL.md +708 -0
- package/skills/dax-doctor/SKILL.md +250 -0
- package/skills/dax-udf/SKILL.md +489 -0
- package/skills/deployment/SKILL.md +320 -0
- package/skills/excel-formulas/SKILL.md +463 -0
- package/skills/fabric-scripts/SKILL.md +454 -0
- package/skills/fast-standard/SKILL.md +509 -0
- package/skills/governance/SKILL.md +205 -0
- package/skills/migration-assistant/SKILL.md +292 -0
- package/skills/model-documenter/SKILL.md +244 -0
- package/skills/pbi-connect/SKILL.md +241 -0
- package/skills/power-query/SKILL.md +406 -0
- package/skills/project-kickoff/SKILL.md +907 -0
- package/skills/query-performance/SKILL.md +480 -0
- package/skills/report-design/SKILL.md +207 -0
- package/skills/report-layout/SKILL.md +298 -0
- package/skills/rls-design/SKILL.md +535 -0
- package/skills/semantic-model/SKILL.md +237 -0
- package/skills/testing-validation/SKILL.md +643 -0
- package/skills/theme-tweaker/SKILL.md +626 -0
- package/src/content/base.md +237 -0
- package/src/content/mcp-requirements.json +69 -0
- package/src/content/routing.md +203 -0
- package/src/content/skills/contributions.md +259 -0
- package/src/content/skills/data-model-design.md +462 -0
- package/src/content/skills/data-modeling.md +246 -0
- package/src/content/skills/data-quality.md +656 -0
- package/src/content/skills/dax-doctor.md +242 -0
- package/src/content/skills/dax-udf.md +481 -0
- package/src/content/skills/dax.md +700 -0
- package/src/content/skills/deployment.md +312 -0
- package/src/content/skills/excel-formulas.md +455 -0
- package/src/content/skills/fabric-scripts.md +446 -0
- package/src/content/skills/fast-standard.md +501 -0
- package/src/content/skills/governance.md +197 -0
- package/src/content/skills/migration-assistant.md +284 -0
- package/src/content/skills/model-documenter.md +236 -0
- package/src/content/skills/pbi-connect.md +233 -0
- package/src/content/skills/power-query.md +398 -0
- package/src/content/skills/project-kickoff.md +899 -0
- package/src/content/skills/query-performance.md +472 -0
- package/src/content/skills/report-design.md +199 -0
- package/src/content/skills/report-layout.md +290 -0
- package/src/content/skills/rls-design.md +527 -0
- package/src/content/skills/semantic-model.md +229 -0
- package/src/content/skills/testing-validation.md +635 -0
- package/src/content/skills/theme-tweaker.md +618 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code Plugin Generator
|
|
3
|
+
* ============================
|
|
4
|
+
*
|
|
5
|
+
* Generates a native Claude Code plugin structure at the target root:
|
|
6
|
+
* - .claude-plugin/plugin.json
|
|
7
|
+
* - .mcp.json
|
|
8
|
+
* - command markdown files
|
|
9
|
+
* - plugin skill directories with SKILL.md files
|
|
10
|
+
*
|
|
11
|
+
* @module lib/generators/claude-plugin
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const {
|
|
17
|
+
PLUGIN_ROOT_LAUNCHER_MODE,
|
|
18
|
+
ABSOLUTE_LAUNCHER_MODE,
|
|
19
|
+
createPluginMcpConfig,
|
|
20
|
+
} = require('../microsoft-mcp');
|
|
21
|
+
const { parseSkillMetadata, getSkillPurpose } = require('./shared');
|
|
22
|
+
|
|
23
|
+
const COMMAND_SKILLS = new Set([
|
|
24
|
+
'project-kickoff',
|
|
25
|
+
'data-model-design',
|
|
26
|
+
'theme-tweaker',
|
|
27
|
+
'pbi-connect',
|
|
28
|
+
'rls-design',
|
|
29
|
+
'fabric-scripts',
|
|
30
|
+
'contributions',
|
|
31
|
+
// Phase 2 — new interactive wizards
|
|
32
|
+
'dax-doctor',
|
|
33
|
+
'model-documenter',
|
|
34
|
+
'migration-assistant',
|
|
35
|
+
'report-layout',
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
const REFERENCE_SKILLS = new Set([
|
|
39
|
+
'dax',
|
|
40
|
+
'power-query',
|
|
41
|
+
'data-modeling',
|
|
42
|
+
'query-performance',
|
|
43
|
+
'testing-validation',
|
|
44
|
+
'data-quality',
|
|
45
|
+
'fast-standard',
|
|
46
|
+
'excel-formulas',
|
|
47
|
+
// Phase 3 — new background knowledge
|
|
48
|
+
'governance',
|
|
49
|
+
'semantic-model',
|
|
50
|
+
'report-design',
|
|
51
|
+
'deployment',
|
|
52
|
+
'dax-udf',
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Ensure a directory exists.
|
|
57
|
+
*
|
|
58
|
+
* @param {string} directory - Directory path
|
|
59
|
+
*/
|
|
60
|
+
function ensureDirectory(directory) {
|
|
61
|
+
if (!fs.existsSync(directory)) {
|
|
62
|
+
fs.mkdirSync(directory, { recursive: true });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Rewrite library references for generated plugin targets.
|
|
68
|
+
*
|
|
69
|
+
* @param {string} content - Original markdown content
|
|
70
|
+
* @param {string} libraryPrefix - Prefix to use for library references
|
|
71
|
+
* @returns {string} Rewritten content
|
|
72
|
+
*/
|
|
73
|
+
function rewriteLibraryReferences(content, libraryPrefix) {
|
|
74
|
+
if (!libraryPrefix || libraryPrefix === 'library') {
|
|
75
|
+
return content;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return content.replace(/(^|[^.`])library\//g, `$1${libraryPrefix}/`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Build a concise frontmatter description for plugin skills.
|
|
83
|
+
*
|
|
84
|
+
* @param {Object} skill - Skill definition object
|
|
85
|
+
* @returns {string} Description string
|
|
86
|
+
*/
|
|
87
|
+
function getPluginDescription(skill) {
|
|
88
|
+
const metadata = parseSkillMetadata(skill.content);
|
|
89
|
+
|
|
90
|
+
if (metadata.triggers.length > 0) {
|
|
91
|
+
const triggerList = metadata.triggers
|
|
92
|
+
.slice(0, 6)
|
|
93
|
+
.map((trigger) => `"${trigger}"`)
|
|
94
|
+
.join(', ');
|
|
95
|
+
return `Use when the user asks about ${metadata.title || skill.name}, especially phrases like ${triggerList}.`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return `${metadata.title || skill.name}: ${getSkillPurpose(skill.name)}.`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Serialize values safely for YAML frontmatter.
|
|
103
|
+
*
|
|
104
|
+
/**
|
|
105
|
+
* @param {string} value - Raw value
|
|
106
|
+
* @returns {string} JSON-escaped string valid in YAML
|
|
107
|
+
*/
|
|
108
|
+
function toFrontmatterValue(value) {
|
|
109
|
+
return JSON.stringify(value);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Build command markdown with Claude Code frontmatter.
|
|
114
|
+
*
|
|
115
|
+
* @param {Object} skill - Skill definition object
|
|
116
|
+
* @param {string} libraryPrefix - Library prefix used in generated target
|
|
117
|
+
* @returns {string} Command markdown
|
|
118
|
+
*/
|
|
119
|
+
function buildCommandMarkdown(skill, libraryPrefix) {
|
|
120
|
+
const description = getSkillPurpose(skill.name);
|
|
121
|
+
const content = rewriteLibraryReferences(skill.content, libraryPrefix);
|
|
122
|
+
|
|
123
|
+
return `---
|
|
124
|
+
description: ${toFrontmatterValue(description)}
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
<!-- Generated by BI Agent Superpowers. Edit src/content/skills/${skill.name}.md instead. -->
|
|
128
|
+
|
|
129
|
+
${content}`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Build plugin skill markdown with required frontmatter.
|
|
134
|
+
*
|
|
135
|
+
* @param {Object} skill - Skill definition object
|
|
136
|
+
* @param {string} version - Package version
|
|
137
|
+
* @param {string} libraryPrefix - Library prefix used in generated target
|
|
138
|
+
* @returns {string} Skill markdown
|
|
139
|
+
*/
|
|
140
|
+
function buildSkillMarkdown(skill, version, libraryPrefix) {
|
|
141
|
+
const content = rewriteLibraryReferences(skill.content, libraryPrefix);
|
|
142
|
+
|
|
143
|
+
return `---
|
|
144
|
+
name: ${toFrontmatterValue(skill.name)}
|
|
145
|
+
description: ${toFrontmatterValue(getPluginDescription(skill))}
|
|
146
|
+
version: ${toFrontmatterValue(version)}
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
<!-- Generated by BI Agent Superpowers. Edit src/content/skills/${skill.name}.md instead. -->
|
|
150
|
+
|
|
151
|
+
${content}`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Remove stale generated files from a managed directory.
|
|
156
|
+
*
|
|
157
|
+
* @param {string} directory - Directory path
|
|
158
|
+
* @param {string[]} expectedEntries - Expected file or directory names
|
|
159
|
+
*/
|
|
160
|
+
function removeStaleEntries(directory, expectedEntries) {
|
|
161
|
+
if (!fs.existsSync(directory)) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const expected = new Set(expectedEntries);
|
|
166
|
+
for (const entry of fs.readdirSync(directory)) {
|
|
167
|
+
if (!expected.has(entry)) {
|
|
168
|
+
fs.rmSync(path.join(directory, entry), { recursive: true, force: true });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Generate a native Claude Code plugin.
|
|
175
|
+
*
|
|
176
|
+
* @param {string} targetDir - Target directory
|
|
177
|
+
* @param {Object[]} skills - Source skill objects
|
|
178
|
+
* @param {Object} [options] - Generator options
|
|
179
|
+
* @param {string} [options.packageDir] - Package installation directory
|
|
180
|
+
* @param {string} [options.version] - Package version
|
|
181
|
+
* @param {boolean} [options.usePluginRootLauncher] - Whether to use ${CLAUDE_PLUGIN_ROOT}
|
|
182
|
+
* @param {string} [options.libraryPrefix] - Library reference prefix
|
|
183
|
+
*/
|
|
184
|
+
async function generate(targetDir, skills, options = {}) {
|
|
185
|
+
const version = options.version || '0.0.0';
|
|
186
|
+
const usePluginRootLauncher = options.usePluginRootLauncher === true;
|
|
187
|
+
const libraryPrefix = options.libraryPrefix || 'library';
|
|
188
|
+
const launcherMode = usePluginRootLauncher ? PLUGIN_ROOT_LAUNCHER_MODE : ABSOLUTE_LAUNCHER_MODE;
|
|
189
|
+
|
|
190
|
+
const pluginDir = path.join(targetDir, '.claude-plugin');
|
|
191
|
+
const commandsDir = path.join(targetDir, 'commands');
|
|
192
|
+
const skillsDir = path.join(targetDir, 'skills');
|
|
193
|
+
|
|
194
|
+
ensureDirectory(pluginDir);
|
|
195
|
+
ensureDirectory(commandsDir);
|
|
196
|
+
ensureDirectory(skillsDir);
|
|
197
|
+
|
|
198
|
+
const pluginManifest = {
|
|
199
|
+
name: 'bi-superpowers',
|
|
200
|
+
description:
|
|
201
|
+
'Claude Code plugin for Power BI, Microsoft Fabric, and semantic model workflows powered by the official Microsoft MCP servers.',
|
|
202
|
+
version,
|
|
203
|
+
author: {
|
|
204
|
+
name: 'Lucas Sanchez',
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
fs.writeFileSync(
|
|
209
|
+
path.join(pluginDir, 'plugin.json'),
|
|
210
|
+
JSON.stringify(pluginManifest, null, 2) + '\n'
|
|
211
|
+
);
|
|
212
|
+
fs.writeFileSync(
|
|
213
|
+
path.join(targetDir, '.mcp.json'),
|
|
214
|
+
JSON.stringify(
|
|
215
|
+
createPluginMcpConfig({
|
|
216
|
+
packageDir: options.packageDir,
|
|
217
|
+
launcherMode,
|
|
218
|
+
}),
|
|
219
|
+
null,
|
|
220
|
+
2
|
|
221
|
+
) + '\n'
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
const commandSkills = skills.filter((skill) => COMMAND_SKILLS.has(skill.name));
|
|
225
|
+
|
|
226
|
+
// Clean up stale entries — commands only have command skills, but ALL skills get SKILL.md
|
|
227
|
+
removeStaleEntries(
|
|
228
|
+
commandsDir,
|
|
229
|
+
commandSkills.map((skill) => `${skill.name}.md`)
|
|
230
|
+
);
|
|
231
|
+
removeStaleEntries(
|
|
232
|
+
skillsDir,
|
|
233
|
+
skills.map((skill) => skill.name)
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
// Command skills → commands/*.md (slash commands, Claude Code only)
|
|
237
|
+
for (const skill of commandSkills) {
|
|
238
|
+
fs.writeFileSync(
|
|
239
|
+
path.join(commandsDir, `${skill.name}.md`),
|
|
240
|
+
buildCommandMarkdown(skill, libraryPrefix)
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ALL skills → skills/*/SKILL.md (universal, discoverable by all Claude tools)
|
|
245
|
+
for (const skill of skills) {
|
|
246
|
+
const skillDir = path.join(skillsDir, skill.name);
|
|
247
|
+
ensureDirectory(skillDir);
|
|
248
|
+
fs.writeFileSync(
|
|
249
|
+
path.join(skillDir, 'SKILL.md'),
|
|
250
|
+
buildSkillMarkdown(skill, version, libraryPrefix)
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
console.log(' ✓ Created Claude Code plugin manifest');
|
|
255
|
+
console.log(' ✓ Created .mcp.json with official Microsoft MCP servers');
|
|
256
|
+
console.log(` ✓ Created ${commandSkills.length} plugin commands`);
|
|
257
|
+
console.log(` ✓ Created ${skills.length} plugin skills (SKILL.md)`);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
module.exports = {
|
|
261
|
+
name: 'Claude Code Plugin',
|
|
262
|
+
description: 'Native Claude Code plugin (recommended)',
|
|
263
|
+
generate,
|
|
264
|
+
COMMAND_SKILLS,
|
|
265
|
+
REFERENCE_SKILLS,
|
|
266
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the native Claude Code plugin generator.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { test, describe } = require('node:test');
|
|
6
|
+
const assert = require('node:assert');
|
|
7
|
+
const fs = require('node:fs');
|
|
8
|
+
const os = require('node:os');
|
|
9
|
+
const path = require('node:path');
|
|
10
|
+
|
|
11
|
+
const claudePlugin = require('./claude-plugin');
|
|
12
|
+
|
|
13
|
+
function makeSkill(name, title) {
|
|
14
|
+
return {
|
|
15
|
+
name,
|
|
16
|
+
path: `/virtual/${name}.md`,
|
|
17
|
+
content: `# ${title}
|
|
18
|
+
|
|
19
|
+
## Trigger
|
|
20
|
+
- "${name}"
|
|
21
|
+
|
|
22
|
+
## Identity
|
|
23
|
+
You are the ${title} guide.
|
|
24
|
+
|
|
25
|
+
## MANDATORY RULES
|
|
26
|
+
1. Stay helpful.
|
|
27
|
+
|
|
28
|
+
Reference: library/snippets/example.md
|
|
29
|
+
`,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
describe('claude-plugin generator', () => {
|
|
34
|
+
test('creates plugin manifest, mcp config, commands, and skills', async () => {
|
|
35
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'bi-superpowers-plugin-'));
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const skills = [
|
|
39
|
+
makeSkill('project-kickoff', 'Project Kickoff'),
|
|
40
|
+
makeSkill('pbi-connect', 'PBI Connect'),
|
|
41
|
+
makeSkill('dax', 'DAX'),
|
|
42
|
+
makeSkill('excel-formulas', 'Excel Formulas'),
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
await claudePlugin.generate(tempDir, skills, {
|
|
46
|
+
packageDir: '/tmp/bi-superpowers',
|
|
47
|
+
version: '9.9.9',
|
|
48
|
+
usePluginRootLauncher: true,
|
|
49
|
+
libraryPrefix: 'library',
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const pluginManifestPath = path.join(tempDir, '.claude-plugin', 'plugin.json');
|
|
53
|
+
const pluginMcpPath = path.join(tempDir, '.mcp.json');
|
|
54
|
+
const commandPath = path.join(tempDir, 'commands', 'project-kickoff.md');
|
|
55
|
+
const skillPath = path.join(tempDir, 'skills', 'dax', 'SKILL.md');
|
|
56
|
+
|
|
57
|
+
assert.ok(fs.existsSync(pluginManifestPath));
|
|
58
|
+
assert.ok(fs.existsSync(pluginMcpPath));
|
|
59
|
+
assert.ok(fs.existsSync(commandPath));
|
|
60
|
+
assert.ok(fs.existsSync(skillPath));
|
|
61
|
+
|
|
62
|
+
const pluginManifest = JSON.parse(fs.readFileSync(pluginManifestPath, 'utf8'));
|
|
63
|
+
const pluginMcp = JSON.parse(fs.readFileSync(pluginMcpPath, 'utf8'));
|
|
64
|
+
const commandContent = fs.readFileSync(commandPath, 'utf8');
|
|
65
|
+
const skillContent = fs.readFileSync(skillPath, 'utf8');
|
|
66
|
+
|
|
67
|
+
assert.strictEqual(pluginManifest.name, 'bi-superpowers');
|
|
68
|
+
assert.strictEqual(pluginManifest.version, '9.9.9');
|
|
69
|
+
|
|
70
|
+
assert.ok(pluginMcp['powerbi-remote']);
|
|
71
|
+
assert.ok(pluginMcp['fabric-mcp-server']);
|
|
72
|
+
assert.ok(pluginMcp['powerbi-modeling-mcp']);
|
|
73
|
+
assert.strictEqual(pluginMcp['powerbi-remote'].type, 'http');
|
|
74
|
+
assert.strictEqual(pluginMcp['fabric-mcp-server'].command, 'npx');
|
|
75
|
+
assert.strictEqual(pluginMcp['powerbi-modeling-mcp'].command, 'node');
|
|
76
|
+
assert.ok(
|
|
77
|
+
pluginMcp['powerbi-modeling-mcp'].args[0].includes(
|
|
78
|
+
'${CLAUDE_PLUGIN_ROOT}/bin/mcp/powerbi-modeling-launcher.js'
|
|
79
|
+
)
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
assert.ok(commandContent.includes('description: "Project analysis and planning"'));
|
|
83
|
+
assert.ok(commandContent.includes('Generated by BI Agent Superpowers'));
|
|
84
|
+
assert.ok(skillContent.includes('name: "dax"'));
|
|
85
|
+
assert.ok(skillContent.includes('version: "9.9.9"'));
|
|
86
|
+
} finally {
|
|
87
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('rewrites library references for generated project outputs', async () => {
|
|
92
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'bi-superpowers-plugin-'));
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
const skills = [makeSkill('dax', 'DAX')];
|
|
96
|
+
|
|
97
|
+
await claudePlugin.generate(tempDir, skills, {
|
|
98
|
+
packageDir: '/tmp/bi-superpowers',
|
|
99
|
+
version: '1.0.0',
|
|
100
|
+
libraryPrefix: '.bi-superpowers/library',
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const skillContent = fs.readFileSync(path.join(tempDir, 'skills', 'dax', 'SKILL.md'), 'utf8');
|
|
104
|
+
|
|
105
|
+
assert.ok(skillContent.includes('.bi-superpowers/library/snippets/example.md'));
|
|
106
|
+
} finally {
|
|
107
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
});
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generators Module
|
|
3
|
+
* ==================
|
|
4
|
+
*
|
|
5
|
+
* Factory and registry for the Claude ecosystem generator.
|
|
6
|
+
* Supports: Claude Code, 1code.dev, Claude Desktop (via MCPB).
|
|
7
|
+
*
|
|
8
|
+
* @module lib/generators
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const claudePlugin = require('./claude-plugin');
|
|
12
|
+
const shared = require('./shared');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* AI Tools Configuration Registry
|
|
16
|
+
*
|
|
17
|
+
* Only claude-plugin is needed — it covers Claude Code + 1code.dev.
|
|
18
|
+
* Claude Desktop is handled separately via `super build-desktop` (MCPB).
|
|
19
|
+
*/
|
|
20
|
+
const AI_TOOLS = {
|
|
21
|
+
'claude-plugin': claudePlugin,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get a generator by tool ID
|
|
26
|
+
* @param {string} toolId - Tool identifier
|
|
27
|
+
* @returns {Object|null} Generator object or null if not found
|
|
28
|
+
*/
|
|
29
|
+
function getGenerator(toolId) {
|
|
30
|
+
return AI_TOOLS[toolId] || null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get all available tool IDs
|
|
35
|
+
* @returns {string[]} Array of tool IDs
|
|
36
|
+
*/
|
|
37
|
+
function getAvailableTools() {
|
|
38
|
+
return Object.keys(AI_TOOLS);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get tool display info for all tools
|
|
43
|
+
* @returns {Object[]} Array of {id, name, description}
|
|
44
|
+
*/
|
|
45
|
+
function getToolsInfo() {
|
|
46
|
+
return Object.entries(AI_TOOLS).map(([id, tool]) => ({
|
|
47
|
+
id,
|
|
48
|
+
name: tool.name,
|
|
49
|
+
description: tool.description,
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Generate config for a specific AI tool
|
|
55
|
+
*
|
|
56
|
+
* @param {string} toolId - Tool ID (e.g., 'claude-plugin')
|
|
57
|
+
* @param {string} targetDir - Target project directory
|
|
58
|
+
* @param {Object[]} skills - Array of skill objects
|
|
59
|
+
* @param {Object} options - Additional options passed to generator
|
|
60
|
+
*/
|
|
61
|
+
async function generateForTool(toolId, targetDir, skills, options = {}) {
|
|
62
|
+
const generator = AI_TOOLS[toolId];
|
|
63
|
+
if (generator && generator.generate) {
|
|
64
|
+
console.log(`Generating for ${generator.name}...`);
|
|
65
|
+
await generator.generate(targetDir, skills, options);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Preview what files would be generated (for dry-run mode)
|
|
71
|
+
*
|
|
72
|
+
* @param {string} targetDir - Target project directory
|
|
73
|
+
* @param {string[]} tools - Array of tool IDs to generate for
|
|
74
|
+
* @param {Object[]} skills - Array of skill objects
|
|
75
|
+
* @param {string} configFile - Name of config file
|
|
76
|
+
*/
|
|
77
|
+
function previewGeneration(targetDir, tools, skills, configFile) {
|
|
78
|
+
const path = require('path');
|
|
79
|
+
|
|
80
|
+
for (const tool of tools) {
|
|
81
|
+
const toolConfig = AI_TOOLS[tool];
|
|
82
|
+
if (!toolConfig) continue;
|
|
83
|
+
|
|
84
|
+
console.log(`${toolConfig.name}:`);
|
|
85
|
+
|
|
86
|
+
if (tool === 'claude-plugin') {
|
|
87
|
+
console.log(` \u{1F4C1} ${path.join(targetDir, '.claude-plugin')}/`);
|
|
88
|
+
console.log(' \u2514\u2500 plugin.json');
|
|
89
|
+
console.log(` \u{1F4C4} ${path.join(targetDir, '.mcp.json')}`);
|
|
90
|
+
console.log(` \u{1F4C1} ${path.join(targetDir, 'commands')}/`);
|
|
91
|
+
console.log(` \u{1F4C1} ${path.join(targetDir, 'skills')}/`);
|
|
92
|
+
}
|
|
93
|
+
console.log('');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log('Config file:');
|
|
97
|
+
console.log(` \u{1F4C4} ${path.join(targetDir, configFile)}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = {
|
|
101
|
+
// Registry
|
|
102
|
+
AI_TOOLS,
|
|
103
|
+
|
|
104
|
+
// Factory functions
|
|
105
|
+
getGenerator,
|
|
106
|
+
getAvailableTools,
|
|
107
|
+
getToolsInfo,
|
|
108
|
+
generateForTool,
|
|
109
|
+
previewGeneration,
|
|
110
|
+
|
|
111
|
+
// Individual generators
|
|
112
|
+
claudePlugin,
|
|
113
|
+
|
|
114
|
+
// Shared utilities
|
|
115
|
+
...shared,
|
|
116
|
+
};
|