@luquimbo/bi-superpowers 3.1.1 → 4.1.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/marketplace.json +5 -3
- package/.claude-plugin/plugin.json +28 -2
- package/.claude-plugin/skill-manifest.json +22 -6
- package/.plugin/plugin.json +1 -1
- package/AGENTS.md +52 -36
- package/CHANGELOG.md +295 -0
- package/README.md +75 -26
- package/bin/build-plugin.js +17 -10
- package/bin/cli.js +278 -322
- package/bin/commands/build-desktop.js +35 -16
- package/bin/commands/diff.js +31 -13
- package/bin/commands/install.js +93 -72
- package/bin/commands/lint.js +40 -26
- package/bin/commands/mcp-setup.js +3 -10
- package/bin/commands/update-check.js +389 -0
- package/bin/lib/agents.js +19 -0
- package/bin/lib/generators/claude-plugin.js +144 -6
- package/bin/lib/generators/shared.js +29 -33
- package/bin/lib/mcp-config.js +191 -16
- package/bin/lib/skills.js +115 -27
- package/bin/postinstall.js +4 -2
- package/bin/utils/mcp-detect.js +2 -2
- package/commands/bi-start.md +218 -0
- package/commands/pbi-connect.md +43 -65
- package/commands/project-kickoff.md +393 -673
- package/commands/report-design.md +403 -0
- package/desktop-extension/manifest.json +5 -12
- package/desktop-extension/server.js +34 -25
- package/package.json +6 -10
- package/skills/bi-start/SKILL.md +220 -0
- package/skills/bi-start/scripts/update-check.js +389 -0
- package/skills/pbi-connect/SKILL.md +45 -67
- package/skills/pbi-connect/scripts/update-check.js +389 -0
- package/skills/project-kickoff/SKILL.md +395 -675
- package/skills/project-kickoff/scripts/update-check.js +389 -0
- package/skills/report-design/SKILL.md +405 -0
- package/skills/report-design/references/cli-commands.md +184 -0
- package/skills/report-design/references/cli-setup.md +101 -0
- package/skills/report-design/references/close-write-open-pattern.md +80 -0
- package/skills/report-design/references/layouts/finance.md +65 -0
- package/skills/report-design/references/layouts/generic.md +46 -0
- package/skills/report-design/references/layouts/hr.md +48 -0
- package/skills/report-design/references/layouts/marketing.md +45 -0
- package/skills/report-design/references/layouts/operations.md +44 -0
- package/skills/report-design/references/layouts/sales.md +50 -0
- package/skills/report-design/references/native-visuals.md +341 -0
- package/skills/report-design/references/pbi-desktop-installation.md +87 -0
- package/skills/report-design/references/pbir-preview-activation.md +40 -0
- package/skills/report-design/references/slicer.md +89 -0
- package/skills/report-design/references/textbox.md +101 -0
- package/skills/report-design/references/themes/BISuperpowers.json +915 -0
- package/skills/report-design/references/troubleshooting.md +135 -0
- package/skills/report-design/references/visual-types.md +78 -0
- package/skills/report-design/scripts/apply-theme.js +243 -0
- package/skills/report-design/scripts/create-visual.js +878 -0
- package/skills/report-design/scripts/ensure-pbi-cli.sh +41 -0
- package/skills/report-design/scripts/update-check.js +389 -0
- package/skills/report-design/scripts/validate-pbir.js +322 -0
- package/src/content/base.md +12 -68
- package/src/content/mcp-requirements.json +0 -25
- package/src/content/routing.md +19 -74
- package/src/content/skills/bi-start.md +191 -0
- package/src/content/skills/pbi-connect.md +22 -65
- package/src/content/skills/project-kickoff.md +372 -673
- package/src/content/skills/report-design/SKILL.md +376 -0
- package/src/content/skills/report-design/references/cli-commands.md +184 -0
- package/src/content/skills/report-design/references/cli-setup.md +101 -0
- package/src/content/skills/report-design/references/close-write-open-pattern.md +80 -0
- package/src/content/skills/report-design/references/layouts/finance.md +65 -0
- package/src/content/skills/report-design/references/layouts/generic.md +46 -0
- package/src/content/skills/report-design/references/layouts/hr.md +48 -0
- package/src/content/skills/report-design/references/layouts/marketing.md +45 -0
- package/src/content/skills/report-design/references/layouts/operations.md +44 -0
- package/src/content/skills/report-design/references/layouts/sales.md +50 -0
- package/src/content/skills/report-design/references/native-visuals.md +341 -0
- package/src/content/skills/report-design/references/pbi-desktop-installation.md +87 -0
- package/src/content/skills/report-design/references/pbir-preview-activation.md +40 -0
- package/src/content/skills/report-design/references/slicer.md +89 -0
- package/src/content/skills/report-design/references/textbox.md +101 -0
- package/src/content/skills/report-design/references/themes/BISuperpowers.json +915 -0
- package/src/content/skills/report-design/references/troubleshooting.md +135 -0
- package/src/content/skills/report-design/references/visual-types.md +78 -0
- package/src/content/skills/report-design/scripts/apply-theme.js +243 -0
- package/src/content/skills/report-design/scripts/create-visual.js +878 -0
- package/src/content/skills/report-design/scripts/ensure-pbi-cli.sh +41 -0
- package/src/content/skills/report-design/scripts/validate-pbir.js +322 -0
- package/bin/commands/add.js +0 -533
- package/bin/commands/add.test.js +0 -77
- package/bin/commands/changelog.js +0 -443
- package/bin/commands/install.test.js +0 -289
- package/bin/commands/lint.test.js +0 -103
- package/bin/commands/pull.js +0 -287
- package/bin/commands/pull.test.js +0 -36
- package/bin/commands/push.js +0 -231
- package/bin/commands/push.test.js +0 -14
- package/bin/commands/search.js +0 -344
- package/bin/commands/search.test.js +0 -115
- package/bin/commands/setup.js +0 -545
- package/bin/commands/setup.test.js +0 -46
- package/bin/commands/sync-profile.js +0 -405
- package/bin/commands/sync-profile.test.js +0 -14
- package/bin/commands/sync-source.js +0 -418
- package/bin/commands/sync-source.test.js +0 -14
- package/bin/lib/generators/claude-plugin.test.js +0 -111
- package/bin/lib/mcp-config.test.js +0 -310
- package/bin/lib/microsoft-mcp.test.js +0 -115
- package/bin/utils/errors.js +0 -159
- package/bin/utils/git.js +0 -298
- package/bin/utils/logger.js +0 -142
- package/bin/utils/mcp-detect.test.js +0 -81
- package/bin/utils/pbix.js +0 -305
- package/bin/utils/pbix.test.js +0 -37
- package/bin/utils/profiles.js +0 -312
- package/bin/utils/projects.js +0 -169
- package/bin/utils/readline.js +0 -206
- package/bin/utils/readline.test.js +0 -47
- package/bin/utils/tui.test.js +0 -127
- package/docs/openrouter-free-models.md +0 -92
- package/library/examples/README.md +0 -151
- package/library/examples/finance-reporting/README.md +0 -351
- package/library/examples/finance-reporting/data-model.md +0 -267
- package/library/examples/finance-reporting/measures.dax +0 -557
- package/library/examples/hr-analytics/README.md +0 -371
- package/library/examples/hr-analytics/data-model.md +0 -315
- package/library/examples/hr-analytics/measures.dax +0 -460
- package/library/examples/marketing-analytics/README.md +0 -37
- package/library/examples/marketing-analytics/data-model.md +0 -62
- package/library/examples/marketing-analytics/measures.dax +0 -110
- package/library/examples/retail-analytics/README.md +0 -439
- package/library/examples/retail-analytics/data-model.md +0 -288
- package/library/examples/retail-analytics/measures.dax +0 -481
- package/library/examples/supply-chain/README.md +0 -37
- package/library/examples/supply-chain/data-model.md +0 -69
- package/library/examples/supply-chain/measures.dax +0 -77
- package/library/examples/udf-library/README.md +0 -228
- package/library/examples/udf-library/functions.dax +0 -571
- package/library/snippets/dax/README.md +0 -292
- package/library/snippets/dax/business-domains.md +0 -576
- package/library/snippets/dax/calculate-patterns.md +0 -276
- package/library/snippets/dax/calculation-groups.md +0 -489
- package/library/snippets/dax/error-handling.md +0 -495
- package/library/snippets/dax/iterators-and-aggregations.md +0 -474
- package/library/snippets/dax/kpis-and-metrics.md +0 -293
- package/library/snippets/dax/rankings-and-topn.md +0 -235
- package/library/snippets/dax/security-patterns.md +0 -413
- package/library/snippets/dax/text-and-formatting.md +0 -316
- package/library/snippets/dax/time-intelligence.md +0 -196
- package/library/snippets/dax/user-defined-functions.md +0 -477
- package/library/snippets/dax/virtual-tables.md +0 -546
- package/library/snippets/excel-formulas/README.md +0 -84
- package/library/snippets/excel-formulas/aggregations.md +0 -330
- package/library/snippets/excel-formulas/dates-and-times.md +0 -361
- package/library/snippets/excel-formulas/dynamic-arrays.md +0 -314
- package/library/snippets/excel-formulas/lookups.md +0 -169
- package/library/snippets/excel-formulas/text-functions.md +0 -363
- package/library/snippets/governance/naming-conventions.md +0 -97
- package/library/snippets/governance/review-checklists.md +0 -107
- package/library/snippets/power-query/README.md +0 -389
- package/library/snippets/power-query/api-integration.md +0 -707
- package/library/snippets/power-query/connections.md +0 -434
- package/library/snippets/power-query/data-cleaning.md +0 -298
- package/library/snippets/power-query/error-handling.md +0 -526
- package/library/snippets/power-query/parameters.md +0 -350
- package/library/snippets/power-query/performance.md +0 -506
- package/library/snippets/power-query/transformations.md +0 -330
- package/library/snippets/report-design/accessibility.md +0 -78
- package/library/snippets/report-design/chart-selection.md +0 -54
- package/library/snippets/report-design/layout-patterns.md +0 -87
- package/library/templates/data-models/README.md +0 -93
- package/library/templates/data-models/finance-model.md +0 -627
- package/library/templates/data-models/retail-star-schema.md +0 -473
- package/library/templates/excel/README.md +0 -83
- package/library/templates/excel/budget-tracker.md +0 -432
- package/library/templates/excel/data-entry-form.md +0 -533
- package/library/templates/power-bi/README.md +0 -72
- package/library/templates/power-bi/finance-report.md +0 -449
- package/library/templates/power-bi/kpi-scorecard.md +0 -461
- package/library/templates/power-bi/sales-dashboard.md +0 -281
- package/library/themes/excel/README.md +0 -436
- package/library/themes/power-bi/README.md +0 -271
- package/library/themes/power-bi/accessible.json +0 -307
- package/library/themes/power-bi/bi-superpowers-default.json +0 -858
- package/library/themes/power-bi/corporate-blue.json +0 -291
- package/library/themes/power-bi/dark-mode.json +0 -291
- package/library/themes/power-bi/minimal.json +0 -292
- package/library/themes/power-bi/print-friendly.json +0 -309
package/bin/utils/projects.js
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Projects Utility
|
|
3
|
-
* =================
|
|
4
|
-
*
|
|
5
|
-
* Shared functions for working with bi-repo projects.
|
|
6
|
-
* Extracted from add.js, pull.js, push.js to reduce duplication.
|
|
7
|
-
*
|
|
8
|
-
* @module utils/projects
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const fs = require('fs');
|
|
12
|
-
const path = require('path');
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Get a specific project by name
|
|
16
|
-
* @param {string} repoPath - Path to bi-repo
|
|
17
|
-
* @param {string} projectName - Name of the project
|
|
18
|
-
* @returns {Object|null} Project config with projectPath added, or null
|
|
19
|
-
*/
|
|
20
|
-
function getProject(repoPath, projectName) {
|
|
21
|
-
const projectPath = path.join(repoPath, 'projects', projectName);
|
|
22
|
-
const configPath = path.join(projectPath, 'project.json');
|
|
23
|
-
|
|
24
|
-
if (!fs.existsSync(configPath)) {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
30
|
-
return {
|
|
31
|
-
...config,
|
|
32
|
-
projectPath,
|
|
33
|
-
};
|
|
34
|
-
} catch (e) {
|
|
35
|
-
// Invalid JSON in config file
|
|
36
|
-
if (process.env.DEBUG === 'true') {
|
|
37
|
-
console.error(`[DEBUG] Failed to parse project config: ${configPath}`);
|
|
38
|
-
}
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Get list of all projects in the repo
|
|
45
|
-
* @param {string} repoPath - Path to bi-repo
|
|
46
|
-
* @returns {Object[]} Array of project configs with projectPath added
|
|
47
|
-
*/
|
|
48
|
-
function getAllProjects(repoPath) {
|
|
49
|
-
const projectsDir = path.join(repoPath, 'projects');
|
|
50
|
-
|
|
51
|
-
if (!fs.existsSync(projectsDir)) {
|
|
52
|
-
return [];
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const projects = [];
|
|
56
|
-
const dirs = fs.readdirSync(projectsDir);
|
|
57
|
-
|
|
58
|
-
for (const dir of dirs) {
|
|
59
|
-
const projectPath = path.join(projectsDir, dir);
|
|
60
|
-
|
|
61
|
-
// Skip if not a directory
|
|
62
|
-
try {
|
|
63
|
-
if (!fs.statSync(projectPath).isDirectory()) {
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
} catch (e) {
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const configPath = path.join(projectPath, 'project.json');
|
|
71
|
-
|
|
72
|
-
if (fs.existsSync(configPath)) {
|
|
73
|
-
try {
|
|
74
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
75
|
-
projects.push({
|
|
76
|
-
...config,
|
|
77
|
-
projectPath,
|
|
78
|
-
});
|
|
79
|
-
} catch (e) {
|
|
80
|
-
// Warn user about broken config so they can fix it
|
|
81
|
-
console.warn(`⚠ Config de proyecto inválido en ${configPath}: ${e.message}`);
|
|
82
|
-
if (process.env.DEBUG === 'true') {
|
|
83
|
-
console.error('[DEBUG] Full error:', e);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return projects;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Detect current project based on working directory
|
|
94
|
-
* @param {string} repoPath - Path to bi-repo
|
|
95
|
-
* @returns {Object|null} Project config or null if not in a project folder
|
|
96
|
-
*/
|
|
97
|
-
function detectCurrentProject(repoPath) {
|
|
98
|
-
const cwd = process.cwd();
|
|
99
|
-
const projectsBase = path.join(repoPath, 'projects');
|
|
100
|
-
|
|
101
|
-
// Check if we're inside a project folder
|
|
102
|
-
if (cwd.startsWith(projectsBase)) {
|
|
103
|
-
const relative = path.relative(projectsBase, cwd);
|
|
104
|
-
const projectName = relative.split(path.sep)[0];
|
|
105
|
-
|
|
106
|
-
if (projectName && projectName !== '..') {
|
|
107
|
-
return getProject(repoPath, projectName);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* List project names
|
|
116
|
-
* @param {string} repoPath - Path to bi-repo
|
|
117
|
-
* @returns {string[]} Array of project names
|
|
118
|
-
*/
|
|
119
|
-
function listProjectNames(repoPath) {
|
|
120
|
-
return getAllProjects(repoPath).map((p) => p.name);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Check if a project exists
|
|
125
|
-
* @param {string} repoPath - Path to bi-repo
|
|
126
|
-
* @param {string} projectName - Name of the project
|
|
127
|
-
* @returns {boolean} True if project exists
|
|
128
|
-
*/
|
|
129
|
-
function projectExists(repoPath, projectName) {
|
|
130
|
-
const projectPath = path.join(repoPath, 'projects', projectName);
|
|
131
|
-
const configPath = path.join(projectPath, 'project.json');
|
|
132
|
-
return fs.existsSync(configPath);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Get project path
|
|
137
|
-
* @param {string} repoPath - Path to bi-repo
|
|
138
|
-
* @param {string} projectName - Name of the project
|
|
139
|
-
* @returns {string} Path to project directory
|
|
140
|
-
*/
|
|
141
|
-
function getProjectPath(repoPath, projectName) {
|
|
142
|
-
return path.join(repoPath, 'projects', projectName);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Print available projects to console
|
|
147
|
-
* @param {string} repoPath - Path to bi-repo
|
|
148
|
-
* @param {string} prefix - Prefix for each line (default: ' - ')
|
|
149
|
-
*/
|
|
150
|
-
function printAvailableProjects(repoPath, prefix = ' - ') {
|
|
151
|
-
const projects = getAllProjects(repoPath);
|
|
152
|
-
if (projects.length === 0) {
|
|
153
|
-
console.log(`${prefix}(no projects found)`);
|
|
154
|
-
} else {
|
|
155
|
-
projects.forEach((p) => {
|
|
156
|
-
console.log(`${prefix}${p.name}`);
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
module.exports = {
|
|
162
|
-
getProject,
|
|
163
|
-
getAllProjects,
|
|
164
|
-
detectCurrentProject,
|
|
165
|
-
listProjectNames,
|
|
166
|
-
projectExists,
|
|
167
|
-
getProjectPath,
|
|
168
|
-
printAvailableProjects,
|
|
169
|
-
};
|
package/bin/utils/readline.js
DELETED
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Readline Utilities for BI Agent Superpowers
|
|
3
|
-
* ============================================
|
|
4
|
-
*
|
|
5
|
-
* Shared readline functions for interactive CLI commands.
|
|
6
|
-
* Consolidates duplicated code from setup.js, add.js, sync-source.js, sync-profile.js
|
|
7
|
-
*
|
|
8
|
-
* @module utils/readline
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const readline = require('readline');
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Create a readline interface for interactive prompts
|
|
15
|
-
* @returns {readline.Interface} Readline interface
|
|
16
|
-
*/
|
|
17
|
-
function createReadline() {
|
|
18
|
-
return readline.createInterface({
|
|
19
|
-
input: process.stdin,
|
|
20
|
-
output: process.stdout,
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Prompt the user for input
|
|
26
|
-
* @param {readline.Interface} rl - Readline interface
|
|
27
|
-
* @param {string} question - Question to ask
|
|
28
|
-
* @returns {Promise<string>} User's answer (trimmed)
|
|
29
|
-
*/
|
|
30
|
-
function prompt(rl, question) {
|
|
31
|
-
return new Promise((resolve) => {
|
|
32
|
-
rl.question(question, (answer) => {
|
|
33
|
-
resolve(answer.trim());
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Ask for yes/no confirmation
|
|
40
|
-
* @param {readline.Interface} rl - Readline interface
|
|
41
|
-
* @param {string} question - Question to ask (without y/n suffix)
|
|
42
|
-
* @param {boolean} defaultValue - Default value if user just presses Enter
|
|
43
|
-
* @returns {Promise<boolean>} True if confirmed, false otherwise
|
|
44
|
-
*/
|
|
45
|
-
function confirm(rl, question, defaultValue = false) {
|
|
46
|
-
const suffix = defaultValue ? ' (S/n): ' : ' (s/N): ';
|
|
47
|
-
return new Promise((resolve) => {
|
|
48
|
-
rl.question(question + suffix, (answer) => {
|
|
49
|
-
const trimmed = answer.trim().toLowerCase();
|
|
50
|
-
if (trimmed === '') {
|
|
51
|
-
resolve(defaultValue);
|
|
52
|
-
} else {
|
|
53
|
-
resolve(trimmed === 's' || trimmed === 'y' || trimmed === 'si' || trimmed === 'yes');
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Ask user to select from a list of options
|
|
61
|
-
* @param {readline.Interface} rl - Readline interface
|
|
62
|
-
* @param {string} question - Question to ask
|
|
63
|
-
* @param {string[]} options - Array of option strings
|
|
64
|
-
* @param {number} defaultIndex - Default option index (0-based)
|
|
65
|
-
* @returns {Promise<{index: number, value: string}>} Selected option
|
|
66
|
-
*/
|
|
67
|
-
async function select(rl, question, options, defaultIndex = 0) {
|
|
68
|
-
console.log(`\n${question}\n`);
|
|
69
|
-
|
|
70
|
-
options.forEach((option, i) => {
|
|
71
|
-
const marker = i === defaultIndex ? '>' : ' ';
|
|
72
|
-
console.log(` ${marker} ${i + 1}. ${option}`);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
console.log('');
|
|
76
|
-
|
|
77
|
-
const answer = await prompt(rl, `Elige una opción (1-${options.length}) [${defaultIndex + 1}]: `);
|
|
78
|
-
|
|
79
|
-
if (answer === '') {
|
|
80
|
-
return { index: defaultIndex, value: options[defaultIndex] };
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const num = parseInt(answer, 10);
|
|
84
|
-
if (isNaN(num) || num < 1 || num > options.length) {
|
|
85
|
-
console.log('Opción no válida, usando la predeterminada.');
|
|
86
|
-
return { index: defaultIndex, value: options[defaultIndex] };
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return { index: num - 1, value: options[num - 1] };
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Ask user to select multiple options from a list
|
|
94
|
-
* @param {readline.Interface} rl - Readline interface
|
|
95
|
-
* @param {string} question - Question to ask
|
|
96
|
-
* @param {Array<{label: string, value: any, selected?: boolean}>} options - Options with labels and values
|
|
97
|
-
* @returns {Promise<any[]>} Array of selected values
|
|
98
|
-
*/
|
|
99
|
-
async function multiSelect(rl, question, options) {
|
|
100
|
-
console.log(`\n${question}\n`);
|
|
101
|
-
console.log(' Instrucciones:');
|
|
102
|
-
console.log(' - Escribe números separados por comas (ej: 1,3,5)');
|
|
103
|
-
console.log(' - "a" para seleccionar todos');
|
|
104
|
-
console.log(' - "n" para deseleccionar todos');
|
|
105
|
-
console.log(' - Enter para confirmar selección actual\n');
|
|
106
|
-
|
|
107
|
-
// Show options with current selection
|
|
108
|
-
options.forEach((option, i) => {
|
|
109
|
-
const marker = option.selected ? '[x]' : '[ ]';
|
|
110
|
-
console.log(` ${marker} ${i + 1}. ${option.label}`);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
console.log('');
|
|
114
|
-
|
|
115
|
-
const answer = await prompt(rl, 'Selección: ');
|
|
116
|
-
|
|
117
|
-
if (answer.toLowerCase() === 'a') {
|
|
118
|
-
return options.map((o) => o.value);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (answer.toLowerCase() === 'n' || answer === '') {
|
|
122
|
-
return options.filter((o) => o.selected).map((o) => o.value);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Parse comma-separated numbers
|
|
126
|
-
const indices = answer
|
|
127
|
-
.split(',')
|
|
128
|
-
.map((s) => parseInt(s.trim(), 10) - 1)
|
|
129
|
-
.filter((n) => !isNaN(n) && n >= 0 && n < options.length);
|
|
130
|
-
|
|
131
|
-
return indices.map((i) => options[i].value);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Show a spinner while executing an async operation
|
|
136
|
-
* @param {string} message - Message to show
|
|
137
|
-
* @param {Promise} promise - Promise to wait for
|
|
138
|
-
* @returns {Promise} Result of the promise
|
|
139
|
-
*/
|
|
140
|
-
async function withSpinner(message, promise) {
|
|
141
|
-
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
142
|
-
let i = 0;
|
|
143
|
-
|
|
144
|
-
const interval = setInterval(() => {
|
|
145
|
-
process.stdout.write(`\r${frames[i]} ${message}`);
|
|
146
|
-
i = (i + 1) % frames.length;
|
|
147
|
-
}, 80);
|
|
148
|
-
|
|
149
|
-
try {
|
|
150
|
-
const result = await promise;
|
|
151
|
-
clearInterval(interval);
|
|
152
|
-
process.stdout.write(`\r✓ ${message}\n`);
|
|
153
|
-
return result;
|
|
154
|
-
} catch (error) {
|
|
155
|
-
clearInterval(interval);
|
|
156
|
-
process.stdout.write(`\r✗ ${message}\n`);
|
|
157
|
-
throw error;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Exit with error message
|
|
163
|
-
* @param {string} message - Error message
|
|
164
|
-
* @param {number} code - Exit code (default 1)
|
|
165
|
-
*/
|
|
166
|
-
function exitWithError(message, code = 1) {
|
|
167
|
-
console.error(`\n✗ ${message}\n`);
|
|
168
|
-
process.exit(code);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Show success message
|
|
173
|
-
* @param {string} message - Success message
|
|
174
|
-
*/
|
|
175
|
-
function showSuccess(message) {
|
|
176
|
-
console.log(`✓ ${message}`);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Show warning message
|
|
181
|
-
* @param {string} message - Warning message
|
|
182
|
-
*/
|
|
183
|
-
function showWarning(message) {
|
|
184
|
-
console.log(`⚠ ${message}`);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Show info message
|
|
189
|
-
* @param {string} message - Info message
|
|
190
|
-
*/
|
|
191
|
-
function showInfo(message) {
|
|
192
|
-
console.log(`ℹ ${message}`);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
module.exports = {
|
|
196
|
-
createReadline,
|
|
197
|
-
prompt,
|
|
198
|
-
confirm,
|
|
199
|
-
select,
|
|
200
|
-
multiSelect,
|
|
201
|
-
withSpinner,
|
|
202
|
-
exitWithError,
|
|
203
|
-
showSuccess,
|
|
204
|
-
showWarning,
|
|
205
|
-
showInfo,
|
|
206
|
-
};
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for Readline Utilities
|
|
3
|
-
* @module utils/readline.test
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const { test, describe } = require('node:test');
|
|
7
|
-
const assert = require('node:assert');
|
|
8
|
-
|
|
9
|
-
const readline = require('./readline');
|
|
10
|
-
|
|
11
|
-
describe('Readline Utilities', () => {
|
|
12
|
-
test('exports createReadline function', () => {
|
|
13
|
-
assert.strictEqual(typeof readline.createReadline, 'function');
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
test('exports prompt function', () => {
|
|
17
|
-
assert.strictEqual(typeof readline.prompt, 'function');
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
test('exports confirm function', () => {
|
|
21
|
-
assert.strictEqual(typeof readline.confirm, 'function');
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test('exports select function', () => {
|
|
25
|
-
assert.strictEqual(typeof readline.select, 'function');
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
test('exports multiSelect function', () => {
|
|
29
|
-
assert.strictEqual(typeof readline.multiSelect, 'function');
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test('exports exitWithError function', () => {
|
|
33
|
-
assert.strictEqual(typeof readline.exitWithError, 'function');
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test('exports showSuccess function', () => {
|
|
37
|
-
assert.strictEqual(typeof readline.showSuccess, 'function');
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test('exports showWarning function', () => {
|
|
41
|
-
assert.strictEqual(typeof readline.showWarning, 'function');
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test('exports showInfo function', () => {
|
|
45
|
-
assert.strictEqual(typeof readline.showInfo, 'function');
|
|
46
|
-
});
|
|
47
|
-
});
|
package/bin/utils/tui.test.js
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
# Configure Claude Code with OpenRouter (Free Models)
|
|
2
|
-
|
|
3
|
-
Use free AI models with Claude Code CLI and 1code.dev via OpenRouter.
|
|
4
|
-
|
|
5
|
-
## Prerequisites
|
|
6
|
-
|
|
7
|
-
- Node.js installed
|
|
8
|
-
- Claude Code CLI installed (`npm install -g @anthropic-ai/claude-code`)
|
|
9
|
-
- Free account at [openrouter.ai](https://openrouter.ai)
|
|
10
|
-
|
|
11
|
-
## Step 1: Create OpenRouter API Key
|
|
12
|
-
|
|
13
|
-
1. Go to [openrouter.ai](https://openrouter.ai) and create account (free)
|
|
14
|
-
2. Go to [openrouter.ai/keys](https://openrouter.ai/keys)
|
|
15
|
-
3. Click "Create Key"
|
|
16
|
-
4. Copy the key (starts with `sk-or-v1-...`)
|
|
17
|
-
|
|
18
|
-
## Step 2: Configure Environment Variables
|
|
19
|
-
|
|
20
|
-
Add to the end of `~/.zshrc` (macOS/Linux) or your shell profile:
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
# OpenRouter + Claude Code
|
|
24
|
-
export ANTHROPIC_BASE_URL="https://openrouter.ai/api"
|
|
25
|
-
export ANTHROPIC_AUTH_TOKEN="sk-or-v1-YOUR-KEY-HERE"
|
|
26
|
-
export ANTHROPIC_API_KEY=""
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
> **Important**: `ANTHROPIC_API_KEY` MUST be empty (`""`). If it doesn't exist, Claude Code will try to authenticate with Anthropic directly.
|
|
30
|
-
|
|
31
|
-
Then reload the shell:
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
source ~/.zshrc
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Step 3: Sign Out of Anthropic (if needed)
|
|
38
|
-
|
|
39
|
-
If you previously used Claude Code with an Anthropic account:
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
claude
|
|
43
|
-
# inside the session:
|
|
44
|
-
/logout
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Step 4: Verify Connection
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
claude
|
|
51
|
-
# inside the session:
|
|
52
|
-
/status
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
Should show that it's connected to OpenRouter.
|
|
56
|
-
|
|
57
|
-
## Recommended Free Models for Coding
|
|
58
|
-
|
|
59
|
-
| Model | OpenRouter ID | Context | Best For |
|
|
60
|
-
|-------|---------------|---------|----------|
|
|
61
|
-
| **Qwen3 Coder 480B** | `qwen/qwen3-coder-480b-a35b:free` | 262K | Best free model for agentic coding |
|
|
62
|
-
| **DeepSeek R1** | `deepseek/deepseek-r1:free` | 164K | Complex reasoning, debugging |
|
|
63
|
-
| **Devstral** | `mistralai/devstral-small:free` | 128K | Multi-file, agentic coding |
|
|
64
|
-
| **Llama 3.3 70B** | `meta-llama/llama-3.3-70b-instruct:free` | 128K | General purpose, fast |
|
|
65
|
-
|
|
66
|
-
To select a specific model, add to `~/.zshrc`:
|
|
67
|
-
|
|
68
|
-
```bash
|
|
69
|
-
export ANTHROPIC_MODEL="qwen/qwen3-coder-480b-a35b:free"
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
Or use the free automatic router:
|
|
73
|
-
|
|
74
|
-
```bash
|
|
75
|
-
export ANTHROPIC_MODEL="openrouter/free"
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
## Limitations
|
|
79
|
-
|
|
80
|
-
- **Rate limits**: ~20 requests/min, ~200 requests/day on free models
|
|
81
|
-
- **Tool use**: Not all free models support tool calling (needed for agents)
|
|
82
|
-
- **Quality**: Inferior to Claude Opus/Sonnet for complex tasks
|
|
83
|
-
- **Availability**: Free models may disappear without notice
|
|
84
|
-
- **Not recommended for production**
|
|
85
|
-
|
|
86
|
-
## Verification
|
|
87
|
-
|
|
88
|
-
1. Open a new terminal (to load the variables)
|
|
89
|
-
2. `echo $ANTHROPIC_BASE_URL` → should show `https://openrouter.ai/api`
|
|
90
|
-
3. `claude` → should start without asking for Anthropic login
|
|
91
|
-
4. `/status` → confirm connection to OpenRouter
|
|
92
|
-
5. Ask a simple coding question to verify response
|