@girardmedia/bootspring 2.1.2 → 2.2.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/bin/bootspring.js CHANGED
@@ -1,15 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Bootspring CLI
5
- * Thin client for the hosted Bootspring MCP and API platform.
4
+ * Bootspring CLI Bridge
5
+ * Routes commands to either the modern v2 monorepo CLI or legacy v1 handlers.
6
6
  */
7
7
 
8
8
  const path = require('path');
9
- const selfUpdate = require('../core/self-update');
10
- const { ensureProjectMcpConfig } = require('../core/mcp-config');
9
+ const fs = require('fs');
11
10
 
12
11
  const VERSION = require('../package.json').version;
12
+ const MONOREPO_CLI_DIST = path.join(__dirname, '../monorepo/apps/cli/dist/index.js');
13
+ const USE_V2 = process.env.BOOTSPRING_V2 === 'true' || process.argv.includes('--v2');
13
14
 
14
15
  const C = {
15
16
  reset: '\x1b[0m',
@@ -21,119 +22,138 @@ const C = {
21
22
  red: '\x1b[31m'
22
23
  };
23
24
 
24
- const LOGO = `${C.cyan}${C.bold}⚡ Bootspring${C.reset} ${C.dim}v${VERSION}${C.reset}`;
25
-
26
- const COMMANDS = {
27
- auth: { handler: '../cli/auth.js', description: 'Login, logout, and account status' },
28
- switch: { handler: '../cli/switch.js', description: 'Switch project context' },
29
- project: { handler: '../cli/project.js', description: 'Manage cloud projects and members' },
30
- init: { handler: '../cli/init.js', description: 'Initialize Bootspring in the current project' },
31
- build: { handler: '../cli/build.js', description: 'Run the local build loop and task workflow' },
32
- agent: { handler: '../cli/agent.js', description: 'Use hosted specialist agents' },
33
- skill: { handler: '../cli/skill.js', description: 'Browse hosted skills and patterns' },
34
- orchestrator: { handler: '../cli/orchestrator.js', description: 'Run hosted workflow orchestration' },
35
- quality: { handler: '../cli/quality.js', description: 'Fetch hosted quality gates' },
36
- billing: { handler: '../cli/billing.js', description: 'View subscription and usage' },
37
- update: { handler: '../cli/update.js', description: 'Check for and apply updates' },
38
- todo: { handler: '../cli/todo.js', description: 'Manage local todo.md tasks' },
39
- dashboard: { handler: '../cli/dashboard.js', description: 'Open the cloud dashboard' },
40
- mcp: { handler: '../cli/mcp.js', description: 'Start the hosted MCP proxy' },
41
- help: { description: 'Show help' }
42
- };
25
+ const LOGO = `${C.cyan}${C.bold}⚡ Bootspring${C.reset} ${C.dim}v${VERSION}${C.reset}${USE_V2 ? ` ${C.green}(v2-beta)${C.reset}` : ''}`;
43
26
 
44
- function showHelp(command = null) {
45
- if (command && COMMANDS[command]) {
46
- console.log(LOGO);
47
- console.log(`\n${C.bold}${command}${C.reset}`);
48
- console.log(`${C.dim}${COMMANDS[command].description}${C.reset}\n`);
49
- return;
50
- }
27
+ /**
28
+ * v2-enabled commands (monorepo implementations)
29
+ */
30
+ const V2_COMMANDS = [
31
+ // Core
32
+ 'auth', 'project', 'switch', 'init', 'mcp', 'health',
33
+ // Content & Agents
34
+ 'agent', 'skill', 'todo', 'billing',
35
+ // Build & Deploy
36
+ 'build', 'deploy', 'loop', 'quality', 'security', 'doctor',
37
+ // Planning & PRD
38
+ 'plan', 'prd', 'preseed', 'preseed-start', 'preseed-from-codebase',
39
+ // Analysis & Monitoring
40
+ 'analyze', 'audit', 'monitor', 'metrics', 'visualize',
41
+ // Documentation & Content
42
+ 'generate', 'dashboard', 'content', 'docs', 'seed',
43
+ // Intelligence
44
+ 'learn', 'memory', 'suggest', 'orchestrator', 'validate', 'watch',
45
+ // Business & Legal
46
+ 'business', 'fundraise', 'legal', 'org',
47
+ // Infrastructure
48
+ 'workspace', 'cloud-sync', 'github', 'onboard', 'checkpoint',
49
+ // Misc
50
+ 'log', 'mvp', 'plugin', 'task', 'telemetry', 'update'
51
+ ];
51
52
 
53
+ /**
54
+ * Legacy v1 commands (kept for backward compat, prefer v2)
55
+ */
56
+ const V1_COMMANDS = {};
57
+ function showHelp() {
52
58
  console.log(LOGO);
53
- console.log(`${C.dim}Thin client powered by Bootspring cloud MCP and API services${C.reset}\n`);
59
+ console.log(`${C.dim}Development scaffolding with intelligence${C.reset}\n`);
54
60
  console.log(`${C.bold}Usage:${C.reset} bootspring <command> [options]\n`);
55
- console.log(`${C.bold}Commands:${C.reset}`);
56
61
 
57
- for (const [name, config] of Object.entries(COMMANDS)) {
58
- const padding = ' '.repeat(Math.max(2, 14 - name.length));
59
- console.log(` ${C.cyan}${name}${C.reset}${padding}${C.dim}${config.description}${C.reset}`);
62
+ const groups = {
63
+ 'Core': ['auth', 'init', 'project', 'switch', 'health', 'mcp', 'dashboard'],
64
+ 'Build & Deploy': ['build', 'deploy', 'loop', 'quality', 'security', 'doctor', 'update'],
65
+ 'Planning': ['plan', 'prd', 'preseed', 'preseed-start', 'preseed-from-codebase', 'seed'],
66
+ 'Agents & Skills': ['agent', 'skill', 'todo', 'billing'],
67
+ 'Analysis': ['analyze', 'audit', 'monitor', 'metrics', 'validate', 'visualize'],
68
+ 'Docs & Content': ['generate', 'content', 'docs'],
69
+ 'Intelligence': ['learn', 'memory', 'suggest', 'orchestrator', 'watch'],
70
+ 'Business': ['business', 'fundraise', 'legal', 'org'],
71
+ 'Infrastructure': ['workspace', 'cloud-sync', 'github', 'onboard', 'checkpoint'],
72
+ 'Tools': ['log', 'mvp', 'plugin', 'task', 'telemetry']
73
+ };
74
+
75
+ for (const [group, cmds] of Object.entries(groups)) {
76
+ console.log(`${C.bold}${group}:${C.reset} ${cmds.map(c => `${C.green}${c}${C.reset}`).join(', ')}`);
60
77
  }
61
78
 
62
- console.log(`\n${C.bold}Get Started:${C.reset}`);
63
- console.log(` ${C.dim}$${C.reset} bootspring auth login`);
64
- console.log(` ${C.dim}$${C.reset} bootspring init`);
65
- console.log(` ${C.dim}$${C.reset} bootspring mcp config`);
66
-
67
- console.log(`\n${C.dim}Docs: https://bootspring.com/docs${C.reset}`);
68
- console.log(`${C.dim}Pricing: https://bootspring.com/pricing${C.reset}\n`);
79
+ console.log(`\n${C.dim}Run "bootspring <command> --help" for command details${C.reset}`);
80
+ console.log(`${C.dim}Docs: https://bootspring.com/docs${C.reset}\n`);
69
81
  }
70
82
 
71
- async function runCommand(command, args) {
72
- const config = COMMANDS[command];
73
- if (!config || !config.handler) {
74
- showHelp(command);
83
+ async function runV1(command, args) {
84
+ const config = V1_COMMANDS[command];
85
+ if (!config) {
86
+ showHelp();
75
87
  return;
76
88
  }
77
-
78
89
  const handler = require(path.resolve(__dirname, config.handler));
79
90
  await handler.run(args);
80
91
  }
81
92
 
93
+ async function runV2(args) {
94
+ if (!fs.existsSync(MONOREPO_CLI_DIST)) {
95
+ console.error(`${C.red}Error: v2 CLI build not found at ${MONOREPO_CLI_DIST}${C.reset}`);
96
+ console.error(`${C.dim}Please run 'cd monorepo && pnpm build' first.${C.reset}`);
97
+ process.exit(1);
98
+ }
99
+
100
+ // Filter out the --v2 flag if present
101
+ const cleanArgs = args.filter(arg => arg !== '--v2');
102
+
103
+ // Use dynamic import for ESM v2 CLI
104
+ const { spawn } = require('child_process');
105
+ const child = spawn('node', [MONOREPO_CLI_DIST, ...cleanArgs], {
106
+ stdio: 'inherit',
107
+ env: { ...process.env, BOOTSPRING_V2_BRIDGE: 'true' }
108
+ });
109
+
110
+ child.on('exit', (code) => {
111
+ process.exit(code || 0);
112
+ });
113
+ }
114
+
82
115
  async function main() {
83
116
  const args = process.argv.slice(2);
84
117
  const command = args[0];
85
118
 
86
- if (!command) {
119
+ if (!command || command === '--help' || command === '-h' || command === 'help') {
87
120
  showHelp();
88
121
  return;
89
122
  }
90
123
 
91
124
  if (command === '--version' || command === '-v') {
92
- console.log(`Bootspring v${VERSION}`);
125
+ console.log(`Bootspring v${VERSION} (Bridge)`);
93
126
  return;
94
127
  }
95
128
 
96
- if (command === '--help' || command === '-h' || command === 'help') {
97
- showHelp(args[1]);
98
- return;
129
+ // Auto-update check (before any command execution)
130
+ try {
131
+ const selfUpdate = require(path.resolve(__dirname, '../core/self-update'));
132
+ selfUpdate.ensureLatestVersion(args);
133
+ } catch {
134
+ // Best-effort; don't block CLI on update failures
99
135
  }
100
136
 
101
- if (!COMMANDS[command]) {
102
- console.log(LOGO);
103
- console.log(`\n${C.red}Command '${command}' is not available in the public thin client.${C.reset}`);
104
- console.log(`${C.dim}Run 'bootspring help' to see supported launch commands.${C.reset}\n`);
105
- process.exitCode = 1;
137
+ // Route to v2 if explicitly requested or if command is migrated
138
+ if (USE_V2 || V2_COMMANDS.includes(command)) {
139
+ await runV2(args);
106
140
  return;
107
141
  }
108
142
 
109
- const updateResult = selfUpdate.ensureLatestVersion(args);
110
- if (updateResult.updated) {
111
- process.exitCode = updateResult.exitCode;
143
+ // Fallback to v1 for unmigrated commands
144
+ if (V1_COMMANDS[command]) {
145
+ try {
146
+ await runV1(command, args.slice(1));
147
+ } catch (error) {
148
+ console.error(`\n${C.red}Legacy Error: ${error.message}${C.reset}\n`);
149
+ process.exit(1);
150
+ }
112
151
  return;
113
152
  }
114
153
 
115
- const mcpConfigResult = ensureProjectMcpConfig(process.cwd(), { createIfMissing: false });
116
- if (mcpConfigResult.status === 'updated') {
117
- console.error('[bootspring] Updated .mcp.json to use the managed latest-package launcher.');
118
- }
119
-
120
- try {
121
- await runCommand(command, args.slice(1));
122
- } catch (error) {
123
- console.log(LOGO);
124
- console.log(`\n${C.red}Error: ${error.message}${C.reset}`);
125
-
126
- if (error.code === 'AUTH_REQUIRED' || error.status === 401) {
127
- console.log(`${C.dim}Run 'bootspring auth login' to authenticate.${C.reset}`);
128
- } else if (error.status === 403) {
129
- console.log(`${C.dim}Upgrade access: https://bootspring.com/pricing${C.reset}`);
130
- } else if (error.code === 'ENOTFOUND' || error.code === 'ECONNREFUSED') {
131
- console.log(`${C.dim}Could not reach the Bootspring API.${C.reset}`);
132
- }
133
-
134
- console.log();
135
- process.exitCode = 1;
136
- }
154
+ console.log(`${C.red}Unknown command: ${command}${C.reset}`);
155
+ showHelp();
156
+ process.exit(1);
137
157
  }
138
158
 
139
159
  main();
@@ -585,15 +585,15 @@ Rate limiting may be applied to certain endpoints.
585
585
  sections.push(`## Endpoints
586
586
 
587
587
  ${Object.entries(endpointsByTag).map(([tag, endpoints]) => {
588
- return `### ${tag}
588
+ return `### ${tag}
589
589
 
590
590
  ${endpoints.map(e => `- [\`${e.method}\` ${e.path}](#${this.slugify(e.method + '-' + e.path)})`).join('\n')}`;
591
- }).join('\n\n')}
591
+ }).join('\n\n')}
592
592
 
593
593
  ---`);
594
594
 
595
595
  // Endpoint Details
596
- sections.push(`## Endpoint Reference\n`);
596
+ sections.push('## Endpoint Reference\n');
597
597
 
598
598
  for (const [tag, endpoints] of Object.entries(endpointsByTag)) {
599
599
  sections.push(`### ${tag}\n`);
@@ -274,17 +274,17 @@ This document tracks significant architecture and design decisions made during t
274
274
  | ID | Title | Category | Status | Impact | Date |
275
275
  |----|-------|----------|--------|--------|------|
276
276
  ${data.decisions.map(d => {
277
- const category = DECISION_CATEGORIES[d.category] || { emoji: '📋', label: d.category };
278
- const impact = IMPACT_LEVELS[d.impact] || { emoji: '🟡', label: d.impact };
279
- return `| [${d.id}](#${d.id.toLowerCase()}) | ${d.title} | ${category.emoji} ${category.label} | ${this.formatStatus(d.status)} | ${impact.emoji} ${impact.label} | ${d.date} |`;
280
- }).join('\n')}
277
+ const category = DECISION_CATEGORIES[d.category] || { emoji: '📋', label: d.category };
278
+ const impact = IMPACT_LEVELS[d.impact] || { emoji: '🟡', label: d.impact };
279
+ return `| [${d.id}](#${d.id.toLowerCase()}) | ${d.title} | ${category.emoji} ${category.label} | ${this.formatStatus(d.status)} | ${impact.emoji} ${impact.label} | ${d.date} |`;
280
+ }).join('\n')}
281
281
 
282
282
  ---`);
283
283
  }
284
284
 
285
285
  // Individual Decisions
286
286
  if (data.decisions.length > 0) {
287
- sections.push(`## Decisions\n`);
287
+ sections.push('## Decisions\n');
288
288
 
289
289
  for (const decision of data.decisions) {
290
290
  sections.push(this.formatDecision(decision));
@@ -318,16 +318,16 @@ bootspring docs decisions add "Decision Title"
318
318
  ### By Category
319
319
 
320
320
  ${Object.entries(stats.byCategory).map(([cat, count]) => {
321
- const category = DECISION_CATEGORIES[cat] || { emoji: '📋', label: cat };
322
- return `- ${category.emoji} **${category.label}:** ${count}`;
323
- }).join('\n')}
321
+ const category = DECISION_CATEGORIES[cat] || { emoji: '📋', label: cat };
322
+ return `- ${category.emoji} **${category.label}:** ${count}`;
323
+ }).join('\n')}
324
324
 
325
325
  ### By Impact
326
326
 
327
327
  ${Object.entries(stats.byImpact).map(([imp, count]) => {
328
- const impact = IMPACT_LEVELS[imp] || { emoji: '🟡', label: imp };
329
- return `- ${impact.emoji} **${impact.label}:** ${count}`;
330
- }).join('\n')}
328
+ const impact = IMPACT_LEVELS[imp] || { emoji: '🟡', label: imp };
329
+ return `- ${impact.emoji} **${impact.label}:** ${count}`;
330
+ }).join('\n')}
331
331
 
332
332
  ---`);
333
333
 
@@ -422,14 +422,14 @@ ${decision.rationale || '_No rationale provided._'}
422
422
  #### Options Considered
423
423
 
424
424
  ${decision.options.map((opt, i) => {
425
- const chosen = opt.chosen ? ' ✅ **CHOSEN**' : '';
426
- return `**Option ${i + 1}: ${opt.name}**${chosen}
425
+ const chosen = opt.chosen ? ' ✅ **CHOSEN**' : '';
426
+ return `**Option ${i + 1}: ${opt.name}**${chosen}
427
427
 
428
428
  ${opt.description || ''}
429
429
 
430
430
  - Pros: ${(opt.pros || []).join(', ') || 'None listed'}
431
431
  - Cons: ${(opt.cons || []).join(', ') || 'None listed'}`;
432
- }).join('\n\n')}
432
+ }).join('\n\n')}
433
433
  `;
434
434
  }
435
435
 
@@ -590,12 +590,12 @@ ${scoreBar} **${metrics.overallScore}%**
590
590
  | Category | Score | Status |
591
591
  |----------|-------|--------|
592
592
  ${Object.entries(METRIC_CATEGORIES).map(([key, config]) => {
593
- const metricKey = this.categoryToMetricKey(key);
594
- const score = metrics[metricKey]?.score || 0;
595
- const catStatus = this.getStatusFromScore(score);
596
- const statusConfig = HEALTH_STATUS[catStatus];
597
- return `| ${config.emoji} ${config.label} | ${score}/100 | ${statusConfig.emoji} ${statusConfig.label} |`;
598
- }).join('\n')}
593
+ const metricKey = this.categoryToMetricKey(key);
594
+ const score = metrics[metricKey]?.score || 0;
595
+ const catStatus = this.getStatusFromScore(score);
596
+ const statusConfig = HEALTH_STATUS[catStatus];
597
+ return `| ${config.emoji} ${config.label} | ${score}/100 | ${statusConfig.emoji} ${statusConfig.label} |`;
598
+ }).join('\n')}
599
599
 
600
600
  ---`);
