@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,354 @@
1
+ /**
2
+ * ApplicationAnalyzer - LLM-powered semantic understanding
3
+ *
4
+ * Combines CodeExplorer's static analysis with LLM reasoning
5
+ * to understand application architecture, patterns, and issues.
6
+ */
7
+
8
+ import type { ActivityExecutor } from '../activity'
9
+ import type { CodeExplorer } from './explorer'
10
+ import type {
11
+ AnalyzeOptions,
12
+ Analysis,
13
+ Diagnosis,
14
+ Pattern
15
+ } from './types'
16
+ import type { Impulse } from '../types'
17
+
18
+ export class ApplicationAnalyzer {
19
+ constructor(
20
+ private executor: ActivityExecutor,
21
+ private explorer: CodeExplorer
22
+ ) {}
23
+
24
+ /**
25
+ * Safe wrapper for Bun.file() with better error handling
26
+ */
27
+ private safeReadFile(path: string): ReturnType<typeof Bun.file> {
28
+ if (!path || typeof path !== 'string') {
29
+ throw new Error(`Invalid file path: expected string, got ${typeof path}`)
30
+ }
31
+ try {
32
+ return Bun.file(path)
33
+ } catch (error) {
34
+ throw new Error(`Failed to create file handle for '${path}': ${error instanceof Error ? error.message : String(error)}`)
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Analyze application architecture and structure
40
+ */
41
+ async analyze(options: AnalyzeOptions): Promise<Analysis> {
42
+ // 1. Fast static exploration
43
+ console.log('Exploring codebase structure...')
44
+ const structure = await this.explorer.explore(options.rootPath)
45
+
46
+ // 2. Create impulses from exploration
47
+ const impulses = this.createImpulsesFromStructure(structure, options.focus)
48
+
49
+ // 3. Load and execute analysis activity
50
+ console.log('Performing semantic analysis with LLM...')
51
+ const template = await this.loadTemplate('explore-codebase')
52
+
53
+ const result = await this.executor.execute({
54
+ template,
55
+ variables: {
56
+ structure: JSON.stringify(structure, null, 2),
57
+ focus: options.focus || 'architecture',
58
+ depth: options.depth || 'medium'
59
+ }
60
+ })
61
+
62
+ // 4. Parse structured output from task results
63
+ return this.parseAnalysisResult(result)
64
+ }
65
+
66
+ /**
67
+ * Diagnose specific problem in application
68
+ */
69
+ async diagnose(problem: string, context?: string[]): Promise<Diagnosis> {
70
+ // 1. Gather relevant context files
71
+ const relevantFiles = context || await this.findRelevantFiles(problem)
72
+
73
+ if (relevantFiles.length === 0) {
74
+ return {
75
+ symptoms: [problem],
76
+ rootCause: 'Unable to find relevant files for analysis',
77
+ affectedFiles: [],
78
+ recommendedFix: 'Please provide specific file paths or more context',
79
+ validationSteps: [],
80
+ confidence: 'low'
81
+ }
82
+ }
83
+
84
+ // 2. Create impulses for relevant files
85
+ const impulses: Impulse[] = relevantFiles.slice(0, 5).map(path => ({
86
+ id: `file_${path.replace(/[^a-zA-Z0-9]/g, '_')}`,
87
+ pointer: { type: 'file', path },
88
+ budget: 2000,
89
+ priority: 'high' as const,
90
+ loaded: false,
91
+ content: null
92
+ }))
93
+
94
+ // 3. Execute diagnosis activity
95
+ console.log(`Diagnosing problem across ${impulses.length} files...`)
96
+ const template = await this.loadTemplate('diagnose-problem')
97
+
98
+ const result = await this.executor.execute({
99
+ template,
100
+ variables: {
101
+ problem,
102
+ files: relevantFiles.join(', ')
103
+ }
104
+ })
105
+
106
+ // 4. Parse diagnosis result
107
+ return this.parseDiagnosis(result)
108
+ }
109
+
110
+ /**
111
+ * Detect patterns in codebase
112
+ */
113
+ async detectPatterns(rootPath: string): Promise<Pattern[]> {
114
+ const structure = await this.explorer.explore(rootPath)
115
+ const patterns: Pattern[] = []
116
+
117
+ // Detect common architecture patterns
118
+ if (structure.dependencies.frameworks.includes('React')) {
119
+ patterns.push({
120
+ type: 'architecture',
121
+ name: 'React SPA',
122
+ description: 'React-based single-page application',
123
+ instances: [{ file: 'package.json' }]
124
+ })
125
+ }
126
+
127
+ if (structure.dependencies.frameworks.includes('Hono') ||
128
+ structure.dependencies.frameworks.includes('Express')) {
129
+ patterns.push({
130
+ type: 'architecture',
131
+ name: 'HTTP Server',
132
+ description: 'HTTP server application',
133
+ instances: [{ file: 'package.json' }]
134
+ })
135
+ }
136
+
137
+ // Detect entry point patterns
138
+ if (structure.entryPoints.includes('index.ts') && structure.entryPoints.includes('server.ts')) {
139
+ patterns.push({
140
+ type: 'architecture',
141
+ name: 'Client-Server Split',
142
+ description: 'Separate client and server entry points',
143
+ instances: [
144
+ { file: 'index.ts', context: 'Client entry' },
145
+ { file: 'server.ts', context: 'Server entry' }
146
+ ]
147
+ })
148
+ }
149
+
150
+ return patterns
151
+ }
152
+
153
+ /**
154
+ * Create impulses from codebase structure
155
+ */
156
+ private createImpulsesFromStructure(
157
+ structure: any,
158
+ focus?: string
159
+ ): Impulse[] {
160
+ const impulses: Impulse[] = []
161
+
162
+ // Add entry point impulses (high priority)
163
+ for (const entryPoint of structure.entryPoints.slice(0, 3)) {
164
+ impulses.push({
165
+ id: `entrypoint_${entryPoint.replace(/[^a-zA-Z0-9]/g, '_')}`,
166
+ pointer: { type: 'file', path: entryPoint, limit: 100 },
167
+ budget: 1500,
168
+ priority: 'high' as const,
169
+ loaded: false,
170
+ content: null
171
+ })
172
+ }
173
+
174
+ // Add package.json for dependency info (medium priority)
175
+ impulses.push({
176
+ id: 'dependencies',
177
+ pointer: { type: 'file', path: 'package.json' },
178
+ budget: 500,
179
+ priority: 'medium' as const,
180
+ loaded: false,
181
+ content: null
182
+ })
183
+
184
+ // Focus-specific impulses
185
+ if (focus === 'components' && structure.filesByType['tsx']) {
186
+ // Add React component files
187
+ impulses.push({
188
+ id: 'react_components',
189
+ pointer: { type: 'memo', content: `Component files: ${structure.filesByType['tsx']} .tsx files found` },
190
+ budget: 300,
191
+ priority: 'medium' as const,
192
+ loaded: false,
193
+ content: null
194
+ })
195
+ }
196
+
197
+ return impulses
198
+ }
199
+
200
+ /**
201
+ * Find files relevant to problem description
202
+ */
203
+ private async findRelevantFiles(problem: string): Promise<string[]> {
204
+ const keywords = this.extractKeywords(problem)
205
+ const structure = await this.explorer.explore(process.cwd())
206
+
207
+ // Build list of all files
208
+ const allFiles: string[] = []
209
+ const collectFiles = (node: any) => {
210
+ if (node.type === 'file') {
211
+ allFiles.push(node.path)
212
+ }
213
+ if (node.children) {
214
+ for (const child of node.children) {
215
+ collectFiles(child)
216
+ }
217
+ }
218
+ }
219
+ collectFiles(structure.directoryStructure)
220
+
221
+ // Filter by keywords
222
+ const relevant = allFiles.filter(file =>
223
+ keywords.some(kw => file.toLowerCase().includes(kw.toLowerCase()))
224
+ )
225
+
226
+ return relevant.slice(0, 10)
227
+ }
228
+
229
+ /**
230
+ * Extract keywords from problem description
231
+ */
232
+ private extractKeywords(text: string): string[] {
233
+ // Remove common words and extract meaningful terms
234
+ const stopWords = new Set(['the', 'is', 'at', 'which', 'on', 'a', 'an', 'and', 'or', 'but', 'in', 'with', 'to', 'for'])
235
+ const words = text.toLowerCase()
236
+ .replace(/[^a-z0-9\s]/g, ' ')
237
+ .split(/\s+/)
238
+ .filter(w => w.length > 2 && !stopWords.has(w))
239
+
240
+ return Array.from(new Set(words))
241
+ }
242
+
243
+ /**
244
+ * Load activity template
245
+ */
246
+ private async loadTemplate(templateId: string): Promise<any> {
247
+ // Try loading from local templates in multiple locations
248
+ const possiblePaths = [
249
+ `templates/understanding/${templateId}.json`,
250
+ `./templates/understanding/${templateId}.json`,
251
+ `${process.cwd()}/templates/understanding/${templateId}.json`,
252
+ // Also try from minibob directory if we're running elsewhere
253
+ `${__dirname}/../../templates/understanding/${templateId}.json`,
254
+ ]
255
+
256
+ for (const templatePath of possiblePaths) {
257
+ try {
258
+ const content = await this.safeReadFile(templatePath).text()
259
+ const template = JSON.parse(content)
260
+ console.log(`Loaded template from: ${templatePath}`)
261
+ return template
262
+ } catch (error) {
263
+ // Try next path
264
+ }
265
+ }
266
+
267
+ // Template not found in any location
268
+ throw new Error(`Template not found: ${templateId}. Tried paths: ${possiblePaths.join(', ')}`)
269
+ }
270
+
271
+ /**
272
+ * Parse analysis result from LLM output
273
+ */
274
+ private parseAnalysisResult(result: any): Analysis {
275
+ // Extract analysis from task results
276
+ const output = result.taskResults?.[0]?.output || result.output || ''
277
+
278
+ // Try to parse as JSON
279
+ try {
280
+ // Look for JSON in output
281
+ const jsonMatch = output.match(/\{[\s\S]*\}/)
282
+ if (jsonMatch) {
283
+ return JSON.parse(jsonMatch[0])
284
+ }
285
+ } catch (error) {
286
+ // Fallback to text parsing
287
+ }
288
+
289
+ // Fallback: create structured response from text
290
+ return {
291
+ architecture: {
292
+ pattern: 'Unknown',
293
+ description: output.substring(0, 500),
294
+ layers: []
295
+ },
296
+ components: {
297
+ summary: 'See full output for details',
298
+ keyModules: []
299
+ },
300
+ dataFlow: {
301
+ description: 'Analysis in progress',
302
+ flows: []
303
+ },
304
+ entryPoints: {
305
+ main: []
306
+ },
307
+ dependencies: {
308
+ external: [],
309
+ internal: []
310
+ },
311
+ techStack: {
312
+ runtime: 'Unknown',
313
+ framework: [],
314
+ language: [],
315
+ tools: []
316
+ }
317
+ }
318
+ }
319
+
320
+ /**
321
+ * Parse diagnosis result from LLM output
322
+ */
323
+ private parseDiagnosis(result: any): Diagnosis {
324
+ const output = result.taskResults?.[0]?.output || result.output || ''
325
+
326
+ // Try to parse as JSON
327
+ try {
328
+ const jsonMatch = output.match(/\{[\s\S]*\}/)
329
+ if (jsonMatch) {
330
+ const parsed = JSON.parse(jsonMatch[0])
331
+ return {
332
+ symptoms: parsed.symptoms || [output.substring(0, 200)],
333
+ rootCause: parsed.rootCause || 'See full analysis',
334
+ affectedFiles: parsed.affectedFiles || [],
335
+ recommendedFix: parsed.recommendedFix || 'Review analysis output',
336
+ validationSteps: parsed.validationSteps || [],
337
+ confidence: parsed.confidence || 'medium'
338
+ }
339
+ }
340
+ } catch (error) {
341
+ // Fallback
342
+ }
343
+
344
+ // Fallback: extract from text
345
+ return {
346
+ symptoms: [output.substring(0, 200)],
347
+ rootCause: 'See full analysis in output',
348
+ affectedFiles: [],
349
+ recommendedFix: output,
350
+ validationSteps: ['Review LLM analysis', 'Validate proposed changes'],
351
+ confidence: 'medium'
352
+ }
353
+ }
354
+ }