@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,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Loading Utilities
|
|
3
|
+
* =======================
|
|
4
|
+
*
|
|
5
|
+
* Centralizes how BI Agent Superpowers loads skill source files.
|
|
6
|
+
* During local development we prefer the repository sources, while
|
|
7
|
+
* installed package usage can fall back to the premium content cache.
|
|
8
|
+
*
|
|
9
|
+
* @module lib/skills
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Read all markdown skills from a directory.
|
|
17
|
+
*
|
|
18
|
+
* @param {string} directory - Directory containing skill markdown files
|
|
19
|
+
* @returns {Array<{name: string, path: string, content: string}>}
|
|
20
|
+
*/
|
|
21
|
+
function readSkillDirectory(directory) {
|
|
22
|
+
if (!directory || !fs.existsSync(directory)) {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return fs
|
|
27
|
+
.readdirSync(directory)
|
|
28
|
+
.filter((file) => file.endsWith('.md'))
|
|
29
|
+
.sort()
|
|
30
|
+
.map((file) => {
|
|
31
|
+
const filePath = path.join(directory, file);
|
|
32
|
+
return {
|
|
33
|
+
name: file.replace(/\.md$/, ''),
|
|
34
|
+
path: filePath,
|
|
35
|
+
content: fs.readFileSync(filePath, 'utf8'),
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Resolve candidate skill directories in preferred order.
|
|
42
|
+
*
|
|
43
|
+
* @param {Object} options - Resolution options
|
|
44
|
+
* @param {string} options.packageDir - Package installation directory
|
|
45
|
+
* @param {string} [options.contentCacheDir] - Premium content cache directory
|
|
46
|
+
* @param {boolean} [options.preferLocal] - Prefer repository-local sources first
|
|
47
|
+
* @returns {string[]} Ordered list of candidate directories
|
|
48
|
+
*/
|
|
49
|
+
function getSkillDirectories(options = {}) {
|
|
50
|
+
const { packageDir, contentCacheDir, preferLocal = false } = options;
|
|
51
|
+
const localDir = packageDir ? path.join(packageDir, 'src', 'content', 'skills') : null;
|
|
52
|
+
const cacheDir = contentCacheDir ? path.join(contentCacheDir, 'src', 'content', 'skills') : null;
|
|
53
|
+
|
|
54
|
+
const ordered = preferLocal ? [localDir, cacheDir] : [cacheDir, localDir];
|
|
55
|
+
return ordered.filter(Boolean);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Load skills from the first directory that contains them.
|
|
60
|
+
*
|
|
61
|
+
* @param {Object} options - Resolution options
|
|
62
|
+
* @param {string} options.packageDir - Package installation directory
|
|
63
|
+
* @param {string} [options.contentCacheDir] - Premium content cache directory
|
|
64
|
+
* @param {boolean} [options.preferLocal] - Prefer repository-local sources first
|
|
65
|
+
* @returns {Array<{name: string, path: string, content: string}>}
|
|
66
|
+
*/
|
|
67
|
+
function loadSkills(options = {}) {
|
|
68
|
+
const directories = getSkillDirectories(options);
|
|
69
|
+
|
|
70
|
+
for (const directory of directories) {
|
|
71
|
+
const skills = readSkillDirectory(directory);
|
|
72
|
+
if (skills.length > 0) {
|
|
73
|
+
return skills;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
module.exports = {
|
|
81
|
+
readSkillDirectory,
|
|
82
|
+
getSkillDirectories,
|
|
83
|
+
loadSkills,
|
|
84
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Power BI Modeling MCP Launcher
|
|
5
|
+
* ==============================
|
|
6
|
+
*
|
|
7
|
+
* Wrapper that locates the official Microsoft Power BI Modeling MCP
|
|
8
|
+
* executable and starts it with stdio transport for Claude Code.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const { spawn } = require('child_process');
|
|
12
|
+
const { findLocalMcp, getModelingMcpError } = require('../utils/mcp-detect');
|
|
13
|
+
|
|
14
|
+
const executablePath = findLocalMcp();
|
|
15
|
+
|
|
16
|
+
if (!executablePath) {
|
|
17
|
+
console.error(getModelingMcpError());
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const child = spawn(executablePath, ['--start', ...process.argv.slice(2)], {
|
|
22
|
+
stdio: 'inherit',
|
|
23
|
+
env: process.env,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
child.on('error', (error) => {
|
|
27
|
+
console.error(`Failed to launch Power BI Modeling MCP: ${error.message}`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
child.on('exit', (code, signal) => {
|
|
32
|
+
if (signal) {
|
|
33
|
+
process.kill(process.pid, signal);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
process.exit(code === null ? 1 : code);
|
|
38
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Post-install script for BI Agent Superpowers
|
|
5
|
+
* Shows helpful information after npm install
|
|
6
|
+
* Developed by Lucas Sanchez (@luquimbo)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const VERSION = require('../package.json').version;
|
|
10
|
+
|
|
11
|
+
console.log(`
|
|
12
|
+
╔═══════════════════════════════════════════════════════════╗
|
|
13
|
+
║ ║
|
|
14
|
+
║ BI Agent Superpowers v${VERSION.padEnd(34)}║
|
|
15
|
+
║ AI-powered toolkit for Power BI, Fabric & Excel ║
|
|
16
|
+
║ Developed by Lucas Sanchez (@luquimbo) ║
|
|
17
|
+
║ ║
|
|
18
|
+
╚═══════════════════════════════════════════════════════════╝
|
|
19
|
+
|
|
20
|
+
Installed successfully!
|
|
21
|
+
|
|
22
|
+
Quick Start:
|
|
23
|
+
1. super unlock Activate your license key
|
|
24
|
+
2. super kickoff Configure for your AI tool(s)
|
|
25
|
+
3. super powers List available skills & resources
|
|
26
|
+
|
|
27
|
+
Get your license at: https://acadevor.com/bi-superpowers
|
|
28
|
+
|
|
29
|
+
Works with:
|
|
30
|
+
• Claude Code Native plugin (skills + commands + MCP)
|
|
31
|
+
• 1code.dev Uses Claude Code SDK (full plugin support)
|
|
32
|
+
• Claude Desktop Via MCPB extension (super build-desktop)
|
|
33
|
+
|
|
34
|
+
After activation, you'll have access to 20+ skills including:
|
|
35
|
+
/project-kickoff Analyze and plan BI projects
|
|
36
|
+
/data-model-design Design star schema data models
|
|
37
|
+
/dax-doctor Debug and optimize DAX measures
|
|
38
|
+
/dax DAX writing best practices
|
|
39
|
+
/power-query Power Query patterns
|
|
40
|
+
/report-design Report design and visualization
|
|
41
|
+
... and more!
|
|
42
|
+
|
|
43
|
+
Documentation: https://github.com/luquimbo/bi-superpowers
|
|
44
|
+
`);
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Handling Utilities
|
|
3
|
+
* ========================
|
|
4
|
+
*
|
|
5
|
+
* Standardized error handling for the CLI.
|
|
6
|
+
* Provides consistent error messages and exit codes.
|
|
7
|
+
*
|
|
8
|
+
* @module utils/errors
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Custom CLI Error class
|
|
13
|
+
* Extends Error with exit code and optional suggestion
|
|
14
|
+
*/
|
|
15
|
+
class CLIError extends Error {
|
|
16
|
+
/**
|
|
17
|
+
* Create a CLI error
|
|
18
|
+
* @param {string} message - Error message
|
|
19
|
+
* @param {number} code - Exit code (default: 1)
|
|
20
|
+
* @param {string|null} suggestion - Optional suggestion for fixing the error
|
|
21
|
+
*/
|
|
22
|
+
constructor(message, code = 1, suggestion = null) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.name = 'CLIError';
|
|
25
|
+
this.code = code;
|
|
26
|
+
this.suggestion = suggestion;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Error codes for common scenarios
|
|
32
|
+
*/
|
|
33
|
+
const ErrorCodes = {
|
|
34
|
+
GENERAL: 1,
|
|
35
|
+
INVALID_INPUT: 2,
|
|
36
|
+
FILE_NOT_FOUND: 3,
|
|
37
|
+
PERMISSION_DENIED: 4,
|
|
38
|
+
NETWORK_ERROR: 5,
|
|
39
|
+
CONFIG_ERROR: 6,
|
|
40
|
+
GIT_ERROR: 7,
|
|
41
|
+
VALIDATION_ERROR: 8,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Handle an error with consistent formatting
|
|
46
|
+
* @param {Error} error - The error to handle
|
|
47
|
+
* @param {Object} options - Options for handling
|
|
48
|
+
* @param {boolean} options.exit - Whether to exit the process (default: true)
|
|
49
|
+
* @param {boolean} options.verbose - Show stack trace (default: false)
|
|
50
|
+
*/
|
|
51
|
+
function handleError(error, options = {}) {
|
|
52
|
+
const { exit = true, verbose = process.env.DEBUG === 'true' } = options;
|
|
53
|
+
|
|
54
|
+
if (error instanceof CLIError) {
|
|
55
|
+
console.error(`Error: ${error.message}`);
|
|
56
|
+
if (error.suggestion) {
|
|
57
|
+
console.error(`\nSuggestion: ${error.suggestion}`);
|
|
58
|
+
}
|
|
59
|
+
if (exit) {
|
|
60
|
+
process.exit(error.code);
|
|
61
|
+
}
|
|
62
|
+
} else if (error instanceof Error) {
|
|
63
|
+
console.error(`Error: ${error.message}`);
|
|
64
|
+
if (verbose && error.stack) {
|
|
65
|
+
console.error(`\nStack trace:\n${error.stack}`);
|
|
66
|
+
}
|
|
67
|
+
if (exit) {
|
|
68
|
+
process.exit(ErrorCodes.GENERAL);
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
console.error(`Error: ${String(error)}`);
|
|
72
|
+
if (exit) {
|
|
73
|
+
process.exit(ErrorCodes.GENERAL);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Wrap an async function with error handling
|
|
80
|
+
* @param {Function} fn - Async function to wrap
|
|
81
|
+
* @param {Object} options - Error handling options
|
|
82
|
+
* @returns {Function} Wrapped function
|
|
83
|
+
*/
|
|
84
|
+
function withErrorHandling(fn, options = {}) {
|
|
85
|
+
return async (...args) => {
|
|
86
|
+
try {
|
|
87
|
+
return await fn(...args);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
handleError(error, options);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Log a warning without exiting
|
|
96
|
+
* @param {string} message - Warning message
|
|
97
|
+
* @param {Object} context - Additional context (optional)
|
|
98
|
+
*/
|
|
99
|
+
function logWarning(message, context = null) {
|
|
100
|
+
console.warn(`Warning: ${message}`);
|
|
101
|
+
if (context && process.env.DEBUG === 'true') {
|
|
102
|
+
console.warn('Context:', context);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Create a CLIError for common scenarios
|
|
108
|
+
*/
|
|
109
|
+
const createError = {
|
|
110
|
+
fileNotFound: (path) =>
|
|
111
|
+
new CLIError(
|
|
112
|
+
`File not found: ${path}`,
|
|
113
|
+
ErrorCodes.FILE_NOT_FOUND,
|
|
114
|
+
'Check that the file path is correct.'
|
|
115
|
+
),
|
|
116
|
+
|
|
117
|
+
invalidInput: (message, suggestion = null) =>
|
|
118
|
+
new CLIError(message, ErrorCodes.INVALID_INPUT, suggestion),
|
|
119
|
+
|
|
120
|
+
configError: (message) =>
|
|
121
|
+
new CLIError(
|
|
122
|
+
message,
|
|
123
|
+
ErrorCodes.CONFIG_ERROR,
|
|
124
|
+
'Run "bi-superpowers setup" to configure the repository.'
|
|
125
|
+
),
|
|
126
|
+
|
|
127
|
+
gitError: (message) =>
|
|
128
|
+
new CLIError(
|
|
129
|
+
message,
|
|
130
|
+
ErrorCodes.GIT_ERROR,
|
|
131
|
+
'Make sure Git is installed and you are in a Git repository.'
|
|
132
|
+
),
|
|
133
|
+
|
|
134
|
+
networkError: (message) =>
|
|
135
|
+
new CLIError(
|
|
136
|
+
message,
|
|
137
|
+
ErrorCodes.NETWORK_ERROR,
|
|
138
|
+
'Check your internet connection and try again.'
|
|
139
|
+
),
|
|
140
|
+
|
|
141
|
+
permissionDenied: (path) =>
|
|
142
|
+
new CLIError(
|
|
143
|
+
`Permission denied: ${path}`,
|
|
144
|
+
ErrorCodes.PERMISSION_DENIED,
|
|
145
|
+
'Check file permissions.'
|
|
146
|
+
),
|
|
147
|
+
|
|
148
|
+
validationError: (message, suggestion = null) =>
|
|
149
|
+
new CLIError(message, ErrorCodes.VALIDATION_ERROR, suggestion),
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
module.exports = {
|
|
153
|
+
CLIError,
|
|
154
|
+
ErrorCodes,
|
|
155
|
+
handleError,
|
|
156
|
+
withErrorHandling,
|
|
157
|
+
logWarning,
|
|
158
|
+
createError,
|
|
159
|
+
};
|
package/bin/utils/git.js
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git Utilities for BI Agent Superpowers
|
|
3
|
+
* =======================================
|
|
4
|
+
*
|
|
5
|
+
* Provides Git operations for the multi-project repo system.
|
|
6
|
+
* Uses simple-git for robust cross-platform Git operations.
|
|
7
|
+
*
|
|
8
|
+
* @module utils/git
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const { execSync, spawnSync } = require('child_process');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Check if Git is installed on the system
|
|
15
|
+
* @returns {boolean} True if Git is available
|
|
16
|
+
*/
|
|
17
|
+
function isGitInstalled() {
|
|
18
|
+
try {
|
|
19
|
+
execSync('git --version', { stdio: 'pipe' });
|
|
20
|
+
return true;
|
|
21
|
+
} catch (e) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Check if a directory is a Git repository
|
|
28
|
+
* @param {string} dir - Directory path to check
|
|
29
|
+
* @returns {boolean} True if directory is a Git repo
|
|
30
|
+
*/
|
|
31
|
+
function isGitRepo(dir) {
|
|
32
|
+
try {
|
|
33
|
+
const result = spawnSync('git', ['rev-parse', '--git-dir'], {
|
|
34
|
+
cwd: dir,
|
|
35
|
+
stdio: 'pipe',
|
|
36
|
+
});
|
|
37
|
+
return result.status === 0;
|
|
38
|
+
} catch (e) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Initialize a new Git repository
|
|
45
|
+
* @param {string} dir - Directory to initialize
|
|
46
|
+
* @returns {boolean} True if successful
|
|
47
|
+
*/
|
|
48
|
+
function initRepo(dir) {
|
|
49
|
+
try {
|
|
50
|
+
const result = spawnSync('git', ['init'], {
|
|
51
|
+
cwd: dir,
|
|
52
|
+
stdio: 'pipe',
|
|
53
|
+
});
|
|
54
|
+
return result.status === 0;
|
|
55
|
+
} catch (e) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Stage files in Git
|
|
62
|
+
* @param {string} dir - Repository directory
|
|
63
|
+
* @param {string|string[]} files - Files to stage (use '.' for all)
|
|
64
|
+
* @returns {boolean} True if successful
|
|
65
|
+
*/
|
|
66
|
+
function stageFiles(dir, files = '.') {
|
|
67
|
+
try {
|
|
68
|
+
const fileArgs = Array.isArray(files) ? files : [files];
|
|
69
|
+
const result = spawnSync('git', ['add', ...fileArgs], {
|
|
70
|
+
cwd: dir,
|
|
71
|
+
stdio: 'pipe',
|
|
72
|
+
});
|
|
73
|
+
return result.status === 0;
|
|
74
|
+
} catch (e) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Create a Git commit
|
|
81
|
+
* @param {string} dir - Repository directory
|
|
82
|
+
* @param {string} message - Commit message
|
|
83
|
+
* @returns {boolean} True if successful
|
|
84
|
+
*/
|
|
85
|
+
function commit(dir, message) {
|
|
86
|
+
try {
|
|
87
|
+
const result = spawnSync('git', ['commit', '-m', message], {
|
|
88
|
+
cwd: dir,
|
|
89
|
+
stdio: 'pipe',
|
|
90
|
+
});
|
|
91
|
+
return result.status === 0;
|
|
92
|
+
} catch (e) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Get the current branch name
|
|
99
|
+
* @param {string} dir - Repository directory
|
|
100
|
+
* @returns {string|null} Branch name or null if not in a repo
|
|
101
|
+
*/
|
|
102
|
+
function getCurrentBranch(dir) {
|
|
103
|
+
try {
|
|
104
|
+
const result = spawnSync('git', ['branch', '--show-current'], {
|
|
105
|
+
cwd: dir,
|
|
106
|
+
stdio: 'pipe',
|
|
107
|
+
});
|
|
108
|
+
if (result.status === 0) {
|
|
109
|
+
return result.stdout.toString().trim();
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
} catch (e) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get the status of the repository
|
|
119
|
+
* @param {string} dir - Repository directory
|
|
120
|
+
* @returns {Object} Status object with modified, staged, untracked files
|
|
121
|
+
*/
|
|
122
|
+
function getStatus(dir) {
|
|
123
|
+
try {
|
|
124
|
+
const result = spawnSync('git', ['status', '--porcelain'], {
|
|
125
|
+
cwd: dir,
|
|
126
|
+
stdio: 'pipe',
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
if (result.status !== 0) {
|
|
130
|
+
return { modified: [], staged: [], untracked: [] };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const lines = result.stdout.toString().trim().split('\n').filter(Boolean);
|
|
134
|
+
const status = { modified: [], staged: [], untracked: [] };
|
|
135
|
+
|
|
136
|
+
for (const line of lines) {
|
|
137
|
+
const code = line.substring(0, 2);
|
|
138
|
+
const file = line.substring(3);
|
|
139
|
+
|
|
140
|
+
if (code.includes('?')) {
|
|
141
|
+
status.untracked.push(file);
|
|
142
|
+
} else if (code[0] !== ' ') {
|
|
143
|
+
status.staged.push(file);
|
|
144
|
+
} else if (code[1] !== ' ') {
|
|
145
|
+
status.modified.push(file);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return status;
|
|
150
|
+
} catch (e) {
|
|
151
|
+
return { modified: [], staged: [], untracked: [] };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Get commit log
|
|
157
|
+
* @param {string} dir - Repository directory
|
|
158
|
+
* @param {number} count - Number of commits to retrieve
|
|
159
|
+
* @returns {Array} Array of commit objects
|
|
160
|
+
*/
|
|
161
|
+
function getLog(dir, count = 10) {
|
|
162
|
+
try {
|
|
163
|
+
const result = spawnSync(
|
|
164
|
+
'git',
|
|
165
|
+
['log', `--max-count=${count}`, '--pretty=format:%H|%s|%ai|%an'],
|
|
166
|
+
{
|
|
167
|
+
cwd: dir,
|
|
168
|
+
stdio: 'pipe',
|
|
169
|
+
}
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
if (result.status !== 0) {
|
|
173
|
+
return [];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const lines = result.stdout.toString().trim().split('\n').filter(Boolean);
|
|
177
|
+
return lines.map((line) => {
|
|
178
|
+
const [hash, subject, date, author] = line.split('|');
|
|
179
|
+
return { hash, subject, date, author };
|
|
180
|
+
});
|
|
181
|
+
} catch (e) {
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Check if there are uncommitted changes
|
|
188
|
+
* @param {string} dir - Repository directory
|
|
189
|
+
* @returns {boolean} True if there are uncommitted changes
|
|
190
|
+
*/
|
|
191
|
+
function hasUncommittedChanges(dir) {
|
|
192
|
+
const status = getStatus(dir);
|
|
193
|
+
return status.modified.length > 0 || status.staged.length > 0 || status.untracked.length > 0;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Checkout a specific commit or branch
|
|
198
|
+
* @param {string} dir - Repository directory
|
|
199
|
+
* @param {string} ref - Reference to checkout (commit hash, branch, tag)
|
|
200
|
+
* @returns {boolean} True if successful
|
|
201
|
+
*/
|
|
202
|
+
function checkout(dir, ref) {
|
|
203
|
+
try {
|
|
204
|
+
const result = spawnSync('git', ['checkout', ref], {
|
|
205
|
+
cwd: dir,
|
|
206
|
+
stdio: 'pipe',
|
|
207
|
+
});
|
|
208
|
+
return result.status === 0;
|
|
209
|
+
} catch (e) {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Get diff between two commits or current changes
|
|
216
|
+
* @param {string} dir - Repository directory
|
|
217
|
+
* @param {string} from - Starting reference (optional)
|
|
218
|
+
* @param {string} to - Ending reference (optional)
|
|
219
|
+
* @returns {string} Diff output
|
|
220
|
+
*/
|
|
221
|
+
function getDiff(dir, from = null, to = null) {
|
|
222
|
+
try {
|
|
223
|
+
const args = ['diff'];
|
|
224
|
+
if (from) args.push(from);
|
|
225
|
+
if (to) args.push(to);
|
|
226
|
+
|
|
227
|
+
const result = spawnSync('git', args, {
|
|
228
|
+
cwd: dir,
|
|
229
|
+
stdio: 'pipe',
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
return result.stdout.toString();
|
|
233
|
+
} catch (e) {
|
|
234
|
+
return '';
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Execute a raw Git command and return output
|
|
240
|
+
* @param {string} dir - Repository directory
|
|
241
|
+
* @param {string[]} args - Git command arguments
|
|
242
|
+
* @returns {string|null} Command output or null on failure
|
|
243
|
+
*/
|
|
244
|
+
function execGitCommand(dir, args) {
|
|
245
|
+
try {
|
|
246
|
+
const result = spawnSync('git', args, {
|
|
247
|
+
cwd: dir,
|
|
248
|
+
stdio: 'pipe',
|
|
249
|
+
maxBuffer: 1024 * 1024 * 10, // 10MB buffer for large outputs
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
if (result.status === 0) {
|
|
253
|
+
return result.stdout.toString();
|
|
254
|
+
}
|
|
255
|
+
return null;
|
|
256
|
+
} catch (e) {
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Get file modification date from last commit
|
|
263
|
+
* @param {string} dir - Repository directory
|
|
264
|
+
* @param {string} filePath - Path to file relative to repo root
|
|
265
|
+
* @returns {Date|null} Last modification date or null
|
|
266
|
+
*/
|
|
267
|
+
function getFileLastModified(dir, filePath) {
|
|
268
|
+
try {
|
|
269
|
+
const result = spawnSync('git', ['log', '-1', '--format=%ai', '--', filePath], {
|
|
270
|
+
cwd: dir,
|
|
271
|
+
stdio: 'pipe',
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
if (result.status === 0) {
|
|
275
|
+
const dateStr = result.stdout.toString().trim();
|
|
276
|
+
return dateStr ? new Date(dateStr) : null;
|
|
277
|
+
}
|
|
278
|
+
return null;
|
|
279
|
+
} catch (e) {
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
module.exports = {
|
|
285
|
+
isGitInstalled,
|
|
286
|
+
isGitRepo,
|
|
287
|
+
initRepo,
|
|
288
|
+
stageFiles,
|
|
289
|
+
commit,
|
|
290
|
+
getCurrentBranch,
|
|
291
|
+
getStatus,
|
|
292
|
+
getLog,
|
|
293
|
+
hasUncommittedChanges,
|
|
294
|
+
checkout,
|
|
295
|
+
getDiff,
|
|
296
|
+
execGitCommand,
|
|
297
|
+
getFileLastModified,
|
|
298
|
+
};
|