601
601
 
@@ -252,7 +252,7 @@ class SprintGenerator {
252
252
  const idealPointsPerDay = sprint.metrics.totalPoints / totalDays;
253
253
 
254
254
  const burndown = [];
255
- let currentDate = new Date(startDate);
255
+ const currentDate = new Date(startDate);
256
256
  let idealRemaining = sprint.metrics.totalPoints;
257
257
 
258
258
  while (currentDate <= endDate) {
@@ -364,10 +364,10 @@ ${progressBar} **${metrics.percentComplete}%** Complete
364
364
  sections.push(`## Sprint Goals
365
365
 
366
366
  ${sprint.goals.map((goal, i) => {
367
- const statusIcon = goal.status === 'completed' ? '✅' : goal.status === 'in_progress' ? '🔄' : '⬜';
368
- return `${i + 1}. ${statusIcon} **${goal.description}**
367
+ const statusIcon = goal.status === 'completed' ? '✅' : goal.status === 'in_progress' ? '🔄' : '⬜';
368
+ return `${i + 1}. ${statusIcon} **${goal.description}**
369
369
  ${goal.success_criteria.length > 0 ? goal.success_criteria.map(c => ` - ${c}`).join('\n') : ''}`;
370
- }).join('\n\n')}
370
+ }).join('\n\n')}
371
371
 
