@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,377 @@
1
+ /**
2
+ * ACP Server Command
3
+ *
4
+ * Launch standalone ACP server for external agent integration.
5
+ *
6
+ * Modes:
7
+ * - stdio: JSON-RPC over stdin/stdout (for editor extensions, CLI piping)
8
+ * - http: HTTP server on specified port (default)
9
+ * - websocket: WebSocket server for streaming
10
+ */
11
+
12
+ import { parseArgs, formatHelp, printInfo, exitWithError } from './index'
13
+ import { loadConfig } from '../config'
14
+ import { ACPSession, handleACPRequest, type ACPServerConfig } from '../acp'
15
+ import { createToolHandlers, getAllToolDefinitions, type ToolHandler } from '../tools'
16
+ import type { ACPMessage } from '../types'
17
+
18
+ export interface ACPServerOptions {
19
+ mode?: 'stdio' | 'http' | 'websocket'
20
+ port?: number
21
+ noTools?: boolean
22
+ tools?: string[]
23
+ denyTools?: string[]
24
+ system?: string
25
+ }
26
+
27
+ /**
28
+ * Run ACP server command
29
+ */
30
+ export async function launchACPServer(args: string[]): Promise<void> {
31
+ const { flags, values } = parseArgs(args)
32
+
33
+ // Show help
34
+ if (flags.help || flags.h) {
35
+ console.log(formatHelp('acp', 'Launch standalone ACP server', [
36
+ { flag: '--mode <mode>', description: 'stdio, http, websocket', default: 'http' },
37
+ { flag: '--port <port>', description: 'Port for http/websocket mode', default: '8080' },
38
+ { flag: '--no-tools', description: 'Disable tool access (prompt only)' },
39
+ { flag: '--tools <list>', description: 'Comma-separated whitelist of allowed tools' },
40
+ { flag: '--deny-tools <list>', description: 'Comma-separated blacklist of denied tools' },
41
+ { flag: '--system <file>', description: 'Custom system prompt from file' },
42
+ { flag: '--help, -h', description: 'Show this help message' },
43
+ ]))
44
+ return
45
+ }
46
+
47
+ const options: ACPServerOptions = {
48
+ mode: (values.mode as 'stdio' | 'http' | 'websocket') || 'http',
49
+ port: values.port ? parseInt(values.port) : 8080,
50
+ noTools: flags['no-tools'],
51
+ tools: values.tools?.split(',').map((t) => t.trim()),
52
+ denyTools: values['deny-tools']?.split(',').map((t) => t.trim()),
53
+ system: values.system,
54
+ }
55
+
56
+ // Load configuration
57
+ const config = await loadConfig()
58
+
59
+ // Load custom system prompt if specified
60
+ let systemPrompt: string | undefined
61
+ if (options.system) {
62
+ try {
63
+ systemPrompt = await Bun.file(options.system).text()
64
+ } catch {
65
+ exitWithError(`Could not read system prompt file: ${options.system}`)
66
+ }
67
+ }
68
+
69
+ const acpConfig: ACPServerConfig = {
70
+ provider: config.provider,
71
+ apiKey: config.apiKey,
72
+ model: config.model,
73
+ workingDirectory: config.workingDirectory,
74
+ systemPrompt,
75
+ }
76
+
77
+ // Filter tools based on options
78
+ const toolFilter = createToolFilter(options)
79
+
80
+ switch (options.mode) {
81
+ case 'stdio':
82
+ await runStdioServer(acpConfig, toolFilter)
83
+ break
84
+ case 'websocket':
85
+ await runWebSocketServer(acpConfig, options.port!, toolFilter)
86
+ break
87
+ case 'http':
88
+ default:
89
+ await runHttpServer(acpConfig, options.port!, toolFilter)
90
+ break
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Create tool filter based on options
96
+ */
97
+ function createToolFilter(options: ACPServerOptions): (name: string) => boolean {
98
+ if (options.noTools) {
99
+ return () => false
100
+ }
101
+
102
+ return (name: string) => {
103
+ // Check whitelist
104
+ if (options.tools && options.tools.length > 0) {
105
+ return options.tools.includes(name)
106
+ }
107
+
108
+ // Check blacklist
109
+ if (options.denyTools && options.denyTools.length > 0) {
110
+ return !options.denyTools.includes(name)
111
+ }
112
+
113
+ return true
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Run stdio mode server
119
+ */
120
+ async function runStdioServer(
121
+ config: ACPServerConfig,
122
+ toolFilter: (name: string) => boolean
123
+ ): Promise<void> {
124
+ printInfo('ACP server running in stdio mode')
125
+ printInfo('Send JSON messages to stdin, receive responses on stdout')
126
+
127
+ const sessions = new Map<string, ACPSession>()
128
+
129
+ // Read lines from stdin
130
+ const reader = Bun.stdin.stream().getReader()
131
+ const decoder = new TextDecoder()
132
+ let buffer = ''
133
+
134
+ try {
135
+ while (true) {
136
+ const { done, value } = await reader.read()
137
+ if (done) break
138
+
139
+ buffer += decoder.decode(value, { stream: true })
140
+
141
+ // Process complete lines
142
+ const lines = buffer.split('\n')
143
+ buffer = lines.pop() || ''
144
+
145
+ for (const line of lines) {
146
+ if (!line.trim()) continue
147
+
148
+ try {
149
+ const message = JSON.parse(line) as ACPMessage & { sessionId?: string }
150
+
151
+ // Get or create session
152
+ const sessionId = message.sessionId || `stdio_${Date.now()}`
153
+ let session = sessions.get(sessionId)
154
+ if (!session) {
155
+ session = new ACPSession(config, sessionId)
156
+ sessions.set(sessionId, session)
157
+ }
158
+
159
+ // Handle message
160
+ const responses = await session.handleMessage(message)
161
+
162
+ // Write responses to stdout
163
+ for (const response of responses) {
164
+ const responseWithSession = { ...response, sessionId }
165
+ console.log(JSON.stringify(responseWithSession))
166
+ }
167
+ } catch (error) {
168
+ console.log(
169
+ JSON.stringify({
170
+ type: 'error',
171
+ error: error instanceof Error ? error.message : String(error),
172
+ })
173
+ )
174
+ }
175
+ }
176
+ }
177
+ } finally {
178
+ reader.releaseLock()
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Run HTTP mode server
184
+ */
185
+ async function runHttpServer(
186
+ config: ACPServerConfig,
187
+ port: number,
188
+ toolFilter: (name: string) => boolean
189
+ ): Promise<void> {
190
+ const server = Bun.serve({
191
+ port,
192
+ hostname: '0.0.0.0',
193
+
194
+ fetch(request) {
195
+ const url = new URL(request.url)
196
+
197
+ // Health check
198
+ if (url.pathname === '/health') {
199
+ return new Response(JSON.stringify({ status: 'ok', mode: 'acp-server' }), {
200
+ headers: { 'Content-Type': 'application/json' },
201
+ })
202
+ }
203
+
204
+ // ACP endpoint
205
+ if (url.pathname === '/acp' && request.method === 'POST') {
206
+ return handleACPRequest(config, request)
207
+ }
208
+
209
+ // List available tools
210
+ if (url.pathname === '/tools' && request.method === 'GET') {
211
+ const allTools = getAllToolDefinitions()
212
+ const filteredTools = allTools.filter((t) => toolFilter(t.name))
213
+ return new Response(JSON.stringify({ tools: filteredTools }), {
214
+ headers: { 'Content-Type': 'application/json' },
215
+ })
216
+ }
217
+
218
+ return new Response(
219
+ JSON.stringify({
220
+ error: 'Not found',
221
+ endpoints: ['GET /health', 'POST /acp', 'GET /tools'],
222
+ }),
223
+ {
224
+ status: 404,
225
+ headers: { 'Content-Type': 'application/json' },
226
+ }
227
+ )
228
+ },
229
+ })
230
+
231
+ console.log(`\nACP server running at http://0.0.0.0:${port}`)
232
+ console.log('\nEndpoints:')
233
+ console.log(' GET /health - Health check')
234
+ console.log(' POST /acp - ACP protocol handler')
235
+ console.log(' GET /tools - List available tools')
236
+ console.log('\nExample request:')
237
+ console.log(` curl -X POST http://localhost:${port}/acp \\`)
238
+ console.log(` -H "Content-Type: application/json" \\`)
239
+ console.log(` -d '{"type":"prompt","messages":[{"role":"user","content":"Hello!"}]}'`)
240
+ console.log('\nPress Ctrl+C to stop')
241
+
242
+ // Handle graceful shutdown
243
+ process.on('SIGINT', () => {
244
+ console.log('\n\nShutting down...')
245
+ server.stop()
246
+ process.exit(0)
247
+ })
248
+
249
+ process.on('SIGTERM', () => {
250
+ console.log('\n\nShutting down...')
251
+ server.stop()
252
+ process.exit(0)
253
+ })
254
+ }
255
+
256
+ /**
257
+ * Run WebSocket mode server
258
+ */
259
+ async function runWebSocketServer(
260
+ config: ACPServerConfig,
261
+ port: number,
262
+ toolFilter: (name: string) => boolean
263
+ ): Promise<void> {
264
+ const sessions = new Map<WebSocket, ACPSession>()
265
+
266
+ const server = Bun.serve({
267
+ port,
268
+ hostname: '0.0.0.0',
269
+
270
+ fetch(request, server) {
271
+ const url = new URL(request.url)
272
+
273
+ // Health check
274
+ if (url.pathname === '/health') {
275
+ return new Response(JSON.stringify({ status: 'ok', mode: 'acp-websocket' }), {
276
+ headers: { 'Content-Type': 'application/json' },
277
+ })
278
+ }
279
+
280
+ // WebSocket upgrade
281
+ if (url.pathname === '/acp/stream') {
282
+ const upgraded = server.upgrade(request)
283
+ if (!upgraded) {
284
+ return new Response('WebSocket upgrade failed', { status: 400 })
285
+ }
286
+ return undefined
287
+ }
288
+
289
+ return new Response(
290
+ JSON.stringify({
291
+ error: 'Not found',
292
+ endpoints: ['GET /health', 'WS /acp/stream'],
293
+ }),
294
+ {
295
+ status: 404,
296
+ headers: { 'Content-Type': 'application/json' },
297
+ }
298
+ )
299
+ },
300
+
301
+ websocket: {
302
+ open(ws) {
303
+ const sessionId = `ws_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
304
+ const session = new ACPSession(config, sessionId)
305
+ sessions.set(ws, session)
306
+
307
+ // Send hello
308
+ ws.send(
309
+ JSON.stringify({
310
+ type: 'hello',
311
+ sessionId,
312
+ version: '1.0',
313
+ capabilities: ['prompt', 'tool_call'],
314
+ })
315
+ )
316
+ },
317
+
318
+ async message(ws, message) {
319
+ const session = sessions.get(ws)
320
+ if (!session) {
321
+ ws.send(JSON.stringify({ type: 'error', error: 'Session not found' }))
322
+ return
323
+ }
324
+
325
+ try {
326
+ const data = JSON.parse(
327
+ typeof message === 'string' ? message : message.toString()
328
+ ) as ACPMessage
329
+
330
+ const responses = await session.handleMessage(data)
331
+ for (const response of responses) {
332
+ ws.send(JSON.stringify(response))
333
+ }
334
+ } catch (error) {
335
+ ws.send(
336
+ JSON.stringify({
337
+ type: 'error',
338
+ error: error instanceof Error ? error.message : String(error),
339
+ })
340
+ )
341
+ }
342
+ },
343
+
344
+ close(ws) {
345
+ sessions.delete(ws)
346
+ },
347
+ },
348
+ })
349
+
350
+ console.log(`\nACP WebSocket server running at ws://0.0.0.0:${port}/acp/stream`)
351
+ console.log('\nEndpoints:')
352
+ console.log(' GET /health - Health check')
353
+ console.log(' WS /acp/stream - WebSocket ACP endpoint')
354
+ console.log('\nPress Ctrl+C to stop')
355
+
356
+ // Handle graceful shutdown
357
+ process.on('SIGINT', () => {
358
+ console.log('\n\nShutting down...')
359
+ server.stop()
360
+ process.exit(0)
361
+ })
362
+
363
+ process.on('SIGTERM', () => {
364
+ console.log('\n\nShutting down...')
365
+ server.stop()
366
+ process.exit(0)
367
+ })
368
+ }
369
+
370
+ // CLI entry point
371
+ if (import.meta.main) {
372
+ const args = process.argv.slice(2)
373
+ launchACPServer(args).catch((error) => {
374
+ console.error('ACP server failed:', error)
375
+ process.exit(1)
376
+ })
377
+ }