@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.
Files changed (193) hide show
  1. package/.claude-plugin/plugin.json +8 -0
  2. package/.mcp.json +25 -0
  3. package/AGENTS.md +244 -0
  4. package/CHANGELOG.md +265 -0
  5. package/LICENSE +21 -0
  6. package/README.md +211 -0
  7. package/bin/build-plugin.js +30 -0
  8. package/bin/cli.js +1064 -0
  9. package/bin/commands/add.js +533 -0
  10. package/bin/commands/add.test.js +77 -0
  11. package/bin/commands/build-desktop.js +166 -0
  12. package/bin/commands/changelog.js +443 -0
  13. package/bin/commands/diff.js +325 -0
  14. package/bin/commands/lint.js +419 -0
  15. package/bin/commands/lint.test.js +103 -0
  16. package/bin/commands/mcp-setup.js +246 -0
  17. package/bin/commands/pull.js +287 -0
  18. package/bin/commands/pull.test.js +36 -0
  19. package/bin/commands/push.js +231 -0
  20. package/bin/commands/push.test.js +14 -0
  21. package/bin/commands/search.js +344 -0
  22. package/bin/commands/search.test.js +115 -0
  23. package/bin/commands/setup.js +545 -0
  24. package/bin/commands/setup.test.js +46 -0
  25. package/bin/commands/sync-profile.js +405 -0
  26. package/bin/commands/sync-profile.test.js +14 -0
  27. package/bin/commands/sync-source.js +418 -0
  28. package/bin/commands/sync-source.test.js +14 -0
  29. package/bin/commands/watch.js +206 -0
  30. package/bin/lib/generators/claude-plugin.js +266 -0
  31. package/bin/lib/generators/claude-plugin.test.js +110 -0
  32. package/bin/lib/generators/index.js +116 -0
  33. package/bin/lib/generators/shared.js +282 -0
  34. package/bin/lib/licensing/index.js +35 -0
  35. package/bin/lib/licensing/storage.js +364 -0
  36. package/bin/lib/licensing/storage.test.js +55 -0
  37. package/bin/lib/licensing/validator.js +213 -0
  38. package/bin/lib/licensing/validator.test.js +137 -0
  39. package/bin/lib/microsoft-mcp.js +176 -0
  40. package/bin/lib/microsoft-mcp.test.js +106 -0
  41. package/bin/lib/skills.js +84 -0
  42. package/bin/mcp/powerbi-modeling-launcher.js +38 -0
  43. package/bin/postinstall.js +44 -0
  44. package/bin/utils/errors.js +159 -0
  45. package/bin/utils/git.js +298 -0
  46. package/bin/utils/logger.js +142 -0
  47. package/bin/utils/mcp-detect.js +274 -0
  48. package/bin/utils/mcp-detect.test.js +105 -0
  49. package/bin/utils/pbix.js +305 -0
  50. package/bin/utils/pbix.test.js +37 -0
  51. package/bin/utils/profiles.js +312 -0
  52. package/bin/utils/projects.js +168 -0
  53. package/bin/utils/readline.js +206 -0
  54. package/bin/utils/readline.test.js +47 -0
  55. package/bin/utils/tui.js +314 -0
  56. package/bin/utils/tui.test.js +127 -0
  57. package/commands/contributions.md +265 -0
  58. package/commands/data-model-design.md +468 -0
  59. package/commands/dax-doctor.md +248 -0
  60. package/commands/fabric-scripts.md +452 -0
  61. package/commands/migration-assistant.md +290 -0
  62. package/commands/model-documenter.md +242 -0
  63. package/commands/pbi-connect.md +239 -0
  64. package/commands/project-kickoff.md +905 -0
  65. package/commands/report-layout.md +296 -0
  66. package/commands/rls-design.md +533 -0
  67. package/commands/theme-tweaker.md +624 -0
  68. package/config.example.json +23 -0
  69. package/config.json +23 -0
  70. package/desktop-extension/manifest.json +37 -0
  71. package/desktop-extension/package.json +10 -0
  72. package/desktop-extension/server.js +95 -0
  73. package/docs/openrouter-free-models.md +92 -0
  74. package/library/examples/README.md +151 -0
  75. package/library/examples/finance-reporting/README.md +351 -0
  76. package/library/examples/finance-reporting/data-model.md +267 -0
  77. package/library/examples/finance-reporting/measures.dax +557 -0
  78. package/library/examples/hr-analytics/README.md +371 -0
  79. package/library/examples/hr-analytics/data-model.md +315 -0
  80. package/library/examples/hr-analytics/measures.dax +460 -0
  81. package/library/examples/marketing-analytics/README.md +37 -0
  82. package/library/examples/marketing-analytics/data-model.md +62 -0
  83. package/library/examples/marketing-analytics/measures.dax +110 -0
  84. package/library/examples/retail-analytics/README.md +439 -0
  85. package/library/examples/retail-analytics/data-model.md +288 -0
  86. package/library/examples/retail-analytics/measures.dax +481 -0
  87. package/library/examples/supply-chain/README.md +37 -0
  88. package/library/examples/supply-chain/data-model.md +69 -0
  89. package/library/examples/supply-chain/measures.dax +77 -0
  90. package/library/examples/udf-library/README.md +228 -0
  91. package/library/examples/udf-library/functions.dax +571 -0
  92. package/library/snippets/dax/README.md +292 -0
  93. package/library/snippets/dax/business-domains.md +576 -0
  94. package/library/snippets/dax/calculate-patterns.md +276 -0
  95. package/library/snippets/dax/calculation-groups.md +489 -0
  96. package/library/snippets/dax/error-handling.md +495 -0
  97. package/library/snippets/dax/iterators-and-aggregations.md +474 -0
  98. package/library/snippets/dax/kpis-and-metrics.md +293 -0
  99. package/library/snippets/dax/rankings-and-topn.md +235 -0
  100. package/library/snippets/dax/security-patterns.md +413 -0
  101. package/library/snippets/dax/text-and-formatting.md +316 -0
  102. package/library/snippets/dax/time-intelligence.md +196 -0
  103. package/library/snippets/dax/user-defined-functions.md +477 -0
  104. package/library/snippets/dax/virtual-tables.md +546 -0
  105. package/library/snippets/excel-formulas/README.md +84 -0
  106. package/library/snippets/excel-formulas/aggregations.md +330 -0
  107. package/library/snippets/excel-formulas/dates-and-times.md +361 -0
  108. package/library/snippets/excel-formulas/dynamic-arrays.md +314 -0
  109. package/library/snippets/excel-formulas/lookups.md +169 -0
  110. package/library/snippets/excel-formulas/text-functions.md +363 -0
  111. package/library/snippets/governance/naming-conventions.md +97 -0
  112. package/library/snippets/governance/review-checklists.md +107 -0
  113. package/library/snippets/power-query/README.md +389 -0
  114. package/library/snippets/power-query/api-integration.md +707 -0
  115. package/library/snippets/power-query/connections.md +434 -0
  116. package/library/snippets/power-query/data-cleaning.md +298 -0
  117. package/library/snippets/power-query/error-handling.md +526 -0
  118. package/library/snippets/power-query/parameters.md +350 -0
  119. package/library/snippets/power-query/performance.md +506 -0
  120. package/library/snippets/power-query/transformations.md +330 -0
  121. package/library/snippets/report-design/accessibility.md +78 -0
  122. package/library/snippets/report-design/chart-selection.md +54 -0
  123. package/library/snippets/report-design/layout-patterns.md +87 -0
  124. package/library/templates/data-models/README.md +93 -0
  125. package/library/templates/data-models/finance-model.md +627 -0
  126. package/library/templates/data-models/retail-star-schema.md +473 -0
  127. package/library/templates/excel/README.md +83 -0
  128. package/library/templates/excel/budget-tracker.md +432 -0
  129. package/library/templates/excel/data-entry-form.md +533 -0
  130. package/library/templates/power-bi/README.md +72 -0
  131. package/library/templates/power-bi/finance-report.md +449 -0
  132. package/library/templates/power-bi/kpi-scorecard.md +461 -0
  133. package/library/templates/power-bi/sales-dashboard.md +281 -0
  134. package/library/themes/excel/README.md +436 -0
  135. package/library/themes/power-bi/README.md +271 -0
  136. package/library/themes/power-bi/accessible.json +307 -0
  137. package/library/themes/power-bi/bi-superpowers-default.json +858 -0
  138. package/library/themes/power-bi/corporate-blue.json +291 -0
  139. package/library/themes/power-bi/dark-mode.json +291 -0
  140. package/library/themes/power-bi/minimal.json +292 -0
  141. package/library/themes/power-bi/print-friendly.json +309 -0
  142. package/package.json +93 -0
  143. package/skills/contributions/SKILL.md +267 -0
  144. package/skills/data-model-design/SKILL.md +470 -0
  145. package/skills/data-modeling/SKILL.md +254 -0
  146. package/skills/data-quality/SKILL.md +664 -0
  147. package/skills/dax/SKILL.md +708 -0
  148. package/skills/dax-doctor/SKILL.md +250 -0
  149. package/skills/dax-udf/SKILL.md +489 -0
  150. package/skills/deployment/SKILL.md +320 -0
  151. package/skills/excel-formulas/SKILL.md +463 -0
  152. package/skills/fabric-scripts/SKILL.md +454 -0
  153. package/skills/fast-standard/SKILL.md +509 -0
  154. package/skills/governance/SKILL.md +205 -0
  155. package/skills/migration-assistant/SKILL.md +292 -0
  156. package/skills/model-documenter/SKILL.md +244 -0
  157. package/skills/pbi-connect/SKILL.md +241 -0
  158. package/skills/power-query/SKILL.md +406 -0
  159. package/skills/project-kickoff/SKILL.md +907 -0
  160. package/skills/query-performance/SKILL.md +480 -0
  161. package/skills/report-design/SKILL.md +207 -0
  162. package/skills/report-layout/SKILL.md +298 -0
  163. package/skills/rls-design/SKILL.md +535 -0
  164. package/skills/semantic-model/SKILL.md +237 -0
  165. package/skills/testing-validation/SKILL.md +643 -0
  166. package/skills/theme-tweaker/SKILL.md +626 -0
  167. package/src/content/base.md +237 -0
  168. package/src/content/mcp-requirements.json +69 -0
  169. package/src/content/routing.md +203 -0
  170. package/src/content/skills/contributions.md +259 -0
  171. package/src/content/skills/data-model-design.md +462 -0
  172. package/src/content/skills/data-modeling.md +246 -0
  173. package/src/content/skills/data-quality.md +656 -0
  174. package/src/content/skills/dax-doctor.md +242 -0
  175. package/src/content/skills/dax-udf.md +481 -0
  176. package/src/content/skills/dax.md +700 -0
  177. package/src/content/skills/deployment.md +312 -0
  178. package/src/content/skills/excel-formulas.md +455 -0
  179. package/src/content/skills/fabric-scripts.md +446 -0
  180. package/src/content/skills/fast-standard.md +501 -0
  181. package/src/content/skills/governance.md +197 -0
  182. package/src/content/skills/migration-assistant.md +284 -0
  183. package/src/content/skills/model-documenter.md +236 -0
  184. package/src/content/skills/pbi-connect.md +233 -0
  185. package/src/content/skills/power-query.md +398 -0
  186. package/src/content/skills/project-kickoff.md +899 -0
  187. package/src/content/skills/query-performance.md +472 -0
  188. package/src/content/skills/report-design.md +199 -0
  189. package/src/content/skills/report-layout.md +290 -0
  190. package/src/content/skills/rls-design.md +527 -0
  191. package/src/content/skills/semantic-model.md +229 -0
  192. package/src/content/skills/testing-validation.md +635 -0
  193. 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
+ };