@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.
Files changed (174) hide show
  1. package/ARCHITECTURE.md +255 -0
  2. package/CHANGELOG.md +112 -0
  3. package/README.md +380 -0
  4. package/bin/minibob.js +36 -0
  5. package/dist/acp-gossip.d.ts +72 -0
  6. package/dist/acp-gossip.d.ts.map +1 -0
  7. package/dist/acp-gossip.js +156 -0
  8. package/dist/acp-gossip.js.map +1 -0
  9. package/dist/acp.d.ts +62 -0
  10. package/dist/acp.d.ts.map +1 -0
  11. package/dist/acp.js +292 -0
  12. package/dist/acp.js.map +1 -0
  13. package/dist/activity.d.ts +157 -0
  14. package/dist/activity.d.ts.map +1 -0
  15. package/dist/activity.js +518 -0
  16. package/dist/activity.js.map +1 -0
  17. package/dist/agent-runtime.d.ts +104 -0
  18. package/dist/agent-runtime.d.ts.map +1 -0
  19. package/dist/boredom.d.ts +125 -0
  20. package/dist/boredom.d.ts.map +1 -0
  21. package/dist/boredom.js +244 -0
  22. package/dist/boredom.js.map +1 -0
  23. package/dist/cli/acp-server.d.ts +23 -0
  24. package/dist/cli/acp-server.d.ts.map +1 -0
  25. package/dist/cli/burrow.d.ts +26 -0
  26. package/dist/cli/burrow.d.ts.map +1 -0
  27. package/dist/cli/doctor.d.ts +22 -0
  28. package/dist/cli/doctor.d.ts.map +1 -0
  29. package/dist/cli/goal.d.ts +22 -0
  30. package/dist/cli/goal.d.ts.map +1 -0
  31. package/dist/cli/index.d.ts +47 -0
  32. package/dist/cli/index.d.ts.map +1 -0
  33. package/dist/cli/instance-registry.d.ts +78 -0
  34. package/dist/cli/instance-registry.d.ts.map +1 -0
  35. package/dist/cli/observe.d.ts +35 -0
  36. package/dist/cli/observe.d.ts.map +1 -0
  37. package/dist/cli/vessel.d.ts +14 -0
  38. package/dist/cli/vessel.d.ts.map +1 -0
  39. package/dist/composition-observer.d.ts +96 -0
  40. package/dist/composition-observer.d.ts.map +1 -0
  41. package/dist/config.d.ts +36 -0
  42. package/dist/config.d.ts.map +1 -0
  43. package/dist/config.js +128 -0
  44. package/dist/config.js.map +1 -0
  45. package/dist/docker/Dockerfile +35 -0
  46. package/dist/environment.d.ts +72 -0
  47. package/dist/environment.d.ts.map +1 -0
  48. package/dist/environment.js +142 -0
  49. package/dist/environment.js.map +1 -0
  50. package/dist/goal-processor.d.ts +165 -0
  51. package/dist/goal-processor.d.ts.map +1 -0
  52. package/dist/helm/minibob-cluster/Chart.yaml +13 -0
  53. package/dist/helm/minibob-cluster/templates/_helpers.tpl +60 -0
  54. package/dist/helm/minibob-cluster/templates/configmap.yaml +11 -0
  55. package/dist/helm/minibob-cluster/templates/deployment.yaml +108 -0
  56. package/dist/helm/minibob-cluster/templates/secret.yaml +10 -0
  57. package/dist/helm/minibob-cluster/templates/service.yaml +37 -0
  58. package/dist/helm/minibob-cluster/values-local.yaml +41 -0
  59. package/dist/helm/minibob-cluster/values-production.yaml +57 -0
  60. package/dist/helm/minibob-cluster/values-testing-cluster.yaml +43 -0
  61. package/dist/helm/minibob-cluster/values.yaml +127 -0
  62. package/dist/improviser.d.ts +74 -0
  63. package/dist/improviser.d.ts.map +1 -0
  64. package/dist/impulse-filter.d.ts +74 -0
  65. package/dist/impulse-filter.d.ts.map +1 -0
  66. package/dist/impulse.d.ts +92 -0
  67. package/dist/impulse.d.ts.map +1 -0
  68. package/dist/impulse.js +234 -0
  69. package/dist/impulse.js.map +1 -0
  70. package/dist/lib.d.ts +29 -0
  71. package/dist/lib.d.ts.map +1 -0
  72. package/dist/lib.js +18561 -0
  73. package/dist/lib.js.map +98 -0
  74. package/dist/lifecycle-hooks.d.ts +99 -0
  75. package/dist/lifecycle-hooks.d.ts.map +1 -0
  76. package/dist/lifecycle-hooks.js +135 -0
  77. package/dist/lifecycle-hooks.js.map +1 -0
  78. package/dist/llm.d.ts +31 -0
  79. package/dist/llm.d.ts.map +1 -0
  80. package/dist/llm.js +349 -0
  81. package/dist/llm.js.map +1 -0
  82. package/dist/mcp-activity-bridge.d.ts +66 -0
  83. package/dist/mcp-activity-bridge.d.ts.map +1 -0
  84. package/dist/mcp-activity-bridge.js +126 -0
  85. package/dist/mcp-activity-bridge.js.map +1 -0
  86. package/dist/mcp.d.ts +216 -0
  87. package/dist/mcp.d.ts.map +1 -0
  88. package/dist/mcp.js +292 -0
  89. package/dist/mcp.js.map +1 -0
  90. package/dist/memory-agent.d.ts +92 -0
  91. package/dist/memory-agent.d.ts.map +1 -0
  92. package/dist/memory-agent.js +277 -0
  93. package/dist/memory-agent.js.map +1 -0
  94. package/dist/runtime-mapping.d.ts +97 -0
  95. package/dist/runtime-mapping.d.ts.map +1 -0
  96. package/dist/search-first-executor.d.ts +113 -0
  97. package/dist/search-first-executor.d.ts.map +1 -0
  98. package/dist/session.d.ts +48 -0
  99. package/dist/session.d.ts.map +1 -0
  100. package/dist/template-extractor.d.ts +9 -0
  101. package/dist/template-extractor.d.ts.map +1 -0
  102. package/dist/template-generator.d.ts +12 -0
  103. package/dist/template-generator.d.ts.map +1 -0
  104. package/dist/tools.d.ts +58 -0
  105. package/dist/tools.d.ts.map +1 -0
  106. package/dist/tools.js +771 -0
  107. package/dist/tools.js.map +1 -0
  108. package/dist/types.d.ts +503 -0
  109. package/dist/types.d.ts.map +1 -0
  110. package/dist/types.js +8 -0
  111. package/dist/types.js.map +1 -0
  112. package/dist/understanding/analyzer.d.ts +55 -0
  113. package/dist/understanding/analyzer.d.ts.map +1 -0
  114. package/dist/understanding/explorer.d.ts +73 -0
  115. package/dist/understanding/explorer.d.ts.map +1 -0
  116. package/dist/understanding/index.d.ts +7 -0
  117. package/dist/understanding/index.d.ts.map +1 -0
  118. package/dist/understanding/types.d.ts +136 -0
  119. package/dist/understanding/types.d.ts.map +1 -0
  120. package/dist/validation.d.ts +29 -0
  121. package/dist/validation.d.ts.map +1 -0
  122. package/dist/validation.js +106 -0
  123. package/dist/validation.js.map +1 -0
  124. package/dist/vessel-bootstrap.d.ts +190 -0
  125. package/dist/vessel-bootstrap.d.ts.map +1 -0
  126. package/dist/vessel-registry.d.ts +229 -0
  127. package/dist/vessel-registry.d.ts.map +1 -0
  128. package/index.ts +1329 -0
  129. package/package.json +54 -0
  130. package/src/acp-gossip.ts +193 -0
  131. package/src/acp.ts +362 -0
  132. package/src/activity.ts +1464 -0
  133. package/src/agent-runtime.ts +365 -0
  134. package/src/boredom.ts +423 -0
  135. package/src/cli/acp-server.ts +377 -0
  136. package/src/cli/burrow.ts +896 -0
  137. package/src/cli/doctor.ts +526 -0
  138. package/src/cli/goal.ts +224 -0
  139. package/src/cli/index.ts +147 -0
  140. package/src/cli/instance-registry.ts +271 -0
  141. package/src/cli/observe.ts +682 -0
  142. package/src/cli/vessel.ts +287 -0
  143. package/src/components/SystemOverview.tsx +331 -0
  144. package/src/composition-observer.ts +449 -0
  145. package/src/config.ts +172 -0
  146. package/src/environment.ts +167 -0
  147. package/src/goal-processor.ts +654 -0
  148. package/src/improviser.ts +591 -0
  149. package/src/impulse-filter.ts +273 -0
  150. package/src/impulse.ts +311 -0
  151. package/src/lib.ts +147 -0
  152. package/src/lifecycle-hooks.ts +181 -0
  153. package/src/llm.ts +434 -0
  154. package/src/mcp-activity-bridge.ts +158 -0
  155. package/src/mcp.ts +747 -0
  156. package/src/memory-agent.ts +316 -0
  157. package/src/runtime-mapping.ts +527 -0
  158. package/src/search-first-executor.ts +666 -0
  159. package/src/session.ts +141 -0
  160. package/src/template-extractor.ts +256 -0
  161. package/src/template-generator.ts +130 -0
  162. package/src/tools.ts +924 -0
  163. package/src/types.ts +497 -0
  164. package/src/understanding/analyzer.ts +354 -0
  165. package/src/understanding/explorer.ts +488 -0
  166. package/src/understanding/index.ts +27 -0
  167. package/src/understanding/types.ts +153 -0
  168. package/src/validation.ts +125 -0
  169. package/src/vessel-bootstrap.ts +440 -0
  170. package/src/vessel-registry.ts +621 -0
  171. package/templates/core/edit-file.json +85 -0
  172. package/templates/understanding/diagnose-problem.json +32 -0
  173. package/templates/understanding/explore-codebase-v2.json +57 -0
  174. 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
+ }