372
372
  ---`);
373
373
  }
@@ -284,8 +284,8 @@ function generateTodo(tasks, options = {}) {
284
284
  const checkbox = task.status === 'completed' ? '[x]' : '[ ]';
285
285
  const statusTag = task.status === 'completed' ? ' (`completed`)' :
286
286
  task.status === 'in_progress' ? ' (`in_progress`)' :
287
- task.status === 'blocked' ? ' (`blocked`)' :
288
- task.status === 'skipped' ? ' (`skipped`)' : '';
287
+ task.status === 'blocked' ? ' (`blocked`)' :
288
+ task.status === 'skipped' ? ' (`skipped`)' : '';
289
289
  content += `- ${checkbox} \`${task.id}\` ${task.title}${statusTag}\n`;
290
290
 
291
291
  // Source traceability
@@ -629,7 +629,7 @@ class VisualDocGenerator {
629
629
  // Add header comment
630
630
  const content = [
631
631
  `%% ${diagram.title}`,
632
- `%% Generated by Bootspring Visual Doc Generator`,
632
+ '%% Generated by Bootspring Visual Doc Generator',
633
633
  `%% ${new Date().toISOString()}`,
634
634
  '',
635
635
  diagram.code
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@girardmedia/bootspring",
3
- "version": "2.1.2",
3
+ "version": "2.2.0",
4
4
  "description": "Thin client for Bootspring cloud MCP, hosted agents, and paywalled workflow intelligence",
5
5
  "keywords": [
6
6
  "ai",
@@ -48,20 +48,7 @@
48
48
  },
49
49
  "files": [
50
50
  "bin/bootspring.js",
51
- "cli/agent.js",
52
- "cli/auth.js",
53
- "cli/billing.js",
54
- "cli/build.js",
55
- "cli/dashboard.js",
56
- "cli/init.js",
57
- "cli/mcp.js",
58
- "cli/orchestrator.js",
59
- "cli/project.js",
60
- "cli/quality.js",
61
- "cli/skill.js",
62
- "cli/switch.js",
63
- "cli/todo.js",
64
- "cli/update.js",
51
+ "monorepo/apps/cli/dist/",
65
52
  "core/api-client.d.ts",
66
53
  "core/api-client.js",
67
54
  "core/auth.d.ts",