Bootspring Internals

Deep dive into the core Bootspring system architecture.

Package Structure#

Bootspring is organized as a monorepo:

bootspring/ ├── packages/ │ ├── cli/ # Command-line interface │ ├── core/ # Core business logic │ ├── mcp-server/ # MCP server implementation │ ├── sdk/ # JavaScript/TypeScript SDK │ └── shared/ # Shared utilities ├── skills/ # Built-in skill patterns ├── agents/ # Agent definitions ├── workflows/ # Workflow definitions └── templates/ # Project templates

Core Package#

The core package contains the primary business logic:

packages/core/ ├── src/ │ ├── agents/ # Agent system │ │ ├── registry.ts │ │ ├── invoker.ts │ │ └── profiles/ │ ├── skills/ # Skill system │ │ ├── loader.ts │ │ ├── applier.ts │ │ └── registry.ts │ ├── context/ # Context generation │ │ ├── generator.ts │ │ ├── analyzers/ │ │ └── templates/ │ ├── workflows/ # Workflow engine │ │ ├── orchestrator.ts │ │ ├── phases.ts │ │ └── signals.ts │ ├── quality/ # Quality gates │ │ ├── gates.ts │ │ └── checks/ │ └── config/ # Configuration │ ├── loader.ts │ └── schema.ts └── tests/

Context Generation System#

Generator Architecture#

1// core/context/generator.ts 2 3export class ContextGenerator { 4 private analyzers: Analyzer[]; 5 private template: Template; 6 7 async generate(projectPath: string): Promise<string> { 8 // 1. Collect project information 9 const projectInfo = await this.collectInfo(projectPath); 10 11 // 2. Run analyzers 12 const analyses = await Promise.all( 13 this.analyzers.map(a => a.analyze(projectPath)) 14 ); 15 16 // 3. Aggregate results 17 const aggregated = this.aggregate(analyses); 18 19 // 4. Apply template 20 const context = this.template.render({ 21 ...projectInfo, 22 ...aggregated, 23 }); 24 25 return context; 26 } 27}

Built-in Analyzers#

AnalyzerPurpose
PackageAnalyzerpackage.json analysis
FrameworkAnalyzerFramework detection
StructureAnalyzerProject structure
DependencyAnalyzerDependency analysis
DatabaseAnalyzerDatabase schema
ApiAnalyzerAPI routes
ComponentAnalyzerUI components
TestAnalyzerTest coverage

Analysis Pipeline#

┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ File Scan │───▶│ Analyze │───▶│ Aggregate │ └─────────────┘ └─────────────┘ └─────────────┘ │ ┌────────────────┼────────────────┐ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Package │ │Framework │ │Structure │ │ Analyzer │ │ Analyzer │ │ Analyzer │ └──────────┘ └──────────┘ └──────────┘

Agent System#

Agent Registry#

1// core/agents/registry.ts 2 3export class AgentRegistry { 4 private agents: Map<string, AgentProfile> = new Map(); 5 6 register(profile: AgentProfile): void { 7 this.agents.set(profile.name, profile); 8 } 9 10 get(name: string): AgentProfile | undefined { 11 return this.agents.get(name); 12 } 13 14 list(filter?: AgentFilter): AgentProfile[] { 15 let results = Array.from(this.agents.values()); 16 if (filter?.category) { 17 results = results.filter(a => a.category === filter.category); 18 } 19 return results; 20 } 21}

Agent Profile Structure#

1interface AgentProfile { 2 name: string; 3 displayName: string; 4 description: string; 5 category: AgentCategory; 6 tier: Tier; 7 8 systemPrompt: string; 9 capabilities: string[]; 10 collaborates: string[]; 11 12 tools?: Tool[]; 13 examples?: Example[]; 14 customInstructions?: string; 15}

Agent Invocation#

