@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.
Files changed (186) hide show
  1. package/.claude-plugin/marketplace.json +5 -3
  2. package/.claude-plugin/plugin.json +28 -2
  3. package/.claude-plugin/skill-manifest.json +22 -6
  4. package/.plugin/plugin.json +1 -1
  5. package/AGENTS.md +52 -36
  6. package/CHANGELOG.md +295 -0
  7. package/README.md +75 -26
  8. package/bin/build-plugin.js +17 -10
  9. package/bin/cli.js +278 -322
  10. package/bin/commands/build-desktop.js +35 -16
  11. package/bin/commands/diff.js +31 -13
  12. package/bin/commands/install.js +93 -72
  13. package/bin/commands/lint.js +40 -26
  14. package/bin/commands/mcp-setup.js +3 -10
  15. package/bin/commands/update-check.js +389 -0
  16. package/bin/lib/agents.js +19 -0
  17. package/bin/lib/generators/claude-plugin.js +144 -6
  18. package/bin/lib/generators/shared.js +29 -33
  19. package/bin/lib/mcp-config.js +191 -16
  20. package/bin/lib/skills.js +115 -27
  21. package/bin/postinstall.js +4 -2
  22. package/bin/utils/mcp-detect.js +2 -2
  23. package/commands/bi-start.md +218 -0
  24. package/commands/pbi-connect.md +43 -65
  25. package/commands/project-kickoff.md +393 -673
  26. package/commands/report-design.md +403 -0
  27. package/desktop-extension/manifest.json +5 -12
  28. package/desktop-extension/server.js +34 -25
  29. package/package.json +6 -10
  30. package/skills/bi-start/SKILL.md +220 -0
  31. package/skills/bi-start/scripts/update-check.js +389 -0
  32. package/skills/pbi-connect/SKILL.md +45 -67
  33. package/skills/pbi-connect/scripts/update-check.js +389 -0
  34. package/skills/project-kickoff/SKILL.md +395 -675
  35. package/skills/project-kickoff/scripts/update-check.js +389 -0
  36. package/skills/report-design/SKILL.md +405 -0
  37. package/skills/report-design/references/cli-commands.md +184 -0
  38. package/skills/report-design/references/cli-setup.md +101 -0
  39. package/skills/report-design/references/close-write-open-pattern.md +80 -0
  40. package/skills/report-design/references/layouts/finance.md +65 -0
  41. package/skills/report-design/references/layouts/generic.md +46 -0
  42. package/skills/report-design/references/layouts/hr.md +48 -0
  43. package/skills/report-design/references/layouts/marketing.md +45 -0
  44. package/skills/report-design/references/layouts/operations.md +44 -0
  45. package/skills/report-design/references/layouts/sales.md +50 -0
  46. package/skills/report-design/references/native-visuals.md +341 -0
  47. package/skills/report-design/references/pbi-desktop-installation.md +87 -0
  48. package/skills/report-design/references/pbir-preview-activation.md +40 -0
  49. package/skills/report-design/references/slicer.md +89 -0
  50. package/skills/report-design/references/textbox.md +101 -0
  51. package/skills/report-design/references/themes/BISuperpowers.json +915 -0
  52. package/skills/report-design/references/troubleshooting.md +135 -0
  53. package/skills/report-design/references/visual-types.md +78 -0
  54. package/skills/report-design/scripts/apply-theme.js +243 -0
  55. package/skills/report-design/scripts/create-visual.js +878 -0
  56. package/skills/report-design/scripts/ensure-pbi-cli.sh +41 -0
  57. package/skills/report-design/scripts/update-check.js +389 -0
  58. package/skills/report-design/scripts/validate-pbir.js +322 -0
  59. package/src/content/base.md +12 -68
  60. package/src/content/mcp-requirements.json +0 -25
  61. package/src/content/routing.md +19 -74
  62. package/src/content/skills/bi-start.md +191 -0
  63. package/src/content/skills/pbi-connect.md +22 -65
  64. package/src/content/skills/project-kickoff.md +372 -673
  65. package/src/content/skills/report-design/SKILL.md +376 -0
  66. package/src/content/skills/report-design/references/cli-commands.md +184 -0
  67. package/src/content/skills/report-design/references/cli-setup.md +101 -0
  68. package/src/content/skills/report-design/references/close-write-open-pattern.md +80 -0
  69. package/src/content/skills/report-design/references/layouts/finance.md +65 -0
  70. package/src/content/skills/report-design/references/layouts/generic.md +46 -0
  71. package/src/content/skills/report-design/references/layouts/hr.md +48 -0
  72. package/src/content/skills/report-design/references/layouts/marketing.md +45 -0
  73. package/src/content/skills/report-design/references/layouts/operations.md +44 -0
  74. package/src/content/skills/report-design/references/layouts/sales.md +50 -0
  75. package/src/content/skills/report-design/references/native-visuals.md +341 -0
  76. package/src/content/skills/report-design/references/pbi-desktop-installation.md +87 -0
  77. package/src/content/skills/report-design/references/pbir-preview-activation.md +40 -0
  78. package/src/content/skills/report-design/references/slicer.md +89 -0
  79. package/src/content/skills/report-design/references/textbox.md +101 -0
  80. package/src/content/skills/report-design/references/themes/BISuperpowers.json +915 -0
  81. package/src/content/skills/report-design/references/troubleshooting.md +135 -0
  82. package/src/content/skills/report-design/references/visual-types.md +78 -0
  83. package/src/content/skills/report-design/scripts/apply-theme.js +243 -0
  84. package/src/content/skills/report-design/scripts/create-visual.js +878 -0
  85. package/src/content/skills/report-design/scripts/ensure-pbi-cli.sh +41 -0
  86. package/src/content/skills/report-design/scripts/validate-pbir.js +322 -0
  87. package/bin/commands/add.js +0 -533
  88. package/bin/commands/add.test.js +0 -77
  89. package/bin/commands/changelog.js +0 -443
  90. package/bin/commands/install.test.js +0 -289
  91. package/bin/commands/lint.test.js +0 -103
  92. package/bin/commands/pull.js +0 -287
  93. package/bin/commands/pull.test.js +0 -36
  94. package/bin/commands/push.js +0 -231
  95. package/bin/commands/push.test.js +0 -14
  96. package/bin/commands/search.js +0 -344
  97. package/bin/commands/search.test.js +0 -115
  98. package/bin/commands/setup.js +0 -545
  99. package/bin/commands/setup.test.js +0 -46
  100. package/bin/commands/sync-profile.js +0 -405
  101. package/bin/commands/sync-profile.test.js +0 -14
  102. package/bin/commands/sync-source.js +0 -418
  103. package/bin/commands/sync-source.test.js +0 -14
  104. package/bin/lib/generators/claude-plugin.test.js +0 -111
  105. package/bin/lib/mcp-config.test.js +0 -310
  106. package/bin/lib/microsoft-mcp.test.js +0 -115
  107. package/bin/utils/errors.js +0 -159
  108. package/bin/utils/git.js +0 -298
  109. package/bin/utils/logger.js +0 -142
  110. package/bin/utils/mcp-detect.test.js +0 -81
  111. package/bin/utils/pbix.js +0 -305
  112. package/bin/utils/pbix.test.js +0 -37
  113. package/bin/utils/profiles.js +0 -312
  114. package/bin/utils/projects.js +0 -169
  115. package/bin/utils/readline.js +0 -206
  116. package/bin/utils/readline.test.js +0 -47
  117. package/bin/utils/tui.test.js +0 -127
  118. package/docs/openrouter-free-models.md +0 -92
  119. package/library/examples/README.md +0 -151
  120. package/library/examples/finance-reporting/README.md +0 -351
  121. package/library/examples/finance-reporting/data-model.md +0 -267
  122. package/library/examples/finance-reporting/measures.dax +0 -557
  123. package/library/examples/hr-analytics/README.md +0 -371
  124. package/library/examples/hr-analytics/data-model.md +0 -315
  125. package/library/examples/hr-analytics/measures.dax +0 -460
  126. package/library/examples/marketing-analytics/README.md +0 -37
  127. package/library/examples/marketing-analytics/data-model.md +0 -62
  128. package/library/examples/marketing-analytics/measures.dax +0 -110
  129. package/library/examples/retail-analytics/README.md +0 -439
  130. package/library/examples/retail-analytics/data-model.md +0 -288
  131. package/library/examples/retail-analytics/measures.dax +0 -481
  132. package/library/examples/supply-chain/README.md +0 -37
  133. package/library/examples/supply-chain/data-model.md +0 -69
  134. package/library/examples/supply-chain/measures.dax +0 -77
  135. package/library/examples/udf-library/README.md +0 -228
  136. package/library/examples/udf-library/functions.dax +0 -571
  137. package/library/snippets/dax/README.md +0 -292
  138. package/library/snippets/dax/business-domains.md +0 -576
  139. package/library/snippets/dax/calculate-patterns.md +0 -276
  140. package/library/snippets/dax/calculation-groups.md +0 -489
  141. package/library/snippets/dax/error-handling.md +0 -495
  142. package/library/snippets/dax/iterators-and-aggregations.md +0 -474
  143. package/library/snippets/dax/kpis-and-metrics.md +0 -293
  144. package/library/snippets/dax/rankings-and-topn.md +0 -235
  145. package/library/snippets/dax/security-patterns.md +0 -413
  146. package/library/snippets/dax/text-and-formatting.md +0 -316
  147. package/library/snippets/dax/time-intelligence.md +0 -196
  148. package/library/snippets/dax/user-defined-functions.md +0 -477
  149. package/library/snippets/dax/virtual-tables.md +0 -546
  150. package/library/snippets/excel-formulas/README.md +0 -84
  151. package/library/snippets/excel-formulas/aggregations.md +0 -330
  152. package/library/snippets/excel-formulas/dates-and-times.md +0 -361
  153. package/library/snippets/excel-formulas/dynamic-arrays.md +0 -314
  154. package/library/snippets/excel-formulas/lookups.md +0 -169
  155. package/library/snippets/excel-formulas/text-functions.md +0 -363
  156. package/library/snippets/governance/naming-conventions.md +0 -97
  157. package/library/snippets/governance/review-checklists.md +0 -107
  158. package/library/snippets/power-query/README.md +0 -389
  159. package/library/snippets/power-query/api-integration.md +0 -707
  160. package/library/snippets/power-query/connections.md +0 -434
  161. package/library/snippets/power-query/data-cleaning.md +0 -298
  162. package/library/snippets/power-query/error-handling.md +0 -526
  163. package/library/snippets/power-query/parameters.md +0 -350
  164. package/library/snippets/power-query/performance.md +0 -506
  165. package/library/snippets/power-query/transformations.md +0 -330
  166. package/library/snippets/report-design/accessibility.md +0 -78
  167. package/library/snippets/report-design/chart-selection.md +0 -54
  168. package/library/snippets/report-design/layout-patterns.md +0 -87
  169. package/library/templates/data-models/README.md +0 -93
  170. package/library/templates/data-models/finance-model.md +0 -627
  171. package/library/templates/data-models/retail-star-schema.md +0 -473
  172. package/library/templates/excel/README.md +0 -83
  173. package/library/templates/excel/budget-tracker.md +0 -432
  174. package/library/templates/excel/data-entry-form.md +0 -533
  175. package/library/templates/power-bi/README.md +0 -72
  176. package/library/templates/power-bi/finance-report.md +0 -449
  177. package/library/templates/power-bi/kpi-scorecard.md +0 -461
  178. package/library/templates/power-bi/sales-dashboard.md +0 -281
  179. package/library/themes/excel/README.md +0 -436
  180. package/library/themes/power-bi/README.md +0 -271
  181. package/library/themes/power-bi/accessible.json +0 -307
  182. package/library/themes/power-bi/bi-superpowers-default.json +0 -858
  183. package/library/themes/power-bi/corporate-blue.json +0 -291
  184. package/library/themes/power-bi/dark-mode.json +0 -291
  185. package/library/themes/power-bi/minimal.json +0 -292
  186. package/library/themes/power-bi/print-friendly.json +0 -309
@@ -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
- };
@@ -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
- });
@@ -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