@metabob/minibob 0.1.2
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/ARCHITECTURE.md +255 -0
- package/CHANGELOG.md +112 -0
- package/README.md +380 -0
- package/bin/minibob.js +36 -0
- package/dist/acp-gossip.d.ts +72 -0
- package/dist/acp-gossip.d.ts.map +1 -0
- package/dist/acp-gossip.js +156 -0
- package/dist/acp-gossip.js.map +1 -0
- package/dist/acp.d.ts +62 -0
- package/dist/acp.d.ts.map +1 -0
- package/dist/acp.js +292 -0
- package/dist/acp.js.map +1 -0
- package/dist/activity.d.ts +157 -0
- package/dist/activity.d.ts.map +1 -0
- package/dist/activity.js +518 -0
- package/dist/activity.js.map +1 -0
- package/dist/agent-runtime.d.ts +104 -0
- package/dist/agent-runtime.d.ts.map +1 -0
- package/dist/boredom.d.ts +125 -0
- package/dist/boredom.d.ts.map +1 -0
- package/dist/boredom.js +244 -0
- package/dist/boredom.js.map +1 -0
- package/dist/cli/acp-server.d.ts +23 -0
- package/dist/cli/acp-server.d.ts.map +1 -0
- package/dist/cli/burrow.d.ts +26 -0
- package/dist/cli/burrow.d.ts.map +1 -0
- package/dist/cli/doctor.d.ts +22 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/goal.d.ts +22 -0
- package/dist/cli/goal.d.ts.map +1 -0
- package/dist/cli/index.d.ts +47 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/instance-registry.d.ts +78 -0
- package/dist/cli/instance-registry.d.ts.map +1 -0
- package/dist/cli/observe.d.ts +35 -0
- package/dist/cli/observe.d.ts.map +1 -0
- package/dist/cli/vessel.d.ts +14 -0
- package/dist/cli/vessel.d.ts.map +1 -0
- package/dist/composition-observer.d.ts +96 -0
- package/dist/composition-observer.d.ts.map +1 -0
- package/dist/config.d.ts +36 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +128 -0
- package/dist/config.js.map +1 -0
- package/dist/docker/Dockerfile +35 -0
- package/dist/environment.d.ts +72 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +142 -0
- package/dist/environment.js.map +1 -0
- package/dist/goal-processor.d.ts +165 -0
- package/dist/goal-processor.d.ts.map +1 -0
- package/dist/helm/minibob-cluster/Chart.yaml +13 -0
- package/dist/helm/minibob-cluster/templates/_helpers.tpl +60 -0
- package/dist/helm/minibob-cluster/templates/configmap.yaml +11 -0
- package/dist/helm/minibob-cluster/templates/deployment.yaml +108 -0
- package/dist/helm/minibob-cluster/templates/secret.yaml +10 -0
- package/dist/helm/minibob-cluster/templates/service.yaml +37 -0
- package/dist/helm/minibob-cluster/values-local.yaml +41 -0
- package/dist/helm/minibob-cluster/values-production.yaml +57 -0
- package/dist/helm/minibob-cluster/values-testing-cluster.yaml +43 -0
- package/dist/helm/minibob-cluster/values.yaml +127 -0
- package/dist/improviser.d.ts +74 -0
- package/dist/improviser.d.ts.map +1 -0
- package/dist/impulse-filter.d.ts +74 -0
- package/dist/impulse-filter.d.ts.map +1 -0
- package/dist/impulse.d.ts +92 -0
- package/dist/impulse.d.ts.map +1 -0
- package/dist/impulse.js +234 -0
- package/dist/impulse.js.map +1 -0
- package/dist/lib.d.ts +29 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +18561 -0
- package/dist/lib.js.map +98 -0
- package/dist/lifecycle-hooks.d.ts +99 -0
- package/dist/lifecycle-hooks.d.ts.map +1 -0
- package/dist/lifecycle-hooks.js +135 -0
- package/dist/lifecycle-hooks.js.map +1 -0
- package/dist/llm.d.ts +31 -0
- package/dist/llm.d.ts.map +1 -0
- package/dist/llm.js +349 -0
- package/dist/llm.js.map +1 -0
- package/dist/mcp-activity-bridge.d.ts +66 -0
- package/dist/mcp-activity-bridge.d.ts.map +1 -0
- package/dist/mcp-activity-bridge.js +126 -0
- package/dist/mcp-activity-bridge.js.map +1 -0
- package/dist/mcp.d.ts +216 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +292 -0
- package/dist/mcp.js.map +1 -0
- package/dist/memory-agent.d.ts +92 -0
- package/dist/memory-agent.d.ts.map +1 -0
- package/dist/memory-agent.js +277 -0
- package/dist/memory-agent.js.map +1 -0
- package/dist/runtime-mapping.d.ts +97 -0
- package/dist/runtime-mapping.d.ts.map +1 -0
- package/dist/search-first-executor.d.ts +113 -0
- package/dist/search-first-executor.d.ts.map +1 -0
- package/dist/session.d.ts +48 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/template-extractor.d.ts +9 -0
- package/dist/template-extractor.d.ts.map +1 -0
- package/dist/template-generator.d.ts +12 -0
- package/dist/template-generator.d.ts.map +1 -0
- package/dist/tools.d.ts +58 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +771 -0
- package/dist/tools.js.map +1 -0
- package/dist/types.d.ts +503 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/understanding/analyzer.d.ts +55 -0
- package/dist/understanding/analyzer.d.ts.map +1 -0
- package/dist/understanding/explorer.d.ts +73 -0
- package/dist/understanding/explorer.d.ts.map +1 -0
- package/dist/understanding/index.d.ts +7 -0
- package/dist/understanding/index.d.ts.map +1 -0
- package/dist/understanding/types.d.ts +136 -0
- package/dist/understanding/types.d.ts.map +1 -0
- package/dist/validation.d.ts +29 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +106 -0
- package/dist/validation.js.map +1 -0
- package/dist/vessel-bootstrap.d.ts +190 -0
- package/dist/vessel-bootstrap.d.ts.map +1 -0
- package/dist/vessel-registry.d.ts +229 -0
- package/dist/vessel-registry.d.ts.map +1 -0
- package/index.ts +1329 -0
- package/package.json +54 -0
- package/src/acp-gossip.ts +193 -0
- package/src/acp.ts +362 -0
- package/src/activity.ts +1464 -0
- package/src/agent-runtime.ts +365 -0
- package/src/boredom.ts +423 -0
- package/src/cli/acp-server.ts +377 -0
- package/src/cli/burrow.ts +896 -0
- package/src/cli/doctor.ts +526 -0
- package/src/cli/goal.ts +224 -0
- package/src/cli/index.ts +147 -0
- package/src/cli/instance-registry.ts +271 -0
- package/src/cli/observe.ts +682 -0
- package/src/cli/vessel.ts +287 -0
- package/src/components/SystemOverview.tsx +331 -0
- package/src/composition-observer.ts +449 -0
- package/src/config.ts +172 -0
- package/src/environment.ts +167 -0
- package/src/goal-processor.ts +654 -0
- package/src/improviser.ts +591 -0
- package/src/impulse-filter.ts +273 -0
- package/src/impulse.ts +311 -0
- package/src/lib.ts +147 -0
- package/src/lifecycle-hooks.ts +181 -0
- package/src/llm.ts +434 -0
- package/src/mcp-activity-bridge.ts +158 -0
- package/src/mcp.ts +747 -0
- package/src/memory-agent.ts +316 -0
- package/src/runtime-mapping.ts +527 -0
- package/src/search-first-executor.ts +666 -0
- package/src/session.ts +141 -0
- package/src/template-extractor.ts +256 -0
- package/src/template-generator.ts +130 -0
- package/src/tools.ts +924 -0
- package/src/types.ts +497 -0
- package/src/understanding/analyzer.ts +354 -0
- package/src/understanding/explorer.ts +488 -0
- package/src/understanding/index.ts +27 -0
- package/src/understanding/types.ts +153 -0
- package/src/validation.ts +125 -0
- package/src/vessel-bootstrap.ts +440 -0
- package/src/vessel-registry.ts +621 -0
- package/templates/core/edit-file.json +85 -0
- package/templates/understanding/diagnose-problem.json +32 -0
- package/templates/understanding/explore-codebase-v2.json +57 -0
- package/templates/understanding/explore-codebase.json +37 -0
|
@@ -0,0 +1,896 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Burrow Command
|
|
3
|
+
*
|
|
4
|
+
* Inject minibob into an existing codebase, making it a vessel.
|
|
5
|
+
*
|
|
6
|
+
* Steps:
|
|
7
|
+
* 1. Analyze codebase structure (frameworks, entry points)
|
|
8
|
+
* 2. Add @metabob/minibob as dependency
|
|
9
|
+
* 3. Create minibob.json config file
|
|
10
|
+
* 4. Create .minibob/ directory for local state
|
|
11
|
+
* 5. Optionally inject vessel bootstrap into entry point
|
|
12
|
+
* 6. Generate initial activities based on detected patterns
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { parseArgs, formatHelp, printSuccess, printWarning, printInfo, exitWithError } from './index'
|
|
16
|
+
import { CodeExplorer } from '../understanding'
|
|
17
|
+
import type { CodeStructure } from '../understanding/types'
|
|
18
|
+
import {
|
|
19
|
+
registerVessel,
|
|
20
|
+
registerVesselWithSync,
|
|
21
|
+
updateVesselMapping,
|
|
22
|
+
getVesselByPath,
|
|
23
|
+
type Vessel,
|
|
24
|
+
type VesselActivity,
|
|
25
|
+
type VesselImpulse,
|
|
26
|
+
type VesselHook,
|
|
27
|
+
type VesselTool,
|
|
28
|
+
type VesselComposition,
|
|
29
|
+
} from '../vessel-registry'
|
|
30
|
+
import {
|
|
31
|
+
analyzeCodebase as analyzeCodebaseMapping,
|
|
32
|
+
inferIntent,
|
|
33
|
+
type CodebaseAnalysis,
|
|
34
|
+
} from '../runtime-mapping'
|
|
35
|
+
|
|
36
|
+
export interface BurrowOptions {
|
|
37
|
+
dryRun?: boolean
|
|
38
|
+
configOnly?: boolean
|
|
39
|
+
analyze?: boolean
|
|
40
|
+
force?: boolean
|
|
41
|
+
entry?: string
|
|
42
|
+
noInject?: boolean
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface BurrowPlan {
|
|
46
|
+
targetDir: string
|
|
47
|
+
structure: CodeStructure
|
|
48
|
+
codebaseAnalysis?: CodebaseAnalysis
|
|
49
|
+
packageManager: 'npm' | 'yarn' | 'pnpm' | 'bun'
|
|
50
|
+
entryPoints: string[]
|
|
51
|
+
primaryEntry?: string
|
|
52
|
+
framework?: string
|
|
53
|
+
actions: BurrowAction[]
|
|
54
|
+
vesselName?: string
|
|
55
|
+
vesselDescription?: string
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface BurrowAction {
|
|
59
|
+
type: 'add-dep' | 'create-config' | 'create-dir' | 'inject-bootstrap' | 'create-activity' | 'register-vessel' | 'create-mapping'
|
|
60
|
+
description: string
|
|
61
|
+
path?: string
|
|
62
|
+
content?: string
|
|
63
|
+
vessel?: Partial<Vessel>
|
|
64
|
+
analysis?: CodebaseAnalysis
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Run burrow command
|
|
69
|
+
*/
|
|
70
|
+
export async function burrow(args: string[]): Promise<void> {
|
|
71
|
+
const { flags, values, positional } = parseArgs(args)
|
|
72
|
+
|
|
73
|
+
// Show help
|
|
74
|
+
if (flags.help || flags.h) {
|
|
75
|
+
console.log(formatHelp('burrow', 'Inject minibob into a codebase', [
|
|
76
|
+
{ flag: '--dry-run', description: 'Show what would be done without making changes' },
|
|
77
|
+
{ flag: '--config-only', description: 'Only create config file, no injection' },
|
|
78
|
+
{ flag: '--analyze', description: 'Show detailed codebase analysis' },
|
|
79
|
+
{ flag: '--force', description: 'Overwrite existing minibob config' },
|
|
80
|
+
{ flag: '--entry <file>', description: 'Specify entry point (auto-detected by default)' },
|
|
81
|
+
{ flag: '--no-inject', description: 'Skip injecting bootstrap code' },
|
|
82
|
+
{ flag: '--help, -h', description: 'Show this help message' },
|
|
83
|
+
]))
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const targetDir = positional[0] || '.'
|
|
88
|
+
const options: BurrowOptions = {
|
|
89
|
+
dryRun: flags['dry-run'],
|
|
90
|
+
configOnly: flags['config-only'],
|
|
91
|
+
analyze: flags.analyze,
|
|
92
|
+
force: flags.force,
|
|
93
|
+
entry: values.entry,
|
|
94
|
+
noInject: flags['no-inject'],
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Validate target directory
|
|
98
|
+
const fs = await import('fs/promises')
|
|
99
|
+
const path = await import('path')
|
|
100
|
+
const absoluteTarget = path.resolve(targetDir)
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
await fs.access(absoluteTarget)
|
|
104
|
+
} catch {
|
|
105
|
+
exitWithError(`Target directory not found: ${absoluteTarget}`)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Check for existing config
|
|
109
|
+
if (!options.force) {
|
|
110
|
+
const existingConfig = await checkExistingConfig(absoluteTarget)
|
|
111
|
+
if (existingConfig) {
|
|
112
|
+
exitWithError(
|
|
113
|
+
`minibob config already exists at ${existingConfig}\nUse --force to overwrite`
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
printInfo(`Analyzing codebase: ${absoluteTarget}`)
|
|
119
|
+
|
|
120
|
+
// Analyze codebase
|
|
121
|
+
const plan = await analyzeCodebase(absoluteTarget, options)
|
|
122
|
+
|
|
123
|
+
// Show analysis if requested
|
|
124
|
+
if (options.analyze) {
|
|
125
|
+
printAnalysis(plan)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Show planned actions
|
|
129
|
+
console.log('\nPlanned actions:')
|
|
130
|
+
for (const action of plan.actions) {
|
|
131
|
+
console.log(` - ${action.description}`)
|
|
132
|
+
if (options.dryRun && action.path) {
|
|
133
|
+
console.log(` Path: ${action.path}`)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Execute if not dry run
|
|
138
|
+
if (options.dryRun) {
|
|
139
|
+
console.log('\n[Dry run] No changes made.')
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
console.log('\nExecuting...\n')
|
|
144
|
+
await executePlan(plan, options)
|
|
145
|
+
|
|
146
|
+
// Save local mapping summary
|
|
147
|
+
const vessel = getVesselByPath(absoluteTarget)
|
|
148
|
+
if (vessel) {
|
|
149
|
+
const mappingSummary = {
|
|
150
|
+
vesselId: vessel.id,
|
|
151
|
+
vesselName: vessel.name,
|
|
152
|
+
createdAt: new Date().toISOString(),
|
|
153
|
+
functionsAnalyzed: plan.codebaseAnalysis?.functions.length || 0,
|
|
154
|
+
classesAnalyzed: plan.codebaseAnalysis?.classes.length || 0,
|
|
155
|
+
activitiesGenerated: vessel.activities.length,
|
|
156
|
+
}
|
|
157
|
+
await Bun.write(`${absoluteTarget}/.minibob/vessel-mapping.json`, JSON.stringify(mappingSummary, null, 2))
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
printSuccess('Burrow complete!')
|
|
161
|
+
console.log('\nVessel registered successfully!')
|
|
162
|
+
if (vessel) {
|
|
163
|
+
console.log(` Vessel ID: ${vessel.id}`)
|
|
164
|
+
console.log(` Vessel Name: ${vessel.name}`)
|
|
165
|
+
}
|
|
166
|
+
console.log('\nNext steps:')
|
|
167
|
+
console.log(' 1. Review minibob.json configuration')
|
|
168
|
+
console.log(' 2. Set ANTHROPIC_API_KEY environment variable')
|
|
169
|
+
console.log(' 3. Run: minibob doctor')
|
|
170
|
+
console.log(' 4. Run: minibob goal "your first goal"')
|
|
171
|
+
console.log('')
|
|
172
|
+
console.log('The vessel is now tracked in the registry. Use:')
|
|
173
|
+
console.log(' minibob vessel list - to see all registered vessels')
|
|
174
|
+
console.log(' minibob vessel info - to see this vessel\'s details')
|
|
175
|
+
console.log('')
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Check for existing minibob configuration
|
|
180
|
+
*/
|
|
181
|
+
async function checkExistingConfig(targetDir: string): Promise<string | null> {
|
|
182
|
+
const configFiles = ['minibob.json', '.minibob.json', '.minibob/config.json']
|
|
183
|
+
|
|
184
|
+
for (const configFile of configFiles) {
|
|
185
|
+
const configPath = `${targetDir}/${configFile}`
|
|
186
|
+
try {
|
|
187
|
+
const file = Bun.file(configPath)
|
|
188
|
+
if (await file.exists()) {
|
|
189
|
+
return configPath
|
|
190
|
+
}
|
|
191
|
+
} catch {
|
|
192
|
+
// File doesn't exist
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return null
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Analyze codebase and create burrow plan
|
|
201
|
+
*/
|
|
202
|
+
async function analyzeCodebase(targetDir: string, options: BurrowOptions): Promise<BurrowPlan> {
|
|
203
|
+
const explorer = new CodeExplorer(targetDir)
|
|
204
|
+
const structure = await explorer.explore('.')
|
|
205
|
+
|
|
206
|
+
// Deep analysis for runtime mapping
|
|
207
|
+
printInfo('Performing deep code analysis for runtime mapping...')
|
|
208
|
+
const codebaseAnalysis = await analyzeCodebaseMapping(targetDir)
|
|
209
|
+
printInfo(`Found ${codebaseAnalysis.functions.length} functions, ${codebaseAnalysis.classes.length} classes`)
|
|
210
|
+
|
|
211
|
+
// Detect package manager
|
|
212
|
+
const packageManager = await detectPackageManager(targetDir)
|
|
213
|
+
|
|
214
|
+
// Detect framework
|
|
215
|
+
const framework = detectFramework(structure)
|
|
216
|
+
|
|
217
|
+
// Determine entry points
|
|
218
|
+
let entryPoints = structure.entryPoints
|
|
219
|
+
if (options.entry) {
|
|
220
|
+
entryPoints = [options.entry]
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Extract vessel name from package.json or directory name
|
|
224
|
+
const path = await import('path')
|
|
225
|
+
let vesselName = path.basename(targetDir)
|
|
226
|
+
let vesselDescription: string | undefined
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
const packageJsonPath = `${targetDir}/package.json`
|
|
230
|
+
const file = Bun.file(packageJsonPath)
|
|
231
|
+
if (await file.exists()) {
|
|
232
|
+
const pkg = JSON.parse(await file.text())
|
|
233
|
+
if (pkg.name) vesselName = pkg.name
|
|
234
|
+
if (pkg.description) vesselDescription = pkg.description
|
|
235
|
+
}
|
|
236
|
+
} catch {
|
|
237
|
+
// Use directory name as fallback
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Create plan
|
|
241
|
+
const plan: BurrowPlan = {
|
|
242
|
+
targetDir,
|
|
243
|
+
structure,
|
|
244
|
+
codebaseAnalysis,
|
|
245
|
+
packageManager,
|
|
246
|
+
entryPoints,
|
|
247
|
+
primaryEntry: entryPoints[0],
|
|
248
|
+
framework,
|
|
249
|
+
vesselName,
|
|
250
|
+
vesselDescription,
|
|
251
|
+
actions: [],
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Plan: Add dependency
|
|
255
|
+
plan.actions.push({
|
|
256
|
+
type: 'add-dep',
|
|
257
|
+
description: `Add @metabob/minibob to ${packageManager === 'bun' ? 'dependencies' : 'devDependencies'}`,
|
|
258
|
+
path: `${targetDir}/package.json`,
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
// Plan: Create .minibob directory
|
|
262
|
+
plan.actions.push({
|
|
263
|
+
type: 'create-dir',
|
|
264
|
+
description: 'Create .minibob/ directory for local state',
|
|
265
|
+
path: `${targetDir}/.minibob`,
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
plan.actions.push({
|
|
269
|
+
type: 'create-dir',
|
|
270
|
+
description: 'Create .minibob/impulses/ for local impulse storage',
|
|
271
|
+
path: `${targetDir}/.minibob/impulses`,
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
plan.actions.push({
|
|
275
|
+
type: 'create-dir',
|
|
276
|
+
description: 'Create .minibob/traces/ for execution trace cache',
|
|
277
|
+
path: `${targetDir}/.minibob/traces`,
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
plan.actions.push({
|
|
281
|
+
type: 'create-dir',
|
|
282
|
+
description: 'Create .minibob/activities/ for local activity templates',
|
|
283
|
+
path: `${targetDir}/.minibob/activities`,
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
plan.actions.push({
|
|
287
|
+
type: 'create-dir',
|
|
288
|
+
description: 'Create .minibob/mappings/ for runtime-to-source mappings',
|
|
289
|
+
path: `${targetDir}/.minibob/mappings`,
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
// Plan: Create config file
|
|
293
|
+
const config = generateConfig(plan)
|
|
294
|
+
plan.actions.push({
|
|
295
|
+
type: 'create-config',
|
|
296
|
+
description: 'Create minibob.json configuration',
|
|
297
|
+
path: `${targetDir}/minibob.json`,
|
|
298
|
+
content: JSON.stringify(config, null, 2),
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
// Plan: Inject bootstrap (unless disabled)
|
|
302
|
+
if (!options.configOnly && !options.noInject && plan.primaryEntry) {
|
|
303
|
+
plan.actions.push({
|
|
304
|
+
type: 'inject-bootstrap',
|
|
305
|
+
description: `Inject vessel bootstrap into ${plan.primaryEntry}`,
|
|
306
|
+
path: `${targetDir}/${plan.primaryEntry}`,
|
|
307
|
+
})
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Plan: Generate initial activities based on detected patterns
|
|
311
|
+
const activities = generateInitialActivities(plan)
|
|
312
|
+
for (const activity of activities) {
|
|
313
|
+
plan.actions.push({
|
|
314
|
+
type: 'create-activity',
|
|
315
|
+
description: `Create activity: ${activity.name}`,
|
|
316
|
+
path: `${targetDir}/.minibob/activities/${activity.id}.json`,
|
|
317
|
+
content: JSON.stringify(activity, null, 2),
|
|
318
|
+
})
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Plan: Register vessel in registry
|
|
322
|
+
const vesselActivities: VesselActivity[] = activities.map(a => ({
|
|
323
|
+
id: a.id,
|
|
324
|
+
name: a.name,
|
|
325
|
+
origin: 'defined' as const,
|
|
326
|
+
executions: 0,
|
|
327
|
+
successRate: 0,
|
|
328
|
+
avgDuration: 0,
|
|
329
|
+
thompson: { alpha: 1, beta: 1 },
|
|
330
|
+
}))
|
|
331
|
+
|
|
332
|
+
// Create vessel impulses for configuration
|
|
333
|
+
const vesselImpulses: VesselImpulse[] = [
|
|
334
|
+
{
|
|
335
|
+
id: 'config-provider',
|
|
336
|
+
type: 'config',
|
|
337
|
+
description: 'LLM provider configuration',
|
|
338
|
+
defaultValue: 'anthropic',
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
id: 'config-model',
|
|
342
|
+
type: 'config',
|
|
343
|
+
description: 'LLM model configuration',
|
|
344
|
+
defaultValue: 'claude-sonnet-4-20250514',
|
|
345
|
+
},
|
|
346
|
+
]
|
|
347
|
+
|
|
348
|
+
// Create default hooks
|
|
349
|
+
const vesselHooks: VesselHook[] = [
|
|
350
|
+
{
|
|
351
|
+
id: 'startup',
|
|
352
|
+
type: 'startup',
|
|
353
|
+
handler: 'vessel-bootstrap.ts::initializeVessel',
|
|
354
|
+
enabled: true,
|
|
355
|
+
},
|
|
356
|
+
]
|
|
357
|
+
|
|
358
|
+
// Create default tools
|
|
359
|
+
const vesselTools: VesselTool[] = [
|
|
360
|
+
{
|
|
361
|
+
name: 'build',
|
|
362
|
+
description: 'Build the project',
|
|
363
|
+
invocation: { type: 'command', target: `${packageManager} run build` },
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
name: 'test',
|
|
367
|
+
description: 'Run tests',
|
|
368
|
+
invocation: { type: 'command', target: `${packageManager} test` },
|
|
369
|
+
},
|
|
370
|
+
]
|
|
371
|
+
|
|
372
|
+
plan.actions.push({
|
|
373
|
+
type: 'register-vessel',
|
|
374
|
+
description: `Register ${vesselName} as a vessel in the registry`,
|
|
375
|
+
vessel: {
|
|
376
|
+
name: vesselName,
|
|
377
|
+
path: targetDir,
|
|
378
|
+
description: vesselDescription,
|
|
379
|
+
activities: vesselActivities,
|
|
380
|
+
impulses: vesselImpulses,
|
|
381
|
+
hooks: vesselHooks,
|
|
382
|
+
tools: vesselTools,
|
|
383
|
+
composition: { compositions: [], sequences: [] },
|
|
384
|
+
},
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
// Plan: Create runtime mappings from code analysis
|
|
388
|
+
if (codebaseAnalysis.functions.length > 0 || codebaseAnalysis.classes.length > 0) {
|
|
389
|
+
plan.actions.push({
|
|
390
|
+
type: 'create-mapping',
|
|
391
|
+
description: `Create runtime mappings for ${codebaseAnalysis.functions.length} functions and ${codebaseAnalysis.classes.length} classes`,
|
|
392
|
+
analysis: codebaseAnalysis,
|
|
393
|
+
})
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return plan
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Detect package manager
|
|
401
|
+
*/
|
|
402
|
+
async function detectPackageManager(targetDir: string): Promise<'npm' | 'yarn' | 'pnpm' | 'bun'> {
|
|
403
|
+
const lockFiles = [
|
|
404
|
+
{ file: 'bun.lockb', manager: 'bun' as const },
|
|
405
|
+
{ file: 'pnpm-lock.yaml', manager: 'pnpm' as const },
|
|
406
|
+
{ file: 'yarn.lock', manager: 'yarn' as const },
|
|
407
|
+
{ file: 'package-lock.json', manager: 'npm' as const },
|
|
408
|
+
]
|
|
409
|
+
|
|
410
|
+
for (const { file, manager } of lockFiles) {
|
|
411
|
+
try {
|
|
412
|
+
const lockFile = Bun.file(`${targetDir}/${file}`)
|
|
413
|
+
if (await lockFile.exists()) {
|
|
414
|
+
return manager
|
|
415
|
+
}
|
|
416
|
+
} catch {
|
|
417
|
+
// File doesn't exist
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return 'npm'
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Detect framework from dependencies
|
|
426
|
+
*/
|
|
427
|
+
function detectFramework(structure: CodeStructure): string | undefined {
|
|
428
|
+
const frameworks = structure.dependencies.frameworks
|
|
429
|
+
if (frameworks.length > 0) {
|
|
430
|
+
return frameworks[0]
|
|
431
|
+
}
|
|
432
|
+
return undefined
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Generate minibob config
|
|
437
|
+
*/
|
|
438
|
+
function generateConfig(plan: BurrowPlan): Record<string, any> {
|
|
439
|
+
const config: Record<string, any> = {
|
|
440
|
+
$schema: 'https://minibob.dev/schema/config.json',
|
|
441
|
+
provider: 'anthropic',
|
|
442
|
+
model: 'claude-sonnet-4-20250514',
|
|
443
|
+
templatesDir: './.minibob/activities',
|
|
444
|
+
autoCommit: false,
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Add framework-specific settings
|
|
448
|
+
if (plan.framework) {
|
|
449
|
+
config.vesselType = plan.framework.toLowerCase()
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Add vessel configuration for MCP backend
|
|
453
|
+
config.vessels = {
|
|
454
|
+
metabob: {
|
|
455
|
+
type: 'http',
|
|
456
|
+
endpoint: process.env.MINIBOB_MCP_ENDPOINT || 'http://api.minibob.local',
|
|
457
|
+
capabilities: ['activities', 'impulses', 'executions', 'thompson-sampling'],
|
|
458
|
+
},
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return config
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Generate initial activities based on detected patterns
|
|
466
|
+
*/
|
|
467
|
+
function generateInitialActivities(plan: BurrowPlan): Array<{
|
|
468
|
+
id: string
|
|
469
|
+
name: string
|
|
470
|
+
description: string
|
|
471
|
+
category: string
|
|
472
|
+
tasks: any[]
|
|
473
|
+
}> {
|
|
474
|
+
const activities: Array<{
|
|
475
|
+
id: string
|
|
476
|
+
name: string
|
|
477
|
+
description: string
|
|
478
|
+
category: string
|
|
479
|
+
tasks: any[]
|
|
480
|
+
}> = []
|
|
481
|
+
|
|
482
|
+
// Build activity
|
|
483
|
+
activities.push({
|
|
484
|
+
id: 'build',
|
|
485
|
+
name: 'Build Project',
|
|
486
|
+
description: 'Build the project using the detected package manager',
|
|
487
|
+
category: 'infrastructure',
|
|
488
|
+
tasks: [
|
|
489
|
+
{
|
|
490
|
+
id: 'build-1',
|
|
491
|
+
description: 'Run build command',
|
|
492
|
+
prompt: {
|
|
493
|
+
template: `Run the build command for this ${plan.framework || 'project'}`,
|
|
494
|
+
},
|
|
495
|
+
validation: {
|
|
496
|
+
requiredFiles: [],
|
|
497
|
+
},
|
|
498
|
+
},
|
|
499
|
+
],
|
|
500
|
+
})
|
|
501
|
+
|
|
502
|
+
// Test activity
|
|
503
|
+
activities.push({
|
|
504
|
+
id: 'test',
|
|
505
|
+
name: 'Run Tests',
|
|
506
|
+
description: 'Run the test suite',
|
|
507
|
+
category: 'infrastructure',
|
|
508
|
+
tasks: [
|
|
509
|
+
{
|
|
510
|
+
id: 'test-1',
|
|
511
|
+
description: 'Run test command',
|
|
512
|
+
prompt: {
|
|
513
|
+
template: 'Run the test suite and report results',
|
|
514
|
+
},
|
|
515
|
+
validation: {
|
|
516
|
+
requiredFiles: [],
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
],
|
|
520
|
+
})
|
|
521
|
+
|
|
522
|
+
// Framework-specific activities
|
|
523
|
+
if (plan.framework === 'React' || plan.framework === 'Next.js') {
|
|
524
|
+
activities.push({
|
|
525
|
+
id: 'create-component',
|
|
526
|
+
name: 'Create React Component',
|
|
527
|
+
description: 'Create a new React component',
|
|
528
|
+
category: 'feature',
|
|
529
|
+
tasks: [
|
|
530
|
+
{
|
|
531
|
+
id: 'create-component-1',
|
|
532
|
+
description: 'Create component file',
|
|
533
|
+
prompt: {
|
|
534
|
+
template:
|
|
535
|
+
'Create a new React component named {{componentName}} in {{path}}',
|
|
536
|
+
variables: [
|
|
537
|
+
{ name: 'componentName', type: 'string', required: true },
|
|
538
|
+
{ name: 'path', type: 'string', default: 'src/components' },
|
|
539
|
+
],
|
|
540
|
+
},
|
|
541
|
+
validation: {
|
|
542
|
+
requiredPatterns: ['export', 'function', 'return'],
|
|
543
|
+
},
|
|
544
|
+
},
|
|
545
|
+
],
|
|
546
|
+
})
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
if (plan.framework === 'Hono' || plan.framework === 'Express') {
|
|
550
|
+
activities.push({
|
|
551
|
+
id: 'create-endpoint',
|
|
552
|
+
name: 'Create API Endpoint',
|
|
553
|
+
description: 'Create a new API endpoint',
|
|
554
|
+
category: 'feature',
|
|
555
|
+
tasks: [
|
|
556
|
+
{
|
|
557
|
+
id: 'create-endpoint-1',
|
|
558
|
+
description: 'Create endpoint handler',
|
|
559
|
+
prompt: {
|
|
560
|
+
template:
|
|
561
|
+
'Create a new {{method}} endpoint at {{path}} that {{description}}',
|
|
562
|
+
variables: [
|
|
563
|
+
{ name: 'method', type: 'string', default: 'GET' },
|
|
564
|
+
{ name: 'path', type: 'string', required: true },
|
|
565
|
+
{ name: 'description', type: 'string', required: true },
|
|
566
|
+
],
|
|
567
|
+
},
|
|
568
|
+
validation: {
|
|
569
|
+
requiredPatterns: ['app.', 'router.', 'async', 'Request', 'Response'],
|
|
570
|
+
},
|
|
571
|
+
},
|
|
572
|
+
],
|
|
573
|
+
})
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
return activities
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Print analysis
|
|
581
|
+
*/
|
|
582
|
+
function printAnalysis(plan: BurrowPlan): void {
|
|
583
|
+
const { structure, codebaseAnalysis } = plan
|
|
584
|
+
|
|
585
|
+
console.log('\n=== Codebase Analysis ===\n')
|
|
586
|
+
console.log(`Target: ${plan.targetDir}`)
|
|
587
|
+
console.log(`Vessel Name: ${plan.vesselName || 'unnamed'}`)
|
|
588
|
+
console.log(`Package Manager: ${plan.packageManager}`)
|
|
589
|
+
console.log(`Framework: ${plan.framework || 'none detected'}`)
|
|
590
|
+
console.log(`Total Files: ${structure.totalFiles}`)
|
|
591
|
+
console.log(`Lines of Code: ${structure.totalLoc.toLocaleString()}`)
|
|
592
|
+
console.log(`Size: ${(structure.totalSize / 1024).toFixed(2)} KB`)
|
|
593
|
+
|
|
594
|
+
console.log('\nFile Types:')
|
|
595
|
+
const sortedTypes = Object.entries(structure.filesByType)
|
|
596
|
+
.sort((a, b) => b[1] - a[1])
|
|
597
|
+
.slice(0, 5)
|
|
598
|
+
for (const [ext, count] of sortedTypes) {
|
|
599
|
+
console.log(` .${ext}: ${count}`)
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
console.log('\nEntry Points:')
|
|
603
|
+
for (const entry of structure.entryPoints) {
|
|
604
|
+
const isPrimary = entry === plan.primaryEntry ? ' (primary)' : ''
|
|
605
|
+
console.log(` ${entry}${isPrimary}`)
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
if (structure.dependencies.runtime && Object.keys(structure.dependencies.runtime).length > 0) {
|
|
609
|
+
console.log('\nKey Dependencies:')
|
|
610
|
+
const deps = Object.entries(structure.dependencies.runtime).slice(0, 10)
|
|
611
|
+
for (const [dep, version] of deps) {
|
|
612
|
+
console.log(` ${dep}: ${version}`)
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// Show runtime mapping analysis
|
|
617
|
+
if (codebaseAnalysis) {
|
|
618
|
+
console.log('\n=== Runtime Mapping Analysis ===\n')
|
|
619
|
+
console.log(`Functions: ${codebaseAnalysis.functions.length}`)
|
|
620
|
+
console.log(`Classes: ${codebaseAnalysis.classes.length}`)
|
|
621
|
+
console.log(`Modules: ${codebaseAnalysis.modules.length}`)
|
|
622
|
+
console.log(`Documentation blocks: ${codebaseAnalysis.documentation.length}`)
|
|
623
|
+
|
|
624
|
+
// Show some function intents
|
|
625
|
+
if (codebaseAnalysis.functions.length > 0) {
|
|
626
|
+
console.log('\nSample Function Intents:')
|
|
627
|
+
const sampleFuncs = codebaseAnalysis.functions.slice(0, 5)
|
|
628
|
+
for (const func of sampleFuncs) {
|
|
629
|
+
const intent = inferIntent(func, codebaseAnalysis.classes, codebaseAnalysis.documentation)
|
|
630
|
+
console.log(` ${func.name}: ${intent.purpose} (${Math.round(intent.confidence * 100)}% confidence)`)
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// Show classes
|
|
635
|
+
if (codebaseAnalysis.classes.length > 0) {
|
|
636
|
+
console.log('\nClasses Found:')
|
|
637
|
+
for (const cls of codebaseAnalysis.classes.slice(0, 5)) {
|
|
638
|
+
const methodCount = cls.methods.length
|
|
639
|
+
console.log(` ${cls.name}: ${methodCount} methods${cls.extends ? ` (extends ${cls.extends})` : ''}`)
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
console.log('')
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* Execute burrow plan
|
|
649
|
+
*/
|
|
650
|
+
async function executePlan(plan: BurrowPlan, options: BurrowOptions): Promise<void> {
|
|
651
|
+
const fs = await import('fs/promises')
|
|
652
|
+
|
|
653
|
+
for (const action of plan.actions) {
|
|
654
|
+
switch (action.type) {
|
|
655
|
+
case 'add-dep':
|
|
656
|
+
await addDependency(plan)
|
|
657
|
+
printSuccess('Added @metabob/minibob dependency')
|
|
658
|
+
break
|
|
659
|
+
|
|
660
|
+
case 'create-dir':
|
|
661
|
+
if (action.path) {
|
|
662
|
+
await fs.mkdir(action.path, { recursive: true })
|
|
663
|
+
printSuccess(`Created ${action.path}`)
|
|
664
|
+
}
|
|
665
|
+
break
|
|
666
|
+
|
|
667
|
+
case 'create-config':
|
|
668
|
+
if (action.path && action.content) {
|
|
669
|
+
await Bun.write(action.path, action.content)
|
|
670
|
+
printSuccess(`Created ${action.path}`)
|
|
671
|
+
}
|
|
672
|
+
break
|
|
673
|
+
|
|
674
|
+
case 'inject-bootstrap':
|
|
675
|
+
if (action.path) {
|
|
676
|
+
await injectBootstrap(action.path, plan)
|
|
677
|
+
printSuccess(`Injected vessel bootstrap into ${action.path}`)
|
|
678
|
+
}
|
|
679
|
+
break
|
|
680
|
+
|
|
681
|
+
case 'create-activity':
|
|
682
|
+
if (action.path && action.content) {
|
|
683
|
+
await Bun.write(action.path, action.content)
|
|
684
|
+
printSuccess(`Created activity: ${action.path}`)
|
|
685
|
+
}
|
|
686
|
+
break
|
|
687
|
+
|
|
688
|
+
case 'register-vessel':
|
|
689
|
+
if (action.vessel) {
|
|
690
|
+
const existingVessel = getVesselByPath(plan.targetDir)
|
|
691
|
+
if (existingVessel) {
|
|
692
|
+
printWarning(`Vessel already registered: ${existingVessel.id}`)
|
|
693
|
+
} else {
|
|
694
|
+
// Collect function mappings for backend sync
|
|
695
|
+
const functionMappings: Array<{
|
|
696
|
+
functionName: string
|
|
697
|
+
file: string
|
|
698
|
+
line: number
|
|
699
|
+
className?: string
|
|
700
|
+
intent?: { purpose: string; confidence: number; source: string }
|
|
701
|
+
}> = []
|
|
702
|
+
|
|
703
|
+
if (plan.codebaseAnalysis) {
|
|
704
|
+
for (const func of plan.codebaseAnalysis.functions) {
|
|
705
|
+
const intent = inferIntent(func, plan.codebaseAnalysis.classes, plan.codebaseAnalysis.documentation)
|
|
706
|
+
functionMappings.push({
|
|
707
|
+
functionName: func.name,
|
|
708
|
+
file: func.file,
|
|
709
|
+
line: func.line,
|
|
710
|
+
intent: {
|
|
711
|
+
purpose: intent.purpose,
|
|
712
|
+
confidence: intent.confidence,
|
|
713
|
+
source: intent.source,
|
|
714
|
+
},
|
|
715
|
+
})
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
for (const cls of plan.codebaseAnalysis.classes) {
|
|
719
|
+
for (const method of cls.methods) {
|
|
720
|
+
const intent = inferIntent(method, plan.codebaseAnalysis.classes, plan.codebaseAnalysis.documentation)
|
|
721
|
+
functionMappings.push({
|
|
722
|
+
functionName: `${cls.name}.${method.name}`,
|
|
723
|
+
file: method.file,
|
|
724
|
+
line: method.line,
|
|
725
|
+
className: cls.name,
|
|
726
|
+
intent: {
|
|
727
|
+
purpose: intent.purpose,
|
|
728
|
+
confidence: intent.confidence,
|
|
729
|
+
source: intent.source,
|
|
730
|
+
},
|
|
731
|
+
})
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// Register with backend sync
|
|
737
|
+
printInfo('Syncing vessel to backend...')
|
|
738
|
+
const vessel = await registerVesselWithSync({
|
|
739
|
+
name: action.vessel.name || 'unnamed',
|
|
740
|
+
path: plan.targetDir,
|
|
741
|
+
description: action.vessel.description,
|
|
742
|
+
version: action.vessel.version,
|
|
743
|
+
activities: action.vessel.activities || [],
|
|
744
|
+
impulses: action.vessel.impulses || [],
|
|
745
|
+
hooks: action.vessel.hooks || [],
|
|
746
|
+
tools: action.vessel.tools || [],
|
|
747
|
+
composition: action.vessel.composition || { compositions: [], sequences: [] },
|
|
748
|
+
}, functionMappings)
|
|
749
|
+
printSuccess(`Registered vessel: ${vessel.name} (${vessel.id})`)
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
break
|
|
753
|
+
|
|
754
|
+
case 'create-mapping':
|
|
755
|
+
if (action.analysis) {
|
|
756
|
+
const vessel = getVesselByPath(plan.targetDir)
|
|
757
|
+
if (vessel) {
|
|
758
|
+
// Map functions to source locations and infer intent (local storage)
|
|
759
|
+
for (const func of action.analysis.functions) {
|
|
760
|
+
const intent = inferIntent(func, action.analysis.classes, action.analysis.documentation)
|
|
761
|
+
|
|
762
|
+
// Store function mapping locally
|
|
763
|
+
updateVesselMapping(vessel.id, 'function', func.name, {
|
|
764
|
+
file: func.file,
|
|
765
|
+
line: func.line,
|
|
766
|
+
functionName: func.name,
|
|
767
|
+
})
|
|
768
|
+
|
|
769
|
+
// Store inferred intent locally
|
|
770
|
+
updateVesselMapping(vessel.id, 'intent', func.name, intent)
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// Map class methods too
|
|
774
|
+
for (const cls of action.analysis.classes) {
|
|
775
|
+
for (const method of cls.methods) {
|
|
776
|
+
const intent = inferIntent(method, action.analysis.classes, action.analysis.documentation)
|
|
777
|
+
|
|
778
|
+
updateVesselMapping(vessel.id, 'function', `${cls.name}.${method.name}`, {
|
|
779
|
+
file: method.file,
|
|
780
|
+
line: method.line,
|
|
781
|
+
functionName: method.name,
|
|
782
|
+
className: cls.name,
|
|
783
|
+
})
|
|
784
|
+
|
|
785
|
+
updateVesselMapping(vessel.id, 'intent', `${cls.name}.${method.name}`, intent)
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
printSuccess(`Created runtime mappings for ${action.analysis.functions.length} functions`)
|
|
790
|
+
} else {
|
|
791
|
+
printWarning('Cannot create mappings: vessel not registered')
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
break
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* Add minibob dependency to package.json
|
|
801
|
+
*/
|
|
802
|
+
async function addDependency(plan: BurrowPlan): Promise<void> {
|
|
803
|
+
const packageJsonPath = `${plan.targetDir}/package.json`
|
|
804
|
+
|
|
805
|
+
try {
|
|
806
|
+
const content = await Bun.file(packageJsonPath).text()
|
|
807
|
+
const packageJson = JSON.parse(content)
|
|
808
|
+
|
|
809
|
+
// Add to devDependencies
|
|
810
|
+
if (!packageJson.devDependencies) {
|
|
811
|
+
packageJson.devDependencies = {}
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
packageJson.devDependencies['@metabob/minibob'] = '^0.1.0'
|
|
815
|
+
|
|
816
|
+
await Bun.write(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n')
|
|
817
|
+
} catch (error) {
|
|
818
|
+
printWarning(`Could not update package.json: ${error}`)
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* Inject vessel bootstrap into entry point
|
|
824
|
+
*/
|
|
825
|
+
async function injectBootstrap(entryPath: string, plan: BurrowPlan): Promise<void> {
|
|
826
|
+
try {
|
|
827
|
+
const content = await Bun.file(entryPath).text()
|
|
828
|
+
|
|
829
|
+
// Check if already injected
|
|
830
|
+
if (content.includes('@metabob/minibob') || content.includes('initializeVessel')) {
|
|
831
|
+
printWarning('Bootstrap already present, skipping injection')
|
|
832
|
+
return
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// Determine injection point and code
|
|
836
|
+
const isTypeScript = entryPath.endsWith('.ts') || entryPath.endsWith('.tsx')
|
|
837
|
+
const appName = plan.framework?.toLowerCase() || 'app'
|
|
838
|
+
|
|
839
|
+
const importStatement = `import { initializeVessel } from '@metabob/minibob/vessel-bootstrap'\n`
|
|
840
|
+
const initCode = `
|
|
841
|
+
// Initialize minibob vessel
|
|
842
|
+
const __vessel = await initializeVessel({
|
|
843
|
+
workingDirectory: import.meta.dir || __dirname,
|
|
844
|
+
provider: (process.env.MINIBOB_PROVIDER as 'anthropic' | 'openai') || 'anthropic',
|
|
845
|
+
apiKey: process.env.ANTHROPIC_API_KEY!,
|
|
846
|
+
model: process.env.MINIBOB_MODEL || 'claude-sonnet-4-20250514',
|
|
847
|
+
vesselType: '${appName}',
|
|
848
|
+
enableSelfImprovement: false // Enable when ready
|
|
849
|
+
})
|
|
850
|
+
|
|
851
|
+
// Export vessel for runtime access
|
|
852
|
+
export { __vessel }
|
|
853
|
+
`
|
|
854
|
+
|
|
855
|
+
// Find injection point
|
|
856
|
+
// Look for existing imports and inject after them
|
|
857
|
+
const importMatch = content.match(/^(import\s+.*?\n)+/m)
|
|
858
|
+
let newContent: string
|
|
859
|
+
|
|
860
|
+
if (importMatch) {
|
|
861
|
+
const importEnd = importMatch.index! + importMatch[0].length
|
|
862
|
+
newContent =
|
|
863
|
+
content.slice(0, importEnd) +
|
|
864
|
+
importStatement +
|
|
865
|
+
content.slice(importEnd)
|
|
866
|
+
} else {
|
|
867
|
+
// No imports, add at beginning
|
|
868
|
+
newContent = importStatement + content
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// Add init code after imports
|
|
872
|
+
const lastImportIndex = newContent.lastIndexOf('import ')
|
|
873
|
+
if (lastImportIndex >= 0) {
|
|
874
|
+
const lineEnd = newContent.indexOf('\n', lastImportIndex)
|
|
875
|
+
newContent =
|
|
876
|
+
newContent.slice(0, lineEnd + 1) +
|
|
877
|
+
initCode +
|
|
878
|
+
newContent.slice(lineEnd + 1)
|
|
879
|
+
} else {
|
|
880
|
+
newContent = importStatement + initCode + content
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
await Bun.write(entryPath, newContent)
|
|
884
|
+
} catch (error) {
|
|
885
|
+
printWarning(`Could not inject bootstrap: ${error}`)
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// CLI entry point
|
|
890
|
+
if (import.meta.main) {
|
|
891
|
+
const args = process.argv.slice(2)
|
|
892
|
+
burrow(args).catch((error) => {
|
|
893
|
+
console.error('Burrow failed:', error)
|
|
894
|
+
process.exit(1)
|
|
895
|
+
})
|
|
896
|
+
}
|