1// core/agents/invoker.ts 2 3export class AgentInvoker { 4 async invoke( 5 agentName: string, 6 prompt: string, 7 options: InvokeOptions 8 ): Promise<AgentResponse> { 9 // 1. Load agent profile 10 const agent = this.registry.get(agentName); 11 if (!agent) throw new AgentNotFoundError(agentName); 12 13 // 2. Check entitlements 14 await this.checkEntitlements(agent); 15 16 // 3. Assemble context 17 const context = await this.contextBuilder.build({ 18 agent, 19 project: options.projectId, 20 files: options.files, 21 }); 22 23 // 4. Build messages 24 const messages = this.buildMessages(agent, prompt, context); 25 26 // 5. Invoke LLM 27 const response = await this.llm.complete(messages, { 28 model: this.selectModel(agent), 29 maxTokens: options.maxTokens, 30 temperature: options.temperature, 31 }); 32 33 // 6. Process response 34 return this.processResponse(response, agent); 35 } 36}

Skill System#

Skill Loader#

1// core/skills/loader.ts 2 3export class SkillLoader { 4 private skillsPath: string; 5 private cache: Map<string, Skill> = new Map(); 6 7 async load(skillPath: string): Promise<Skill> { 8 // Check cache 9 if (this.cache.has(skillPath)) { 10 return this.cache.get(skillPath)!; 11 } 12 13 // Load from filesystem 14 const fullPath = path.join(this.skillsPath, skillPath); 15 const content = await fs.readFile(fullPath, 'utf-8'); 16 17 // Parse frontmatter and content 18 const skill = this.parseSkill(content); 19 20 // Cache and return 21 this.cache.set(skillPath, skill); 22 return skill; 23 } 24}

Skill Structure#

1interface Skill { 2 name: string; 3 path: string; 4 category: string; 5 description: string; 6 tier: Tier; 7 8 frontmatter: Record<string, unknown>; 9 content: string; 10 codeBlocks: CodeBlock[]; 11 files: SkillFile[]; 12}

Skill Application#

1// core/skills/applier.ts 2 3export class SkillApplier { 4 async apply(skill: Skill, options: ApplyOptions): Promise<ApplyResult> { 5 const result: ApplyResult = { 6 applied: [], 7 skipped: [], 8 errors: [], 9 }; 10 11 for (const file of skill.files) { 12 try { 13 // Process template 14 const content = await this.processTemplate(file.content, options.vars); 15 16 // Determine target path 17 const targetPath = this.resolveTargetPath(file.path, options.target); 18 19 // Apply file 20 if (options.dryRun) { 21 result.applied.push({ path: targetPath, action: 'would-create' }); 22 } else { 23 await this.writeFile(targetPath, content); 24 result.applied.push({ path: targetPath, action: 'created' }); 25 } 26 } catch (error) { 27 result.errors.push({ path: file.path, error }); 28 } 29 } 30 31 return result; 32 } 33}

Workflow Engine#

Orchestrator#

1// core/workflows/orchestrator.ts 2 3export class WorkflowOrchestrator { 4 async execute(workflow: Workflow, options: ExecuteOptions): Promise<WorkflowResult> { 5 const execution = new WorkflowExecution(workflow); 6 7 for (const phase of workflow.phases) { 8 // Check if phase can start 9 if (!this.canStartPhase(execution, phase)) { 10 await this.handleBlockedPhase(execution, phase); 11 continue; 12 } 13 14 // Execute phase 15 execution.startPhase(phase.name); 16 17 try { 18 // Run phase tasks 19 const tasks = this.getPhasesTasks(phase); 20 const results = phase.parallel 21 ? await this.runParallel(tasks, execution) 22 : await this.runSequential(tasks, execution); 23 24 // Check completion signals 25 const completed = await this.checkCompletionSignals(phase, results); 26 27 if (completed) { 28 execution.completePhase(phase.name); 29 } else { 30 execution.failPhase(phase.name, 'Completion signals not met'); 31 } 32 } catch (error) { 33 execution.failPhase(phase.name, error.message); 34 35 if (phase.critical) { 36 throw new WorkflowAbortedError(workflow.name, phase.name); 37 } 38 } 39 } 40 41 return execution.getResult(); 42 } 43}

Phase Management#

