@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,527 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime Mapping
|
|
3
|
+
*
|
|
4
|
+
* Maps runtime behavior (traces) to source code (instructions) to intent (purpose).
|
|
5
|
+
*
|
|
6
|
+
* The Three Ideograms:
|
|
7
|
+
* - VESSEL (Instructional State): Source code, activity templates
|
|
8
|
+
* - BECOMING (Transient State): Execution traces, function calls
|
|
9
|
+
* - INSTANCE (Functional State): Running process, outcomes
|
|
10
|
+
*
|
|
11
|
+
* This module bridges these states by:
|
|
12
|
+
* 1. Capturing runtime traces (what happens)
|
|
13
|
+
* 2. Locating source code (what the code says)
|
|
14
|
+
* 3. Inferring intent (why the code exists)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'fs'
|
|
18
|
+
import { join, relative, extname } from 'path'
|
|
19
|
+
import type { SourceLocation, InferredIntent } from './vessel-registry'
|
|
20
|
+
|
|
21
|
+
// =============================================================================
|
|
22
|
+
// TYPES
|
|
23
|
+
// =============================================================================
|
|
24
|
+
|
|
25
|
+
export interface TraceToSourceMapping {
|
|
26
|
+
/** The trace activity ID */
|
|
27
|
+
traceId: string
|
|
28
|
+
/** Function/method that was called */
|
|
29
|
+
functionName: string
|
|
30
|
+
/** Source location if found */
|
|
31
|
+
sourceLocation?: SourceLocation
|
|
32
|
+
/** Inferred intent */
|
|
33
|
+
intent?: InferredIntent
|
|
34
|
+
/** Confidence in the mapping */
|
|
35
|
+
confidence: number
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface CodebaseAnalysis {
|
|
39
|
+
/** Root path of the codebase */
|
|
40
|
+
rootPath: string
|
|
41
|
+
/** All functions/methods found */
|
|
42
|
+
functions: FunctionInfo[]
|
|
43
|
+
/** All classes found */
|
|
44
|
+
classes: ClassInfo[]
|
|
45
|
+
/** Module structure */
|
|
46
|
+
modules: ModuleInfo[]
|
|
47
|
+
/** Entry points */
|
|
48
|
+
entryPoints: string[]
|
|
49
|
+
/** Comments and docstrings */
|
|
50
|
+
documentation: DocumentationInfo[]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface FunctionInfo {
|
|
54
|
+
name: string
|
|
55
|
+
file: string
|
|
56
|
+
line: number
|
|
57
|
+
params: string[]
|
|
58
|
+
returnType?: string
|
|
59
|
+
isAsync: boolean
|
|
60
|
+
isExported: boolean
|
|
61
|
+
docstring?: string
|
|
62
|
+
comments?: string[]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface ClassInfo {
|
|
66
|
+
name: string
|
|
67
|
+
file: string
|
|
68
|
+
line: number
|
|
69
|
+
methods: FunctionInfo[]
|
|
70
|
+
properties: string[]
|
|
71
|
+
extends?: string
|
|
72
|
+
implements?: string[]
|
|
73
|
+
docstring?: string
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface ModuleInfo {
|
|
77
|
+
name: string
|
|
78
|
+
file: string
|
|
79
|
+
exports: string[]
|
|
80
|
+
imports: { from: string; items: string[] }[]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface DocumentationInfo {
|
|
84
|
+
file: string
|
|
85
|
+
line: number
|
|
86
|
+
type: 'comment' | 'docstring' | 'jsdoc' | 'inline'
|
|
87
|
+
content: string
|
|
88
|
+
associatedWith?: string // function/class name
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// =============================================================================
|
|
92
|
+
// SOURCE ANALYSIS
|
|
93
|
+
// =============================================================================
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Analyze a codebase to extract function/class/module information
|
|
97
|
+
*/
|
|
98
|
+
export async function analyzeCodebase(rootPath: string): Promise<CodebaseAnalysis> {
|
|
99
|
+
const functions: FunctionInfo[] = []
|
|
100
|
+
const classes: ClassInfo[] = []
|
|
101
|
+
const modules: ModuleInfo[] = []
|
|
102
|
+
const documentation: DocumentationInfo[] = []
|
|
103
|
+
const entryPoints: string[] = []
|
|
104
|
+
|
|
105
|
+
const sourceExtensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs']
|
|
106
|
+
|
|
107
|
+
function walkDirectory(dir: string): void {
|
|
108
|
+
const entries = readdirSync(dir)
|
|
109
|
+
|
|
110
|
+
for (const entry of entries) {
|
|
111
|
+
const fullPath = join(dir, entry)
|
|
112
|
+
|
|
113
|
+
// Skip node_modules, .git, build artifacts, etc.
|
|
114
|
+
if (entry === 'node_modules' || entry === '.git' || entry === 'dist' || entry === 'build' ||
|
|
115
|
+
entry === '.local' || entry === 'test-workspace' || entry === 'coverage' || entry === '.turbo') {
|
|
116
|
+
continue
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let stat
|
|
120
|
+
try {
|
|
121
|
+
stat = statSync(fullPath)
|
|
122
|
+
} catch (err) {
|
|
123
|
+
// Skip broken symlinks or inaccessible files
|
|
124
|
+
continue
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (stat.isDirectory()) {
|
|
128
|
+
walkDirectory(fullPath)
|
|
129
|
+
} else if (sourceExtensions.includes(extname(entry))) {
|
|
130
|
+
analyzeSourceFile(fullPath, rootPath, functions, classes, modules, documentation)
|
|
131
|
+
|
|
132
|
+
// Detect entry points
|
|
133
|
+
if (entry === 'index.ts' || entry === 'index.js' || entry === 'main.ts' || entry === 'main.js') {
|
|
134
|
+
entryPoints.push(relative(rootPath, fullPath))
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
walkDirectory(rootPath)
|
|
141
|
+
|
|
142
|
+
// Also check package.json for entry points
|
|
143
|
+
const packageJsonPath = join(rootPath, 'package.json')
|
|
144
|
+
if (existsSync(packageJsonPath)) {
|
|
145
|
+
try {
|
|
146
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'))
|
|
147
|
+
if (pkg.main) entryPoints.push(pkg.main)
|
|
148
|
+
if (pkg.module) entryPoints.push(pkg.module)
|
|
149
|
+
if (pkg.bin) {
|
|
150
|
+
if (typeof pkg.bin === 'string') {
|
|
151
|
+
entryPoints.push(pkg.bin)
|
|
152
|
+
} else {
|
|
153
|
+
entryPoints.push(...Object.values(pkg.bin) as string[])
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
} catch (e) {
|
|
157
|
+
// Ignore parse errors
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
rootPath,
|
|
163
|
+
functions,
|
|
164
|
+
classes,
|
|
165
|
+
modules,
|
|
166
|
+
entryPoints: [...new Set(entryPoints)],
|
|
167
|
+
documentation,
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Analyze a single source file
|
|
173
|
+
*/
|
|
174
|
+
function analyzeSourceFile(
|
|
175
|
+
filePath: string,
|
|
176
|
+
rootPath: string,
|
|
177
|
+
functions: FunctionInfo[],
|
|
178
|
+
classes: ClassInfo[],
|
|
179
|
+
modules: ModuleInfo[],
|
|
180
|
+
documentation: DocumentationInfo[]
|
|
181
|
+
): void {
|
|
182
|
+
try {
|
|
183
|
+
const content = readFileSync(filePath, 'utf-8')
|
|
184
|
+
const lines = content.split('\n')
|
|
185
|
+
const relativePath = relative(rootPath, filePath)
|
|
186
|
+
|
|
187
|
+
// Extract imports
|
|
188
|
+
const imports: { from: string; items: string[] }[] = []
|
|
189
|
+
const exports: string[] = []
|
|
190
|
+
|
|
191
|
+
// Simple regex-based extraction (more robust would use AST)
|
|
192
|
+
const functionRegex = /(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(/g
|
|
193
|
+
const classRegex = /(?:export\s+)?class\s+(\w+)(?:\s+extends\s+(\w+))?/g
|
|
194
|
+
const arrowFunctionRegex = /(?:export\s+)?(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?\([^)]*\)\s*=>/g
|
|
195
|
+
const methodRegex = /(?:async\s+)?(\w+)\s*\([^)]*\)\s*(?::\s*\w+)?\s*\{/g
|
|
196
|
+
const exportRegex = /export\s+(?:default\s+)?(?:const|let|var|function|class|interface|type)\s+(\w+)/g
|
|
197
|
+
const importRegex = /import\s+(?:{([^}]+)}|(\w+))\s+from\s+['"]([^'"]+)['"]/g
|
|
198
|
+
const commentRegex = /\/\*\*([\s\S]*?)\*\/|\/\/\s*(.*)/g
|
|
199
|
+
const docstringRegex = /\/\*\*\s*([\s\S]*?)\s*\*\//g
|
|
200
|
+
|
|
201
|
+
// Extract functions
|
|
202
|
+
let match
|
|
203
|
+
while ((match = functionRegex.exec(content)) !== null) {
|
|
204
|
+
const name = match[1]
|
|
205
|
+
const line = content.slice(0, match.index).split('\n').length
|
|
206
|
+
|
|
207
|
+
// Look for preceding docstring
|
|
208
|
+
const docstring = findPrecedingDocstring(lines, line - 1)
|
|
209
|
+
|
|
210
|
+
functions.push({
|
|
211
|
+
name,
|
|
212
|
+
file: relativePath,
|
|
213
|
+
line,
|
|
214
|
+
params: extractParams(content, match.index),
|
|
215
|
+
isAsync: match[0].includes('async'),
|
|
216
|
+
isExported: match[0].includes('export'),
|
|
217
|
+
docstring,
|
|
218
|
+
})
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Extract arrow functions
|
|
222
|
+
while ((match = arrowFunctionRegex.exec(content)) !== null) {
|
|
223
|
+
const name = match[1]
|
|
224
|
+
const line = content.slice(0, match.index).split('\n').length
|
|
225
|
+
|
|
226
|
+
const docstring = findPrecedingDocstring(lines, line - 1)
|
|
227
|
+
|
|
228
|
+
functions.push({
|
|
229
|
+
name,
|
|
230
|
+
file: relativePath,
|
|
231
|
+
line,
|
|
232
|
+
params: extractParams(content, match.index),
|
|
233
|
+
isAsync: match[0].includes('async'),
|
|
234
|
+
isExported: match[0].includes('export'),
|
|
235
|
+
docstring,
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Extract classes
|
|
240
|
+
while ((match = classRegex.exec(content)) !== null) {
|
|
241
|
+
const name = match[1]
|
|
242
|
+
const extendsClass = match[2]
|
|
243
|
+
const line = content.slice(0, match.index).split('\n').length
|
|
244
|
+
|
|
245
|
+
const docstring = findPrecedingDocstring(lines, line - 1)
|
|
246
|
+
|
|
247
|
+
// Find methods in the class
|
|
248
|
+
const classEndIndex = findMatchingBrace(content, content.indexOf('{', match.index))
|
|
249
|
+
const classBody = content.slice(match.index, classEndIndex)
|
|
250
|
+
const classMethods: FunctionInfo[] = []
|
|
251
|
+
|
|
252
|
+
let methodMatch
|
|
253
|
+
const classMethodRegex = /(?:async\s+)?(\w+)\s*\([^)]*\)\s*(?::\s*[\w<>\[\]]+)?\s*\{/g
|
|
254
|
+
while ((methodMatch = classMethodRegex.exec(classBody)) !== null) {
|
|
255
|
+
if (methodMatch[1] !== 'constructor' && methodMatch[1] !== 'if' && methodMatch[1] !== 'for' && methodMatch[1] !== 'while') {
|
|
256
|
+
classMethods.push({
|
|
257
|
+
name: methodMatch[1],
|
|
258
|
+
file: relativePath,
|
|
259
|
+
line: line + classBody.slice(0, methodMatch.index).split('\n').length - 1,
|
|
260
|
+
params: extractParams(classBody, methodMatch.index),
|
|
261
|
+
isAsync: methodMatch[0].includes('async'),
|
|
262
|
+
isExported: false,
|
|
263
|
+
})
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
classes.push({
|
|
268
|
+
name,
|
|
269
|
+
file: relativePath,
|
|
270
|
+
line,
|
|
271
|
+
methods: classMethods,
|
|
272
|
+
properties: [],
|
|
273
|
+
extends: extendsClass,
|
|
274
|
+
docstring,
|
|
275
|
+
})
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Extract exports
|
|
279
|
+
while ((match = exportRegex.exec(content)) !== null) {
|
|
280
|
+
exports.push(match[1])
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Extract imports
|
|
284
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
285
|
+
const items = (match[1] || match[2]).split(',').map(s => s.trim())
|
|
286
|
+
const from = match[3]
|
|
287
|
+
imports.push({ from, items })
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (exports.length > 0 || imports.length > 0) {
|
|
291
|
+
modules.push({
|
|
292
|
+
name: relativePath,
|
|
293
|
+
file: relativePath,
|
|
294
|
+
exports,
|
|
295
|
+
imports,
|
|
296
|
+
})
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Extract documentation
|
|
300
|
+
while ((match = docstringRegex.exec(content)) !== null) {
|
|
301
|
+
const line = content.slice(0, match.index).split('\n').length
|
|
302
|
+
documentation.push({
|
|
303
|
+
file: relativePath,
|
|
304
|
+
line,
|
|
305
|
+
type: 'docstring',
|
|
306
|
+
content: match[1].trim(),
|
|
307
|
+
})
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
} catch (error) {
|
|
311
|
+
// Ignore files that can't be read
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// =============================================================================
|
|
316
|
+
// HELPER FUNCTIONS
|
|
317
|
+
// =============================================================================
|
|
318
|
+
|
|
319
|
+
function findPrecedingDocstring(lines: string[], lineIndex: number): string | undefined {
|
|
320
|
+
// Look backwards for a docstring
|
|
321
|
+
for (let i = lineIndex - 1; i >= 0 && i >= lineIndex - 5; i--) {
|
|
322
|
+
const line = lines[i]?.trim()
|
|
323
|
+
if (line?.startsWith('/**')) {
|
|
324
|
+
// Found start of docstring, collect until */
|
|
325
|
+
const docLines: string[] = []
|
|
326
|
+
for (let j = i; j <= lineIndex; j++) {
|
|
327
|
+
docLines.push(lines[j]?.replace(/^\s*\*\s?/, '').replace(/\*\//, '').trim() || '')
|
|
328
|
+
if (lines[j]?.includes('*/')) break
|
|
329
|
+
}
|
|
330
|
+
return docLines.filter(l => l).join(' ')
|
|
331
|
+
}
|
|
332
|
+
if (line?.startsWith('//')) {
|
|
333
|
+
return line.slice(2).trim()
|
|
334
|
+
}
|
|
335
|
+
if (line && !line.startsWith('*')) {
|
|
336
|
+
break
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return undefined
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function extractParams(content: string, startIndex: number): string[] {
|
|
343
|
+
const parenStart = content.indexOf('(', startIndex)
|
|
344
|
+
const parenEnd = findMatchingParen(content, parenStart)
|
|
345
|
+
const paramString = content.slice(parenStart + 1, parenEnd)
|
|
346
|
+
|
|
347
|
+
if (!paramString.trim()) return []
|
|
348
|
+
|
|
349
|
+
// Simple param extraction
|
|
350
|
+
return paramString
|
|
351
|
+
.split(',')
|
|
352
|
+
.map(p => p.split(':')[0].trim())
|
|
353
|
+
.filter(p => p && !p.startsWith('{') && !p.startsWith('['))
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function findMatchingParen(content: string, startIndex: number): number {
|
|
357
|
+
let depth = 0
|
|
358
|
+
for (let i = startIndex; i < content.length; i++) {
|
|
359
|
+
if (content[i] === '(') depth++
|
|
360
|
+
if (content[i] === ')') {
|
|
361
|
+
depth--
|
|
362
|
+
if (depth === 0) return i
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return startIndex
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function findMatchingBrace(content: string, startIndex: number): number {
|
|
369
|
+
let depth = 0
|
|
370
|
+
for (let i = startIndex; i < content.length; i++) {
|
|
371
|
+
if (content[i] === '{') depth++
|
|
372
|
+
if (content[i] === '}') {
|
|
373
|
+
depth--
|
|
374
|
+
if (depth === 0) return i
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return content.length
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// =============================================================================
|
|
381
|
+
// INTENT INFERENCE
|
|
382
|
+
// =============================================================================
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Infer the intent of a function from its name, params, and context
|
|
386
|
+
*/
|
|
387
|
+
export function inferIntent(
|
|
388
|
+
func: FunctionInfo,
|
|
389
|
+
classes: ClassInfo[],
|
|
390
|
+
documentation: DocumentationInfo[]
|
|
391
|
+
): InferredIntent {
|
|
392
|
+
const name = func.name
|
|
393
|
+
const docstring = func.docstring
|
|
394
|
+
|
|
395
|
+
// Start with docstring if available
|
|
396
|
+
if (docstring) {
|
|
397
|
+
return {
|
|
398
|
+
purpose: docstring,
|
|
399
|
+
confidence: 0.9,
|
|
400
|
+
source: 'docstring',
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Infer from function name patterns
|
|
405
|
+
const patterns: Array<{ regex: RegExp; purpose: (match: RegExpMatchArray) => string }> = [
|
|
406
|
+
{ regex: /^get(\w+)$/, purpose: (m) => `Retrieve ${camelToSpaces(m[1])}` },
|
|
407
|
+
{ regex: /^set(\w+)$/, purpose: (m) => `Set ${camelToSpaces(m[1])}` },
|
|
408
|
+
{ regex: /^is(\w+)$/, purpose: (m) => `Check if ${camelToSpaces(m[1])}` },
|
|
409
|
+
{ regex: /^has(\w+)$/, purpose: (m) => `Check if has ${camelToSpaces(m[1])}` },
|
|
410
|
+
{ regex: /^create(\w+)$/, purpose: (m) => `Create ${camelToSpaces(m[1])}` },
|
|
411
|
+
{ regex: /^delete(\w+)$/, purpose: (m) => `Delete ${camelToSpaces(m[1])}` },
|
|
412
|
+
{ regex: /^remove(\w+)$/, purpose: (m) => `Remove ${camelToSpaces(m[1])}` },
|
|
413
|
+
{ regex: /^add(\w+)$/, purpose: (m) => `Add ${camelToSpaces(m[1])}` },
|
|
414
|
+
{ regex: /^update(\w+)$/, purpose: (m) => `Update ${camelToSpaces(m[1])}` },
|
|
415
|
+
{ regex: /^fetch(\w+)$/, purpose: (m) => `Fetch ${camelToSpaces(m[1])} from remote` },
|
|
416
|
+
{ regex: /^load(\w+)$/, purpose: (m) => `Load ${camelToSpaces(m[1])}` },
|
|
417
|
+
{ regex: /^save(\w+)$/, purpose: (m) => `Save ${camelToSpaces(m[1])}` },
|
|
418
|
+
{ regex: /^validate(\w+)$/, purpose: (m) => `Validate ${camelToSpaces(m[1])}` },
|
|
419
|
+
{ regex: /^parse(\w+)$/, purpose: (m) => `Parse ${camelToSpaces(m[1])}` },
|
|
420
|
+
{ regex: /^convert(\w+)$/, purpose: (m) => `Convert ${camelToSpaces(m[1])}` },
|
|
421
|
+
{ regex: /^transform(\w+)$/, purpose: (m) => `Transform ${camelToSpaces(m[1])}` },
|
|
422
|
+
{ regex: /^handle(\w+)$/, purpose: (m) => `Handle ${camelToSpaces(m[1])}` },
|
|
423
|
+
{ regex: /^process(\w+)$/, purpose: (m) => `Process ${camelToSpaces(m[1])}` },
|
|
424
|
+
{ regex: /^send(\w+)$/, purpose: (m) => `Send ${camelToSpaces(m[1])}` },
|
|
425
|
+
{ regex: /^receive(\w+)$/, purpose: (m) => `Receive ${camelToSpaces(m[1])}` },
|
|
426
|
+
{ regex: /^init(\w*)$/, purpose: (m) => m[1] ? `Initialize ${camelToSpaces(m[1])}` : 'Initialize' },
|
|
427
|
+
{ regex: /^start(\w*)$/, purpose: (m) => m[1] ? `Start ${camelToSpaces(m[1])}` : 'Start' },
|
|
428
|
+
{ regex: /^stop(\w*)$/, purpose: (m) => m[1] ? `Stop ${camelToSpaces(m[1])}` : 'Stop' },
|
|
429
|
+
{ regex: /^on(\w+)$/, purpose: (m) => `Handler for ${camelToSpaces(m[1])} event` },
|
|
430
|
+
]
|
|
431
|
+
|
|
432
|
+
for (const { regex, purpose } of patterns) {
|
|
433
|
+
const match = name.match(regex)
|
|
434
|
+
if (match) {
|
|
435
|
+
return {
|
|
436
|
+
purpose: purpose(match),
|
|
437
|
+
confidence: 0.7,
|
|
438
|
+
source: 'function-name',
|
|
439
|
+
inputs: func.params,
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Fallback to generic inference from name
|
|
445
|
+
return {
|
|
446
|
+
purpose: camelToSpaces(name),
|
|
447
|
+
confidence: 0.4,
|
|
448
|
+
source: 'function-name',
|
|
449
|
+
inputs: func.params,
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function camelToSpaces(str: string): string {
|
|
454
|
+
return str
|
|
455
|
+
.replace(/([A-Z])/g, ' $1')
|
|
456
|
+
.toLowerCase()
|
|
457
|
+
.trim()
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// =============================================================================
|
|
461
|
+
// TRACE TO SOURCE MAPPING
|
|
462
|
+
// =============================================================================
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Map a trace's function call to source code location
|
|
466
|
+
*/
|
|
467
|
+
export function mapTraceToSource(
|
|
468
|
+
functionName: string,
|
|
469
|
+
analysis: CodebaseAnalysis
|
|
470
|
+
): TraceToSourceMapping {
|
|
471
|
+
// Search for function in analysis
|
|
472
|
+
const func = analysis.functions.find(f => f.name === functionName)
|
|
473
|
+
|
|
474
|
+
if (func) {
|
|
475
|
+
const intent = inferIntent(func, analysis.classes, analysis.documentation)
|
|
476
|
+
|
|
477
|
+
return {
|
|
478
|
+
traceId: '',
|
|
479
|
+
functionName,
|
|
480
|
+
sourceLocation: {
|
|
481
|
+
file: func.file,
|
|
482
|
+
line: func.line,
|
|
483
|
+
functionName: func.name,
|
|
484
|
+
},
|
|
485
|
+
intent,
|
|
486
|
+
confidence: intent.confidence,
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Search in class methods
|
|
491
|
+
for (const cls of analysis.classes) {
|
|
492
|
+
const method = cls.methods.find(m => m.name === functionName)
|
|
493
|
+
if (method) {
|
|
494
|
+
const intent = inferIntent(method, analysis.classes, analysis.documentation)
|
|
495
|
+
|
|
496
|
+
return {
|
|
497
|
+
traceId: '',
|
|
498
|
+
functionName,
|
|
499
|
+
sourceLocation: {
|
|
500
|
+
file: method.file,
|
|
501
|
+
line: method.line,
|
|
502
|
+
functionName: method.name,
|
|
503
|
+
className: cls.name,
|
|
504
|
+
},
|
|
505
|
+
intent,
|
|
506
|
+
confidence: intent.confidence,
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Not found
|
|
512
|
+
return {
|
|
513
|
+
traceId: '',
|
|
514
|
+
functionName,
|
|
515
|
+
confidence: 0,
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// =============================================================================
|
|
520
|
+
// EXPORTS
|
|
521
|
+
// =============================================================================
|
|
522
|
+
|
|
523
|
+
export const runtimeMapping = {
|
|
524
|
+
analyzeCodebase,
|
|
525
|
+
mapTraceToSource,
|
|
526
|
+
inferIntent,
|
|
527
|
+
}
|