@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
package/bin/cli.js ADDED
@@ -0,0 +1,1064 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * BI Agent Superpowers - Command Line Interface
5
+ * ==============================================
6
+ *
7
+ * This is the main entry point for the BI Agent Superpowers CLI tool.
8
+ * It provides commands to initialize, configure, and manage AI-powered
9
+ * assistance for Power BI, Fabric, and Excel development.
10
+ *
11
+ * Architecture:
12
+ * - Single Source of Truth: Skills are defined ONCE in src/content/skills/
13
+ * and generated for the Claude Code plugin.
14
+ * - Supports: Claude Code, 1code.dev, Claude Desktop (via MCPB).
15
+ *
16
+ * License System:
17
+ * - Premium content requires activation via `bi-superpowers unlock`
18
+ * - License validation happens against https://acadevor.com API
19
+ * - Content is cached locally in ~/.bi-superpowers/
20
+ *
21
+ * @module cli
22
+ * @author Lucas Sanchez (@luquimbo)
23
+ * @license MIT
24
+ */
25
+
26
+ const fs = require('fs');
27
+ const path = require('path');
28
+ const { execSync } = require('child_process');
29
+ const readline = require('readline');
30
+ const os = require('os');
31
+ const { loadSkills } = require('./lib/skills');
32
+
33
+ // Import lib modules (extracted from cli.js for better organization)
34
+ let licensing, generators;
35
+ try {
36
+ licensing = require('./lib/licensing');
37
+ generators = require('./lib/generators');
38
+ } catch (e) {
39
+ // Modules may not be available during npm install phase
40
+ }
41
+
42
+ // Import command modules - these provide the xray, checkup, scan, sentinel, and mcp-setup commands
43
+ // Wrapped in try-catch because they may not be available during initial npm install
44
+ let searchCommand, lintCommand, diffCommand, watchCommand, mcpSetupCommand, tui;
45
+ let setupCommand, addCommand, pullCommand, pushCommand, syncProfileCommand, syncSourceCommand;
46
+ let changelogCommand, buildDesktopCommand;
47
+ try {
48
+ searchCommand = require('./commands/search'); // Fuzzy search across library content
49
+ lintCommand = require('./commands/lint'); // Skill file validation
50
+ diffCommand = require('./commands/diff'); // Compare source vs generated configs
51
+ watchCommand = require('./commands/watch'); // Auto-regenerate on file changes
52
+ mcpSetupCommand = require('./commands/mcp-setup'); // MCP server configuration
53
+ tui = require('./utils/tui'); // Terminal UI helpers (colors, tables, etc.)
54
+
55
+ // Repo multi-project commands (v3)
56
+ setupCommand = require('./commands/setup'); // Onboarding wizard for bi-repo
57
+ addCommand = require('./commands/add'); // Add project to repo
58
+ pullCommand = require('./commands/pull'); // Pull changes from original file
59
+ pushCommand = require('./commands/push'); // Push changes to original file
60
+ syncProfileCommand = require('./commands/sync-profile'); // Sync snippets to profile
61
+ syncSourceCommand = require('./commands/sync-source'); // Bidirectional sync
62
+ changelogCommand = require('./commands/changelog'); // Generate changelog from Git
63
+ buildDesktopCommand = require('./commands/build-desktop'); // Build MCPB for Claude Desktop
64
+ } catch (e) {
65
+ // Silent fail - commands may not be available during npm install phase
66
+ // This is expected behavior, not an error condition
67
+ }
68
+
69
+ // ============================================
70
+ // CONFIGURATION CONSTANTS
71
+ // ============================================
72
+
73
+ /** Package version from package.json */
74
+ const VERSION = require('../package.json').version;
75
+
76
+ /** Dynamic skill/command counts from the plugin generator (avoids hardcoding) */
77
+ let COMMAND_COUNT = 11;
78
+ let TOTAL_SKILL_COUNT = 24;
79
+ try {
80
+ const { COMMAND_SKILLS, REFERENCE_SKILLS } = require('./lib/generators/claude-plugin');
81
+ COMMAND_COUNT = COMMAND_SKILLS.size;
82
+ TOTAL_SKILL_COUNT = COMMAND_SKILLS.size + REFERENCE_SKILLS.size;
83
+ } catch (_) {
84
+ // Fallback to defaults during npm install phase
85
+ }
86
+
87
+ /** Directory where the npm package is installed */
88
+ const PACKAGE_DIR = path.dirname(__dirname);
89
+
90
+ /** Directory for cached premium content (skills, snippets, themes) */
91
+ const CONTENT_CACHE_DIR = path.join(os.homedir(), '.bi-superpowers');
92
+
93
+ // File paths - premium content is downloaded to CONTENT_CACHE_DIR
94
+ /** Directory containing skill definition files (.md) */
95
+ const SKILLS_DIR = path.join(CONTENT_CACHE_DIR, 'src', 'content', 'skills');
96
+
97
+ /** Directory containing code snippets, templates, and themes */
98
+ const LIBRARY_DIR = path.join(CONTENT_CACHE_DIR, 'library');
99
+
100
+ /** Project-local config file name (stores tool selections) */
101
+ const CONFIG_FILE = '.bi-superpowers.json';
102
+
103
+ /** Symlink name created in projects pointing to content cache */
104
+ const SYMLINK_NAME = '.bi-superpowers';
105
+
106
+ /** npm package name for update commands */
107
+ const PACKAGE_NAME = '@luquimbo/bi-superpowers';
108
+ const DEFAULT_TOOLS = ['claude-plugin'];
109
+
110
+ /**
111
+ * AI Tools Configuration - uses generators from lib/generators module
112
+ * Falls back to empty object if module not loaded
113
+ */
114
+ const AI_TOOLS = generators ? generators.AI_TOOLS : {};
115
+
116
+ /**
117
+ * Command Routing Map
118
+ *
119
+ * Maps CLI command names to their handler functions.
120
+ * Supports both branded names (kickoff, recharge) and legacy names (init, sync)
121
+ * for backward compatibility.
122
+ *
123
+ * Command Categories:
124
+ * - Core: Basic info commands (help, version, about, status)
125
+ * - Setup: Project initialization and configuration (kickoff, recharge, unlock, upgrade)
126
+ * - Developer: Advanced tools for content management (xray, checkup, scan, sentinel, powers)
127
+ * - Legacy: Old command names maintained for backward compatibility
128
+ */
129
+ const commands = {
130
+ // Core commands - basic info and status
131
+ help: showHelp,
132
+ version: showVersion,
133
+ about: showInfo,
134
+ status: showLicenseStatus,
135
+
136
+ // Setup & sync - project configuration
137
+ kickoff: initProject, // Interactive project setup wizard
138
+ recharge: syncProject, // Regenerate AI tool configs from skills
139
+ unlock: activateLicense, // Activate license key
140
+ upgrade: updatePackage, // Update to latest npm version
141
+
142
+ // Developer tools - content management
143
+ xray: runSearch, // Fuzzy search library content
144
+ checkup: runLint, // Validate skill files
145
+ scan: runDiff, // Compare source vs generated
146
+ sentinel: runWatch, // Watch and auto-regenerate
147
+ powers: listAgents, // List available skills
148
+ 'mcp-setup': runMcpSetup, // Configure MCP servers for AI tools
149
+ mcp: runMcpSetup, // Alias for mcp-setup
150
+ 'build-desktop': runBuildDesktop, // Build MCPB extension for Claude Desktop
151
+
152
+ // Repo multi-project commands (v3)
153
+ setup: runSetup, // Onboarding wizard - creates bi-repo
154
+ add: runAdd, // Add project to bi-repo
155
+ pull: runPull, // Pull changes from original file to repo
156
+ push: runPush, // Push changes from repo to original file
157
+ 'sync-source': runSyncSource, // Bidirectional sync with original file
158
+ 'sync-profile': runSyncProfile, // Sync snippets to base profile
159
+ changelog: runChangelog, // Generate changelog from Git history
160
+
161
+ // Legacy aliases (backward compatibility with v2.0.x)
162
+ init: initProject,
163
+ sync: syncProject,
164
+ activate: activateLicense,
165
+ update: updatePackage,
166
+ search: runSearch,
167
+ lint: runLint,
168
+ diff: runDiff,
169
+ watch: runWatch,
170
+ list: listAgents,
171
+ info: showInfo,
172
+ };
173
+
174
+ /**
175
+ * Main entry point - parses CLI arguments and routes to appropriate command
176
+ */
177
+ function main() {
178
+ const args = process.argv.slice(2);
179
+ const command = args[0] || 'help';
180
+
181
+ if (commands[command]) {
182
+ commands[command](args.slice(1));
183
+ } else {
184
+ console.error(`Unknown command: ${command}`);
185
+ showHelp();
186
+ process.exit(1);
187
+ }
188
+ }
189
+
190
+ function showHelp() {
191
+ console.log(`
192
+ BI Agent Superpowers v${VERSION}
193
+ ================================
194
+
195
+ Claude Code plugin for Power BI, Fabric & Excel development.
196
+ Works with: Claude Code, 1code.dev, Claude Desktop.
197
+
198
+ Usage:
199
+ super <command> [options]
200
+
201
+ Commands:
202
+ unlock Activate your license key
203
+ kickoff [path] Initialize the Claude Code plugin in your project
204
+ recharge [path] Regenerate the plugin from source skills
205
+ build-desktop Build .mcpb extension for Claude Desktop
206
+ powers List available skills and resources
207
+ xray <query> Search snippets and library content
208
+ checkup [file] Validate skill files
209
+ scan Show changes between source and generated configs
210
+ sentinel Watch for changes and auto-regenerate
211
+ mcp-setup Configure official Microsoft MCP servers
212
+ upgrade Update to latest version
213
+ status Show license status
214
+ about Show installation info
215
+ help Show this help
216
+
217
+ Repo Multi-Proyecto (v3):
218
+ setup Create your bi-repo for version control
219
+ add <file> Add a .pbix/.xlsx project to the repo
220
+ pull [project] Pull changes from original file to repo
221
+ push [project] Push changes from repo to original file
222
+ sync-source Bidirectional sync (detects which is newer)
223
+ sync-profile Sync snippets to a base profile
224
+
225
+ Options:
226
+ --dry-run Preview changes without creating files (kickoff, recharge)
227
+
228
+ Examples:
229
+ super unlock # Activate your license
230
+ super kickoff # Initialize plugin in current directory
231
+ super kickoff ./my-project # Initialize in specific directory
232
+ super kickoff --dry-run # Preview what would be created
233
+ super recharge # Regenerate plugin from source skills
234
+ super build-desktop # Build .mcpb for Claude Desktop
235
+ super xray "YTD" # Search for time intelligence patterns
236
+ super xray --category dax # Search within a category
237
+ super checkup # Validate all skills
238
+ super mcp-setup # Configure official Microsoft MCPs
239
+ super powers # Show available superpowers
240
+ claude --plugin-dir . # Run Claude Code with the plugin
241
+
242
+ Repo Multi-Proyecto:
243
+ super setup # Create your bi-repo (first time)
244
+ super add "Sales.pbix" # Add project to repo
245
+ super pull # Pull changes from original
246
+ super push # Push changes to original
247
+ super sync-source # Auto-detect and sync
248
+ super sync-profile # Save snippets to profile
249
+
250
+ Get your license at: https://acadevor.com/bi-superpowers
251
+ Documentation: https://github.com/luquimbo/bi-superpowers
252
+ `);
253
+ }
254
+
255
+ function showVersion() {
256
+ console.log(`BI Agent Superpowers v${VERSION}`);
257
+ }
258
+
259
+ function showInfo() {
260
+ const skillCount = getSkillFiles().length;
261
+ const license = loadLicense();
262
+ const licenseStatus = license ? `Active (${license.email})` : 'Not activated';
263
+ const contentStatus = isContentInstalled() ? `Installed (${skillCount} skills)` : 'Not installed';
264
+ const aiToolsList = Object.entries(AI_TOOLS)
265
+ .map(([_k, v]) => ` - ${v.name}`)
266
+ .join('\n');
267
+
268
+ console.log(`
269
+ BI Agent Superpowers - Installation Info
270
+ ========================================
271
+
272
+ Version: ${VERSION}
273
+ Author: Lucas Sanchez (@luquimbo)
274
+ Package dir: ${PACKAGE_DIR}
275
+ Content dir: ${CONTENT_CACHE_DIR}
276
+ License: ${licenseStatus}
277
+ Content: ${contentStatus}
278
+
279
+ Architecture: Single Source of Truth
280
+ Skills defined once, generated for the Claude Code plugin
281
+
282
+ Compatible with:
283
+ - Claude Code (plugin)
284
+ - 1code.dev (uses Claude Code SDK)
285
+ - Claude Desktop (via MCPB extension)
286
+
287
+ Plugin Generators:
288
+ ${aiToolsList}
289
+
290
+ GitHub: https://github.com/luquimbo/bi-superpowers
291
+ `);
292
+ }
293
+
294
+ /**
295
+ * Creates a readline interface for interactive CLI prompts
296
+ * @returns {readline.Interface} Readline interface
297
+ */
298
+ function createReadline() {
299
+ return readline.createInterface({
300
+ input: process.stdin,
301
+ output: process.stdout,
302
+ });
303
+ }
304
+
305
+ /**
306
+ * Promisified readline question
307
+ * @param {readline.Interface} rl - Readline interface
308
+ * @param {string} question - Question to display
309
+ * @returns {Promise<string>} User's trimmed answer
310
+ */
311
+ function prompt(rl, question) {
312
+ return new Promise((resolve) => {
313
+ rl.question(question, (answer) => resolve(answer.trim()));
314
+ });
315
+ }
316
+
317
+ // ============================================
318
+ // LICENSE MANAGEMENT
319
+ // ============================================
320
+ // These functions handle license storage, validation, and premium content downloads.
321
+ // Licenses are stored in ~/.bi-superpowers-license as JSON.
322
+
323
+ // License functions - delegated to lib/licensing module
324
+ function loadLicense() {
325
+ return licensing ? licensing.loadLicense() : null;
326
+ }
327
+
328
+ function saveLicense(data) {
329
+ if (licensing) licensing.saveLicense(data);
330
+ }
331
+
332
+ // HTTP/download/extract functions - delegated to lib/licensing module
333
+ function isContentInstalled() {
334
+ return licensing ? licensing.isContentInstalled() : false;
335
+ }
336
+
337
+ async function validateLicense(licenseKey) {
338
+ return licensing
339
+ ? licensing.validateLicense(licenseKey)
340
+ : { valid: false, error: 'Module not loaded' };
341
+ }
342
+
343
+ async function downloadPremiumContent(downloadUrl) {
344
+ return licensing ? licensing.downloadPremiumContent(downloadUrl) : false;
345
+ }
346
+
347
+ /**
348
+ * Show license status command handler
349
+ *
350
+ * Displays current license status including:
351
+ * - Whether a license is activated
352
+ * - Email and activation date if active
353
+ * - Instructions for activation if not active
354
+ */
355
+ function showLicenseStatus() {
356
+ const license = loadLicense();
357
+
358
+ if (!license) {
359
+ console.log(`
360
+ License Status: NOT ACTIVATED
361
+
362
+ Run 'bi-superpowers unlock' to activate your license.
363
+ Get your license at: https://acadevor.com/bi-superpowers
364
+ `);
365
+ return;
366
+ }
367
+
368
+ console.log(`
369
+ License Status: ACTIVE ✓
370
+
371
+ Email: ${license.email}
372
+ License: ${license.license.substring(0, 8)}...
373
+ Activated: ${license.activatedAt || 'Unknown'}
374
+
375
+ Run 'bi-superpowers unlock' to re-activate or change license.
376
+ `);
377
+ }
378
+
379
+ /**
380
+ * Activate license command handler (super unlock)
381
+ *
382
+ * Interactive command that:
383
+ * 1. Prompts user for their license key
384
+ * 2. Validates the key against the API
385
+ * 3. Downloads premium content if validation succeeds
386
+ * 4. Saves the license locally for future sessions
387
+ *
388
+ * This is typically the first command users run after installation.
389
+ */
390
+ async function activateLicense() {
391
+ console.log(`
392
+ BI Agent Superpowers - License Activation
393
+ =========================================
394
+ `);
395
+
396
+ const existingLicense = loadLicense();
397
+ if (existingLicense) {
398
+ console.log(
399
+ `Current license: ${existingLicense.license.substring(0, 8)}... (${existingLicense.email})`
400
+ );
401
+ console.log('');
402
+ }
403
+
404
+ const rl = createReadline();
405
+
406
+ try {
407
+ const licenseKey = await prompt(rl, 'Enter your license key: ');
408
+
409
+ if (!licenseKey) {
410
+ console.log('\nNo license key provided.');
411
+ process.exit(1);
412
+ }
413
+
414
+ console.log('\nValidating license...');
415
+
416
+ const result = await validateLicense(licenseKey);
417
+
418
+ if (!result.valid) {
419
+ console.log(`\n✗ License invalid: ${result.error || 'Unknown error'}`);
420
+ console.log('\nGet your license at: https://acadevor.com/bi-superpowers');
421
+ process.exit(1);
422
+ }
423
+
424
+ console.log('\n✓ License validated!');
425
+ console.log(` Email: ${result.email}`);
426
+ if (result.name) {
427
+ console.log(` Name: ${result.name}`);
428
+ }
429
+
430
+ // Download premium content
431
+ console.log('');
432
+ if (result.downloadUrl) {
433
+ const downloaded = await downloadPremiumContent(result.downloadUrl);
434
+ if (!downloaded) {
435
+ console.log('\n⚠ Content download failed. You can retry with "bi-superpowers unlock"');
436
+ }
437
+ }
438
+
439
+ // Save license
440
+ saveLicense({
441
+ license: licenseKey,
442
+ email: result.email,
443
+ name: result.name,
444
+ activatedAt: new Date().toISOString(),
445
+ contentVersion: VERSION,
446
+ });
447
+
448
+ console.log(`
449
+ ════════════════════════════════════════════════════════════
450
+ License activated successfully!
451
+ ════════════════════════════════════════════════════════════
452
+
453
+ You can now run:
454
+
455
+ super kickoff # Initialize in current project
456
+ super powers # See available skills
457
+
458
+ ════════════════════════════════════════════════════════════
459
+ `);
460
+ } finally {
461
+ rl.close();
462
+ }
463
+ }
464
+
465
+ // requireLicense - delegated to lib/licensing module
466
+ async function requireLicense() {
467
+ return licensing ? licensing.requireLicense(VERSION) : process.exit(1);
468
+ }
469
+
470
+ // ============================================
471
+ // SKILL FILE MANAGEMENT
472
+ // ============================================
473
+ // Functions for reading and parsing skill definition files.
474
+ // Skills are markdown files in src/content/skills/ that define
475
+ // AI assistant behaviors, triggers, and code patterns.
476
+
477
+ /**
478
+ * Get all skill files from the source directory
479
+ *
480
+ * Reads all .md files from the skills directory and returns their metadata.
481
+ * Each skill file contains:
482
+ * - Trigger keywords that activate the skill
483
+ * - Identity/role description for the AI
484
+ * - Mandatory rules and constraints
485
+ * - Code examples and patterns
486
+ *
487
+ * @returns {Array<{name: string, path: string, content: string}>} Array of skill objects
488
+ */
489
+ function getSkillFiles() {
490
+ return loadSkills({
491
+ packageDir: PACKAGE_DIR,
492
+ contentCacheDir: CONTENT_CACHE_DIR,
493
+ });
494
+ }
495
+
496
+ /**
497
+ * Extract skill metadata from markdown content
498
+ * Delegates to generators module if available
499
+ */
500
+ function parseSkillMetadata(content) {
501
+ if (generators && generators.parseSkillMetadata) {
502
+ return generators.parseSkillMetadata(content);
503
+ }
504
+ // Fallback for when module not loaded
505
+ const metadata = { title: '', triggers: [], identity: '' };
506
+ const titleMatch = content.match(/^#\s+(.+)/m);
507
+ if (titleMatch) metadata.title = titleMatch[1];
508
+ return metadata;
509
+ }
510
+
511
+ // ============================================
512
+ // PROJECT INITIALIZATION
513
+ // ============================================
514
+ // Functions for setting up BI Agent Superpowers in a user's project.
515
+ // This involves generating tool-specific config files and saving preferences.
516
+
517
+ /**
518
+ * Initialize project with interactive prompts (super kickoff)
519
+ *
520
+ * This is the main setup wizard that:
521
+ * 1. Validates the user's license
522
+ * 2. Prompts for which AI tools to configure
523
+ * 3. Generates config files for each selected tool
524
+ * 4. Creates a symlink to the content cache for easy access
525
+ * 5. Saves the selected tools to .bi-superpowers.json
526
+ *
527
+ * Supports --dry-run flag to preview changes without writing files.
528
+ *
529
+ * @param {string[]} args - Command line arguments
530
+ * @param {string} [args[0]] - Target directory (defaults to cwd)
531
+ */
532
+ async function initProject(args) {
533
+ const dryRun = hasDryRunFlag(args);
534
+ const cleanArgs = removeDryRunFlag(args);
535
+ const targetDir = cleanArgs[0] ? path.resolve(cleanArgs[0]) : process.cwd();
536
+
537
+ // Require valid license
538
+ await requireLicense();
539
+
540
+ const skills = getSkillFiles();
541
+
542
+ console.log(`
543
+ BI Agent Superpowers v${VERSION}
544
+ ================================
545
+ Claude Code Plugin Setup
546
+
547
+ Initializing in: ${targetDir}
548
+ Skills available: ${skills.length}
549
+ `);
550
+
551
+ // Show dry-run notice
552
+ if (dryRun) {
553
+ if (tui) {
554
+ tui.dryRunNotice();
555
+ } else {
556
+ console.log('══════════════════════════════════════');
557
+ console.log(' DRY RUN MODE');
558
+ console.log(' No files will be created or modified');
559
+ console.log('══════════════════════════════════════\n');
560
+ }
561
+ }
562
+
563
+ const selectedTools = [...DEFAULT_TOOLS];
564
+
565
+ console.log('Plugin output:');
566
+ console.log(' • .claude-plugin/plugin.json');
567
+ console.log(' • .mcp.json');
568
+ console.log(` • commands/ (${COMMAND_COUNT} slash commands)`);
569
+ console.log(` • skills/ (${TOTAL_SKILL_COUNT} SKILL.md files)\n`);
570
+
571
+ if (dryRun) {
572
+ console.log('[DRY RUN] Would create the following files:\n');
573
+ previewGeneration(targetDir, selectedTools, skills);
574
+ previewProjectConfigJson(targetDir);
575
+ console.log('\nRun without --dry-run to apply changes.');
576
+ return;
577
+ }
578
+
579
+ // Save selected tools to config
580
+ saveToolConfig(targetDir, selectedTools);
581
+
582
+ // Ensure project-level config.json exists (used as AI preferences/context)
583
+ ensureProjectConfigJson(targetDir);
584
+
585
+ // Create library symlink used by generated plugin files
586
+ createSymlink(targetDir);
587
+
588
+ // Generate configs for selected tools
589
+ console.log('');
590
+ for (const tool of selectedTools) {
591
+ await generateForTool(tool, targetDir, skills);
592
+ }
593
+
594
+ showCompletionMessage(targetDir, selectedTools, skills.length);
595
+ }
596
+
597
+ /**
598
+ * Sync project - regenerate configs without prompts (super recharge)
599
+ *
600
+ * Regenerates the Claude Code plugin and any configured legacy adapters.
601
+ * Uses the saved tool preferences from .bi-superpowers.json.
602
+ *
603
+ * Use this command after:
604
+ * - Editing skill files in src/content/skills/
605
+ * - Updating the package to a new version
606
+ * - Adding new AI tools to your project
607
+ *
608
+ * Supports --dry-run flag to preview changes without writing files.
609
+ *
610
+ * @param {string[]} args - Command line arguments
611
+ * @param {string} [args[0]] - Target directory (defaults to cwd)
612
+ */
613
+ async function syncProject(args) {
614
+ const dryRun = hasDryRunFlag(args);
615
+ const cleanArgs = removeDryRunFlag(args);
616
+ const targetDir = cleanArgs[0] ? path.resolve(cleanArgs[0]) : process.cwd();
617
+
618
+ // Require valid license
619
+ await requireLicense();
620
+
621
+ const skills = getSkillFiles();
622
+
623
+ console.log(`
624
+ BI Agent Superpowers - Sync
625
+ ===========================
626
+ Regenerating configs from ${skills.length} skills...
627
+ `);
628
+
629
+ // Show dry-run notice
630
+ if (dryRun) {
631
+ if (tui) {
632
+ tui.dryRunNotice();
633
+ } else {
634
+ console.log('══════════════════════════════════════');
635
+ console.log(' DRY RUN MODE');
636
+ console.log(' No files will be created or modified');
637
+ console.log('══════════════════════════════════════\n');
638
+ }
639
+ }
640
+
641
+ // Read saved config or default to plugin only
642
+ const config = loadToolConfig(targetDir);
643
+ const selectedTools = ensurePluginTool(config.tools || [...DEFAULT_TOOLS]);
644
+
645
+ if (dryRun) {
646
+ // Preview what would be regenerated
647
+ console.log('[DRY RUN] Would regenerate the following files:\n');
648
+ previewGeneration(targetDir, selectedTools, skills);
649
+ console.log('\nRun without --dry-run to apply changes.');
650
+ return;
651
+ }
652
+
653
+ for (const tool of selectedTools) {
654
+ await generateForTool(tool, targetDir, skills);
655
+ }
656
+
657
+ console.log(`
658
+ Done! Plugin regenerated for: ${selectedTools.map((t) => AI_TOOLS[t].name).join(', ')}
659
+
660
+ If you modified skills in src/content/skills/,
661
+ your Claude Code plugin is now updated.
662
+
663
+ Tip: Run 'super build-desktop' to rebuild the Claude Desktop extension.
664
+ `);
665
+ }
666
+
667
+ /**
668
+ * Preview what files would be generated (for dry-run mode)
669
+ * Delegates to the generators module
670
+ */
671
+ function previewGeneration(targetDir, tools, skills) {
672
+ if (generators) {
673
+ generators.previewGeneration(targetDir, tools, skills, CONFIG_FILE);
674
+ }
675
+ }
676
+
677
+ /**
678
+ * Save tool configuration to project directory
679
+ * @param {string} targetDir - Project directory path
680
+ * @param {string[]} tools - Array of selected AI tool IDs
681
+ */
682
+ function ensurePluginTool(tools = []) {
683
+ const normalized = Array.from(new Set((tools || []).filter(Boolean)));
684
+ if (!normalized.includes('claude-plugin')) {
685
+ normalized.unshift('claude-plugin');
686
+ }
687
+ return normalized;
688
+ }
689
+
690
+ /**
691
+ * Resolve generation options for a target directory.
692
+ *
693
+ * @param {string} targetDir - Target project directory
694
+ * @returns {Object} Options passed into generators
695
+ */
696
+ function getGenerationOptions(targetDir) {
697
+ const usePluginRootLauncher = path.resolve(targetDir) === PACKAGE_DIR;
698
+ const libraryPrefix = fs.existsSync(path.join(targetDir, 'library'))
699
+ ? 'library'
700
+ : `${SYMLINK_NAME}/library`;
701
+
702
+ return {
703
+ packageDir: PACKAGE_DIR,
704
+ version: VERSION,
705
+ usePluginRootLauncher,
706
+ libraryPrefix,
707
+ };
708
+ }
709
+
710
+ function saveToolConfig(targetDir, tools) {
711
+ const configPath = path.join(targetDir, CONFIG_FILE);
712
+ const normalizedTools = ensurePluginTool(tools);
713
+ const config = {
714
+ tools: normalizedTools,
715
+ plugin: {
716
+ name: 'bi-superpowers',
717
+ enabled: true,
718
+ },
719
+ legacyTools: normalizedTools.filter((tool) => tool !== 'claude-plugin'),
720
+ version: VERSION,
721
+ lastSync: new Date().toISOString(),
722
+ };
723
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
724
+ }
725
+
726
+ /**
727
+ * Preview project-level config.json creation (for dry-run mode)
728
+ * This file is used by AI assistants as preferences/context, not by the CLI runtime.
729
+ * @param {string} targetDir - Project directory path
730
+ */
731
+ function previewProjectConfigJson(targetDir) {
732
+ const projectConfigPath = path.join(targetDir, 'config.json');
733
+ if (fs.existsSync(projectConfigPath)) {
734
+ return;
735
+ }
736
+
737
+ if (tui) {
738
+ tui.info(`Would create: ${tui.formatPath(projectConfigPath)}`);
739
+ } else {
740
+ console.log(`[DRY RUN] Would create: ${projectConfigPath}`);
741
+ }
742
+ }
743
+
744
+ /**
745
+ * Ensure project-level config.json exists
746
+ * Copies the package template config.json into the project directory if missing.
747
+ * @param {string} targetDir - Project directory path
748
+ * @returns {{created: boolean, path: string, reason?: string, error?: string}}
749
+ */
750
+ function ensureProjectConfigJson(targetDir) {
751
+ const projectConfigPath = path.join(targetDir, 'config.json');
752
+ if (fs.existsSync(projectConfigPath)) {
753
+ return { created: false, path: projectConfigPath, reason: 'already-exists' };
754
+ }
755
+
756
+ const templatePath = path.join(PACKAGE_DIR, 'config.json');
757
+ if (!fs.existsSync(templatePath)) {
758
+ if (process.env.DEBUG === 'true') {
759
+ console.error(`[DEBUG] Missing config.json template at: ${templatePath}`);
760
+ }
761
+ return { created: false, path: projectConfigPath, reason: 'template-missing' };
762
+ }
763
+
764
+ try {
765
+ fs.copyFileSync(templatePath, projectConfigPath);
766
+ return { created: true, path: projectConfigPath };
767
+ } catch (e) {
768
+ if (process.env.DEBUG === 'true') {
769
+ console.error(`[DEBUG] Failed to create config.json: ${e.message}`);
770
+ }
771
+ return { created: false, path: projectConfigPath, reason: 'copy-failed', error: e.message };
772
+ }
773
+ }
774
+
775
+ /**
776
+ * Load tool configuration from project directory
777
+ * @param {string} targetDir - Project directory path
778
+ * @returns {Object} Configuration object or empty object if not found
779
+ */
780
+ function loadToolConfig(targetDir) {
781
+ const configPath = path.join(targetDir, CONFIG_FILE);
782
+ if (fs.existsSync(configPath)) {
783
+ try {
784
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
785
+ config.tools = ensurePluginTool(config.tools || []);
786
+ config.legacyTools = config.tools.filter((tool) => tool !== 'claude-plugin');
787
+ if (!config.plugin) {
788
+ config.plugin = {
789
+ name: 'bi-superpowers',
790
+ enabled: true,
791
+ };
792
+ }
793
+ return config;
794
+ } catch (e) {
795
+ return {};
796
+ }
797
+ }
798
+ return {
799
+ tools: [...DEFAULT_TOOLS],
800
+ legacyTools: [],
801
+ plugin: {
802
+ name: 'bi-superpowers',
803
+ enabled: true,
804
+ },
805
+ };
806
+ }
807
+
808
+ /**
809
+ * Generate config for a specific AI tool
810
+ * Delegates to the tool-specific generator from lib/generators module
811
+ */
812
+ async function generateForTool(tool, targetDir, skills) {
813
+ if (generators) {
814
+ await generators.generateForTool(tool, targetDir, skills, getGenerationOptions(targetDir));
815
+ }
816
+ }
817
+
818
+ /**
819
+ * Create symlink to content cache directory for easy access to library
820
+ */
821
+ function createSymlink(targetDir) {
822
+ if (generators) {
823
+ generators.createSymlink(targetDir, CONTENT_CACHE_DIR, SYMLINK_NAME);
824
+ }
825
+ }
826
+
827
+ function showCompletionMessage(targetDir, tools, skillCount) {
828
+ console.log(`
829
+ ════════════════════════════════════════════════════════════
830
+ BI Agent Superpowers configured successfully!
831
+ ════════════════════════════════════════════════════════════
832
+
833
+ Skills: ${skillCount} skills generated
834
+ Library: ${LIBRARY_DIR}
835
+
836
+ ────────────────────────────────────────────────────────────
837
+ CLAUDE CODE / 1CODE.DEV
838
+ ────────────────────────────────────────────────────────────
839
+
840
+ Plugin files:
841
+ ${path.join(targetDir, '.claude-plugin', 'plugin.json')}
842
+ ${path.join(targetDir, '.mcp.json')}
843
+ ${path.join(targetDir, 'commands')}/ (${COMMAND_COUNT} slash commands)
844
+ ${path.join(targetDir, 'skills')}/ (${skillCount} SKILL.md)
845
+
846
+ To run with Claude Code:
847
+
848
+ claude --plugin-dir ${targetDir}
849
+
850
+ ────────────────────────────────────────────────────────────
851
+ CLAUDE DESKTOP
852
+ ────────────────────────────────────────────────────────────
853
+
854
+ To build the Desktop extension:
855
+
856
+ super build-desktop
857
+
858
+ Then double-click bi-superpowers.mcpb to install.
859
+
860
+ ────────────────────────────────────────────────────────────
861
+
862
+ Regenerate after changes: super recharge
863
+ Free models (OpenRouter): See docs/openrouter-free-models.md
864
+
865
+ ════════════════════════════════════════════════════════════
866
+ `);
867
+ }
868
+
869
+ /**
870
+ * Update the package to the latest version using detected package manager
871
+ */
872
+ function updatePackage() {
873
+ console.log(`Updating ${PACKAGE_NAME}...\n`);
874
+
875
+ try {
876
+ const userAgent = process.env.npm_config_user_agent || '';
877
+ let pm = 'npm';
878
+ if (userAgent.includes('pnpm')) pm = 'pnpm';
879
+ else if (userAgent.includes('yarn')) pm = 'yarn';
880
+ else if (userAgent.includes('bun')) pm = 'bun';
881
+
882
+ const cmd =
883
+ pm === 'yarn'
884
+ ? `yarn global add ${PACKAGE_NAME}@latest`
885
+ : `${pm} install -g ${PACKAGE_NAME}@latest`;
886
+
887
+ console.log(`Running: ${cmd}\n`);
888
+ execSync(cmd, { stdio: 'inherit' });
889
+ console.log('\n✓ Update complete!');
890
+ } catch (error) {
891
+ console.error('Update failed:', error.message);
892
+ console.log(`\nTry manually: npm install -g ${PACKAGE_NAME}@latest`);
893
+ process.exit(1);
894
+ }
895
+ }
896
+
897
+ function listAgents() {
898
+ const skills = getSkillFiles();
899
+ const license = loadLicense();
900
+ const contentInstalled = isContentInstalled();
901
+
902
+ console.log(`
903
+ BI Agent Superpowers - Skills & Resources
904
+ =========================================
905
+ Single Source of Truth: ${skills.length} skills
906
+ License: ${license ? `Active (${license.email})` : 'Not activated'}
907
+ Content: ${contentInstalled ? 'Installed' : 'Not installed (run "bi-superpowers unlock")'}
908
+ `);
909
+
910
+ if (!contentInstalled) {
911
+ console.log('⚠ Premium content not installed. Run "bi-superpowers unlock" first.\n');
912
+ return;
913
+ }
914
+
915
+ console.log('Skills (source content for the plugin and optional adapters):');
916
+ for (const skill of skills) {
917
+ const meta = parseSkillMetadata(skill.content);
918
+ console.log(` /${skill.name.padEnd(20)} ${meta.title || ''}`);
919
+ }
920
+ console.log('');
921
+
922
+ // List snippets
923
+ const snippetsDir = path.join(LIBRARY_DIR, 'snippets');
924
+ if (fs.existsSync(snippetsDir)) {
925
+ console.log('Snippets:');
926
+ fs.readdirSync(snippetsDir).forEach((category) => {
927
+ const categoryPath = path.join(snippetsDir, category);
928
+ try {
929
+ if (fs.statSync(categoryPath).isDirectory()) {
930
+ const count = fs.readdirSync(categoryPath).filter((f) => f.endsWith('.md')).length;
931
+ console.log(` 📝 ${category} (${count} patterns)`);
932
+ }
933
+ } catch (e) {
934
+ // Skip directories that can't be read
935
+ }
936
+ });
937
+ console.log('');
938
+ }
939
+
940
+ // List themes
941
+ const themesDir = path.join(LIBRARY_DIR, 'themes', 'power-bi');
942
+ if (fs.existsSync(themesDir)) {
943
+ console.log('Themes:');
944
+ fs.readdirSync(themesDir).forEach((file) => {
945
+ if (file.endsWith('.json')) {
946
+ console.log(` 🎨 ${file.replace('.json', '')}`);
947
+ }
948
+ });
949
+ console.log('');
950
+ }
951
+
952
+ console.log('Supported AI Tools:');
953
+ Object.entries(AI_TOOLS).forEach(([, config]) => {
954
+ console.log(` • ${config.name}`);
955
+ });
956
+ console.log('');
957
+
958
+ console.log("Run 'bi-superpowers recharge' to regenerate configs after editing skills.");
959
+ }
960
+
961
+ // ============================================
962
+ // COMMAND WRAPPERS
963
+ // ============================================
964
+
965
+ /**
966
+ * Get configuration object for commands
967
+ */
968
+ function getCommandConfig() {
969
+ return {
970
+ skillsDir: SKILLS_DIR,
971
+ libraryDir: LIBRARY_DIR,
972
+ contentCacheDir: CONTENT_CACHE_DIR,
973
+ packageDir: PACKAGE_DIR,
974
+ version: VERSION,
975
+ };
976
+ }
977
+
978
+ /**
979
+ * Create a command wrapper with error handling
980
+ * Factory pattern to reduce boilerplate in command handlers
981
+ *
982
+ * @param {Function|null} commandModule - The command module function
983
+ * @param {string} commandName - Display name for error messages
984
+ * @returns {Function} Wrapped command handler
985
+ */
986
+ function createCommandWrapper(commandModule, commandName) {
987
+ return (args) => {
988
+ if (!commandModule) {
989
+ console.error(`${commandName} command not available. Try reinstalling the package.`);
990
+ process.exit(1);
991
+ }
992
+ commandModule(args, getCommandConfig());
993
+ };
994
+ }
995
+
996
+ // Command wrappers using factory pattern
997
+ const runSearch = createCommandWrapper(searchCommand, 'Search');
998
+ const runLint = createCommandWrapper(lintCommand, 'Lint');
999
+ const runDiff = createCommandWrapper(diffCommand, 'Diff');
1000
+ const runMcpSetup = createCommandWrapper(mcpSetupCommand, 'MCP setup');
1001
+ const runSetup = createCommandWrapper(setupCommand, 'Setup');
1002
+ const runAdd = createCommandWrapper(addCommand, 'Add');
1003
+ const runPull = createCommandWrapper(pullCommand, 'Pull');
1004
+ const runPush = createCommandWrapper(pushCommand, 'Push');
1005
+ const runSyncSource = createCommandWrapper(syncSourceCommand, 'Sync-source');
1006
+ const runSyncProfile = createCommandWrapper(syncProfileCommand, 'Sync-profile');
1007
+ const runChangelog = createCommandWrapper(changelogCommand, 'Changelog');
1008
+ const runBuildDesktop = createCommandWrapper(buildDesktopCommand, 'Build Desktop');
1009
+
1010
+ /**
1011
+ * Run watch command (special case - needs additional context)
1012
+ */
1013
+ function runWatch(args) {
1014
+ if (!watchCommand) {
1015
+ console.error('Watch command not available. Try reinstalling the package.');
1016
+ process.exit(1);
1017
+ }
1018
+
1019
+ // Create a reference to sync function for watch to use
1020
+ const cliModule = {
1021
+ syncProjectInternal: (targetDir, tools) => {
1022
+ const skills = getSkillFiles();
1023
+ for (const tool of tools) {
1024
+ const config = AI_TOOLS[tool];
1025
+ if (config && config.generate) {
1026
+ config.generate(targetDir, skills, { packageDir: PACKAGE_DIR });
1027
+ }
1028
+ }
1029
+ },
1030
+ };
1031
+
1032
+ watchCommand(args, getCommandConfig(), cliModule);
1033
+ }
1034
+
1035
+ /**
1036
+ * Check if --dry-run flag is present in args
1037
+ */
1038
+ function hasDryRunFlag(args) {
1039
+ return args.includes('--dry-run') || args.includes('-n');
1040
+ }
1041
+
1042
+ /**
1043
+ * Remove --dry-run flag from args
1044
+ */
1045
+ function removeDryRunFlag(args) {
1046
+ return args.filter((a) => a !== '--dry-run' && a !== '-n');
1047
+ }
1048
+
1049
+ // ============================================
1050
+ // EXPORT FOR INTERNAL USE
1051
+ // ============================================
1052
+
1053
+ module.exports = {
1054
+ getSkillFiles,
1055
+ loadToolConfig,
1056
+ saveToolConfig,
1057
+ AI_TOOLS,
1058
+ SKILLS_DIR,
1059
+ LIBRARY_DIR,
1060
+ CONTENT_CACHE_DIR,
1061
+ VERSION,
1062
+ };
1063
+
1064
+ main();