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#
| Analyzer | Purpose |
|---|---|
PackageAnalyzer | package.json analysis |
FrameworkAnalyzer | Framework detection |
StructureAnalyzer | Project structure |
DependencyAnalyzer | Dependency analysis |
DatabaseAnalyzer | Database schema |
ApiAnalyzer | API routes |
ComponentAnalyzer | UI components |
TestAnalyzer | Test 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#
| Event | When Fired |
|---|---|
context.generated | Context file created |
agent.invoked | Agent called |
agent.completed | Agent finished |
skill.applied | Skill applied |
workflow.started | Workflow begun |
workflow.completed | Workflow finished |
quality.passed | Quality gate passed |
quality.failed | Quality 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}