1interface Phase { 2 name: string; 3 description: string; 4 agents: string[]; 5 tasks: Task[]; 6 parallel: boolean; 7 critical: boolean; 8 completionSignals: CompletionSignal[]; 9 rollbackStrategy?: RollbackStrategy; 10} 11 12interface CompletionSignal { 13 type: 'file_exists' | 'test_passes' | 'manual' | 'api_response'; 14 config: Record<string, unknown>; 15}

Configuration System#

Config Loader#

1// core/config/loader.ts 2 3export class ConfigLoader { 4 private configPaths = [ 5 'bootspring.config.js', 6 'bootspring.config.ts', 7 'bootspring.config.json', 8 ]; 9 10 async load(projectPath: string): Promise<Config> { 11 // Find config file 12 const configPath = await this.findConfig(projectPath); 13 14 if (!configPath) { 15 return this.defaultConfig(); 16 } 17 18 // Load and validate 19 const rawConfig = await this.loadFile(configPath); 20 const validated = this.validate(rawConfig); 21 22 return validated; 23 } 24}

Configuration Schema#

1interface Config { 2 project: { 3 name: string; 4 type: ProjectType; 5 }; 6 7 context: { 8 path: string; 9 include: string[]; 10 exclude: string[]; 11 autoRegenerate: boolean; 12 }; 13 14 agents: { 15 enabled: string[]; 16 customInstructions: Record<string, string>; 17 }; 18 19 skills: { 20 paths: string[]; 21 customSkills: string; 22 }; 23 24 quality: { 25 preCommit: QualityConfig; 26 prePush: QualityConfig; 27 preDeploy: QualityConfig; 28 }; 29 30 mcp: { 31 port: number; 32 transport: 'stdio' | 'http'; 33 }; 34}

Event System#

Event Emitter#

1// core/events/emitter.ts 2 3export class BootspringEvents extends EventEmitter { 4 emit(event: EventType, payload: EventPayload): boolean { 5 // Log event 6 this.logger.debug(`Event: ${event}`, payload); 7 8 // Track if enabled 9 if (this.trackingEnabled) { 10 this.tracker.track(event, payload); 11 } 12 13 return super.emit(event, payload); 14 } 15}

Event Types#

EventWhen Fired
context.generatedContext file created
agent.invokedAgent called
agent.completedAgent finished
skill.appliedSkill applied
workflow.startedWorkflow begun
workflow.completedWorkflow finished
quality.passedQuality gate passed
quality.failedQuality gate failed

Caching#

Cache Manager#

1// core/cache/manager.ts 2 3export class CacheManager { 4 private memoryCache: Map<string, CacheEntry> = new Map(); 5 private diskCache: DiskCache; 6 7 async get<T>(key: string): Promise<T | null> { 8 // Check memory cache 9 const memEntry = this.memoryCache.get(key); 10 if (memEntry && !this.isExpired(memEntry)) { 11 return memEntry.value as T; 12 } 13 14 // Check disk cache 15 const diskEntry = await this.diskCache.get(key); 16 if (diskEntry && !this.isExpired(diskEntry)) { 17 // Populate memory cache 18 this.memoryCache.set(key, diskEntry); 19 return diskEntry.value as T; 20 } 21 22 return null; 23 } 24 25 async set<T>(key: string, value: T, ttl?: number): Promise<void> { 26 const entry: CacheEntry = { 27 value, 28 expires: ttl ? Date.now() + ttl : undefined, 29 }; 30 31 this.memoryCache.set(key, entry); 32 await this.diskCache.set(key, entry); 33 } 34}

Error Handling#

Error Hierarchy#

1// Bootspring error base 2export class BootspringError extends Error { 3 code: string; 4 context?: Record<string, unknown>; 5} 6 7// Specific errors 8export class AgentNotFoundError extends BootspringError { 9 code = 'AGENT_NOT_FOUND'; 10} 11 12export class SkillNotFoundError extends BootspringError { 13 code = 'SKILL_NOT_FOUND'; 14} 15 16export class WorkflowAbortedError extends BootspringError { 17 code = 'WORKFLOW_ABORTED'; 18} 19 20export class EntitlementError extends BootspringError { 21 code = 'ENTITLEMENT_REQUIRED'; 22}