@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
@@ -1,580 +0,0 @@
1
- /**
2
- * Bootspring Context Loader
3
- * Loads and indexes project context files for AI agent access
4
- *
5
- * Context Types:
6
- * - seed: SEED.md project configuration
7
- * - claude: CLAUDE.md AI context
8
- * - plan: Planning documents (MASTER_PLAN, PRD, etc.)
9
- * - business: Business documents (plans, competitive analysis)
10
- * - legal: Legal documents (terms, privacy)
11
- * - mvp: MVP source code
12
- * - logs: Action logs and history
13
- *
14
- * @package bootspring
15
- * @module core/context-loader
16
- */
17
-
18
- const fs = require('fs');
19
- const path = require('path');
20
- const config = require('./config');
21
- const utils = require('./utils');
22
-
23
- /**
24
- * Default context file locations
25
- */
26
- const CONTEXT_LOCATIONS = {
27
- // Root level files
28
- seed: 'SEED.md',
29
- claude: 'CLAUDE.md',
30
- todo: 'todo.md',
31
- readme: 'README.md',
32
- changelog: 'CHANGELOG.md',
33
-
34
- // Planning folder
35
- plan: 'planning/MASTER_PLAN.md',
36
- prd: 'planning/PRD.md',
37
- architecture: 'planning/ARCHITECTURE.md',
38
- decisions: 'planning/DECISION_LOG.md',
39
- roadmap: 'planning/ROADMAP.md',
40
-
41
- // Business documents
42
- business_plan: 'planning/BUSINESS_PLAN.md',
43
- pitch_deck: 'planning/PITCH_DECK.md',
44
- competitive: 'planning/COMPETITIVE_ANALYSIS.md',
45
- market: 'planning/MARKET_RESEARCH.md',
46
- financial: 'planning/FINANCIAL_MODEL.md',
47
-
48
- // Legal documents
49
- terms: 'legal/TERMS_OF_SERVICE.md',
50
- privacy: 'legal/PRIVACY_POLICY.md',
51
- legal_checklist: 'legal/LEGAL_CHECKLIST.md',
52
-
53
- // MVP and code context
54
- mvp_source: '.bootspring/mvp/source/',
55
- mvp_references: '.bootspring/mvp/references/',
56
-
57
- // Logs
58
- logs: '.bootspring/logs/',
59
- context_index: '.bootspring/context-index.json'
60
- };
61
-
62
- /**
63
- * Agent to context mapping
64
- * Each agent receives relevant context files automatically
65
- */
66
- const AGENT_CONTEXT_MAP = {
67
- // Technical agents
68
- 'frontend-expert': ['seed', 'architecture', 'prd', 'mvp_source'],
69
- 'backend-expert': ['seed', 'architecture', 'prd', 'mvp_source'],
70
- 'database-expert': ['seed', 'architecture', 'mvp_source'],
71
- 'api-expert': ['seed', 'architecture', 'prd'],
72
- 'security-expert': ['architecture', 'legal_checklist', 'prd'],
73
- 'testing-expert': ['architecture', 'prd'],
74
- 'performance-expert': ['architecture', 'mvp_source'],
75
- 'devops-expert': ['architecture', 'plan'],
76
- 'ui-ux-expert': ['seed', 'prd', 'mvp_source'],
77
- 'code-review-expert': ['architecture', 'mvp_source'],
78
-
79
- // Business agents
80
- 'business-strategy-expert': ['seed', 'business_plan', 'competitive', 'plan'],
81
- 'financial-expert': ['seed', 'business_plan', 'financial'],
82
- 'fundraising-expert': ['pitch_deck', 'financial', 'business_plan'],
83
- 'legal-expert': ['terms', 'privacy', 'legal_checklist'],
84
- 'marketing-expert': ['business_plan', 'competitive', 'plan'],
85
- 'sales-expert': ['business_plan', 'competitive', 'prd'],
86
- 'growth-expert': ['plan', 'competitive', 'market'],
87
- 'operations-expert': ['plan', 'architecture'],
88
- 'competitive-analysis-expert': ['competitive', 'market', 'business_plan'],
89
- 'market-research-expert': ['market', 'competitive', 'business_plan'],
90
- 'investor-relations-expert': ['pitch_deck', 'financial', 'plan'],
91
-
92
- // Research agents
93
- 'research-expert': ['plan', 'architecture', 'prd'],
94
- 'data-modeling-expert': ['architecture', 'prd', 'mvp_source'],
95
-
96
- // Technical specialists
97
- 'vercel-expert': ['architecture', 'prd', 'plan'],
98
- 'architecture-expert': ['architecture', 'prd', 'plan', 'mvp_source'],
99
- 'ai-integration-expert': ['seed', 'architecture', 'prd', 'mvp_source'],
100
- 'auth-expert': ['seed', 'architecture', 'prd', 'mvp_source'],
101
- 'email-expert': ['seed', 'prd', 'mvp_source'],
102
- 'monitoring-expert': ['architecture', 'plan'],
103
- 'payment-expert': ['seed', 'business_plan', 'prd', 'mvp_source'],
104
- 'railway-expert': ['architecture', 'plan'],
105
-
106
- // Product and content
107
- 'content-expert': ['seed', 'prd', 'plan'],
108
- 'product-expert': ['seed', 'prd', 'plan', 'roadmap'],
109
- 'mobile-expert': ['seed', 'architecture', 'prd', 'mvp_source'],
110
- 'infrastructure-expert': ['architecture', 'plan'],
111
-
112
- // Business specialists
113
- 'partnerships-expert': ['business_plan', 'competitive', 'plan'],
114
- 'private-equity-expert': ['pitch_deck', 'financial', 'business_plan']
115
- };
116
-
117
- /**
118
- * Get the bootspring directory path
119
- * @param {string} projectRoot - Project root path
120
- * @returns {string} Bootspring directory path
121
- */
122
- function getBootspringDir(projectRoot) {
123
- return path.join(projectRoot, '.bootspring');
124
- }
125
-
126
- /**
127
- * Ensure bootspring directories exist
128
- * @param {string} projectRoot - Project root path
129
- */
130
- function ensureDirectories(projectRoot) {
131
- const bootspringDir = getBootspringDir(projectRoot);
132
- const dirs = [
133
- bootspringDir,
134
- path.join(bootspringDir, 'logs'),
135
- path.join(bootspringDir, 'logs', 'agents'),
136
- path.join(bootspringDir, 'logs', 'code'),
137
- path.join(bootspringDir, 'logs', 'decisions'),
138
- path.join(bootspringDir, 'logs', 'fixes'),
139
- path.join(bootspringDir, 'logs', 'business'),
140
- path.join(bootspringDir, 'logs', 'meetings'),
141
- path.join(bootspringDir, 'mvp'),
142
- path.join(bootspringDir, 'mvp', 'source'),
143
- path.join(bootspringDir, 'mvp', 'references')
144
- ];
145
-
146
- for (const dir of dirs) {
147
- utils.ensureDir(dir);
148
- }
149
- }
150
-
151
- /**
152
- * Load a specific context file
153
- * @param {string} contextType - Type of context (seed, plan, prd, etc.)
154
- * @param {object} [options] - Options
155
- * @returns {object} Context data { exists, path, content, metadata }
156
- */
157
- function load(contextType, options = {}) {
158
- const cfg = options.config || config.load();
159
- const projectRoot = cfg._projectRoot;
160
-
161
- const relativePath = CONTEXT_LOCATIONS[contextType];
162
- if (!relativePath) {
163
- return { exists: false, error: `Unknown context type: ${contextType}` };
164
- }
165
-
166
- const fullPath = path.join(projectRoot, relativePath);
167
-
168
- // Check if it's a directory (like mvp_source)
169
- if (relativePath.endsWith('/')) {
170
- return loadDirectory(fullPath, contextType);
171
- }
172
-
173
- // Load single file
174
- if (!utils.fileExists(fullPath)) {
175
- return {
176
- exists: false,
177
- type: contextType,
178
- path: relativePath,
179
- content: null
180
- };
181
- }
182
-
183
- const content = utils.readFile(fullPath);
184
- const stats = fs.statSync(fullPath);
185
-
186
- return {
187
- exists: true,
188
- type: contextType,
189
- path: relativePath,
190
- fullPath: fullPath,
191
- content: content,
192
- metadata: {
193
- size: stats.size,
194
- modified: stats.mtime,
195
- modifiedRelative: utils.formatRelativeTime(stats.mtime)
196
- }
197
- };
198
- }
199
-
200
- /**
201
- * Load all files from a directory
202
- * @param {string} dirPath - Directory path
203
- * @param {string} contextType - Context type
204
- * @returns {object} Directory context data
205
- */
206
- function loadDirectory(dirPath, contextType) {
207
- if (!utils.fileExists(dirPath)) {
208
- return {
209
- exists: false,
210
- type: contextType,
211
- path: dirPath,
212
- files: []
213
- };
214
- }
215
-
216
- const files = [];
217
-
218
- function walkDir(dir, relativeTo) {
219
- try {
220
- const entries = fs.readdirSync(dir, { withFileTypes: true });
221
-
222
- for (const entry of entries) {
223
- const fullPath = path.join(dir, entry.name);
224
- const relativePath = path.relative(relativeTo, fullPath);
225
-
226
- // Skip hidden files and common excludes
227
- if (entry.name.startsWith('.') ||
228
- entry.name === 'node_modules' ||
229
- entry.name === 'dist' ||
230
- entry.name === 'build') {
231
- continue;
232
- }
233
-
234
- if (entry.isDirectory()) {
235
- walkDir(fullPath, relativeTo);
236
- } else if (entry.isFile()) {
237
- const stats = fs.statSync(fullPath);
238
- files.push({
239
- name: entry.name,
240
- path: relativePath,
241
- fullPath: fullPath,
242
- size: stats.size,
243
- modified: stats.mtime,
244
- extension: path.extname(entry.name)
245
- });
246
- }
247
- }
248
- } catch (err) {
249
- // Directory read error, skip
250
- }
251
- }
252
-
253
- walkDir(dirPath, dirPath);
254
-
255
- return {
256
- exists: true,
257
- type: contextType,
258
- path: dirPath,
259
- files: files,
260
- fileCount: files.length
261
- };
262
- }
263
-
264
- /**
265
- * Load a specific file from a directory context
266
- * @param {string} contextType - Context type (e.g., 'mvp_source')
267
- * @param {string} filePath - Relative file path within the context
268
- * @param {object} [options] - Options
269
- * @returns {object} File context data
270
- */
271
- function loadFile(contextType, filePath, options = {}) {
272
- const cfg = options.config || config.load();
273
- const projectRoot = cfg._projectRoot;
274
-
275
- const basePath = CONTEXT_LOCATIONS[contextType];
276
- if (!basePath) {
277
- return { exists: false, error: `Unknown context type: ${contextType}` };
278
- }
279
-
280
- const fullPath = path.join(projectRoot, basePath, filePath);
281
-
282
- if (!utils.fileExists(fullPath)) {
283
- return {
284
- exists: false,
285
- type: contextType,
286
- path: filePath,
287
- content: null
288
- };
289
- }
290
-
291
- const content = utils.readFile(fullPath);
292
- const stats = fs.statSync(fullPath);
293
-
294
- return {
295
- exists: true,
296
- type: contextType,
297
- path: filePath,
298
- fullPath: fullPath,
299
- content: content,
300
- metadata: {
301
- size: stats.size,
302
- modified: stats.mtime,
303
- extension: path.extname(fullPath)
304
- }
305
- };
306
- }
307
-
308
- /**
309
- * Get all context for a specific agent
310
- * @param {string} agentName - Agent name
311
- * @param {object} [options] - Options
312
- * @returns {object} Agent context data
313
- */
314
- function getAgentContext(agentName, options = {}) {
315
- const contextTypes = AGENT_CONTEXT_MAP[agentName] || [];
316
- const context = {
317
- agent: agentName,
318
- files: [],
319
- summary: []
320
- };
321
-
322
- for (const contextType of contextTypes) {
323
- const loaded = load(contextType, options);
324
- if (loaded.exists) {
325
- if (loaded.files) {
326
- // Directory context
327
- context.files.push({
328
- type: contextType,
329
- isDirectory: true,
330
- fileCount: loaded.fileCount,
331
- files: loaded.files.slice(0, 10) // Limit to first 10 files
332
- });
333
- context.summary.push(`${contextType}: ${loaded.fileCount} files`);
334
- } else {
335
- // Single file context
336
- context.files.push({
337
- type: contextType,
338
- isDirectory: false,
339
- path: loaded.path,
340
- content: loaded.content,
341
- metadata: loaded.metadata
342
- });
343
- context.summary.push(`${contextType}: ${loaded.path}`);
344
- }
345
- }
346
- }
347
-
348
- return context;
349
- }
350
-
351
- /**
352
- * List all available context types and their status
353
- * @param {object} [options] - Options
354
- * @returns {object[]} List of context statuses
355
- */
356
- function list(options = {}) {
357
- const cfg = options.config || config.load();
358
- const projectRoot = cfg._projectRoot;
359
- const results = [];
360
-
361
- for (const [type, relativePath] of Object.entries(CONTEXT_LOCATIONS)) {
362
- const fullPath = path.join(projectRoot, relativePath);
363
- const exists = utils.fileExists(fullPath);
364
-
365
- const result = {
366
- type,
367
- path: relativePath,
368
- exists
369
- };
370
-
371
- if (exists) {
372
- if (relativePath.endsWith('/')) {
373
- // Directory
374
- const dirContext = loadDirectory(fullPath, type);
375
- result.isDirectory = true;
376
- result.fileCount = dirContext.fileCount;
377
- } else {
378
- // File
379
- const stats = fs.statSync(fullPath);
380
- result.isDirectory = false;
381
- result.size = stats.size;
382
- result.modified = stats.mtime;
383
- result.modifiedRelative = utils.formatRelativeTime(stats.mtime);
384
- }
385
- }
386
-
387
- results.push(result);
388
- }
389
-
390
- return results;
391
- }
392
-
393
- /**
394
- * Search across all context files
395
- * @param {string} query - Search query
396
- * @param {object} [options] - Options { types, limit }
397
- * @returns {object[]} Search results
398
- */
399
- function search(query, options = {}) {
400
- const cfg = options.config || config.load();
401
- const projectRoot = cfg._projectRoot;
402
- const types = options.types || Object.keys(CONTEXT_LOCATIONS);
403
- const limit = options.limit || 20;
404
- const results = [];
405
- const queryLower = query.toLowerCase();
406
-
407
- for (const type of types) {
408
- if (!CONTEXT_LOCATIONS[type]) continue;
409
-
410
- const loaded = load(type, { config: cfg });
411
- if (!loaded.exists) continue;
412
-
413
- if (loaded.files) {
414
- // Directory - search file names and contents
415
- for (const file of loaded.files) {
416
- // Search in filename
417
- if (file.name.toLowerCase().includes(queryLower)) {
418
- results.push({
419
- type,
420
- file: file.path,
421
- matchType: 'filename',
422
- preview: file.name
423
- });
424
- }
425
-
426
- // Search in content for text files
427
- if (['.md', '.txt', '.js', '.ts', '.json', '.yaml', '.yml'].includes(file.extension)) {
428
- const content = utils.readFile(file.fullPath);
429
- if (content && content.toLowerCase().includes(queryLower)) {
430
- const lines = content.split('\n');
431
- const matchingLines = lines
432
- .map((line, i) => ({ line, num: i + 1 }))
433
- .filter(l => l.line.toLowerCase().includes(queryLower))
434
- .slice(0, 3);
435
-
436
- results.push({
437
- type,
438
- file: file.path,
439
- matchType: 'content',
440
- matches: matchingLines.map(m => ({
441
- line: m.num,
442
- preview: utils.truncate(m.line.trim(), 100)
443
- }))
444
- });
445
- }
446
- }
447
-
448
- if (results.length >= limit) break;
449
- }
450
- } else if (loaded.content) {
451
- // Single file - search content
452
- const content = loaded.content;
453
- if (content.toLowerCase().includes(queryLower)) {
454
- const lines = content.split('\n');
455
- const matchingLines = lines
456
- .map((line, i) => ({ line, num: i + 1 }))
457
- .filter(l => l.line.toLowerCase().includes(queryLower))
458
- .slice(0, 5);
459
-
460
- results.push({
461
- type,
462
- file: loaded.path,
463
- matchType: 'content',
464
- matches: matchingLines.map(m => ({
465
- line: m.num,
466
- preview: utils.truncate(m.line.trim(), 100)
467
- }))
468
- });
469
- }
470
- }
471
-
472
- if (results.length >= limit) break;
473
- }
474
-
475
- return results.slice(0, limit);
476
- }
477
-
478
- /**
479
- * Build or update the context index
480
- * @param {object} [options] - Options
481
- * @returns {object} Index data
482
- */
483
- function buildIndex(options = {}) {
484
- const cfg = options.config || config.load();
485
- const projectRoot = cfg._projectRoot;
486
-
487
- ensureDirectories(projectRoot);
488
-
489
- const index = {
490
- version: '1.0',
491
- lastUpdated: new Date().toISOString(),
492
- projectRoot: projectRoot,
493
- files: [],
494
- agents: {}
495
- };
496
-
497
- // Index all context files
498
- const contextList = list({ config: cfg });
499
- for (const ctx of contextList) {
500
- if (ctx.exists) {
501
- index.files.push({
502
- type: ctx.type,
503
- path: ctx.path,
504
- isDirectory: ctx.isDirectory,
505
- fileCount: ctx.fileCount,
506
- size: ctx.size,
507
- modified: ctx.modified
508
- });
509
- }
510
- }
511
-
512
- // Build agent context mappings
513
- for (const [agent, types] of Object.entries(AGENT_CONTEXT_MAP)) {
514
- const availableTypes = types.filter(t => {
515
- const ctx = contextList.find(c => c.type === t);
516
- return ctx && ctx.exists;
517
- });
518
- index.agents[agent] = availableTypes;
519
- }
520
-
521
- // Save index
522
- const indexPath = path.join(projectRoot, CONTEXT_LOCATIONS.context_index);
523
- utils.writeFile(indexPath, JSON.stringify(index, null, 2));
524
-
525
- return index;
526
- }
527
-
528
- /**
529
- * Load the context index
530
- * @param {object} [options] - Options
531
- * @returns {object|null} Index data or null
532
- */
533
- function loadIndex(options = {}) {
534
- const cfg = options.config || config.load();
535
- const projectRoot = cfg._projectRoot;
536
- const indexPath = path.join(projectRoot, CONTEXT_LOCATIONS.context_index);
537
-
538
- if (!utils.fileExists(indexPath)) {
539
- return null;
540
- }
541
-
542
- try {
543
- const content = utils.readFile(indexPath);
544
- return JSON.parse(content);
545
- } catch {
546
- return null;
547
- }
548
- }
549
-
550
- /**
551
- * Get context locations configuration
552
- * @returns {object} Context locations
553
- */
554
- function getLocations() {
555
- return { ...CONTEXT_LOCATIONS };
556
- }
557
-
558
- /**
559
- * Get agent context mapping
560
- * @returns {object} Agent context map
561
- */
562
- function getAgentMap() {
563
- return { ...AGENT_CONTEXT_MAP };
564
- }
565
-
566
- module.exports = {
567
- load,
568
- loadFile,
569
- loadDirectory,
570
- getAgentContext,
571
- list,
572
- search,
573
- buildIndex,
574
- loadIndex,
575
- ensureDirectories,
576
- getLocations,
577
- getAgentMap,
578
- CONTEXT_LOCATIONS,
579
- AGENT_CONTEXT_MAP
580
- };
package/core/context.d.ts DELETED
@@ -1,61 +0,0 @@
1
- /**
2
- * Bootspring Context Types
3
- * @module core/context
4
- */
5
-
6
- export interface ProjectContext {
7
- project: {
8
- name: string;
9
- description: string;
10
- version: string;
11
- };
12
- stack: {
13
- framework: string;
14
- language: string;
15
- database: string;
16
- hosting: string;
17
- };
18
- paths: {
19
- root: string;
20
- config: string;
21
- context: string;
22
- };
23
- plugins: Record<string, { enabled: boolean; provider?: string }>;
24
- agents: string[];
25
- skills: string[];
26
- }
27
-
28
- export interface ValidationResult {
29
- valid: boolean;
30
- errors: string[];
31
- warnings: string[];
32
- }
33
-
34
- /**
35
- * Get project context
36
- * @param projectRoot - Optional project root path
37
- * @returns Project context object
38
- */
39
- export function get(projectRoot?: string): ProjectContext;
40
-
41
- /**
42
- * Validate project context
43
- * @param context - Context to validate
44
- * @returns Validation result
45
- */
46
- export function validate(context: unknown): ValidationResult;
47
-
48
- /**
49
- * Load context from CLAUDE.md file
50
- * @param filePath - Path to CLAUDE.md
51
- * @returns Parsed context or null
52
- */
53
- export function load(filePath: string): ProjectContext | null;
54
-
55
- /**
56
- * Save context to CLAUDE.md file
57
- * @param context - Context to save
58
- * @param filePath - Path to save to
59
- * @returns True if saved successfully
60
- */
61
- export function save(context: ProjectContext, filePath: string): boolean;