@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
@@ -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;