@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
package/bin/utils/tui.js
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI Utilities - Rich terminal UI helpers
|
|
3
|
+
* =========================================
|
|
4
|
+
* Provides consistent styling and formatting for CLI output.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const chalk = require('chalk');
|
|
8
|
+
const boxen = require('boxen');
|
|
9
|
+
const Table = require('cli-table3');
|
|
10
|
+
|
|
11
|
+
// Color scheme
|
|
12
|
+
const colors = {
|
|
13
|
+
primary: chalk.cyan,
|
|
14
|
+
success: chalk.green,
|
|
15
|
+
error: chalk.red,
|
|
16
|
+
warning: chalk.yellow,
|
|
17
|
+
info: chalk.blue,
|
|
18
|
+
muted: chalk.gray,
|
|
19
|
+
highlight: chalk.bold.white,
|
|
20
|
+
code: chalk.magenta,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// Icons
|
|
24
|
+
const icons = {
|
|
25
|
+
success: chalk.green('✓'),
|
|
26
|
+
error: chalk.red('✗'),
|
|
27
|
+
warning: chalk.yellow('⚠'),
|
|
28
|
+
info: chalk.blue('ℹ'),
|
|
29
|
+
bullet: chalk.cyan('•'),
|
|
30
|
+
arrow: chalk.cyan('→'),
|
|
31
|
+
skill: '📚',
|
|
32
|
+
snippet: '📝',
|
|
33
|
+
theme: '🎨',
|
|
34
|
+
search: '🔍',
|
|
35
|
+
lint: '🔬',
|
|
36
|
+
diff: '📊',
|
|
37
|
+
watch: '👁',
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Print a success message
|
|
42
|
+
* @param {string} message - Message to display
|
|
43
|
+
*/
|
|
44
|
+
function success(message) {
|
|
45
|
+
console.log(`${icons.success} ${message}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Print an error message
|
|
50
|
+
* @param {string} message - Message to display
|
|
51
|
+
*/
|
|
52
|
+
function error(message) {
|
|
53
|
+
console.log(`${icons.error} ${colors.error(message)}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Print a warning message
|
|
58
|
+
* @param {string} message - Message to display
|
|
59
|
+
*/
|
|
60
|
+
function warning(message) {
|
|
61
|
+
console.log(`${icons.warning} ${colors.warning(message)}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Print an info message
|
|
66
|
+
* @param {string} message - Message to display
|
|
67
|
+
*/
|
|
68
|
+
function info(message) {
|
|
69
|
+
console.log(`${icons.info} ${colors.info(message)}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Print a muted message
|
|
74
|
+
* @param {string} message - Message to display
|
|
75
|
+
*/
|
|
76
|
+
function muted(message) {
|
|
77
|
+
console.log(colors.muted(message));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Print a header box
|
|
82
|
+
* @param {string} title - Title text
|
|
83
|
+
* @param {string} [subtitle] - Optional subtitle
|
|
84
|
+
*/
|
|
85
|
+
function header(title, subtitle) {
|
|
86
|
+
const content = subtitle
|
|
87
|
+
? `${colors.highlight(title)}\n${colors.muted(subtitle)}`
|
|
88
|
+
: colors.highlight(title);
|
|
89
|
+
|
|
90
|
+
console.log(
|
|
91
|
+
boxen(content, {
|
|
92
|
+
padding: 1,
|
|
93
|
+
margin: { top: 1, bottom: 1 },
|
|
94
|
+
borderStyle: 'round',
|
|
95
|
+
borderColor: 'cyan',
|
|
96
|
+
})
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Print a section header
|
|
102
|
+
* @param {string} title - Section title
|
|
103
|
+
*/
|
|
104
|
+
function section(title) {
|
|
105
|
+
console.log(`\n${colors.primary(title)}`);
|
|
106
|
+
console.log(colors.muted('─'.repeat(title.length + 4)));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Create a table for displaying data
|
|
111
|
+
* @param {string[]} headers - Table headers
|
|
112
|
+
* @param {string[][]} rows - Table rows
|
|
113
|
+
* @param {Object} [options] - Additional options
|
|
114
|
+
* @returns {string} Formatted table string
|
|
115
|
+
*/
|
|
116
|
+
function createTable(headers, rows, options = {}) {
|
|
117
|
+
const table = new Table({
|
|
118
|
+
head: headers.map((h) => colors.primary(h)),
|
|
119
|
+
style: {
|
|
120
|
+
head: [],
|
|
121
|
+
border: ['gray'],
|
|
122
|
+
},
|
|
123
|
+
...options,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
rows.forEach((row) => table.push(row));
|
|
127
|
+
return table.toString();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Format a file path for display
|
|
132
|
+
* @param {string} filePath - File path to format
|
|
133
|
+
* @returns {string} Formatted path
|
|
134
|
+
*/
|
|
135
|
+
function formatPath(filePath) {
|
|
136
|
+
return colors.code(filePath);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Format a code snippet for display
|
|
141
|
+
* @param {string} code - Code to format
|
|
142
|
+
* @param {string} [language] - Optional language hint
|
|
143
|
+
* @returns {string} Formatted code
|
|
144
|
+
*/
|
|
145
|
+
function formatCode(code, language) {
|
|
146
|
+
const prefix = language ? colors.muted(`[${language}] `) : '';
|
|
147
|
+
return `${prefix}${colors.code(code)}`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Print a key-value pair
|
|
152
|
+
* @param {string} key - Key/label
|
|
153
|
+
* @param {string} value - Value
|
|
154
|
+
*/
|
|
155
|
+
function keyValue(key, value) {
|
|
156
|
+
console.log(` ${colors.muted(key + ':')} ${value}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Print a list item
|
|
161
|
+
* @param {string} text - Item text
|
|
162
|
+
* @param {number} [indent=0] - Indentation level
|
|
163
|
+
*/
|
|
164
|
+
function listItem(text, indent = 0) {
|
|
165
|
+
const padding = ' '.repeat(indent);
|
|
166
|
+
console.log(`${padding}${icons.bullet} ${text}`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Print a diff line (addition)
|
|
171
|
+
* @param {string} text - Line text
|
|
172
|
+
*/
|
|
173
|
+
function diffAdd(text) {
|
|
174
|
+
console.log(colors.success(`+ ${text}`));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Print a diff line (deletion)
|
|
179
|
+
* @param {string} text - Line text
|
|
180
|
+
*/
|
|
181
|
+
function diffRemove(text) {
|
|
182
|
+
console.log(colors.error(`- ${text}`));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Print a diff line (unchanged)
|
|
187
|
+
* @param {string} text - Line text
|
|
188
|
+
*/
|
|
189
|
+
function diffUnchanged(text) {
|
|
190
|
+
console.log(colors.muted(` ${text}`));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Print search result
|
|
195
|
+
* @param {Object} result - Search result object
|
|
196
|
+
* @param {string} result.title - Result title
|
|
197
|
+
* @param {string} result.path - File path
|
|
198
|
+
* @param {string} result.preview - Preview text
|
|
199
|
+
* @param {number} result.score - Match score
|
|
200
|
+
*/
|
|
201
|
+
function searchResult(result) {
|
|
202
|
+
console.log(`\n${icons.search} ${colors.highlight(result.title)}`);
|
|
203
|
+
console.log(` ${colors.muted(result.path)}`);
|
|
204
|
+
if (result.preview) {
|
|
205
|
+
console.log(` ${colors.muted(result.preview.substring(0, 100))}...`);
|
|
206
|
+
}
|
|
207
|
+
if (result.score !== undefined) {
|
|
208
|
+
console.log(` ${colors.muted('Score:')} ${result.score.toFixed(2)}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Print lint result
|
|
214
|
+
* @param {Object} result - Lint result
|
|
215
|
+
* @param {string} result.file - File name
|
|
216
|
+
* @param {Object[]} result.errors - Array of errors
|
|
217
|
+
* @param {Object[]} result.warnings - Array of warnings
|
|
218
|
+
*/
|
|
219
|
+
function lintResult(result) {
|
|
220
|
+
const hasIssues = result.errors.length > 0 || result.warnings.length > 0;
|
|
221
|
+
|
|
222
|
+
if (!hasIssues) {
|
|
223
|
+
console.log(`${icons.success} ${formatPath(result.file)}`);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
console.log(`\n${formatPath(result.file)}`);
|
|
228
|
+
|
|
229
|
+
result.errors.forEach((err) => {
|
|
230
|
+
console.log(` ${icons.error} ${colors.error(err.message)}`);
|
|
231
|
+
if (err.line) {
|
|
232
|
+
console.log(` ${colors.muted(`Line ${err.line}`)}`);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
result.warnings.forEach((warn) => {
|
|
237
|
+
console.log(` ${icons.warning} ${colors.warning(warn.message)}`);
|
|
238
|
+
if (warn.line) {
|
|
239
|
+
console.log(` ${colors.muted(`Line ${warn.line}`)}`);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Print a summary box
|
|
246
|
+
* @param {Object} summary - Summary data
|
|
247
|
+
* @param {string} summary.title - Summary title
|
|
248
|
+
* @param {Object} summary.stats - Key-value stats
|
|
249
|
+
*/
|
|
250
|
+
function summaryBox(summary) {
|
|
251
|
+
let content = colors.highlight(summary.title) + '\n\n';
|
|
252
|
+
|
|
253
|
+
Object.entries(summary.stats).forEach(([key, value]) => {
|
|
254
|
+
content += `${colors.muted(key + ':')} ${value}\n`;
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
console.log(
|
|
258
|
+
boxen(content.trim(), {
|
|
259
|
+
padding: 1,
|
|
260
|
+
margin: { top: 1, bottom: 1 },
|
|
261
|
+
borderStyle: 'round',
|
|
262
|
+
borderColor: 'green',
|
|
263
|
+
})
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Print dry-run notice
|
|
269
|
+
*/
|
|
270
|
+
function dryRunNotice() {
|
|
271
|
+
console.log(
|
|
272
|
+
boxen(colors.warning('DRY RUN MODE\n') + colors.muted('No files will be created or modified'), {
|
|
273
|
+
padding: 1,
|
|
274
|
+
borderStyle: 'round',
|
|
275
|
+
borderColor: 'yellow',
|
|
276
|
+
})
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Truncate text with ellipsis
|
|
282
|
+
* @param {string} text - Text to truncate
|
|
283
|
+
* @param {number} maxLength - Maximum length
|
|
284
|
+
* @returns {string} Truncated text
|
|
285
|
+
*/
|
|
286
|
+
function truncate(text, maxLength) {
|
|
287
|
+
if (text.length <= maxLength) return text;
|
|
288
|
+
return text.substring(0, maxLength - 3) + '...';
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
module.exports = {
|
|
292
|
+
colors,
|
|
293
|
+
icons,
|
|
294
|
+
success,
|
|
295
|
+
error,
|
|
296
|
+
warning,
|
|
297
|
+
info,
|
|
298
|
+
muted,
|
|
299
|
+
header,
|
|
300
|
+
section,
|
|
301
|
+
createTable,
|
|
302
|
+
formatPath,
|
|
303
|
+
formatCode,
|
|
304
|
+
keyValue,
|
|
305
|
+
listItem,
|
|
306
|
+
diffAdd,
|
|
307
|
+
diffRemove,
|
|
308
|
+
diffUnchanged,
|
|
309
|
+
searchResult,
|
|
310
|
+
lintResult,
|
|
311
|
+
summaryBox,
|
|
312
|
+
dryRunNotice,
|
|
313
|
+
truncate,
|
|
314
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the TUI (Terminal UI) utilities
|
|
3
|
+
*
|
|
4
|
+
* Run with: npm test
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { test, describe } = require('node:test');
|
|
8
|
+
const assert = require('node:assert');
|
|
9
|
+
|
|
10
|
+
// Import TUI module if available
|
|
11
|
+
let tui;
|
|
12
|
+
try {
|
|
13
|
+
tui = require('./tui');
|
|
14
|
+
} catch (e) {
|
|
15
|
+
// TUI module may not be available in all environments
|
|
16
|
+
tui = null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe('TUI Utilities', () => {
|
|
20
|
+
test('truncate should limit string length', () => {
|
|
21
|
+
// Basic truncation logic
|
|
22
|
+
const truncate = (str, maxLen) => {
|
|
23
|
+
if (str.length <= maxLen) return str;
|
|
24
|
+
return str.substring(0, maxLen - 3) + '...';
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const longText = 'This is a very long text that should be truncated';
|
|
28
|
+
const truncated = truncate(longText, 20);
|
|
29
|
+
|
|
30
|
+
assert.ok(truncated.length <= 20, 'Truncated text should not exceed max length');
|
|
31
|
+
assert.ok(truncated.endsWith('...'), 'Truncated text should end with ellipsis');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('formatPath should handle library paths', () => {
|
|
35
|
+
// Basic path formatting logic
|
|
36
|
+
const formatPath = (filePath) => {
|
|
37
|
+
const libraryIndex = filePath.indexOf('library');
|
|
38
|
+
if (libraryIndex !== -1) {
|
|
39
|
+
return filePath.substring(libraryIndex);
|
|
40
|
+
}
|
|
41
|
+
return filePath;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const fullPath = '/Users/test/.bi-superpowers/library/snippets/dax/time-intelligence.md';
|
|
45
|
+
const formatted = formatPath(fullPath);
|
|
46
|
+
|
|
47
|
+
assert.ok(formatted.startsWith('library'), 'Should start with library');
|
|
48
|
+
assert.ok(!formatted.includes('/Users/'), 'Should not include user path');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('icons object should have required icons', () => {
|
|
52
|
+
const requiredIcons = ['success', 'error', 'warning', 'info', 'bullet'];
|
|
53
|
+
|
|
54
|
+
// Mock icons object
|
|
55
|
+
const icons = {
|
|
56
|
+
success: '✓',
|
|
57
|
+
error: '✗',
|
|
58
|
+
warning: '⚠',
|
|
59
|
+
info: 'ℹ',
|
|
60
|
+
bullet: '•',
|
|
61
|
+
watch: '👁',
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
requiredIcons.forEach((icon) => {
|
|
65
|
+
assert.ok(icons[icon], `Should have ${icon} icon`);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('Table Formatting', () => {
|
|
71
|
+
test('table should have headers and rows', () => {
|
|
72
|
+
const headers = ['Metric', 'Count'];
|
|
73
|
+
const rows = [
|
|
74
|
+
['Files checked', '10'],
|
|
75
|
+
['Errors', '0'],
|
|
76
|
+
['Warnings', '2'],
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
assert.strictEqual(headers.length, 2, 'Should have 2 headers');
|
|
80
|
+
assert.strictEqual(rows.length, 3, 'Should have 3 rows');
|
|
81
|
+
assert.strictEqual(rows[0].length, headers.length, 'Rows should match header count');
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('Color Functions', () => {
|
|
86
|
+
test('color names should be defined', () => {
|
|
87
|
+
const colorNames = ['primary', 'success', 'error', 'warning', 'info', 'muted', 'highlight'];
|
|
88
|
+
|
|
89
|
+
// Mock colors object
|
|
90
|
+
const colors = {
|
|
91
|
+
primary: (s) => s,
|
|
92
|
+
success: (s) => s,
|
|
93
|
+
error: (s) => s,
|
|
94
|
+
warning: (s) => s,
|
|
95
|
+
info: (s) => s,
|
|
96
|
+
muted: (s) => s,
|
|
97
|
+
highlight: (s) => s,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
colorNames.forEach((color) => {
|
|
101
|
+
assert.ok(typeof colors[color] === 'function', `${color} should be a function`);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('color functions should return strings', () => {
|
|
106
|
+
// Mock color function
|
|
107
|
+
const colorFn = (str) => `\x1b[32m${str}\x1b[0m`;
|
|
108
|
+
const result = colorFn('test');
|
|
109
|
+
|
|
110
|
+
assert.strictEqual(typeof result, 'string', 'Color function should return string');
|
|
111
|
+
assert.ok(result.includes('test'), 'Result should include original text');
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('Diff Display', () => {
|
|
116
|
+
test('diff add should format additions', () => {
|
|
117
|
+
const formatAdd = (line) => `+ ${line}`;
|
|
118
|
+
const result = formatAdd('new line');
|
|
119
|
+
assert.ok(result.startsWith('+'), 'Addition should start with +');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test('diff remove should format deletions', () => {
|
|
123
|
+
const formatRemove = (line) => `- ${line}`;
|
|
124
|
+
const result = formatRemove('old line');
|
|
125
|
+
assert.ok(result.startsWith('-'), 'Deletion should start with -');
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Contribution validation"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<!-- Generated by BI Agent Superpowers. Edit src/content/skills/contributions.md instead. -->
|
|
6
|
+
|
|
7
|
+
# Contributions Validation Skill
|
|
8
|
+
|
|
9
|
+
## Trigger
|
|
10
|
+
Activate this skill when:
|
|
11
|
+
- Reviewing a pull request or contribution
|
|
12
|
+
- User mentions: "review contribution", "validate PR", "check submission"
|
|
13
|
+
- User mentions: "contribution guidelines", "review checklist"
|
|
14
|
+
- Evaluating code quality for DAX, Power Query, Excel, or themes
|
|
15
|
+
|
|
16
|
+
## Identity
|
|
17
|
+
You are a **Contribution Reviewer** who validates community submissions against BI Agent Superpowers quality standards. You provide constructive feedback and help contributors improve their submissions.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Review Checklist
|
|
22
|
+
|
|
23
|
+
### General Quality
|
|
24
|
+
- [ ] Follows naming conventions for the content type
|
|
25
|
+
- [ ] Includes clear comments/documentation
|
|
26
|
+
- [ ] No hardcoded sensitive data (passwords, API keys, server names)
|
|
27
|
+
- [ ] Tested and verified working
|
|
28
|
+
- [ ] No duplicate of existing content
|
|
29
|
+
|
|
30
|
+
### Documentation Requirements
|
|
31
|
+
- [ ] Clear description of purpose/use case
|
|
32
|
+
- [ ] Usage examples provided
|
|
33
|
+
- [ ] Parameters/inputs documented
|
|
34
|
+
- [ ] Expected output described
|
|
35
|
+
- [ ] Edge cases mentioned (if applicable)
|
|
36
|
+
|
|
37
|
+
### File Structure
|
|
38
|
+
- [ ] Placed in correct folder
|
|
39
|
+
- [ ] File naming follows conventions
|
|
40
|
+
- [ ] Markdown formatting is correct
|
|
41
|
+
- [ ] Code blocks have proper language tags
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Content-Specific Criteria
|
|
46
|
+
|
|
47
|
+
### DAX Snippets
|
|
48
|
+
```
|
|
49
|
+
Location: library/snippets/dax/
|
|
50
|
+
Naming: kebab-case.md (e.g., time-intelligence.md)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Required sections:**
|
|
54
|
+
- Title and description
|
|
55
|
+
- Basic pattern with explanation
|
|
56
|
+
- Variations/advanced usage
|
|
57
|
+
- When to use / when not to use
|
|
58
|
+
|
|
59
|
+
**Quality checks:**
|
|
60
|
+
- [ ] Uses VAR/RETURN pattern
|
|
61
|
+
- [ ] Uses DIVIDE() instead of division operator
|
|
62
|
+
- [ ] No hardcoded values (dates, numbers)
|
|
63
|
+
- [ ] Follows naming conventions (measures: PascalCase, variables: _PascalCase)
|
|
64
|
+
- [ ] Comments explain non-obvious logic
|
|
65
|
+
|
|
66
|
+
### Power Query Snippets
|
|
67
|
+
```
|
|
68
|
+
Location: library/snippets/power-query/
|
|
69
|
+
Naming: kebab-case.md
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Required sections:**
|
|
73
|
+
- Title and description
|
|
74
|
+
- M code with step-by-step explanation
|
|
75
|
+
- Parameters (if applicable)
|
|
76
|
+
- Performance notes (query folding)
|
|
77
|
+
|
|
78
|
+
**Quality checks:**
|
|
79
|
+
- [ ] Query folding preserved where possible
|
|
80
|
+
- [ ] Error handling included
|
|
81
|
+
- [ ] Uses parameters instead of hardcoded values
|
|
82
|
+
- [ ] Step names are descriptive
|
|
83
|
+
- [ ] Follows naming conventions (queries, parameters, functions)
|
|
84
|
+
|
|
85
|
+
### Excel Formula Snippets
|
|
86
|
+
```
|
|
87
|
+
Location: library/snippets/excel-formulas/
|
|
88
|
+
Naming: kebab-case.md
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Required sections:**
|
|
92
|
+
- Title and description
|
|
93
|
+
- Formula with explanation
|
|
94
|
+
- Variations for different scenarios
|
|
95
|
+
- Compatibility notes (Excel version requirements)
|
|
96
|
+
|
|
97
|
+
**Quality checks:**
|
|
98
|
+
- [ ] Uses modern functions where appropriate (XLOOKUP > VLOOKUP)
|
|
99
|
+
- [ ] Includes error handling
|
|
100
|
+
- [ ] Uses structured references with tables
|
|
101
|
+
- [ ] Notes if formula requires Excel 365/2021+
|
|
102
|
+
- [ ] Avoids volatile functions without justification
|
|
103
|
+
|
|
104
|
+
### Power BI Themes
|
|
105
|
+
```
|
|
106
|
+
Location: library/themes/power-bi/
|
|
107
|
+
Naming: kebab-case.json
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Required elements:**
|
|
111
|
+
- Valid JSON structure
|
|
112
|
+
- `name` property
|
|
113
|
+
- `dataColors` array (6-12 colors)
|
|
114
|
+
- `background`, `foreground` colors
|
|
115
|
+
- `good`, `bad`, `neutral` sentiment colors
|
|
116
|
+
|
|
117
|
+
**Quality checks:**
|
|
118
|
+
- [ ] JSON is valid (no syntax errors)
|
|
119
|
+
- [ ] Colors are accessible (sufficient contrast)
|
|
120
|
+
- [ ] Consistent visual style across properties
|
|
121
|
+
- [ ] Includes textClasses definitions
|
|
122
|
+
- [ ] Covers common visual types
|
|
123
|
+
- [ ] README update with theme description
|
|
124
|
+
|
|
125
|
+
### Skills
|
|
126
|
+
```
|
|
127
|
+
Location: src/content/skills/
|
|
128
|
+
Files: [skill-name].md
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Required sections:**
|
|
132
|
+
- Trigger keywords
|
|
133
|
+
- Identity/persona description
|
|
134
|
+
- Naming conventions (if applicable)
|
|
135
|
+
- Best practices
|
|
136
|
+
- Common patterns with examples
|
|
137
|
+
- Anti-patterns to avoid
|
|
138
|
+
|
|
139
|
+
**Quality checks:**
|
|
140
|
+
- [ ] Comprehensive trigger keywords
|
|
141
|
+
- [ ] Clear identity statement
|
|
142
|
+
- [ ] Actionable best practices
|
|
143
|
+
- [ ] Real, working code examples
|
|
144
|
+
- [ ] Links to related resources
|
|
145
|
+
|
|
146
|
+
### Templates
|
|
147
|
+
```
|
|
148
|
+
Location: library/templates/[type]/
|
|
149
|
+
Files: descriptive-name.md or folder
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Required elements:**
|
|
153
|
+
- Purpose description
|
|
154
|
+
- Structure overview
|
|
155
|
+
- Usage instructions
|
|
156
|
+
- Customization guide
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Feedback Guidelines
|
|
161
|
+
|
|
162
|
+
### Be Constructive
|
|
163
|
+
```
|
|
164
|
+
BAD: "This code is wrong."
|
|
165
|
+
GOOD: "This pattern could cause performance issues because... Consider using..."
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Be Specific
|
|
169
|
+
```
|
|
170
|
+
BAD: "Naming is inconsistent."
|
|
171
|
+
GOOD: "Line 15 uses snake_case but line 23 uses PascalCase. Please standardize to PascalCase per our conventions."
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Acknowledge Good Work
|
|
175
|
+
```
|
|
176
|
+
"Nice use of the VAR/RETURN pattern! One suggestion to make it even better..."
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Offer Help
|
|
180
|
+
```
|
|
181
|
+
"If you'd like, I can help you refactor this section to improve query folding."
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Prioritize Feedback
|
|
185
|
+
```
|
|
186
|
+
1. MUST FIX: Security issues, broken code, major errors
|
|
187
|
+
2. SHOULD FIX: Best practice violations, performance issues
|
|
188
|
+
3. CONSIDER: Style preferences, minor improvements
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Common Issues and Solutions
|
|
194
|
+
|
|
195
|
+
### DAX Issues
|
|
196
|
+
| Issue | Solution |
|
|
197
|
+
|-------|----------|
|
|
198
|
+
| Division without DIVIDE | Replace `A/B` with `DIVIDE(A, B, 0)` |
|
|
199
|
+
| Repeated expressions | Use VAR to calculate once |
|
|
200
|
+
| FILTER instead of simple condition | Use `Column = Value` in CALCULATE |
|
|
201
|
+
| Hardcoded dates | Use dynamic expressions or parameters |
|
|
202
|
+
|
|
203
|
+
### Power Query Issues
|
|
204
|
+
| Issue | Solution |
|
|
205
|
+
|-------|----------|
|
|
206
|
+
| Query folding broken | Move operations that fold before those that don't |
|
|
207
|
+
| No error handling | Add `try...otherwise` for conversions |
|
|
208
|
+
| Hardcoded connections | Use parameters |
|
|
209
|
+
| Poor step names | Rename to descriptive PascalCase |
|
|
210
|
+
|
|
211
|
+
### Excel Formula Issues
|
|
212
|
+
| Issue | Solution |
|
|
213
|
+
|-------|----------|
|
|
214
|
+
| VLOOKUP used | Suggest XLOOKUP or INDEX/MATCH |
|
|
215
|
+
| No error handling | Add IFERROR or IFNA |
|
|
216
|
+
| Volatile functions | Suggest INDEX over OFFSET |
|
|
217
|
+
| Nested IFs | Suggest IFS or SWITCH |
|
|
218
|
+
|
|
219
|
+
### Theme Issues
|
|
220
|
+
| Issue | Solution |
|
|
221
|
+
|-------|----------|
|
|
222
|
+
| Low contrast colors | Verify WCAG AA compliance |
|
|
223
|
+
| Missing properties | Check against default theme structure |
|
|
224
|
+
| Invalid JSON | Use JSON validator |
|
|
225
|
+
| No sentiment colors | Add good/bad/neutral properties |
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Approval Workflow
|
|
230
|
+
|
|
231
|
+
### Ready to Merge
|
|
232
|
+
- All MUST FIX items resolved
|
|
233
|
+
- Documentation complete
|
|
234
|
+
- Code tested and working
|
|
235
|
+
- Follows all conventions
|
|
236
|
+
|
|
237
|
+
### Request Changes
|
|
238
|
+
- MUST FIX items present
|
|
239
|
+
- Missing required sections
|
|
240
|
+
- Broken or untested code
|
|
241
|
+
|
|
242
|
+
### Needs Discussion
|
|
243
|
+
- Architectural decisions
|
|
244
|
+
- New patterns that may set precedent
|
|
245
|
+
- Major additions requiring review
|
|
246
|
+
|
|
247
|
+
## Complexity Adaptation
|
|
248
|
+
|
|
249
|
+
Adjust depth based on `config.json → experienceLevel`:
|
|
250
|
+
- **beginner**: Step-by-step with explanations, reference library examples
|
|
251
|
+
- **intermediate**: Standard depth, explain non-obvious decisions
|
|
252
|
+
- **advanced**: Concise, skip basics, focus on edge cases and optimization
|
|
253
|
+
|
|
254
|
+
## Related Skills
|
|
255
|
+
|
|
256
|
+
- `/governance` — Code standards to follow when contributing
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## Related Resources
|
|
261
|
+
|
|
262
|
+
- [CONTRIBUTING.md](../../../CONTRIBUTING.md) - Full contribution guidelines
|
|
263
|
+
- [DAX Skill](./dax.md) - DAX conventions
|
|
264
|
+
- [Power Query Skill](./power-query.md) - Power Query conventions
|
|
265
|
+
- [Excel Formulas Skill](./excel-formulas.md) - Excel conventions
|