@girardmedia/bootspring 2.1.3 → 2.2.1

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 (65) hide show
  1. package/bin/bootspring.js +157 -83
  2. package/claude-commands/agent.md +34 -0
  3. package/claude-commands/bs.md +31 -0
  4. package/claude-commands/build.md +25 -0
  5. package/claude-commands/skill.md +31 -0
  6. package/claude-commands/todo.md +25 -0
  7. package/dist/core/index.d.ts +5814 -0
  8. package/dist/core.js +5779 -0
  9. package/dist/index.js +93883 -0
  10. package/dist/mcp/index.d.ts +1 -0
  11. package/dist/mcp-server.js +2298 -0
  12. package/generators/api-docs.js +3 -3
  13. package/generators/decisions.js +14 -14
  14. package/generators/health.js +6 -6
  15. package/generators/sprint.js +4 -4
  16. package/generators/templates/build-planning.template.js +2 -2
  17. package/generators/visual-doc-generator.js +1 -1
  18. package/package.json +22 -68
  19. package/cli/agent.js +0 -799
  20. package/cli/auth.js +0 -896
  21. package/cli/billing.js +0 -320
  22. package/cli/build.js +0 -1442
  23. package/cli/dashboard.js +0 -123
  24. package/cli/init.js +0 -669
  25. package/cli/mcp.js +0 -240
  26. package/cli/orchestrator.js +0 -240
  27. package/cli/project.js +0 -825
  28. package/cli/quality.js +0 -281
  29. package/cli/skill.js +0 -503
  30. package/cli/switch.js +0 -453
  31. package/cli/todo.js +0 -629
  32. package/cli/update.js +0 -132
  33. package/core/api-client.d.ts +0 -69
  34. package/core/api-client.js +0 -1482
  35. package/core/auth.d.ts +0 -98
  36. package/core/auth.js +0 -737
  37. package/core/build-orchestrator.js +0 -508
  38. package/core/build-state.js +0 -612
  39. package/core/config.d.ts +0 -106
  40. package/core/config.js +0 -1328
  41. package/core/context-loader.js +0 -580
  42. package/core/context.d.ts +0 -61
  43. package/core/context.js +0 -327
  44. package/core/entitlements.d.ts +0 -70
  45. package/core/entitlements.js +0 -322
  46. package/core/index.d.ts +0 -53
  47. package/core/index.js +0 -62
  48. package/core/mcp-config.js +0 -115
  49. package/core/policies.d.ts +0 -43
  50. package/core/policies.js +0 -113
  51. package/core/policy-matrix.js +0 -303
  52. package/core/project-activity.js +0 -175
  53. package/core/redaction.d.ts +0 -5
  54. package/core/redaction.js +0 -63
  55. package/core/self-update.js +0 -259
  56. package/core/session.js +0 -353
  57. package/core/task-extractor.js +0 -1098
  58. package/core/telemetry.d.ts +0 -55
  59. package/core/telemetry.js +0 -617
  60. package/core/tier-enforcement.js +0 -928
  61. package/core/utils.d.ts +0 -90
  62. package/core/utils.js +0 -455
  63. package/core/validation.js +0 -572
  64. package/mcp/server.d.ts +0 -57
  65. package/mcp/server.js +0 -264
