@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,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
+ };
@@ -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
+ };