milens 0.2.0
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/LICENSE +21 -0
- package/README.md +213 -0
- package/dist/analyzer/engine.d.ts +11 -0
- package/dist/analyzer/engine.d.ts.map +1 -0
- package/dist/analyzer/engine.js +163 -0
- package/dist/analyzer/engine.js.map +1 -0
- package/dist/analyzer/resolver.d.ts +12 -0
- package/dist/analyzer/resolver.d.ts.map +1 -0
- package/dist/analyzer/resolver.js +119 -0
- package/dist/analyzer/resolver.js.map +1 -0
- package/dist/analyzer/scanner.d.ts +6 -0
- package/dist/analyzer/scanner.d.ts.map +1 -0
- package/dist/analyzer/scanner.js +73 -0
- package/dist/analyzer/scanner.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +269 -0
- package/dist/cli.js.map +1 -0
- package/dist/parser/extract.d.ts +24 -0
- package/dist/parser/extract.d.ts.map +1 -0
- package/dist/parser/extract.js +239 -0
- package/dist/parser/extract.js.map +1 -0
- package/dist/parser/lang-go.d.ts +4 -0
- package/dist/parser/lang-go.d.ts.map +1 -0
- package/dist/parser/lang-go.js +45 -0
- package/dist/parser/lang-go.js.map +1 -0
- package/dist/parser/lang-java.d.ts +4 -0
- package/dist/parser/lang-java.d.ts.map +1 -0
- package/dist/parser/lang-java.js +46 -0
- package/dist/parser/lang-java.js.map +1 -0
- package/dist/parser/lang-js.d.ts +4 -0
- package/dist/parser/lang-js.d.ts.map +1 -0
- package/dist/parser/lang-js.js +64 -0
- package/dist/parser/lang-js.js.map +1 -0
- package/dist/parser/lang-php.d.ts +4 -0
- package/dist/parser/lang-php.d.ts.map +1 -0
- package/dist/parser/lang-php.js +54 -0
- package/dist/parser/lang-php.js.map +1 -0
- package/dist/parser/lang-py.d.ts +4 -0
- package/dist/parser/lang-py.d.ts.map +1 -0
- package/dist/parser/lang-py.js +46 -0
- package/dist/parser/lang-py.js.map +1 -0
- package/dist/parser/lang-rust.d.ts +4 -0
- package/dist/parser/lang-rust.d.ts.map +1 -0
- package/dist/parser/lang-rust.js +54 -0
- package/dist/parser/lang-rust.js.map +1 -0
- package/dist/parser/lang-ts.d.ts +4 -0
- package/dist/parser/lang-ts.d.ts.map +1 -0
- package/dist/parser/lang-ts.js +88 -0
- package/dist/parser/lang-ts.js.map +1 -0
- package/dist/parser/lang-vue.d.ts +12 -0
- package/dist/parser/lang-vue.d.ts.map +1 -0
- package/dist/parser/lang-vue.js +28 -0
- package/dist/parser/lang-vue.js.map +1 -0
- package/dist/parser/languages.d.ts +6 -0
- package/dist/parser/languages.d.ts.map +1 -0
- package/dist/parser/languages.js +24 -0
- package/dist/parser/languages.js.map +1 -0
- package/dist/parser/loader.d.ts +5 -0
- package/dist/parser/loader.d.ts.map +1 -0
- package/dist/parser/loader.js +37 -0
- package/dist/parser/loader.js.map +1 -0
- package/dist/server/mcp.d.ts +5 -0
- package/dist/server/mcp.d.ts.map +1 -0
- package/dist/server/mcp.js +355 -0
- package/dist/server/mcp.js.map +1 -0
- package/dist/skills.d.ts +8 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +178 -0
- package/dist/skills.js.map +1 -0
- package/dist/store/db.d.ts +59 -0
- package/dist/store/db.d.ts.map +1 -0
- package/dist/store/db.js +260 -0
- package/dist/store/db.js.map +1 -0
- package/dist/store/registry.d.ts +13 -0
- package/dist/store/registry.d.ts.map +1 -0
- package/dist/store/registry.js +69 -0
- package/dist/store/registry.js.map +1 -0
- package/dist/store/schema.sql +60 -0
- package/dist/types.d.ts +67 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 milens contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<strong>milens</strong><br>
|
|
3
|
+
<em>Lightweight Code Intelligence Engine</em>
|
|
4
|
+
</p>
|
|
5
|
+
|
|
6
|
+
<p align="center">
|
|
7
|
+
<a href="#features">Features</a> •
|
|
8
|
+
<a href="#quick-start">Quick Start</a> •
|
|
9
|
+
<a href="#cli-commands">CLI</a> •
|
|
10
|
+
<a href="#mcp-server">MCP Server</a> •
|
|
11
|
+
<a href="#architecture">Architecture</a> •
|
|
12
|
+
<a href="#adding-a-language">Extend</a>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
Parse codebases into **knowledge graphs** — symbols, imports, calls, inheritance — and serve them to **AI agents** via the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/).
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- **8 languages** — TypeScript, JavaScript, Python, Java, Go, Rust, PHP, Vue
|
|
22
|
+
- **Declarative grammars** — add a new language by writing a config object, not extraction code
|
|
23
|
+
- **SQLite + FTS5** — full-text symbol search + recursive CTE graph traversal
|
|
24
|
+
- **Token-compact MCP** — 4 tools with minimal output, saving 40-60% tokens for AI agents
|
|
25
|
+
- **Incremental indexing** — file-hash based, only re-parses changed files
|
|
26
|
+
- **Multi-repo registry** — manage multiple codebases from `~/.milens/`
|
|
27
|
+
- **Dual transport** — MCP over stdio (VS Code / Cursor) or HTTP (remote agents)
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Install dependencies
|
|
33
|
+
npm install
|
|
34
|
+
|
|
35
|
+
# Index the current project
|
|
36
|
+
npx tsx src/cli.ts analyze -p . --verbose
|
|
37
|
+
|
|
38
|
+
# Search for symbols
|
|
39
|
+
npx tsx src/cli.ts search "UserService"
|
|
40
|
+
|
|
41
|
+
# Start MCP server for AI agents
|
|
42
|
+
npx tsx src/cli.ts serve --http --port 3100
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## CLI Commands
|
|
46
|
+
|
|
47
|
+
### `analyze` — Index a codebase
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
milens analyze -p /path/to/repo --verbose --force
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Scans source files, parses symbols using tree-sitter, resolves imports/calls/inheritance, and stores everything in a local SQLite database at `.milens/milens.db`.
|
|
54
|
+
|
|
55
|
+
| Flag | Description |
|
|
56
|
+
|---|---|
|
|
57
|
+
| `-p, --path` | Repository root (default: `.`) |
|
|
58
|
+
| `-o, --output` | Custom output directory for the database |
|
|
59
|
+
| `-v, --verbose` | Show detailed progress |
|
|
60
|
+
| `-f, --force` | Force full re-index (skip hash check) |
|
|
61
|
+
|
|
62
|
+
### `search` — Find symbols
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
milens search "createUser" --limit 10
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Full-text search across all indexed symbol names using FTS5.
|
|
69
|
+
|
|
70
|
+
### `inspect` — 360° symbol context
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
milens inspect "AuthService"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Shows a symbol's incoming references (who calls/uses it) and outgoing dependencies (what it calls/imports/extends).
|
|
77
|
+
|
|
78
|
+
### `impact` — Blast radius analysis
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
milens impact "UserModel" --direction upstream --depth 3
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Answers: *"What breaks if this symbol changes?"* Uses recursive CTEs to traverse the dependency graph up to N levels deep.
|
|
85
|
+
|
|
86
|
+
| Flag | Description |
|
|
87
|
+
|---|---|
|
|
88
|
+
| `-d, --direction` | `upstream` (default) or `downstream` |
|
|
89
|
+
| `--depth` | Max traversal depth (default: `3`) |
|
|
90
|
+
|
|
91
|
+
### `status` — Index stats
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
milens status -p /path/to/repo
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Shows symbol count, link count, file count, and last indexed time.
|
|
98
|
+
|
|
99
|
+
### `serve` — Start MCP server
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# stdio transport (for VS Code / Cursor)
|
|
103
|
+
milens serve -p /path/to/repo
|
|
104
|
+
|
|
105
|
+
# HTTP transport (for remote agents)
|
|
106
|
+
milens serve -p /path/to/repo --http --port 3100
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## MCP Server
|
|
110
|
+
|
|
111
|
+
milens exposes 4 tools via the Model Context Protocol:
|
|
112
|
+
|
|
113
|
+
| Tool | Description | Key params |
|
|
114
|
+
|---|---|---|
|
|
115
|
+
| `search` | Find symbols by name/keyword (FTS5) | `query`, `limit` |
|
|
116
|
+
| `inspect` | Incoming refs, outgoing deps, hierarchy | `name` |
|
|
117
|
+
| `impact` | Blast radius with depth grouping | `target`, `direction`, `depth` |
|
|
118
|
+
| `status` | Index stats for a repository | `repo` |
|
|
119
|
+
|
|
120
|
+
### VS Code / Cursor Integration
|
|
121
|
+
|
|
122
|
+
Add to your MCP settings (`mcp.json`):
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"servers": {
|
|
127
|
+
"milens": {
|
|
128
|
+
"command": "npx",
|
|
129
|
+
"args": ["tsx", "/path/to/milens/src/cli.ts", "serve", "-p", "/path/to/repo"]
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### HTTP Mode
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
milens serve --http --port 3100
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Endpoint: `POST http://localhost:3100/mcp`
|
|
142
|
+
|
|
143
|
+
## Architecture
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
src/
|
|
147
|
+
cli.ts — CLI entry point (commander)
|
|
148
|
+
types.ts — Shared type definitions
|
|
149
|
+
parser/
|
|
150
|
+
loader.ts — Tree-sitter WASM loading
|
|
151
|
+
extract.ts — Universal extractor + LangSpec interface
|
|
152
|
+
lang-*.ts — Declarative grammar per language
|
|
153
|
+
languages.ts — Language registry
|
|
154
|
+
analyzer/
|
|
155
|
+
scanner.ts — File discovery (.gitignore aware)
|
|
156
|
+
resolver.ts — Import + call + heritage resolution
|
|
157
|
+
engine.ts — Pipeline orchestrator
|
|
158
|
+
store/
|
|
159
|
+
schema.sql — SQLite schema with FTS5
|
|
160
|
+
db.ts — Database adapter with recursive CTE queries
|
|
161
|
+
registry.ts — Multi-repo registry (~/.milens/)
|
|
162
|
+
server/
|
|
163
|
+
mcp.ts — MCP server (stdio + HTTP transports)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### How It Works
|
|
167
|
+
|
|
168
|
+
1. **Scan** — Discover source files respecting `.gitignore`
|
|
169
|
+
2. **Parse** — Extract symbols (functions, classes, interfaces, ...) via tree-sitter WASM grammars
|
|
170
|
+
3. **Resolve** — Link imports → symbols, calls → definitions, inheritance chains
|
|
171
|
+
4. **Store** — Write symbols + links to SQLite with FTS5 search index
|
|
172
|
+
5. **Serve** — Expose the knowledge graph via MCP tools or CLI
|
|
173
|
+
|
|
174
|
+
### Design Decisions
|
|
175
|
+
|
|
176
|
+
- **Declarative `LangSpec`**: Each language is a config object with tree-sitter queries. One universal extractor processes all languages — no per-language extraction code.
|
|
177
|
+
- **SQLite recursive CTE**: Impact analysis traverses the graph inside the database. No need to load the full graph into memory.
|
|
178
|
+
- **Token-compact output**: MCP responses use minimal structured text, not verbose descriptions.
|
|
179
|
+
- **Incremental by default**: File content is hashed; only changed files get re-parsed on subsequent runs.
|
|
180
|
+
|
|
181
|
+
## Adding a Language
|
|
182
|
+
|
|
183
|
+
Create `src/parser/lang-xxx.ts`:
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
import type { LangSpec } from './extract.js';
|
|
187
|
+
|
|
188
|
+
const spec: LangSpec = {
|
|
189
|
+
id: 'xxx',
|
|
190
|
+
extensions: ['.xxx'],
|
|
191
|
+
wasmName: 'tree-sitter-xxx',
|
|
192
|
+
queries: {
|
|
193
|
+
functions: `(function_definition name: (identifier) @name) @def`,
|
|
194
|
+
classes: `(class_definition name: (identifier) @name) @def`,
|
|
195
|
+
// add queries using tree-sitter playground
|
|
196
|
+
},
|
|
197
|
+
resolveImport(raw, fromFile, root, aliases) {
|
|
198
|
+
// return resolved file path or null
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
export default spec;
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Then register it in `src/parser/languages.ts`.
|
|
206
|
+
|
|
207
|
+
## Requirements
|
|
208
|
+
|
|
209
|
+
- Node.js >= 20.0.0
|
|
210
|
+
|
|
211
|
+
## License
|
|
212
|
+
|
|
213
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AnalysisStats } from '../types.js';
|
|
2
|
+
interface EngineOptions {
|
|
3
|
+
rootPath: string;
|
|
4
|
+
dbPath: string;
|
|
5
|
+
verbose?: boolean;
|
|
6
|
+
force?: boolean;
|
|
7
|
+
aliases?: Record<string, string>;
|
|
8
|
+
}
|
|
9
|
+
export declare function analyze(opts: EngineOptions): Promise<AnalysisStats>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/analyzer/engine.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAiE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIhH,UAAU,aAAa;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAkIzE"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { scanFiles } from './scanner.js';
|
|
4
|
+
import { langForFile } from '../parser/languages.js';
|
|
5
|
+
import { getParser, loadLanguage } from '../parser/loader.js';
|
|
6
|
+
import { extractFromTree, clearQueryCache } from '../parser/extract.js';
|
|
7
|
+
import { extractVueScript } from '../parser/lang-vue.js';
|
|
8
|
+
import { resolveLinks } from './resolver.js';
|
|
9
|
+
import { Database } from '../store/db.js';
|
|
10
|
+
export async function analyze(opts) {
|
|
11
|
+
const t0 = Date.now();
|
|
12
|
+
const rootPath = resolve(opts.rootPath);
|
|
13
|
+
const db = new Database(opts.dbPath);
|
|
14
|
+
const aliases = opts.aliases ?? {};
|
|
15
|
+
if (opts.force)
|
|
16
|
+
db.clear();
|
|
17
|
+
// Phase 1: Scan files
|
|
18
|
+
const files = scanFiles(rootPath, opts.verbose);
|
|
19
|
+
if (opts.verbose)
|
|
20
|
+
console.log(`[scan] Found ${files.length} source files`);
|
|
21
|
+
// Phase 2: Group files by language for cache-friendly processing
|
|
22
|
+
const langGroups = new Map();
|
|
23
|
+
for (const file of files) {
|
|
24
|
+
const spec = langForFile(file.relativePath);
|
|
25
|
+
if (!spec)
|
|
26
|
+
continue;
|
|
27
|
+
const group = langGroups.get(spec.wasmName) ?? [];
|
|
28
|
+
group.push({ ...file, spec });
|
|
29
|
+
langGroups.set(spec.wasmName, group);
|
|
30
|
+
}
|
|
31
|
+
// Phase 3: Parse & extract — process each language group together
|
|
32
|
+
// This keeps the same parser/language/compiled queries hot in cache
|
|
33
|
+
const symbolsByFile = new Map();
|
|
34
|
+
const allSymbols = [];
|
|
35
|
+
const allImports = [];
|
|
36
|
+
const allCalls = [];
|
|
37
|
+
const allHeritage = [];
|
|
38
|
+
const resolvedImportPaths = new Map();
|
|
39
|
+
const parsedFiles = new Set();
|
|
40
|
+
let filesParsed = 0;
|
|
41
|
+
for (const [wasmName, group] of langGroups) {
|
|
42
|
+
// Pre-load parser + language once per group
|
|
43
|
+
const parser = await getParser(wasmName);
|
|
44
|
+
const lang = await loadLanguage(wasmName);
|
|
45
|
+
for (const file of group) {
|
|
46
|
+
const source = readFileSync(file.absolutePath, 'utf-8');
|
|
47
|
+
// Skip unchanged files (incremental)
|
|
48
|
+
if (!opts.force && db.isFileUpToDate(file.relativePath, source)) {
|
|
49
|
+
if (opts.verbose)
|
|
50
|
+
console.log(`[skip] ${file.relativePath} (unchanged)`);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const result = parseFile(source, file.relativePath, file.spec, parser, lang);
|
|
55
|
+
if (!result)
|
|
56
|
+
continue;
|
|
57
|
+
symbolsByFile.set(file.relativePath, result.symbols);
|
|
58
|
+
allSymbols.push(...result.symbols);
|
|
59
|
+
allImports.push(...result.imports);
|
|
60
|
+
allCalls.push(...result.calls);
|
|
61
|
+
allHeritage.push(...result.heritage);
|
|
62
|
+
// Resolve import paths eagerly
|
|
63
|
+
for (const imp of result.imports) {
|
|
64
|
+
const resolved = file.spec.resolveImport(imp.modulePath, imp.filePath, rootPath, aliases);
|
|
65
|
+
if (resolved) {
|
|
66
|
+
resolvedImportPaths.set(`${imp.filePath}::${imp.modulePath}`, resolved);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
db.upsertFileHash(file.relativePath, source);
|
|
70
|
+
parsedFiles.add(file.relativePath);
|
|
71
|
+
filesParsed++;
|
|
72
|
+
if (opts.verbose)
|
|
73
|
+
console.log(`[parse] ${file.relativePath}: ${result.symbols.length} symbols`);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
if (opts.verbose)
|
|
77
|
+
console.error(`[error] ${file.relativePath}: ${err}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Phase 4: Load unchanged files' symbols for cross-file resolution
|
|
82
|
+
if (!opts.force) {
|
|
83
|
+
for (const [, group] of langGroups) {
|
|
84
|
+
for (const file of group) {
|
|
85
|
+
if (!parsedFiles.has(file.relativePath)) {
|
|
86
|
+
const existing = db.getSymbolsByFile(file.relativePath);
|
|
87
|
+
if (existing.length > 0) {
|
|
88
|
+
symbolsByFile.set(file.relativePath, existing);
|
|
89
|
+
allSymbols.push(...existing);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Phase 5: Resolve cross-file links
|
|
96
|
+
const links = resolveLinks({
|
|
97
|
+
symbolsByFile,
|
|
98
|
+
allSymbols,
|
|
99
|
+
imports: allImports,
|
|
100
|
+
calls: allCalls,
|
|
101
|
+
heritage: allHeritage,
|
|
102
|
+
resolvedImportPaths,
|
|
103
|
+
});
|
|
104
|
+
if (opts.verbose)
|
|
105
|
+
console.log(`[link] Resolved ${links.length} relationships`);
|
|
106
|
+
// Phase 6: Persist to database in single transaction
|
|
107
|
+
db.transaction(() => {
|
|
108
|
+
if (opts.force) {
|
|
109
|
+
db.clearSymbolsAndLinks();
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
for (const fp of parsedFiles)
|
|
113
|
+
db.deleteFileData(fp);
|
|
114
|
+
}
|
|
115
|
+
for (const sym of allSymbols) {
|
|
116
|
+
if (opts.force || parsedFiles.has(sym.filePath))
|
|
117
|
+
db.insertSymbol(sym);
|
|
118
|
+
}
|
|
119
|
+
for (const link of links)
|
|
120
|
+
db.insertLink(link);
|
|
121
|
+
db.rebuildSearch();
|
|
122
|
+
});
|
|
123
|
+
const stats = {
|
|
124
|
+
filesScanned: files.length,
|
|
125
|
+
filesParsed,
|
|
126
|
+
symbolCount: allSymbols.length,
|
|
127
|
+
linkCount: links.length,
|
|
128
|
+
durationMs: Date.now() - t0,
|
|
129
|
+
};
|
|
130
|
+
if (opts.verbose) {
|
|
131
|
+
console.log(`[done] ${stats.symbolCount} symbols, ${stats.linkCount} links in ${stats.durationMs}ms`);
|
|
132
|
+
}
|
|
133
|
+
clearQueryCache();
|
|
134
|
+
db.close();
|
|
135
|
+
return stats;
|
|
136
|
+
}
|
|
137
|
+
function parseFile(source, filePath, spec, parser, lang) {
|
|
138
|
+
let code = source;
|
|
139
|
+
let lineOffset = 0;
|
|
140
|
+
// Vue SFC: extract <script> block
|
|
141
|
+
if (spec.id === 'vue') {
|
|
142
|
+
const script = extractVueScript(source);
|
|
143
|
+
if (!script)
|
|
144
|
+
return null;
|
|
145
|
+
code = script.content;
|
|
146
|
+
lineOffset = script.lineOffset;
|
|
147
|
+
}
|
|
148
|
+
const tree = parser.parse(code);
|
|
149
|
+
const result = extractFromTree(tree, lang, spec, filePath);
|
|
150
|
+
// Adjust line numbers for Vue offset
|
|
151
|
+
if (lineOffset > 0) {
|
|
152
|
+
for (const sym of result.symbols) {
|
|
153
|
+
sym.startLine += lineOffset;
|
|
154
|
+
sym.endLine += lineOffset;
|
|
155
|
+
}
|
|
156
|
+
for (const imp of result.imports)
|
|
157
|
+
imp.line += lineOffset;
|
|
158
|
+
for (const call of result.calls)
|
|
159
|
+
call.line += lineOffset;
|
|
160
|
+
}
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/analyzer/engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CodeSymbol, SymbolLink, RawImport, RawCall, RawHeritage } from '../types.js';
|
|
2
|
+
interface ResolutionInput {
|
|
3
|
+
symbolsByFile: Map<string, CodeSymbol[]>;
|
|
4
|
+
allSymbols: CodeSymbol[];
|
|
5
|
+
imports: RawImport[];
|
|
6
|
+
calls: RawCall[];
|
|
7
|
+
heritage: RawHeritage[];
|
|
8
|
+
resolvedImportPaths: Map<string, string>;
|
|
9
|
+
}
|
|
10
|
+
export declare function resolveLinks(input: ResolutionInput): SymbolLink[];
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/analyzer/resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAY,MAAM,aAAa,CAAC;AAErG,UAAU,eAAe;IACvB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;IACzC,UAAU,EAAE,UAAU,EAAE,CAAC;IACzB,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,eAAe,GAAG,UAAU,EAAE,CA+FjE"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
export function resolveLinks(input) {
|
|
2
|
+
const links = [];
|
|
3
|
+
const symbolByName = buildNameIndex(input.allSymbols);
|
|
4
|
+
// Build imported names per file: file → (name → targetFile)
|
|
5
|
+
const importedNamesPerFile = new Map();
|
|
6
|
+
for (const imp of input.imports) {
|
|
7
|
+
const targetFile = input.resolvedImportPaths.get(`${imp.filePath}::${imp.modulePath}`);
|
|
8
|
+
if (!targetFile)
|
|
9
|
+
continue;
|
|
10
|
+
let fileImports = importedNamesPerFile.get(imp.filePath);
|
|
11
|
+
if (!fileImports) {
|
|
12
|
+
fileImports = new Map();
|
|
13
|
+
importedNamesPerFile.set(imp.filePath, fileImports);
|
|
14
|
+
}
|
|
15
|
+
for (const { name } of imp.names) {
|
|
16
|
+
fileImports.set(name, targetFile);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
// ── Resolve imports ──
|
|
20
|
+
for (const imp of input.imports) {
|
|
21
|
+
const targetFile = input.resolvedImportPaths.get(`${imp.filePath}::${imp.modulePath}`);
|
|
22
|
+
if (!targetFile)
|
|
23
|
+
continue;
|
|
24
|
+
const targetSymbols = input.symbolsByFile.get(targetFile);
|
|
25
|
+
if (!targetSymbols)
|
|
26
|
+
continue;
|
|
27
|
+
// Find the importing symbol (file-level or specific)
|
|
28
|
+
const fromId = `${imp.filePath}#module:_top:0`;
|
|
29
|
+
if (imp.names.length > 0) {
|
|
30
|
+
for (const { name } of imp.names) {
|
|
31
|
+
const target = targetSymbols.find(s => s.name === name && s.exported);
|
|
32
|
+
if (target) {
|
|
33
|
+
links.push(makeLink(fromId, target.id, 'imports', 0.95, imp.line));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
// Wildcard or default import — link to file module
|
|
39
|
+
const target = targetSymbols.find(s => s.exported);
|
|
40
|
+
if (target) {
|
|
41
|
+
links.push(makeLink(fromId, target.id, 'imports', 0.7, imp.line));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// ── Resolve calls (import-aware) ──
|
|
46
|
+
for (const call of input.calls) {
|
|
47
|
+
const candidates = symbolByName.get(call.calleeName);
|
|
48
|
+
if (!candidates || candidates.length === 0)
|
|
49
|
+
continue;
|
|
50
|
+
// Priority: same file > imported symbol > unique global > ambiguous
|
|
51
|
+
const sameFile = candidates.filter(s => s.filePath === call.filePath);
|
|
52
|
+
if (sameFile.length > 0) {
|
|
53
|
+
links.push(makeLink(call.enclosingSymbolId, sameFile[0].id, 'calls', 0.9, call.line));
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
// Check if callee was imported into this file
|
|
57
|
+
const fileImports = importedNamesPerFile.get(call.filePath);
|
|
58
|
+
const importedFromFile = fileImports?.get(call.calleeName);
|
|
59
|
+
if (importedFromFile) {
|
|
60
|
+
const imported = candidates.find(s => s.filePath === importedFromFile);
|
|
61
|
+
if (imported) {
|
|
62
|
+
links.push(makeLink(call.enclosingSymbolId, imported.id, 'calls', 0.95, call.line));
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const match = candidates[0];
|
|
67
|
+
const confidence = candidates.length === 1 ? 0.8 : 0.5;
|
|
68
|
+
links.push(makeLink(call.enclosingSymbolId, match.id, 'calls', confidence, call.line));
|
|
69
|
+
}
|
|
70
|
+
// ── Resolve heritage (extends/implements) ──
|
|
71
|
+
for (const h of input.heritage) {
|
|
72
|
+
const children = symbolByName.get(h.childName);
|
|
73
|
+
const parents = symbolByName.get(h.parentName);
|
|
74
|
+
if (!children || !parents)
|
|
75
|
+
continue;
|
|
76
|
+
const child = children.find(s => s.filePath === h.filePath) ?? children[0];
|
|
77
|
+
const parent = parents[0];
|
|
78
|
+
if (child && parent) {
|
|
79
|
+
links.push(makeLink(child.id, parent.id, h.type, 0.95, h.line));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// ── Containment links (method → class) ──
|
|
83
|
+
for (const sym of input.allSymbols) {
|
|
84
|
+
if (sym.parentId) {
|
|
85
|
+
links.push(makeLink(sym.parentId, sym.id, 'contains', 1.0));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return deduplicateLinks(links);
|
|
89
|
+
}
|
|
90
|
+
// ── Helpers ──
|
|
91
|
+
function buildNameIndex(symbols) {
|
|
92
|
+
const index = new Map();
|
|
93
|
+
for (const s of symbols) {
|
|
94
|
+
const arr = index.get(s.name) ?? [];
|
|
95
|
+
arr.push(s);
|
|
96
|
+
index.set(s.name, arr);
|
|
97
|
+
}
|
|
98
|
+
return index;
|
|
99
|
+
}
|
|
100
|
+
function makeLink(fromId, toId, type, confidence, line) {
|
|
101
|
+
return {
|
|
102
|
+
id: `${fromId}->${type}->${toId}`,
|
|
103
|
+
fromId,
|
|
104
|
+
toId,
|
|
105
|
+
type,
|
|
106
|
+
confidence,
|
|
107
|
+
line,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function deduplicateLinks(links) {
|
|
111
|
+
const seen = new Set();
|
|
112
|
+
return links.filter(l => {
|
|
113
|
+
if (seen.has(l.id))
|
|
114
|
+
return false;
|
|
115
|
+
seen.add(l.id);
|
|
116
|
+
return true;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../src/analyzer/resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/analyzer/scanner.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,WAAW,EAAE,CA0C1E"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { join, relative } from 'node:path';
|
|
2
|
+
import { readFileSync, statSync, readdirSync } from 'node:fs';
|
|
3
|
+
import ignore from 'ignore';
|
|
4
|
+
import { supportedExtensions } from '../parser/languages.js';
|
|
5
|
+
export function scanFiles(rootPath, verbose = false) {
|
|
6
|
+
const exts = new Set(supportedExtensions());
|
|
7
|
+
const ig = loadIgnoreRules(rootPath);
|
|
8
|
+
const results = [];
|
|
9
|
+
function walk(dir) {
|
|
10
|
+
let entries;
|
|
11
|
+
try {
|
|
12
|
+
entries = readdirSync(dir);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
for (const entry of entries) {
|
|
18
|
+
const abs = join(dir, entry);
|
|
19
|
+
const rel = relative(rootPath, abs).replace(/\\/g, '/');
|
|
20
|
+
// Skip hidden dirs and common non-source dirs
|
|
21
|
+
if (entry.startsWith('.'))
|
|
22
|
+
continue;
|
|
23
|
+
if (SKIP_DIRS.has(entry) && dir === rootPath)
|
|
24
|
+
continue;
|
|
25
|
+
if (ig.ignores(rel))
|
|
26
|
+
continue;
|
|
27
|
+
let stat;
|
|
28
|
+
try {
|
|
29
|
+
stat = statSync(abs);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (stat.isDirectory()) {
|
|
35
|
+
if (SKIP_DIRS.has(entry))
|
|
36
|
+
continue;
|
|
37
|
+
walk(abs);
|
|
38
|
+
}
|
|
39
|
+
else if (stat.isFile()) {
|
|
40
|
+
const ext = '.' + entry.split('.').pop()?.toLowerCase();
|
|
41
|
+
if (exts.has(ext)) {
|
|
42
|
+
results.push({ relativePath: rel, absolutePath: abs });
|
|
43
|
+
}
|
|
44
|
+
else if (verbose) {
|
|
45
|
+
// Skip non-supported files silently
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
walk(rootPath);
|
|
51
|
+
return results;
|
|
52
|
+
}
|
|
53
|
+
function loadIgnoreRules(rootPath) {
|
|
54
|
+
const ig = ignore();
|
|
55
|
+
// Always ignore these
|
|
56
|
+
ig.add(['node_modules', 'dist', 'build', '.git', '__pycache__', 'vendor', 'target']);
|
|
57
|
+
// Read .gitignore if present
|
|
58
|
+
try {
|
|
59
|
+
const content = readFileSync(join(rootPath, '.gitignore'), 'utf-8');
|
|
60
|
+
ig.add(content);
|
|
61
|
+
}
|
|
62
|
+
catch { /* no .gitignore */ }
|
|
63
|
+
return ig;
|
|
64
|
+
}
|
|
65
|
+
const SKIP_DIRS = new Set([
|
|
66
|
+
'node_modules', '.git', 'dist', 'build', 'out',
|
|
67
|
+
'.next', '.nuxt', '.svelte-kit',
|
|
68
|
+
'__pycache__', '.venv', 'venv', 'env',
|
|
69
|
+
'vendor', 'target',
|
|
70
|
+
'.idea', '.vscode',
|
|
71
|
+
'coverage', '.nyc_output',
|
|
72
|
+
]);
|
|
73
|
+
//# sourceMappingURL=scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/analyzer/scanner.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|