@girardmedia/bootspring 2.2.0 → 2.3.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 (50) hide show
  1. package/README.md +2 -2
  2. package/bin/bootspring.js +35 -96
  3. package/claude-commands/agent.md +34 -0
  4. package/claude-commands/bs.md +31 -0
  5. package/claude-commands/build.md +25 -0
  6. package/claude-commands/skill.md +31 -0
  7. package/claude-commands/todo.md +25 -0
  8. package/dist/cli/index.cjs +17808 -0
  9. package/dist/core/index.d.ts +5814 -0
  10. package/dist/core.js +5780 -0
  11. package/dist/mcp/index.d.ts +1 -0
  12. package/dist/mcp-server.js +2299 -0
  13. package/generators/api-docs.js +2 -2
  14. package/generators/decisions.js +3 -3
  15. package/generators/health.js +16 -16
  16. package/generators/sprint.js +2 -2
  17. package/package.json +27 -59
  18. package/core/api-client.d.ts +0 -69
  19. package/core/api-client.js +0 -1482
  20. package/core/auth.d.ts +0 -98
  21. package/core/auth.js +0 -737
  22. package/core/build-orchestrator.js +0 -508
  23. package/core/build-state.js +0 -612
  24. package/core/config.d.ts +0 -106
  25. package/core/config.js +0 -1328
  26. package/core/context-loader.js +0 -580
  27. package/core/context.d.ts +0 -61
  28. package/core/context.js +0 -327
  29. package/core/entitlements.d.ts +0 -70
  30. package/core/entitlements.js +0 -322
  31. package/core/index.d.ts +0 -53
  32. package/core/index.js +0 -62
  33. package/core/mcp-config.js +0 -115
  34. package/core/policies.d.ts +0 -43
  35. package/core/policies.js +0 -113
  36. package/core/policy-matrix.js +0 -303
  37. package/core/project-activity.js +0 -175
  38. package/core/redaction.d.ts +0 -5
  39. package/core/redaction.js +0 -63
  40. package/core/self-update.js +0 -259
  41. package/core/session.js +0 -353
  42. package/core/task-extractor.js +0 -1098
  43. package/core/telemetry.d.ts +0 -55
  44. package/core/telemetry.js +0 -617
  45. package/core/tier-enforcement.js +0 -928
  46. package/core/utils.d.ts +0 -90
  47. package/core/utils.js +0 -455
  48. package/core/validation.js +0 -572
  49. package/mcp/server.d.ts +0 -57
  50. package/mcp/server.js +0 -264