package/cli/switch.js DELETED
@@ -1,453 +0,0 @@
1
- /**
2
- * Bootspring Switch Command
3
- *
4
- * Switch between project contexts.
5
- *
6
- * Commands:
7
- * (default) Show current project and list available
8
- * <project> Switch to a project by name or slug
9
- * recent Show and switch to recent projects
10
- * init Create .bootspring.json in current directory
11
- * clear Clear current project context
12
- * status Show detailed context status
13
- *
14
- * @package bootspring
15
- * @command switch
16
- */
17
-
18
- const https = require('https');
19
- const http = require('http');
20
- const auth = require('../core/auth');
21
- const session = require('../core/session');
22
- const utils = require('../core/utils');
23
-
24
- const API_BASE = process.env.BOOTSPRING_API_URL || 'https://www.bootspring.com';
25
-
26
- /**
27
- * Make a direct API request (without v1 prefix)
28
- */
29
- async function directRequest(method, path) {
30
- const apiKey = auth.getApiKey();
31
- const token = auth.getToken();
32
- const deviceId = auth.getDeviceId();
33
- const url = new URL(`/api${path}`, API_BASE);
34
- const isHttps = url.protocol === 'https:';
35
- const httpModule = isHttps ? https : http;
36
-
37
- const headers = {
38
- 'Content-Type': 'application/json',
39
- 'User-Agent': `bootspring-cli/${require('../package.json').version}`,
40
- 'X-Device-Id': deviceId
41
- };
42
-
43
- if (apiKey) {
44
- headers['X-API-Key'] = apiKey;
45
- } else if (token) {
46
- headers['Authorization'] = `Bearer ${token}`;
47
- }
48
-
49
- return new Promise((resolve, reject) => {
50
- const req = httpModule.request(url, {
51
- method,
52
- headers,
53
- timeout: 30000
54
- }, (res) => {
55
- let body = '';
56
- res.on('data', chunk => body += chunk);
57
- res.on('end', () => {
58
- try {
59
- const json = JSON.parse(body);
60
- if (res.statusCode >= 400) {
61
- const error = new Error(json.error || json.message || 'API Error');
62
- error.status = res.statusCode;
63
- reject(error);
64
- } else {
65
- resolve(json);
66
- }
67
- } catch {
68
- if (res.statusCode >= 400) {
69
- reject(new Error(`API Error: ${res.statusCode}`));
70
- } else {
71
- resolve(body);
72
- }
73
- }
74
- });
75
- });
76
-
77
- req.on('error', reject);
78
- req.on('timeout', () => {
79
- req.destroy();
80
- reject(new Error('Request timeout'));
81
- });
82
- req.end();
83
- });
84
- }
85
-
86
- // ANSI colors
87
- const colors = {
88
- reset: '\x1b[0m',
89
- bold: '\x1b[1m',
90
- dim: '\x1b[2m',
91
- green: '\x1b[32m',
92
- yellow: '\x1b[33m',
93
- red: '\x1b[31m',
94
- cyan: '\x1b[36m'
95
- };
96
-
97
- /**
98
- * Run switch command
99
- */
100
- async function run(args) {
101
- const parsedArgs = utils.parseArgs(args);
102
- const subcommand = parsedArgs._[0];
103
-
104
- // Handle --init flag anywhere
105
- if (parsedArgs.init) {
106
- if (!auth.isAuthenticated()) {
107
- console.log(`${colors.yellow}Not logged in${colors.reset}`);
108
- console.log(`${colors.dim}Run 'bootspring auth login' first${colors.reset}`);
109
- return;
110
- }
111
- await initLocalConfig();
112
- return;
113
- }
114
-
115
- // Handle --help flag
116
- if (parsedArgs.help || parsedArgs.h || subcommand === 'help') {
117
- showHelp();
118
- return;
119
- }
120
-
121
- // Handle subcommands that don't require auth (local data only)
122
- if (subcommand === 'status') {
123
- showStatus();
124
- return;
125
- }
126
-
127
- if (subcommand === 'clear') {
128
- clearContext();
129
- return;
130
- }
131
-
132
- if (subcommand === 'recent') {
133
- showRecent();
134
- return;
135
- }
136
-
137
- // Commands that require authentication
138
- if (!auth.isAuthenticated()) {
139
- console.log(`${colors.yellow}Not logged in${colors.reset}`);
140
- console.log(`${colors.dim}Run 'bootspring auth login' first${colors.reset}`);
141
- return;
142
- }
143
-
144
- if (subcommand === 'init') {
145
- await initLocalConfig();
146
- return;
147
- }
148
-
149
- // If no subcommand or unknown, treat as project identifier or show projects
150
- if (!subcommand) {
151
- await showProjects();
152
- return;
153
- }
154
-
155
- // Switch to specified project
156
- await switchTo(subcommand);
157
- }
158
-
159
- /**
160
- * Show current project and list available projects
161
- */
162
- async function showProjects() {
163
- console.log(`\n${colors.bold}Project Context${colors.reset}\n`);
164
-
165
- // Show current project
166
- const current = session.getEffectiveProject();
167
- if (current) {
168
- console.log(`${colors.cyan}Current:${colors.reset} ${colors.bold}${current.name}${colors.reset}`);
169
- if (current.slug) {
170
- console.log(`${colors.dim} ${current.slug}${colors.reset}`);
171
- }
172
- const sourceLabel = current.source === 'local' ? '(from .bootspring.json)' : '(from session)';
173
- console.log(`${colors.dim} ${sourceLabel}${colors.reset}`);
174
- console.log();
175
- } else {
176
- console.log(`${colors.yellow}No project selected${colors.reset}\n`);
177
- }
178
-
179
- // Fetch available projects
180
- console.log(`${colors.dim}Fetching projects...${colors.reset}`);
181
-
182
- try {
183
- const response = await directRequest('GET', '/projects');
184
- const projects = response.projects || [];
185
-
186
- if (projects.length === 0) {
187
- console.log(`\n${colors.yellow}No projects found${colors.reset}`);
188
- console.log(`${colors.dim}Create a project at https://bootspring.com/dashboard/projects${colors.reset}`);
189
- return;
190
- }
191
-
192
- console.log(`\n${colors.bold}Available Projects${colors.reset}\n`);
193
- for (const project of projects) {
194
- const isCurrent = current?.id === project.id;
195
- const marker = isCurrent ? colors.green + '* ' + colors.reset : ' ';
196
- console.log(`${marker}${colors.bold}${project.name}${colors.reset} ${colors.dim}(${project.slug})${colors.reset}`);
197
- }
198
-
199
- console.log(`\n${colors.dim}Usage: bootspring switch <project-name-or-slug>${colors.reset}`);
200
- } catch (error) {
201
- console.log(`\n${colors.red}Failed to fetch projects: ${error.message}${colors.reset}`);
202
- }
203
- }
204
-
205
- /**
206
- * Switch to a specific project
207
- */
208
- async function switchTo(identifier) {
209
- console.log(`${colors.dim}Switching to project...${colors.reset}`);
210
-
211
- try {
212
- const response = await directRequest('GET', '/projects');
213
- const projects = response.projects || [];
214
-
215
- // Find project by name or slug
216
- const project = projects.find(
217
- p => p.name.toLowerCase() === identifier.toLowerCase() ||
218
- p.slug === identifier.toLowerCase()
219
- );
220
-
221
- if (!project) {
222
- console.log(`\n${colors.red}Project not found: ${identifier}${colors.reset}`);
223
- console.log(`${colors.dim}Run 'bootspring switch' to see available projects${colors.reset}`);
224
- return;
225
- }
226
-
227
- // Update session
228
- session.setCurrentProject(project);
229
- session.addRecentProject(project);
230
-
231
- console.log(`\n${colors.green}Switched to: ${project.name}${colors.reset}`);
232
-
233
- // Automatically create/update local config to lock this directory to the project
234
- const existingConfig = session.findLocalConfig();
235
- if (existingConfig && existingConfig._dir === process.cwd()) {
236
- // Update existing config in same directory
237
- try {
238
- session.createLocalConfig(process.cwd(), project);
239
- console.log(`${colors.dim}Updated: ${existingConfig._path}${colors.reset}`);
240
- } catch {
241
- // Ignore errors
242
- }
243
- } else if (!existingConfig) {
244
- // Create new local config to lock this directory
245
- try {
246
- const configPath = session.createLocalConfig(process.cwd(), project);
247
- console.log(`${colors.dim}Locked directory to "${project.name}"${colors.reset}`);
248
- console.log(`${colors.dim}Config: ${configPath}${colors.reset}`);
249
- } catch {
250
- // Ignore errors - some directories may not be writable
251
- }
252
- } else {
253
- // Config exists in parent directory
254
- console.log(`${colors.dim}Note: Parent directory locked to "${existingConfig.projectName}"${colors.reset}`);
255
- console.log(`${colors.dim}Run 'bootspring switch --init' here to override${colors.reset}`);
256
- }
257
- } catch (error) {
258
- console.log(`\n${colors.red}Failed to switch project: ${error.message}${colors.reset}`);
259
- }
260
- }
261
-
262
- /**
263
- * Show recent projects and allow quick switching
264
- */
265
- function showRecent() {
266
- const recent = session.getRecentProjects();
267
-
268
- console.log(`\n${colors.bold}Recent Projects${colors.reset}\n`);
269
-
270
- if (recent.length === 0) {
271
- console.log(`${colors.dim}No recent projects${colors.reset}`);
272
- console.log(`${colors.dim}Use 'bootspring switch <project>' to switch to a project${colors.reset}`);
273
- return;
274
- }
275
-
276
- const current = session.getEffectiveProject();
277
-
278
- for (let i = 0; i < recent.length; i++) {
279
- const project = recent[i];
280
- const isCurrent = current?.id === project.id;
281
- const marker = isCurrent ? colors.green + '* ' + colors.reset : ' ';
282
- const index = colors.dim + `[${i + 1}]` + colors.reset;
283
- const lastUsed = project.lastUsed ? formatRelativeTime(project.lastUsed) : '';
284
-
285
- console.log(`${marker}${index} ${colors.bold}${project.name}${colors.reset} ${colors.dim}(${project.slug})${colors.reset}`);
286
- if (lastUsed) {
287
- console.log(` ${colors.dim}${lastUsed}${colors.reset}`);
288
- }
289
- }
290
-
291
- console.log(`\n${colors.dim}Usage: bootspring switch <name-or-number>${colors.reset}`);
292
- }
293
-
294
- /**
295
- * Initialize local .bootspring.json config
296
- */
297
- async function initLocalConfig() {
298
- const current = session.getEffectiveProject();
299
-
300
- if (!current) {
301
- console.log(`${colors.yellow}No project selected${colors.reset}`);
302
- console.log(`${colors.dim}First switch to a project: bootspring switch <project>${colors.reset}`);
303
- return;
304
- }
305
-
306
- // Check if local config already exists
307
- const existingConfig = session.findLocalConfig(process.cwd());
308
- if (existingConfig && existingConfig._dir === process.cwd()) {
309
- console.log(`${colors.yellow}Local config already exists${colors.reset}`);
310
- console.log(`${colors.dim}Path: ${existingConfig._path}${colors.reset}`);
311
- console.log(`${colors.dim}Project: ${existingConfig.projectName || existingConfig.projectId}${colors.reset}`);
312
- return;
313
- }
314
-
315
- // Create local config
316
- try {
317
- const configPath = session.createLocalConfig(process.cwd(), current);
318
- console.log(`\n${colors.green}Created local config${colors.reset}`);
319
- console.log(`${colors.dim}Path: ${configPath}${colors.reset}`);
320
- console.log(`${colors.dim}Project: ${current.name}${colors.reset}`);
321
- console.log(`\n${colors.dim}This directory is now linked to project "${current.name}"${colors.reset}`);
322
- } catch (error) {
323
- console.log(`${colors.red}Failed to create config: ${error.message}${colors.reset}`);
324
- }
325
- }
326
-
327
- /**
328
- * Show detailed context status
329
- */
330
- function showStatus() {
331
- const state = session.getSessionState();
332
-
333
- console.log(`\n${colors.bold}${colors.cyan}⚡ Project Context Status${colors.reset}\n`);
334
-
335
- // Current project
336
- console.log(`${colors.bold}Current Project${colors.reset}`);
337
- if (state.project) {
338
- console.log(` Name: ${colors.bold}${state.project.name}${colors.reset}`);
339
- if (state.project.slug) {
340
- console.log(` Slug: ${state.project.slug}`);
341
- }
342
- console.log(` ID: ${colors.dim}${state.project.id}${colors.reset}`);
343
- console.log(` Source: ${state.source === 'local' ? colors.green + 'local config' : colors.cyan + 'session'}${colors.reset}`);
344
- } else {
345
- console.log(` ${colors.yellow}None selected${colors.reset}`);
346
- }
347
- console.log();
348
-
349
- // Local config
350
- console.log(`${colors.bold}Local Config${colors.reset}`);
351
- if (state.hasLocalConfig) {
352
- console.log(` ${colors.green}●${colors.reset} Found: ${state.localConfigPath}`);
353
- } else {
354
- console.log(` ${colors.dim}○ Not found in current directory or ancestors${colors.reset}`);
355
- console.log(` ${colors.dim}Run 'bootspring switch --init' to create one${colors.reset}`);
356
- }
357
- console.log();
358
-
359
- // Session
360
- console.log(`${colors.bold}Session${colors.reset}`);
361
- console.log(` Status: ${state.hasSession ? colors.green + 'active' : colors.dim + 'inactive'}${colors.reset}`);
362
- if (state.lastUpdated) {
363
- console.log(` Last updated: ${formatRelativeTime(state.lastUpdated)}`);
364
- }
365
- console.log(` Recent projects: ${state.recentProjects.length}`);
366
- console.log();
367
-
368
- // Paths
369
- console.log(`${colors.bold}Paths${colors.reset}`);
370
- console.log(` Session file: ${colors.dim}${session.SESSION_FILE}${colors.reset}`);
371
- console.log(` Config name: ${colors.dim}${session.LOCAL_CONFIG_NAME}${colors.reset}`);
372
- }
373
-
374
- /**
375
- * Clear current project context
376
- */
377
- function clearContext() {
378
- const current = session.getEffectiveProject();
379
-
380
- if (!current) {
381
- console.log(`${colors.dim}No project context to clear${colors.reset}`);
382
- return;
383
- }
384
-
385
- // Clear session project
386
- session.setCurrentProject(null);
387
-
388
- console.log(`\n${colors.green}Cleared project context${colors.reset}`);
389
- console.log(`${colors.dim}Previous project: ${current.name}${colors.reset}`);
390
-
391
- // Check for local config
392
- const localConfig = session.findLocalConfig();
393
- if (localConfig) {
394
- console.log(`\n${colors.yellow}Note:${colors.reset} Local config still exists at:`);
395
- console.log(` ${localConfig._path}`);
396
- console.log(`${colors.dim}Delete it manually if needed${colors.reset}`);
397
- }
398
- }
399
-
400
- /**
401
- * Show help
402
- */
403
- function showHelp() {
404
- console.log(`
405
- ${colors.cyan}${colors.bold}⚡ Bootspring Switch${colors.reset}
406
- ${colors.dim}Switch between project contexts${colors.reset}
407
-
408
- ${colors.bold}Usage:${colors.reset}
409
- bootspring switch Show current and available projects
410
- bootspring switch <project> Switch to a project by name or slug
411
- bootspring switch recent Show recent projects
412
- bootspring switch init Create .bootspring.json in current directory
413
- bootspring switch --init Same as above
414
- bootspring switch clear Clear current project context
415
- bootspring switch status Show detailed context status
416
-
417
- ${colors.bold}Options:${colors.reset}
418
- --init Create local .bootspring.json config
419
- --help, -h Show this help
420
-
421
- ${colors.bold}Examples:${colors.reset}
422
- bootspring switch my-project Switch to "my-project"
423
- bootspring switch List all available projects
424
- bootspring switch recent Show recently used projects
425
- bootspring switch --init Link current directory to active project
426
-
427
- ${colors.bold}Context Priority:${colors.reset}
428
- 1. Local .bootspring.json (in current directory or ancestors)
429
- 2. Session project (from 'bootspring switch <project>')
430
- `);
431
- }
432
-
433
- /**
434
- * Format relative time
435
- */
436
- function formatRelativeTime(isoDate) {
437
- const date = new Date(isoDate);
438
- const now = new Date();
439
- const diffMs = now - date;
440
- const diffSecs = Math.floor(diffMs / 1000);
441
- const diffMins = Math.floor(diffSecs / 60);
442
- const diffHours = Math.floor(diffMins / 60);
443
- const diffDays = Math.floor(diffHours / 24);
444
-
445
- if (diffSecs < 60) return 'just now';
446
- if (diffMins < 60) return `${diffMins}m ago`;
447
- if (diffHours < 24) return `${diffHours}h ago`;
448
- if (diffDays < 7) return `${diffDays}d ago`;
449
-
450
- return date.toLocaleDateString();
451
- }
452
-
453
- module.exports = { run };