@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,488 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CodeExplorer - Fast static analysis of codebase structure
|
|
3
|
+
*
|
|
4
|
+
* Uses Bun file APIs to analyze code without LLM calls.
|
|
5
|
+
* Provides foundation for semantic understanding.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
CodeStructure,
|
|
10
|
+
DirectoryNode,
|
|
11
|
+
Dependencies,
|
|
12
|
+
FileAnalysis,
|
|
13
|
+
Import,
|
|
14
|
+
Export,
|
|
15
|
+
FunctionDeclaration,
|
|
16
|
+
Component
|
|
17
|
+
} from './types'
|
|
18
|
+
|
|
19
|
+
export class CodeExplorer {
|
|
20
|
+
private ignorePatterns: string[] = [
|
|
21
|
+
'node_modules',
|
|
22
|
+
'.git',
|
|
23
|
+
'dist',
|
|
24
|
+
'build',
|
|
25
|
+
'.next',
|
|
26
|
+
'.cache',
|
|
27
|
+
'coverage',
|
|
28
|
+
'.DS_Store',
|
|
29
|
+
'*.log',
|
|
30
|
+
'.env',
|
|
31
|
+
'.env.local',
|
|
32
|
+
'bun.lockb',
|
|
33
|
+
'package-lock.json',
|
|
34
|
+
'yarn.lock'
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
constructor(
|
|
38
|
+
private workingDir: string = process.cwd(),
|
|
39
|
+
additionalIgnore?: string[]
|
|
40
|
+
) {
|
|
41
|
+
if (additionalIgnore) {
|
|
42
|
+
this.ignorePatterns.push(...additionalIgnore)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Safe wrapper for Bun.file() with better error handling
|
|
48
|
+
*/
|
|
49
|
+
private safeReadFile(path: string): ReturnType<typeof Bun.file> {
|
|
50
|
+
if (!path || typeof path !== 'string') {
|
|
51
|
+
throw new Error(`Invalid file path: expected string, got ${typeof path}`)
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
return Bun.file(path)
|
|
55
|
+
} catch (error) {
|
|
56
|
+
throw new Error(`Failed to create file handle for '${path}': ${error instanceof Error ? error.message : String(error)}`)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Explore codebase and return comprehensive structure
|
|
62
|
+
*/
|
|
63
|
+
async explore(rootPath: string): Promise<CodeStructure> {
|
|
64
|
+
const absolutePath = this.resolvePath(rootPath)
|
|
65
|
+
const files = await this.listAllFiles(absolutePath)
|
|
66
|
+
|
|
67
|
+
const filesByType = this.groupByExtension(files)
|
|
68
|
+
const dependencies = await this.extractDependencies(absolutePath, files)
|
|
69
|
+
const entryPoints = await this.findEntryPoints(absolutePath, files)
|
|
70
|
+
const { totalSize, totalLoc } = await this.calculateCodebaseSize(absolutePath, files)
|
|
71
|
+
const directoryStructure = await this.analyzeDirectoryStructure(absolutePath)
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
totalFiles: files.length,
|
|
75
|
+
filesByType,
|
|
76
|
+
directoryStructure,
|
|
77
|
+
dependencies,
|
|
78
|
+
entryPoints,
|
|
79
|
+
totalSize,
|
|
80
|
+
totalLoc
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Analyze specific file in detail
|
|
86
|
+
*/
|
|
87
|
+
async analyzeFile(filePath: string): Promise<FileAnalysis> {
|
|
88
|
+
const absolutePath = this.resolvePath(filePath)
|
|
89
|
+
const bunFile = this.safeReadFile(absolutePath)
|
|
90
|
+
const content = await bunFile.text()
|
|
91
|
+
const size = await bunFile.size
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
path: filePath,
|
|
95
|
+
imports: this.extractImports(content),
|
|
96
|
+
exports: this.extractExports(content),
|
|
97
|
+
functions: this.extractFunctions(content),
|
|
98
|
+
components: this.extractReactComponents(content),
|
|
99
|
+
loc: content.split('\n').length,
|
|
100
|
+
size
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* List all files in directory (non-ignored)
|
|
106
|
+
*/
|
|
107
|
+
private async listAllFiles(rootPath: string): Promise<string[]> {
|
|
108
|
+
const glob = new Bun.Glob("**/*")
|
|
109
|
+
const files: string[] = []
|
|
110
|
+
|
|
111
|
+
for await (const file of glob.scan({ cwd: rootPath })) {
|
|
112
|
+
if (!this.shouldIgnore(file)) {
|
|
113
|
+
files.push(file)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return files
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Check if file should be ignored
|
|
122
|
+
*/
|
|
123
|
+
private shouldIgnore(path: string): boolean {
|
|
124
|
+
return this.ignorePatterns.some(pattern => {
|
|
125
|
+
// Simple pattern matching (could be enhanced with minimatch)
|
|
126
|
+
if (pattern.includes('*')) {
|
|
127
|
+
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$')
|
|
128
|
+
return regex.test(path)
|
|
129
|
+
}
|
|
130
|
+
return path.includes(pattern)
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Group files by extension
|
|
136
|
+
*/
|
|
137
|
+
private groupByExtension(files: string[]): Record<string, number> {
|
|
138
|
+
const groups: Record<string, number> = {}
|
|
139
|
+
|
|
140
|
+
for (const file of files) {
|
|
141
|
+
const ext = file.split('.').pop() || 'unknown'
|
|
142
|
+
groups[ext] = (groups[ext] || 0) + 1
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return groups
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Extract dependencies from package.json files
|
|
150
|
+
*/
|
|
151
|
+
private async extractDependencies(rootPath: string, files: string[]): Promise<Dependencies> {
|
|
152
|
+
const packageJsons = files.filter(f => f === 'package.json' || f.endsWith('/package.json'))
|
|
153
|
+
const dependencies: Dependencies = {
|
|
154
|
+
runtime: {},
|
|
155
|
+
dev: {},
|
|
156
|
+
frameworks: [],
|
|
157
|
+
internal: []
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
for (const pkgPath of packageJsons) {
|
|
161
|
+
try {
|
|
162
|
+
const content = await this.safeReadFile(`${rootPath}/${pkgPath}`).text()
|
|
163
|
+
const pkg = JSON.parse(content)
|
|
164
|
+
|
|
165
|
+
// Extract runtime dependencies
|
|
166
|
+
if (pkg.dependencies) {
|
|
167
|
+
Object.assign(dependencies.runtime, pkg.dependencies)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Extract dev dependencies
|
|
171
|
+
if (pkg.devDependencies) {
|
|
172
|
+
Object.assign(dependencies.dev, pkg.devDependencies)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Identify common frameworks
|
|
176
|
+
if (pkg.dependencies?.['react'] || pkg.devDependencies?.['react']) {
|
|
177
|
+
dependencies.frameworks.push('React')
|
|
178
|
+
}
|
|
179
|
+
if (pkg.dependencies?.['@hono/hono'] || pkg.dependencies?.['hono']) {
|
|
180
|
+
dependencies.frameworks.push('Hono')
|
|
181
|
+
}
|
|
182
|
+
if (pkg.dependencies?.['express']) {
|
|
183
|
+
dependencies.frameworks.push('Express')
|
|
184
|
+
}
|
|
185
|
+
if (pkg.dependencies?.['next']) {
|
|
186
|
+
dependencies.frameworks.push('Next.js')
|
|
187
|
+
}
|
|
188
|
+
} catch (error) {
|
|
189
|
+
// Ignore malformed package.json
|
|
190
|
+
console.warn(`Could not parse ${pkgPath}:`, error)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Deduplicate frameworks
|
|
195
|
+
dependencies.frameworks = Array.from(new Set(dependencies.frameworks))
|
|
196
|
+
|
|
197
|
+
return dependencies
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Find application entry points
|
|
202
|
+
*/
|
|
203
|
+
private async findEntryPoints(rootPath: string, files: string[]): Promise<string[]> {
|
|
204
|
+
const entryPoints: string[] = []
|
|
205
|
+
const candidates = [
|
|
206
|
+
'index.ts',
|
|
207
|
+
'index.js',
|
|
208
|
+
'index.tsx',
|
|
209
|
+
'index.jsx',
|
|
210
|
+
'main.ts',
|
|
211
|
+
'main.js',
|
|
212
|
+
'app.ts',
|
|
213
|
+
'app.js',
|
|
214
|
+
'server.ts',
|
|
215
|
+
'server.js',
|
|
216
|
+
'cli.ts',
|
|
217
|
+
'cli.js'
|
|
218
|
+
]
|
|
219
|
+
|
|
220
|
+
for (const candidate of candidates) {
|
|
221
|
+
// Check root level
|
|
222
|
+
if (files.includes(candidate)) {
|
|
223
|
+
entryPoints.push(candidate)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Check src/ directory
|
|
227
|
+
if (files.includes(`src/${candidate}`)) {
|
|
228
|
+
entryPoints.push(`src/${candidate}`)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Also check package.json for main/bin entries
|
|
233
|
+
if (files.includes('package.json')) {
|
|
234
|
+
try {
|
|
235
|
+
const content = await this.safeReadFile(`${rootPath}/package.json`).text()
|
|
236
|
+
const pkg = JSON.parse(content)
|
|
237
|
+
|
|
238
|
+
if (pkg.main && !entryPoints.includes(pkg.main)) {
|
|
239
|
+
entryPoints.push(pkg.main)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (pkg.bin) {
|
|
243
|
+
const binEntries = typeof pkg.bin === 'string' ? [pkg.bin] : Object.values(pkg.bin)
|
|
244
|
+
for (const binEntry of binEntries) {
|
|
245
|
+
if (typeof binEntry === 'string' && !entryPoints.includes(binEntry)) {
|
|
246
|
+
entryPoints.push(binEntry)
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
} catch (error) {
|
|
251
|
+
// Ignore
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return entryPoints
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Calculate total size and lines of code
|
|
260
|
+
*/
|
|
261
|
+
private async calculateCodebaseSize(rootPath: string, files: string[]): Promise<{
|
|
262
|
+
totalSize: number
|
|
263
|
+
totalLoc: number
|
|
264
|
+
}> {
|
|
265
|
+
let totalSize = 0
|
|
266
|
+
let totalLoc = 0
|
|
267
|
+
|
|
268
|
+
const codeExtensions = ['ts', 'tsx', 'js', 'jsx', 'py', 'go', 'rs', 'java', 'c', 'cpp', 'h']
|
|
269
|
+
|
|
270
|
+
for (const file of files) {
|
|
271
|
+
const ext = file.split('.').pop()
|
|
272
|
+
if (ext && codeExtensions.includes(ext)) {
|
|
273
|
+
try {
|
|
274
|
+
const filePath = `${rootPath}/${file}`
|
|
275
|
+
const bunFile = this.safeReadFile(filePath)
|
|
276
|
+
totalSize += await bunFile.size
|
|
277
|
+
const content = await bunFile.text()
|
|
278
|
+
totalLoc += content.split('\n').length
|
|
279
|
+
} catch (error) {
|
|
280
|
+
// Skip files we can't read
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return { totalSize, totalLoc }
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Build directory structure tree
|
|
290
|
+
*/
|
|
291
|
+
private async analyzeDirectoryStructure(rootPath: string): Promise<DirectoryNode> {
|
|
292
|
+
const files = await this.listAllFiles(rootPath)
|
|
293
|
+
|
|
294
|
+
const root: DirectoryNode = {
|
|
295
|
+
name: rootPath.split('/').pop() || 'root',
|
|
296
|
+
path: '',
|
|
297
|
+
type: 'directory',
|
|
298
|
+
children: []
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
for (const file of files) {
|
|
302
|
+
const parts = file.split('/')
|
|
303
|
+
let currentNode = root
|
|
304
|
+
|
|
305
|
+
for (let i = 0; i < parts.length; i++) {
|
|
306
|
+
const part = parts[i]
|
|
307
|
+
const isFile = i === parts.length - 1
|
|
308
|
+
|
|
309
|
+
if (!currentNode.children) {
|
|
310
|
+
currentNode.children = []
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
let child = currentNode.children.find(c => c.name === part)
|
|
314
|
+
|
|
315
|
+
if (!child) {
|
|
316
|
+
child = {
|
|
317
|
+
name: part,
|
|
318
|
+
path: parts.slice(0, i + 1).join('/'),
|
|
319
|
+
type: isFile ? 'file' : 'directory',
|
|
320
|
+
children: isFile ? undefined : []
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (isFile) {
|
|
324
|
+
child.extension = part.split('.').pop()
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
currentNode.children.push(child)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
currentNode = child
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return root
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Extract imports from file content
|
|
339
|
+
*/
|
|
340
|
+
private extractImports(content: string): Import[] {
|
|
341
|
+
const imports: Import[] = []
|
|
342
|
+
|
|
343
|
+
// ES6 imports: import { a, b } from 'module'
|
|
344
|
+
const es6ImportRegex = /import\s+(?:{([^}]+)}|(\w+))\s+from\s+['"]([^'"]+)['"]/g
|
|
345
|
+
let match
|
|
346
|
+
|
|
347
|
+
while ((match = es6ImportRegex.exec(content)) !== null) {
|
|
348
|
+
imports.push({
|
|
349
|
+
named: match[1]?.split(',').map(s => s.trim()).filter(Boolean) || [],
|
|
350
|
+
default: match[2] || undefined,
|
|
351
|
+
source: match[3]
|
|
352
|
+
})
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// CommonJS require: const x = require('module')
|
|
356
|
+
const requireRegex = /(?:const|let|var)\s+(?:{([^}]+)}|(\w+))\s*=\s*require\(['"]([^'"]+)['"]\)/g
|
|
357
|
+
|
|
358
|
+
while ((match = requireRegex.exec(content)) !== null) {
|
|
359
|
+
imports.push({
|
|
360
|
+
named: match[1]?.split(',').map(s => s.trim()).filter(Boolean) || [],
|
|
361
|
+
default: match[2] || undefined,
|
|
362
|
+
source: match[3]
|
|
363
|
+
})
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return imports
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Extract exports from file content
|
|
371
|
+
*/
|
|
372
|
+
private extractExports(content: string): Export[] {
|
|
373
|
+
const exports: Export[] = []
|
|
374
|
+
|
|
375
|
+
// export function name()
|
|
376
|
+
const exportFunctionRegex = /export\s+(?:async\s+)?function\s+(\w+)/g
|
|
377
|
+
let match
|
|
378
|
+
|
|
379
|
+
while ((match = exportFunctionRegex.exec(content)) !== null) {
|
|
380
|
+
exports.push({
|
|
381
|
+
name: match[1],
|
|
382
|
+
type: 'function'
|
|
383
|
+
})
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// export class Name
|
|
387
|
+
const exportClassRegex = /export\s+class\s+(\w+)/g
|
|
388
|
+
|
|
389
|
+
while ((match = exportClassRegex.exec(content)) !== null) {
|
|
390
|
+
exports.push({
|
|
391
|
+
name: match[1],
|
|
392
|
+
type: 'class'
|
|
393
|
+
})
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// export const name
|
|
397
|
+
const exportConstRegex = /export\s+const\s+(\w+)/g
|
|
398
|
+
|
|
399
|
+
while ((match = exportConstRegex.exec(content)) !== null) {
|
|
400
|
+
exports.push({
|
|
401
|
+
name: match[1],
|
|
402
|
+
type: 'const'
|
|
403
|
+
})
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// export default
|
|
407
|
+
if (content.includes('export default')) {
|
|
408
|
+
exports.push({
|
|
409
|
+
name: 'default',
|
|
410
|
+
type: 'default'
|
|
411
|
+
})
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return exports
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Extract function declarations
|
|
419
|
+
*/
|
|
420
|
+
private extractFunctions(content: string): FunctionDeclaration[] {
|
|
421
|
+
const functions: FunctionDeclaration[] = []
|
|
422
|
+
|
|
423
|
+
// function name(params)
|
|
424
|
+
const functionRegex = /(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)/g
|
|
425
|
+
let match
|
|
426
|
+
|
|
427
|
+
while ((match = functionRegex.exec(content)) !== null) {
|
|
428
|
+
functions.push({
|
|
429
|
+
name: match[1],
|
|
430
|
+
isAsync: content.substring(match.index, match.index + 20).includes('async'),
|
|
431
|
+
parameters: match[2].split(',').map(s => s.trim()).filter(Boolean)
|
|
432
|
+
})
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// const name = function(params) or const name = (params) =>
|
|
436
|
+
const arrowFunctionRegex = /const\s+(\w+)\s*=\s*(?:async\s+)?\(?([^)]*)\)?\s*=>/g
|
|
437
|
+
|
|
438
|
+
while ((match = arrowFunctionRegex.exec(content)) !== null) {
|
|
439
|
+
functions.push({
|
|
440
|
+
name: match[1],
|
|
441
|
+
isAsync: content.substring(match.index, match.index + 30).includes('async'),
|
|
442
|
+
parameters: match[2].split(',').map(s => s.trim()).filter(Boolean)
|
|
443
|
+
})
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return functions
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Extract React components
|
|
451
|
+
*/
|
|
452
|
+
private extractReactComponents(content: string): Component[] {
|
|
453
|
+
const components: Component[] = []
|
|
454
|
+
|
|
455
|
+
// Functional components: function Name() or const Name = () =>
|
|
456
|
+
const functionalRegex = /(?:export\s+)?(?:function|const)\s+([A-Z]\w+)\s*[=:()].*(?:React\.FC|=>|:.*JSX\.Element)/g
|
|
457
|
+
let match
|
|
458
|
+
|
|
459
|
+
while ((match = functionalRegex.exec(content)) !== null) {
|
|
460
|
+
components.push({
|
|
461
|
+
name: match[1],
|
|
462
|
+
type: 'functional'
|
|
463
|
+
})
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Class components: class Name extends React.Component
|
|
467
|
+
const classRegex = /class\s+([A-Z]\w+)\s+extends\s+(?:React\.)?(?:Component|PureComponent)/g
|
|
468
|
+
|
|
469
|
+
while ((match = classRegex.exec(content)) !== null) {
|
|
470
|
+
components.push({
|
|
471
|
+
name: match[1],
|
|
472
|
+
type: 'class'
|
|
473
|
+
})
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return components
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Resolve path relative to working directory
|
|
481
|
+
*/
|
|
482
|
+
private resolvePath(path: string): string {
|
|
483
|
+
if (path.startsWith('/')) {
|
|
484
|
+
return path
|
|
485
|
+
}
|
|
486
|
+
return `${this.workingDir}/${path}`.replace(/\/+/g, '/')
|
|
487
|
+
}
|
|
488
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Understanding module exports
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { CodeExplorer } from './explorer'
|
|
6
|
+
export { ApplicationAnalyzer } from './analyzer'
|
|
7
|
+
|
|
8
|
+
export type {
|
|
9
|
+
CodeStructure,
|
|
10
|
+
DirectoryNode,
|
|
11
|
+
Dependencies,
|
|
12
|
+
FileAnalysis,
|
|
13
|
+
Import,
|
|
14
|
+
Export,
|
|
15
|
+
FunctionDeclaration,
|
|
16
|
+
Component,
|
|
17
|
+
AnalyzeOptions,
|
|
18
|
+
Analysis,
|
|
19
|
+
ArchitectureAnalysis,
|
|
20
|
+
ComponentAnalysis,
|
|
21
|
+
DataFlowAnalysis,
|
|
22
|
+
EntryPointAnalysis,
|
|
23
|
+
DependencyAnalysis,
|
|
24
|
+
TechStackAnalysis,
|
|
25
|
+
Diagnosis,
|
|
26
|
+
Pattern
|
|
27
|
+
} from './types'
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for understanding capabilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface CodeStructure {
|
|
6
|
+
totalFiles: number
|
|
7
|
+
filesByType: Record<string, number>
|
|
8
|
+
directoryStructure: DirectoryNode
|
|
9
|
+
dependencies: Dependencies
|
|
10
|
+
entryPoints: string[]
|
|
11
|
+
totalSize: number
|
|
12
|
+
totalLoc: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface DirectoryNode {
|
|
16
|
+
name: string
|
|
17
|
+
path: string
|
|
18
|
+
type: 'file' | 'directory'
|
|
19
|
+
children?: DirectoryNode[]
|
|
20
|
+
size?: number
|
|
21
|
+
extension?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface Dependencies {
|
|
25
|
+
runtime: Record<string, string>
|
|
26
|
+
dev: Record<string, string>
|
|
27
|
+
frameworks: string[]
|
|
28
|
+
internal: string[]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface FileAnalysis {
|
|
32
|
+
path: string
|
|
33
|
+
imports: Import[]
|
|
34
|
+
exports: Export[]
|
|
35
|
+
functions: FunctionDeclaration[]
|
|
36
|
+
components: Component[]
|
|
37
|
+
loc: number
|
|
38
|
+
size: number
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface Import {
|
|
42
|
+
named: string[]
|
|
43
|
+
default?: string
|
|
44
|
+
source: string
|
|
45
|
+
line?: number
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface Export {
|
|
49
|
+
name: string
|
|
50
|
+
type: 'function' | 'class' | 'const' | 'default'
|
|
51
|
+
line?: number
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface FunctionDeclaration {
|
|
55
|
+
name: string
|
|
56
|
+
isAsync: boolean
|
|
57
|
+
parameters: string[]
|
|
58
|
+
line?: number
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface Component {
|
|
62
|
+
name: string
|
|
63
|
+
type: 'functional' | 'class'
|
|
64
|
+
props?: string[]
|
|
65
|
+
line?: number
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface AnalyzeOptions {
|
|
69
|
+
rootPath: string
|
|
70
|
+
focus?: 'architecture' | 'dependencies' | 'components' | 'all'
|
|
71
|
+
depth?: 'shallow' | 'medium' | 'deep'
|
|
72
|
+
ignorePatterns?: string[]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface Analysis {
|
|
76
|
+
architecture: ArchitectureAnalysis
|
|
77
|
+
components: ComponentAnalysis
|
|
78
|
+
dataFlow: DataFlowAnalysis
|
|
79
|
+
entryPoints: EntryPointAnalysis
|
|
80
|
+
dependencies: DependencyAnalysis
|
|
81
|
+
techStack: TechStackAnalysis
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface ArchitectureAnalysis {
|
|
85
|
+
pattern: string
|
|
86
|
+
description: string
|
|
87
|
+
layers: string[]
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface ComponentAnalysis {
|
|
91
|
+
summary: string
|
|
92
|
+
keyModules: Array<{
|
|
93
|
+
name: string
|
|
94
|
+
purpose: string
|
|
95
|
+
files: string[]
|
|
96
|
+
}>
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface DataFlowAnalysis {
|
|
100
|
+
description: string
|
|
101
|
+
flows: Array<{
|
|
102
|
+
source: string
|
|
103
|
+
destination: string
|
|
104
|
+
dataType: string
|
|
105
|
+
}>
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface EntryPointAnalysis {
|
|
109
|
+
main: string[]
|
|
110
|
+
server?: string
|
|
111
|
+
client?: string
|
|
112
|
+
cli?: string
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface DependencyAnalysis {
|
|
116
|
+
external: Array<{
|
|
117
|
+
name: string
|
|
118
|
+
version: string
|
|
119
|
+
purpose: string
|
|
120
|
+
}>
|
|
121
|
+
internal: Array<{
|
|
122
|
+
module: string
|
|
123
|
+
dependsOn: string[]
|
|
124
|
+
}>
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface TechStackAnalysis {
|
|
128
|
+
runtime: string
|
|
129
|
+
framework: string[]
|
|
130
|
+
language: string[]
|
|
131
|
+
tools: string[]
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface Diagnosis {
|
|
135
|
+
symptoms: string[]
|
|
136
|
+
rootCause: string
|
|
137
|
+
affectedFiles: string[]
|
|
138
|
+
recommendedFix: string
|
|
139
|
+
validationSteps: string[]
|
|
140
|
+
confidence: 'low' | 'medium' | 'high'
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export interface Pattern {
|
|
144
|
+
type: 'architecture' | 'design' | 'security' | 'performance'
|
|
145
|
+
name: string
|
|
146
|
+
description: string
|
|
147
|
+
instances: Array<{
|
|
148
|
+
file: string
|
|
149
|
+
line?: number
|
|
150
|
+
context?: string
|
|
151
|
+
}>
|
|
152
|
+
recommendation?: string
|
|
153
|
+
}
|