package/core/utils.d.ts DELETED
@@ -1,90 +0,0 @@
1
- /**
2
- * Bootspring Utils Types
3
- * @module core/utils
4
- */
5
-
6
- export interface Spinner {
7
- start(): Spinner;
8
- stop(): Spinner;
9
- succeed(text?: string): Spinner;
10
- fail(text?: string): Spinner;
11
- warn(text?: string): Spinner;
12
- info(text?: string): Spinner;
13
- }
14
-
15
- export interface ParsedArgs {
16
- _: string[];
17
- [key: string]: string | boolean | string[] | undefined;
18
- }
19
-
20
- /** ANSI color codes */
21
- export const COLORS: {
22
- reset: string;
23
- bold: string;
24
- dim: string;
25
- red: string;
26
- green: string;
27
- yellow: string;
28
- blue: string;
29
- cyan: string;
30
- magenta: string;
31
- };
32
-
33
- /** Print helpers */
34
- export const print: {
35
- success(message: string): void;
36
- error(message: string): void;
37
- warning(message: string): void;
38
- info(message: string): void;
39
- dim(message: string): void;
40
- };
41
-
42
- /**
43
- * Parse command line arguments
44
- * @param args - Arguments array
45
- * @returns Parsed arguments object
46
- */
47
- export function parseArgs(args: string[]): ParsedArgs;
48
-
49
- /**
50
- * Create a CLI spinner
51
- * @param text - Spinner text
52
- * @returns Spinner instance
53
- */
54
- export function createSpinner(text: string): Spinner;
55
-
56
- /**
57
- * Format date for display
58
- * @param date - Date to format
59
- * @returns Formatted date string
60
- */
61
- export function formatDate(date: Date | string | number): string;
62
-
63
- /**
64
- * Format relative time
65
- * @param date - Date to format
66
- * @returns Relative time string (e.g., "2 hours ago")
67
- */
68
- export function formatRelativeTime(date: Date | string | number): string;
69
-
70
- /**
71
- * Check if file exists
72
- * @param filePath - Path to check
73
- * @returns True if file exists
74
- */
75
- export function fileExists(filePath: string): boolean;
76
-
77
- /**
78
- * Read file contents
79
- * @param filePath - Path to read
80
- * @returns File contents or null
81
- */
82
- export function readFile(filePath: string): string | null;
83
-
84
- /**
85
- * Create a formatted table
86
- * @param headers - Table headers
87
- * @param rows - Table rows
88
- * @returns Formatted table string
89
- */
90
- export function createTable(headers: string[], rows: string[][]): string;
package/core/utils.js DELETED
@@ -1,455 +0,0 @@
1
- /**
2
- * Bootspring Utilities
3
- * Shared utility functions
4
- *
5
- * @package bootspring
6
- * @module core/utils
7
- */
8
-
9
- const fs = require('fs');
10
- const path = require('path');
11
-
12
- // ANSI color codes
13
- const COLORS = {
14
- reset: '\x1b[0m',
15
- bold: '\x1b[1m',
16
- dim: '\x1b[2m',
17
- italic: '\x1b[3m',
18
- underline: '\x1b[4m',
19
-
20
- // Foreground
21
- black: '\x1b[30m',
22
- red: '\x1b[31m',
23
- green: '\x1b[32m',
24
- yellow: '\x1b[33m',
25
- blue: '\x1b[34m',
26
- magenta: '\x1b[35m',
27
- cyan: '\x1b[36m',
28
- white: '\x1b[37m',
29
-
30
- // Background
31
- bgBlack: '\x1b[40m',
32
- bgRed: '\x1b[41m',
33
- bgGreen: '\x1b[42m',
34
- bgYellow: '\x1b[43m',
35
- bgBlue: '\x1b[44m',
36
- bgMagenta: '\x1b[45m',
37
- bgCyan: '\x1b[46m',
38
- bgWhite: '\x1b[47m'
39
- };
40
-
41
- /**
42
- * Styled console output
43
- */
44
- const print = {
45
- info: (msg) => console.log(`${COLORS.cyan}ℹ${COLORS.reset} ${msg}`),
46
- success: (msg) => console.log(`${COLORS.green}✓${COLORS.reset} ${msg}`),
47
- warning: (msg) => console.log(`${COLORS.yellow}⚠${COLORS.reset} ${msg}`),
48
- error: (msg) => console.log(`${COLORS.red}✗${COLORS.reset} ${msg}`),
49
- debug: (msg) => process.env.DEBUG && console.log(`${COLORS.dim}⋯ ${msg}${COLORS.reset}`),
50
- header: (msg) => console.log(`\n${COLORS.bold}${COLORS.cyan}${msg}${COLORS.reset}\n`),
51
- dim: (msg) => console.log(`${COLORS.dim}${msg}${COLORS.reset}`),
52
- brand: (msg) => console.log(`${COLORS.cyan}⚡${COLORS.reset} ${msg}`)
53
- };
54
-
55
- /**
56
- * Create a spinner for async operations
57
- * @param {string} message - Spinner message
58
- * @returns {object} Spinner controller
59
- */
60
- function createSpinner(message) {
61
- const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
62
- let frameIndex = 0;
63
- let interval = null;
64
- const isTTY = process.stdout.isTTY;
65
-
66
- // Helper to clear line safely
67
- const clearLine = () => {
68
- if (isTTY && process.stdout.clearLine) {
69
- process.stdout.clearLine(0);
70
- process.stdout.cursorTo(0);
71
- }
72
- };
73
-
74
- return {
75
- start() {
76
- if (isTTY) {
77
- process.stdout.write(`${COLORS.cyan}${frames[0]}${COLORS.reset} ${message}`);
78
- interval = setInterval(() => {
79
- frameIndex = (frameIndex + 1) % frames.length;
80
- clearLine();
81
- process.stdout.write(`${COLORS.cyan}${frames[frameIndex]}${COLORS.reset} ${message}`);
82
- }, 80);
83
- }
84
- return this;
85
- },
86
-
87
- succeed(text = message) {
88
- if (interval) clearInterval(interval);
89
- clearLine();
90
- console.log(`${COLORS.green}✓${COLORS.reset} ${text}`);
91
- return this;
92
- },
93
-
94
- fail(text = message) {
95
- if (interval) clearInterval(interval);
96
- clearLine();
97
- console.log(`${COLORS.red}✗${COLORS.reset} ${text}`);
98
- return this;
99
- },
100
-
101
- warn(text = message) {
102
- if (interval) clearInterval(interval);
103
- clearLine();
104
- console.log(`${COLORS.yellow}⚠${COLORS.reset} ${text}`);
105
- return this;
106
- },
107
-
108
- info(text = message) {
109
- if (interval) clearInterval(interval);
110
- clearLine();
111
- console.log(`${COLORS.cyan}ℹ${COLORS.reset} ${text}`);
112
- return this;
113
- },
114
-
115
- stop() {
116
- if (interval) clearInterval(interval);
117
- clearLine();
118
- return this;
119
- }
120
- };
121
- }
122
-
123
- /**
124
- * Ensure directory exists
125
- * @param {string} dirPath - Directory path
126
- * @returns {boolean} Success status
127
- */
128
- function ensureDir(dirPath) {
129
- try {
130
- if (!fs.existsSync(dirPath)) {
131
- fs.mkdirSync(dirPath, { recursive: true });
132
- }
133
- return true;
134
- } catch {
135
- return false;
136
- }
137
- }
138
-
139
- /**
140
- * Read file safely
141
- * @param {string} filepath - File path
142
- * @param {string} [defaultValue=null] - Default value if file doesn't exist
143
- * @returns {string|null} File contents or default value (null if not found)
144
- */
145
- function readFile(filepath, defaultValue = null) {
146
- try {
147
- return fs.readFileSync(filepath, 'utf-8');
148
- } catch {
149
- return defaultValue;
150
- }
151
- }
152
-
153
- /**
154
- * Write file safely
155
- * @param {string} filepath - File path
156
- * @param {string} content - File content
157
- * @returns {boolean} Success status
158
- */
159
- function writeFile(filepath, content) {
160
- try {
161
- const dir = path.dirname(filepath);
162
- ensureDir(dir);
163
- fs.writeFileSync(filepath, content, 'utf-8');
164
- return true;
165
- } catch {
166
- return false;
167
- }
168
- }
169
-
170
- /**
171
- * Check if file exists
172
- * @param {string} filepath - File path
173
- * @returns {boolean} Exists status
174
- */
175
- function fileExists(filepath) {
176
- try {
177
- return fs.existsSync(filepath);
178
- } catch {
179
- return false;
180
- }
181
- }
182
-
183
- /**
184
- * Get file modification time
185
- * @param {string} filepath - File path
186
- * @returns {Date|null} Modification date or null
187
- */
188
- function getFileTime(filepath) {
189
- try {
190
- const stats = fs.statSync(filepath);
191
- return stats.mtime;
192
- } catch {
193
- return null;
194
- }
195
- }
196
-
197
- /**
198
- * Format date as ISO string without time
199
- * @param {Date} date - Date object
200
- * @returns {string} Formatted date
201
- */
202
- function formatDate(date = new Date()) {
203
- return date.toISOString().split('T')[0];
204
- }
205
-
206
- /**
207
- * Format date as relative time
208
- * @param {Date} date - Date object
209
- * @returns {string} Relative time string
210
- */
211
- function formatRelativeTime(date) {
212
- const now = new Date();
213
- const diff = now - date;
214
- const seconds = Math.floor(diff / 1000);
215
- const minutes = Math.floor(seconds / 60);
216
- const hours = Math.floor(minutes / 60);
217
- const days = Math.floor(hours / 24);
218
-
219
- if (days > 0) return `${days} day${days > 1 ? 's' : ''} ago`;
220
- if (hours > 0) return `${hours} hour${hours > 1 ? 's' : ''} ago`;
221
- if (minutes > 0) return `${minutes} minute${minutes > 1 ? 's' : ''} ago`;
222
- return 'just now';
223
- }
224
-
225
- /**
226
- * Slugify a string
227
- * @param {string} str - Input string
228
- * @returns {string} Slugified string
229
- */
230
- function slugify(str) {
231
- return str
232
- .toLowerCase()
233
- .trim()
234
- .replace(/[^\w\s-]/g, '')
235
- .replace(/[\s_-]+/g, '-')
236
- .replace(/^-+|-+$/g, '');
237
- }
238
-
239
- /**
240
- * Truncate string with ellipsis
241
- * @param {string} str - Input string
242
- * @param {number} length - Max length
243
- * @returns {string} Truncated string
244
- */
245
- function truncate(str, length = 50) {
246
- if (str.length <= length) return str;
247
- return str.slice(0, length - 3) + '...';
248
- }
249
-
250
- /**
251
- * Parse command line arguments
252
- * @param {string[]} args - Argument array
253
- * @returns {object} Parsed arguments { _: positional, ...flags }
254
- */
255
- function parseArgs(args) {
256
- const result = { _: [] };
257
-
258
- for (let i = 0; i < args.length; i++) {
259
- const arg = args[i];
260
-
261
- if (arg.startsWith('--')) {
262
- const key = arg.slice(2);
263
- const nextArg = args[i + 1];
264
-
265
- if (key.includes('=')) {
266
- const [k, v] = key.split('=');
267
- result[k] = v;
268
- } else if (nextArg && !nextArg.startsWith('-')) {
269
- result[key] = nextArg;
270
- i++;
271
- } else {
272
- result[key] = true;
273
- }
274
- } else if (arg.startsWith('-') && arg.length === 2) {
275
- // Short flag (e.g., -v, -n)
276
- const key = arg.slice(1);
277
- const nextArg = args[i + 1];
278
-
279
- // Check if next argument is a value (not another flag)
280
- if (nextArg && !nextArg.startsWith('-')) {
281
- result[key] = nextArg;
282
- i++;
283
- } else {
284
- result[key] = true;
285
- }
286
- } else if (arg.startsWith('-')) {
287
- // Combined short flags (e.g., -abc) - treat each as boolean
288
- const keys = arg.slice(1).split('');
289
- for (const key of keys) {
290
- result[key] = true;
291
- }
292
- } else {
293
- result._.push(arg);
294
- }
295
- }
296
-
297
- return result;
298
- }
299
-
300
- /**
301
- * Create a simple table for console output
302
- * @param {string[]} headers - Table headers
303
- * @param {string[][]} rows - Table rows
304
- * @returns {string} Formatted table
305
- */
306
- function createTable(headers, rows) {
307
- const widths = headers.map((h, i) => {
308
- const colValues = [h, ...rows.map(r => String(r[i] || ''))];
309
- return Math.max(...colValues.map(v => v.length));
310
- });
311
-
312
- const separator = widths.map(w => '─'.repeat(w + 2)).join('┼');
313
- const formatRow = (row) => row.map((cell, i) => ` ${String(cell).padEnd(widths[i])} `).join('│');
314
-
315
- const lines = [
316
- formatRow(headers),
317
- separator,
318
- ...rows.map(formatRow)
319
- ];
320
-
321
- return lines.join('\n');
322
- }
323
-
324
- /**
325
- * Deep clone an object
326
- * @param {object} obj - Object to clone
327
- * @returns {object} Cloned object
328
- */
329
- function deepClone(obj) {
330
- return JSON.parse(JSON.stringify(obj));
331
- }
332
-
333
- /**
334
- * Check if running in CI environment
335
- * @returns {boolean} CI status
336
- */
337
- function isCI() {
338
- return !!(
339
- process.env.CI ||
340
- process.env.CONTINUOUS_INTEGRATION ||
341
- process.env.GITHUB_ACTIONS ||
342
- process.env.GITLAB_CI ||
343
- process.env.CIRCLECI ||
344
- process.env.TRAVIS
345
- );
346
- }
347
-
348
- /**
349
- * Get package.json from project
350
- * @param {string} projectRoot - Project root path
351
- * @returns {object|null} Package.json contents or null
352
- */
353
- function getPackageJson(projectRoot) {
354
- const pkgPath = path.join(projectRoot, 'package.json');
355
- try {
356
- return JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
357
- } catch {
358
- return null;
359
- }
360
- }
361
-
362
- /**
363
- * Check if running in MCP context (AI assistant integration only)
364
- * @returns {boolean} MCP status
365
- */
366
- function isMCPContext() {
367
- return !!(
368
- process.env.BOOTSPRING_MCP ||
369
- process.env.MCP_SERVER ||
370
- process.env.CLAUDE_CODE_MCP
371
- );
372
- }
373
-
374
- /**
375
- * Print MCP recommendation message and return false if not in MCP context.
376
- * This is a soft gate - callers should provide degraded functionality.
377
- * @param {string} feature - Feature name that benefits from MCP
378
- * @param {Object} options - Configuration options
379
- * @param {boolean} options.silent - Don't print any message
380
- * @param {boolean} options.brief - Print brief message instead of full
381
- * @returns {boolean} True if in MCP context, false otherwise
382
- */
383
- function requireMCP(feature, options = {}) {
384
- if (isMCPContext()) {
385
- return true;
386
- }
387
-
388
- if (options.silent) {
389
- return false;
390
- }
391
-
392
- if (options.brief) {
393
- console.log(`${COLORS.dim}Note: ${feature} has enhanced features when used with MCP integration.${COLORS.reset}`);
394
- console.log(`${COLORS.dim}Run "bootspring mcp" for setup instructions.${COLORS.reset}\n`);
395
- return false;
396
- }
397
-
398
- console.log(`
399
- ${COLORS.yellow}${COLORS.bold}Limited Mode - MCP Integration Recommended${COLORS.reset}
400
-
401
- ${feature} works in CLI mode but has enhanced features through MCP integration.
402
- MCP enables real-time AI-assisted workflows and deeper integration.
403
-
404
- ${COLORS.bold}Current Mode:${COLORS.reset} CLI (limited functionality)
405
- ${COLORS.bold}Enhanced Mode:${COLORS.reset} MCP (full AI integration)
406
-
407
- ${COLORS.bold}To enable full features:${COLORS.reset}
408
- 1. Start the MCP server:
409
- ${COLORS.cyan}bootspring mcp start${COLORS.reset}
410
-
411
- 2. Add Bootspring to your AI client's MCP configuration
412
-
413
- ${COLORS.bold}Documentation:${COLORS.reset}
414
- https://bootspring.com/docs/mcp-setup
415
-
416
- ${COLORS.dim}Run "bootspring mcp" for server options${COLORS.reset}
417
- `);
418
-
419
- return false;
420
- }
421
-
422
- /**
423
- * Print a warning about MCP-only features (non-blocking)
424
- * Use this when a command works without MCP but has reduced functionality
425
- * @param {string} feature - Feature with limited functionality
426
- */
427
- function warnMCPLimited(feature) {
428
- if (isMCPContext()) return;
429
-
430
- console.log(`${COLORS.dim}Note: ${feature} - some features require MCP integration.${COLORS.reset}`);
431
- console.log(`${COLORS.dim}Run "bootspring mcp" for setup instructions.${COLORS.reset}\n`);
432
- }
433
-
434
- module.exports = {
435
- COLORS,
436
- print,
437
- createSpinner,
438
- ensureDir,
439
- readFile,
440
- writeFile,
441
- fileExists,
442
- getFileTime,
443
- formatDate,
444
- formatRelativeTime,
445
- slugify,
446
- truncate,
447
- parseArgs,
448
- createTable,
449
- deepClone,
450
- isCI,
451
- getPackageJson,
452
- isMCPContext,
453
- requireMCP,
454
- warnMCPLimited
455
- };