sverklo 0.1.1 → 0.1.3
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/README.md +96 -87
- package/dist/bin/sverklo.js +45 -10
- package/dist/bin/sverklo.js.map +1 -1
- package/dist/src/indexer/indexer.js +11 -5
- package/dist/src/indexer/indexer.js.map +1 -1
- package/dist/src/search/hybrid-search.js +28 -4
- package/dist/src/search/hybrid-search.js.map +1 -1
- package/dist/src/server/http-server.d.ts +2 -0
- package/dist/src/server/http-server.js +355 -0
- package/dist/src/server/http-server.js.map +1 -0
- package/dist/src/server/mcp-server.js +50 -1
- package/dist/src/server/mcp-server.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,132 +1,141 @@
|
|
|
1
1
|
# Sverklo
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Your AI agent wastes 70% of tokens reading irrelevant files.** Sverklo fixes that.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Why
|
|
8
|
-
|
|
9
|
-
AI coding agents waste tokens reading irrelevant files. Claude Code has no built-in codebase indexing. Existing solutions are either cloud-dependent (Augment, Greptile) or incomplete (CocoIndex lacks graph ranking, Aider lacks MCP).
|
|
10
|
-
|
|
11
|
-
Sverklo fills the gap:
|
|
12
|
-
- **AST-aware parsing** — extracts functions, classes, types, interfaces from 10 languages
|
|
13
|
-
- **PageRank ranking** — structurally important files surface first
|
|
14
|
-
- **Semantic embeddings** — all-MiniLM-L6-v2 via ONNX, runs locally, no API keys
|
|
15
|
-
- **Hybrid search** — BM25 text + vector similarity + Reciprocal Rank Fusion
|
|
16
|
-
- **Token-budgeted** — returns exactly what fits in your context window
|
|
17
|
-
- **Incremental** — watches for file changes, updates in real-time
|
|
18
|
-
- **Zero config** — auto-detects project, auto-indexes, respects .gitignore
|
|
19
|
-
|
|
20
|
-
## Quick Start
|
|
5
|
+
One command gives Claude Code, Cursor, or any MCP agent deep codebase understanding — semantic search, dependency ranking, and persistent memory. Everything runs locally. No API keys. No cloud.
|
|
21
6
|
|
|
22
7
|
```bash
|
|
23
|
-
|
|
24
|
-
git clone https://github.com/nicenemo/sverklo
|
|
25
|
-
cd sverklo
|
|
26
|
-
npm install && npm run build
|
|
27
|
-
|
|
28
|
-
# 2. Download the embedding model (~90MB, one-time)
|
|
29
|
-
npx sverklo setup
|
|
30
|
-
|
|
31
|
-
# 3. Add to Claude Code
|
|
32
|
-
claude mcp add sverklo -- node /path/to/sverklo/dist/bin/sverklo.js .
|
|
8
|
+
claude mcp add sverklo -- npx sverklo .
|
|
33
9
|
```
|
|
34
10
|
|
|
35
|
-
|
|
11
|
+
That's it. Your agent now has 10 new tools for code search and memory.
|
|
12
|
+
|
|
13
|
+
---
|
|
36
14
|
|
|
37
|
-
|
|
38
|
-
Hybrid text + semantic code search with PageRank boosting.
|
|
15
|
+
## Before & After
|
|
39
16
|
|
|
17
|
+
**Without Sverklo** — agent greps for "auth", reads 15 files, burns 50K tokens, misses the relevant one:
|
|
40
18
|
```
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
language: "typescript" # filter by language
|
|
45
|
-
type: "function" # filter by symbol type
|
|
19
|
+
> How does auth work?
|
|
20
|
+
Searched for 3 patterns...
|
|
21
|
+
This project doesn't have any auth middleware. # Wrong
|
|
46
22
|
```
|
|
47
23
|
|
|
48
|
-
|
|
49
|
-
Structural codebase map. Shows most important files and their symbols, ranked by PageRank.
|
|
50
|
-
|
|
24
|
+
**With Sverklo** — semantic search finds the right code in <50ms:
|
|
51
25
|
```
|
|
52
|
-
|
|
53
|
-
|
|
26
|
+
> How does auth work?
|
|
27
|
+
Called sverklo_search with query "authentication"
|
|
28
|
+
Found validateToken() in src/middleware/auth.ts # Correct
|
|
54
29
|
```
|
|
55
30
|
|
|
56
|
-
|
|
57
|
-
Direct symbol lookup by name. Returns full definitions.
|
|
31
|
+
---
|
|
58
32
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
33
|
+
## What It Does
|
|
34
|
+
|
|
35
|
+
| Tool | What |
|
|
36
|
+
|------|------|
|
|
37
|
+
| `sverklo_search` | Hybrid semantic + text search across entire codebase |
|
|
38
|
+
| `sverklo_overview` | Structural codebase map ranked by PageRank importance |
|
|
39
|
+
| `sverklo_lookup` | Find any function, class, or type by name |
|
|
40
|
+
| `sverklo_refs` | Find all references to a symbol |
|
|
41
|
+
| `sverklo_deps` | Show file dependency graph (imports + importers) |
|
|
42
|
+
| `sverklo_status` | Index health check |
|
|
43
|
+
| `sverklo_remember` | Save decisions, preferences, patterns with git state |
|
|
44
|
+
| `sverklo_recall` | Semantic search over saved memories |
|
|
45
|
+
| `sverklo_forget` | Delete a memory |
|
|
46
|
+
| `sverklo_memories` | List all memories with health metrics |
|
|
63
47
|
|
|
64
|
-
|
|
65
|
-
Find all references to a symbol across the codebase.
|
|
48
|
+
## How It Works
|
|
66
49
|
|
|
67
50
|
```
|
|
68
|
-
|
|
69
|
-
|
|
51
|
+
Your code → Parse (10 languages) → Embed (ONNX, local)
|
|
52
|
+
→ Build dependency graph
|
|
53
|
+
→ Compute PageRank
|
|
54
|
+
↓
|
|
55
|
+
Agent query → BM25 text search ──┐
|
|
56
|
+
→ Vector similarity ──┼→ RRF fusion → Token-budgeted response
|
|
57
|
+
→ PageRank boost ────┘
|
|
70
58
|
```
|
|
71
59
|
|
|
72
|
-
|
|
73
|
-
|
|
60
|
+
1. **Parses** your codebase into functions, classes, types (TS, JS, Python, Go, Rust, Java, C, C++, Ruby, PHP)
|
|
61
|
+
2. **Embeds** code using all-MiniLM-L6-v2 ONNX model (384d vectors, fully local)
|
|
62
|
+
3. **Builds** a dependency graph and computes PageRank (structurally important files rank higher)
|
|
63
|
+
4. **Searches** using hybrid BM25 + vector similarity + PageRank, fused via Reciprocal Rank Fusion
|
|
64
|
+
5. **Remembers** decisions and patterns across sessions, linked to git state
|
|
65
|
+
6. **Watches** for file changes and updates incrementally
|
|
66
|
+
|
|
67
|
+
## Quick Start
|
|
74
68
|
|
|
69
|
+
### Claude Code
|
|
70
|
+
```bash
|
|
71
|
+
claude mcp add sverklo -- npx sverklo .
|
|
75
72
|
```
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
73
|
+
|
|
74
|
+
### Cursor
|
|
75
|
+
Add to `.cursor/mcp.json`:
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"mcpServers": {
|
|
79
|
+
"sverklo": {
|
|
80
|
+
"command": "npx",
|
|
81
|
+
"args": ["sverklo", "."]
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
79
85
|
```
|
|
80
86
|
|
|
81
|
-
###
|
|
82
|
-
|
|
87
|
+
### Any MCP Client
|
|
88
|
+
```bash
|
|
89
|
+
npx sverklo /path/to/your/project
|
|
90
|
+
```
|
|
83
91
|
|
|
84
|
-
|
|
92
|
+
The ONNX model (~90MB) downloads automatically on first run. No setup needed.
|
|
85
93
|
|
|
86
|
-
|
|
94
|
+
## Performance
|
|
87
95
|
|
|
88
|
-
|
|
96
|
+
| Metric | Value |
|
|
97
|
+
|--------|-------|
|
|
98
|
+
| Index 38 files | 640ms |
|
|
99
|
+
| Search query | <50ms |
|
|
100
|
+
| Memory footprint | ~200MB |
|
|
101
|
+
| Languages | 10 |
|
|
102
|
+
| Dependencies | zero config |
|
|
89
103
|
|
|
90
|
-
|
|
91
|
-
2. **AST parsing** — structural extraction of functions, classes, types, imports
|
|
92
|
-
3. **NL descriptions** — generates natural language descriptions from code metadata (embed descriptions, not raw code)
|
|
93
|
-
4. **Embeddings** — all-MiniLM-L6-v2 ONNX model, 384d vectors, fully local
|
|
94
|
-
5. **Dependency graph** — resolves imports, builds file-level graph, computes PageRank
|
|
95
|
-
6. **Hybrid search** — BM25 + cosine similarity + PageRank via Reciprocal Rank Fusion
|
|
96
|
-
7. **Token budgeting** — packs results to fit within the specified budget
|
|
104
|
+
## Why Not...
|
|
97
105
|
|
|
98
|
-
|
|
106
|
+
| Alternative | Gap |
|
|
107
|
+
|-------------|-----|
|
|
108
|
+
| **Built-in grep** | No semantic understanding. Burns tokens reading irrelevant files. |
|
|
109
|
+
| **Augment** | Cloud-only, closed source, $20-200/mo |
|
|
110
|
+
| **Greptile** | Cloud-only, $30/dev/mo, no memory |
|
|
111
|
+
| **CocoIndex** | No PageRank ranking, no hybrid search, no memory |
|
|
112
|
+
| **Aider repo-map** | No MCP, no semantic search, no memory |
|
|
113
|
+
| **claude-mem** | Memory only, no code search, ChromaDB overhead |
|
|
99
114
|
|
|
100
|
-
|
|
101
|
-
- Indexing: **681ms** (including ONNX embedding generation)
|
|
102
|
-
- Search: **<50ms** per query
|
|
103
|
-
- Memory: **~200MB** (ONNX runtime + cached vectors)
|
|
115
|
+
Sverklo is the only tool that combines **code search + memory + dependency graph** in one local-first MCP server.
|
|
104
116
|
|
|
105
117
|
## Configuration
|
|
106
118
|
|
|
107
119
|
| Setting | Location |
|
|
108
120
|
|---------|----------|
|
|
109
|
-
| Model files | `~/.sverklo/models
|
|
110
|
-
| Index database | `~/.sverklo/<project
|
|
121
|
+
| Model files | `~/.sverklo/models/` (auto-downloaded) |
|
|
122
|
+
| Index database | `~/.sverklo/<project>/index.db` |
|
|
111
123
|
| Custom ignores | `.sverkloignore` in project root |
|
|
112
124
|
| Debug logging | `SVERKLO_DEBUG=1` |
|
|
113
125
|
|
|
114
|
-
##
|
|
126
|
+
## Open Source, Open Core
|
|
127
|
+
|
|
128
|
+
The full MCP server is **free and open source** (MIT). All 10 tools, no limits.
|
|
129
|
+
|
|
130
|
+
**Sverklo Pro** (coming soon) adds smart auto-capture, cross-project patterns, and better models.
|
|
115
131
|
|
|
116
|
-
|
|
132
|
+
**Sverklo Team** (coming soon) adds shared team memory and on-prem deployment.
|
|
117
133
|
|
|
118
|
-
|
|
119
|
-
- Session memory — decisions, preferences, patterns across sessions
|
|
120
|
-
- Memory quality scoring — confidence levels, staleness detection
|
|
121
|
-
- Git-state linked memories — what the code looked like when a decision was made
|
|
122
|
-
- Cross-project pattern transfer
|
|
123
|
-
- Better embedding models
|
|
134
|
+
## Links
|
|
124
135
|
|
|
125
|
-
|
|
126
|
-
-
|
|
127
|
-
-
|
|
128
|
-
- On-prem deployment
|
|
129
|
-
- Admin dashboard
|
|
136
|
+
- [Website](https://sverklo.com)
|
|
137
|
+
- [npm](https://www.npmjs.com/package/sverklo)
|
|
138
|
+
- [Issues](https://github.com/sverklo/sverklo/issues)
|
|
130
139
|
|
|
131
140
|
## License
|
|
132
141
|
|
package/dist/bin/sverklo.js
CHANGED
|
@@ -7,25 +7,48 @@ if (command === "setup" || command === "install") {
|
|
|
7
7
|
await setupModels();
|
|
8
8
|
process.exit(0);
|
|
9
9
|
}
|
|
10
|
+
if (command === "ui" || command === "dashboard") {
|
|
11
|
+
const projectPath = resolve(args[1] || process.cwd());
|
|
12
|
+
const { existsSync } = await import("node:fs");
|
|
13
|
+
const { join } = await import("node:path");
|
|
14
|
+
const { homedir } = await import("node:os");
|
|
15
|
+
const modelDir = join(homedir(), ".sverklo", "models");
|
|
16
|
+
if (!existsSync(join(modelDir, "model.onnx"))) {
|
|
17
|
+
console.log("Downloading embedding model (~90MB)...");
|
|
18
|
+
const { setupModels } = await import("../src/indexer/setup.js");
|
|
19
|
+
await setupModels().catch(() => { });
|
|
20
|
+
}
|
|
21
|
+
const { getProjectConfig } = await import("../src/utils/config.js");
|
|
22
|
+
const { Indexer } = await import("../src/indexer/indexer.js");
|
|
23
|
+
const { startHttpServer } = await import("../src/server/http-server.js");
|
|
24
|
+
const config = getProjectConfig(projectPath);
|
|
25
|
+
const indexer = new Indexer(config);
|
|
26
|
+
await indexer.index();
|
|
27
|
+
startHttpServer(indexer);
|
|
28
|
+
const port = 3847;
|
|
29
|
+
console.log(`\nSverklo Dashboard: http://localhost:${port}\n`);
|
|
30
|
+
// Open browser
|
|
31
|
+
const { exec } = await import("node:child_process");
|
|
32
|
+
exec(`open http://localhost:${port} 2>/dev/null || xdg-open http://localhost:${port} 2>/dev/null`);
|
|
33
|
+
// Keep alive
|
|
34
|
+
process.on("SIGINT", () => { indexer.close(); process.exit(0); });
|
|
35
|
+
await new Promise(() => { }); // block forever
|
|
36
|
+
}
|
|
10
37
|
if (command === "--help" || command === "-h") {
|
|
11
38
|
console.log(`
|
|
12
39
|
sverklo — code intelligence for AI agents
|
|
13
40
|
|
|
14
41
|
Usage:
|
|
15
42
|
sverklo [project-path] Start the MCP server (stdio transport)
|
|
43
|
+
sverklo ui [project-path] Open the web dashboard
|
|
16
44
|
sverklo setup Download the embedding model (~90MB)
|
|
17
45
|
sverklo --help Show this help
|
|
18
46
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
dependencies Show file dependency graph
|
|
25
|
-
index_status Check index health
|
|
26
|
-
|
|
27
|
-
Add to Claude Code:
|
|
28
|
-
claude mcp add sverklo -- npx sverklo .
|
|
47
|
+
Add to your AI agent:
|
|
48
|
+
Claude Code: claude mcp add sverklo -- npx sverklo .
|
|
49
|
+
Cursor: Add to .cursor/mcp.json
|
|
50
|
+
Windsurf: Add to ~/.windsurf/mcp.json
|
|
51
|
+
VS Code: Add to .vscode/mcp.json
|
|
29
52
|
|
|
30
53
|
Environment:
|
|
31
54
|
SVERKLO_DEBUG=1 Enable debug logging to stderr
|
|
@@ -33,6 +56,18 @@ Environment:
|
|
|
33
56
|
process.exit(0);
|
|
34
57
|
}
|
|
35
58
|
const rootPath = resolve(command || process.cwd());
|
|
59
|
+
// Auto-download model if missing (no separate setup step needed)
|
|
60
|
+
const { existsSync } = await import("node:fs");
|
|
61
|
+
const { join } = await import("node:path");
|
|
62
|
+
const { homedir } = await import("node:os");
|
|
63
|
+
const modelDir = join(homedir(), ".sverklo", "models");
|
|
64
|
+
if (!existsSync(join(modelDir, "model.onnx"))) {
|
|
65
|
+
process.stderr.write("[sverklo] First run — downloading embedding model (~90MB)...\n");
|
|
66
|
+
const { setupModels } = await import("../src/indexer/setup.js");
|
|
67
|
+
await setupModels().catch(() => {
|
|
68
|
+
process.stderr.write("[sverklo] Model download failed. Search will use lightweight embeddings.\n");
|
|
69
|
+
});
|
|
70
|
+
}
|
|
36
71
|
const { startMcpServer } = await import("../src/index.js");
|
|
37
72
|
startMcpServer(rootPath).catch((err) => {
|
|
38
73
|
console.error("Failed to start sverklo:", err);
|
package/dist/bin/sverklo.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sverklo.js","sourceRoot":"","sources":["../../bin/sverklo.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AAExB,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;IACjD,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAC;IAChE,MAAM,WAAW,EAAE,CAAC;IACpB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;IAC7C,OAAO,CAAC,GAAG,CAAC
|
|
1
|
+
{"version":3,"file":"sverklo.js","sourceRoot":"","sources":["../../bin/sverklo.ts"],"names":[],"mappings":"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"}
|
|
@@ -53,12 +53,12 @@ export class Indexer {
|
|
|
53
53
|
this.progress = { done: 0, total: files.length };
|
|
54
54
|
log(`Discovered ${files.length} files`);
|
|
55
55
|
// 2. Determine which files need (re)indexing
|
|
56
|
+
// Use mtime for fast change detection (avoid reading file content twice)
|
|
56
57
|
const toIndex = files.filter((f) => {
|
|
57
58
|
const existing = this.fileStore.getByPath(f.relativePath);
|
|
58
59
|
if (!existing)
|
|
59
60
|
return true;
|
|
60
|
-
|
|
61
|
-
return existing.hash !== contentHash;
|
|
61
|
+
return existing.last_modified !== f.lastModified;
|
|
62
62
|
});
|
|
63
63
|
// 3. Remove files that no longer exist
|
|
64
64
|
const currentPaths = new Set(files.map((f) => f.relativePath));
|
|
@@ -83,7 +83,7 @@ export class Indexer {
|
|
|
83
83
|
for (const file of toIndex) {
|
|
84
84
|
try {
|
|
85
85
|
const content = readFileSync(file.absolutePath, "utf-8");
|
|
86
|
-
const contentHash =
|
|
86
|
+
const contentHash = createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
87
87
|
// Upsert file record
|
|
88
88
|
const fileId = this.fileStore.upsert(file.relativePath, file.language, contentHash, file.lastModified, file.sizeBytes);
|
|
89
89
|
// Clear old chunks for this file
|
|
@@ -136,11 +136,17 @@ export class Indexer {
|
|
|
136
136
|
async reindexFile(relativePath, absolutePath, language) {
|
|
137
137
|
try {
|
|
138
138
|
const content = readFileSync(absolutePath, "utf-8");
|
|
139
|
-
const contentHash =
|
|
140
|
-
const
|
|
139
|
+
const contentHash = createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
140
|
+
const { statSync } = await import("node:fs");
|
|
141
|
+
const stat = statSync(absolutePath);
|
|
141
142
|
const fileId = this.fileStore.upsert(relativePath, language, contentHash, stat.mtimeMs, stat.size);
|
|
142
143
|
this.chunkStore.deleteByFile(fileId);
|
|
143
144
|
const result = parseFile(content, language);
|
|
145
|
+
// Rebuild dependency edges for this file
|
|
146
|
+
this.graphStore.deleteBySourceFile(fileId);
|
|
147
|
+
const fileImports = new Map();
|
|
148
|
+
fileImports.set(relativePath, result.imports);
|
|
149
|
+
buildGraph(fileImports, this.fileStore, this.graphStore, this.config.rootPath);
|
|
144
150
|
for (const chunk of result.chunks) {
|
|
145
151
|
const description = describeChunk(chunk, relativePath, language);
|
|
146
152
|
const tokenCount = estimateTokens(chunk.content);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexer.js","sourceRoot":"","sources":["../../../src/indexer/indexer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAC5E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAGnD,MAAM,OAAO,OAAO;IAWE;IAVZ,EAAE,CAAoB;IACvB,SAAS,CAAY;IACrB,UAAU,CAAa;IACvB,cAAc,CAAiB;IAC/B,UAAU,CAAa;IACvB,WAAW,CAAc;IACzB,oBAAoB,CAAuB;IAC1C,QAAQ,GAAG,KAAK,CAAC;IACjB,QAAQ,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAEzC,YAAoB,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;QACvC,IAAI,CAAC,EAAE,GAAG,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACxC,IAAI,CAAC,SAAS,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,UAAU,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC1C,IAAI,CAAC,cAAc,GAAG,IAAI,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClD,IAAI,CAAC,UAAU,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC1C,IAAI,CAAC,WAAW,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5C,IAAI,CAAC,oBAAoB,GAAG,IAAI,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QAErB,IAAI,CAAC;YACH,GAAG,CAAC,YAAY,IAAI,CAAC,MAAM,CAAC,QAAQ,KAAK,CAAC,CAAC;YAC3C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAE7B,MAAM,YAAY,EAAE,CAAC;YAErB,oBAAoB;YACpB,MAAM,YAAY,GAAG,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC9D,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;YAChE,IAAI,CAAC,QAAQ,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;YACjD,GAAG,CAAC,cAAc,KAAK,CAAC,MAAM,QAAQ,CAAC,CAAC;YAExC,6CAA6C;YAC7C,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;gBACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;gBAC1D,IAAI,CAAC,QAAQ;oBAAE,OAAO,IAAI,CAAC;gBAC3B,
|
|
1
|
+
{"version":3,"file":"indexer.js","sourceRoot":"","sources":["../../../src/indexer/indexer.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,12 +6,36 @@ export async function hybridSearch(indexer, options) {
|
|
|
6
6
|
// Signal A: BM25 text search
|
|
7
7
|
const ftsResults = indexer.chunkStore.searchFts(query, 50);
|
|
8
8
|
// Signal B: Vector similarity search
|
|
9
|
+
// Optimization: only scan vectors for FTS candidate files + top PageRank files
|
|
10
|
+
// instead of ALL embeddings (O(n) brute force)
|
|
9
11
|
const [queryVector] = await embed([query]);
|
|
10
|
-
const
|
|
12
|
+
const candidateChunkIds = new Set();
|
|
13
|
+
// Add all FTS result chunk IDs
|
|
14
|
+
for (const r of ftsResults)
|
|
15
|
+
candidateChunkIds.add(r.id);
|
|
16
|
+
// Add chunks from same files as FTS results (sibling functions matter)
|
|
17
|
+
const ftsFileIds = new Set(ftsResults.map((r) => r.file_id));
|
|
18
|
+
if (ftsFileIds.size > 0) {
|
|
19
|
+
for (const fileId of ftsFileIds) {
|
|
20
|
+
for (const chunk of indexer.chunkStore.getByFile(fileId)) {
|
|
21
|
+
candidateChunkIds.add(chunk.id);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// Add chunks from top PageRank files (structurally important)
|
|
26
|
+
const topFiles = indexer.fileStore.getAll().slice(0, 20); // already sorted by pagerank DESC
|
|
27
|
+
for (const f of topFiles) {
|
|
28
|
+
for (const chunk of indexer.chunkStore.getByFile(f.id)) {
|
|
29
|
+
candidateChunkIds.add(chunk.id);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Only compute cosine similarity for candidate chunks (~100-500 vs thousands)
|
|
11
33
|
const vectorScores = [];
|
|
12
|
-
for (const
|
|
13
|
-
const
|
|
14
|
-
|
|
34
|
+
for (const chunkId of candidateChunkIds) {
|
|
35
|
+
const vec = indexer.embeddingStore.get(chunkId);
|
|
36
|
+
if (!vec)
|
|
37
|
+
continue;
|
|
38
|
+
vectorScores.push({ chunkId, score: cosineSimilarity(queryVector, vec) });
|
|
15
39
|
}
|
|
16
40
|
vectorScores.sort((a, b) => b.score - a.score);
|
|
17
41
|
const topVector = vectorScores.slice(0, 50);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hybrid-search.js","sourceRoot":"","sources":["../../../src/search/hybrid-search.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAYjE,kCAAkC;AAClC,MAAM,KAAK,GAAG,EAAE,CAAC;AAEjB,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAgB,EAChB,OAAsB;IAEtB,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;IAE9D,6BAA6B;IAC7B,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAE3D,qCAAqC;IACrC,MAAM,CAAC,WAAW,CAAC,GAAG,MAAM,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"hybrid-search.js","sourceRoot":"","sources":["../../../src/search/hybrid-search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { log } from "../utils/logger.js";
|
|
5
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
export function startHttpServer(indexer, port = 3847) {
|
|
7
|
+
const server = createServer(async (req, res) => {
|
|
8
|
+
const url = new URL(req.url || "/", `http://localhost:${port}`);
|
|
9
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
10
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE");
|
|
11
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
12
|
+
if (req.method === "OPTIONS") {
|
|
13
|
+
res.writeHead(204);
|
|
14
|
+
res.end();
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
// API routes
|
|
18
|
+
if (url.pathname === "/api/status") {
|
|
19
|
+
const status = indexer.getStatus();
|
|
20
|
+
json(res, status);
|
|
21
|
+
}
|
|
22
|
+
else if (url.pathname === "/api/files") {
|
|
23
|
+
const files = indexer.fileStore.getAll();
|
|
24
|
+
json(res, files);
|
|
25
|
+
}
|
|
26
|
+
else if (url.pathname === "/api/memories") {
|
|
27
|
+
const memories = indexer.memoryStore.getAll(100);
|
|
28
|
+
json(res, memories.map(m => ({
|
|
29
|
+
...m,
|
|
30
|
+
tags: m.tags ? JSON.parse(m.tags) : [],
|
|
31
|
+
related_files: m.related_files ? JSON.parse(m.related_files) : [],
|
|
32
|
+
})));
|
|
33
|
+
}
|
|
34
|
+
else if (url.pathname === "/api/overview") {
|
|
35
|
+
const files = indexer.fileStore.getAll();
|
|
36
|
+
const overview = files.map(f => ({
|
|
37
|
+
...f,
|
|
38
|
+
chunks: indexer.chunkStore.getByFile(f.id).map(c => ({
|
|
39
|
+
name: c.name,
|
|
40
|
+
type: c.type,
|
|
41
|
+
start_line: c.start_line,
|
|
42
|
+
end_line: c.end_line,
|
|
43
|
+
})),
|
|
44
|
+
}));
|
|
45
|
+
json(res, overview);
|
|
46
|
+
}
|
|
47
|
+
else if (url.pathname === "/api/deps") {
|
|
48
|
+
const files = indexer.fileStore.getAll();
|
|
49
|
+
const fileMap = new Map(files.map(f => [f.id, f.path]));
|
|
50
|
+
const edges = [];
|
|
51
|
+
for (const f of files) {
|
|
52
|
+
const deps = indexer.graphStore.getImports(f.id);
|
|
53
|
+
for (const d of deps) {
|
|
54
|
+
const targetPath = fileMap.get(d.target_file_id);
|
|
55
|
+
if (targetPath) {
|
|
56
|
+
edges.push({
|
|
57
|
+
source: f.path,
|
|
58
|
+
target: targetPath,
|
|
59
|
+
count: d.reference_count,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
json(res, { nodes: files.map(f => ({ path: f.path, pagerank: f.pagerank, language: f.language })), edges });
|
|
65
|
+
}
|
|
66
|
+
else if (url.pathname === "/api/search" && url.searchParams.get("q")) {
|
|
67
|
+
const { hybridSearch, formatResults } = await import("../search/hybrid-search.js");
|
|
68
|
+
const results = await hybridSearch(indexer, {
|
|
69
|
+
query: url.searchParams.get("q"),
|
|
70
|
+
tokenBudget: 8000,
|
|
71
|
+
});
|
|
72
|
+
json(res, results.map(r => ({
|
|
73
|
+
file: r.file.path,
|
|
74
|
+
name: r.chunk.name,
|
|
75
|
+
type: r.chunk.type,
|
|
76
|
+
startLine: r.chunk.start_line,
|
|
77
|
+
endLine: r.chunk.end_line,
|
|
78
|
+
content: r.chunk.content,
|
|
79
|
+
score: r.score,
|
|
80
|
+
pagerank: r.file.pagerank,
|
|
81
|
+
})));
|
|
82
|
+
}
|
|
83
|
+
else if (url.pathname === "/") {
|
|
84
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
85
|
+
res.end(getDashboardHTML());
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
res.writeHead(404);
|
|
89
|
+
res.end("Not found");
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
server.listen(port, () => {
|
|
93
|
+
log(`Dashboard running at http://localhost:${port}`);
|
|
94
|
+
});
|
|
95
|
+
server.on("error", (err) => {
|
|
96
|
+
if (err.code === "EADDRINUSE") {
|
|
97
|
+
log(`Port ${port} in use, trying ${port + 1}`);
|
|
98
|
+
startHttpServer(indexer, port + 1);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
function json(res, data) {
|
|
103
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
104
|
+
res.end(JSON.stringify(data));
|
|
105
|
+
}
|
|
106
|
+
function getDashboardHTML() {
|
|
107
|
+
return `<!DOCTYPE html>
|
|
108
|
+
<html lang="en">
|
|
109
|
+
<head>
|
|
110
|
+
<meta charset="UTF-8">
|
|
111
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
112
|
+
<title>Sverklo Dashboard</title>
|
|
113
|
+
<style>
|
|
114
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
115
|
+
:root { --bg: #0a0a0a; --card: #141414; --border: #222; --text: #eee; --dim: #888; --accent: #7c6aef; --green: #34d399; --red: #f87171; }
|
|
116
|
+
body { background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; line-height: 1.5; }
|
|
117
|
+
.container { max-width: 1200px; margin: 0 auto; padding: 20px; }
|
|
118
|
+
header { display: flex; align-items: center; justify-content: space-between; padding: 16px 0; border-bottom: 1px solid var(--border); margin-bottom: 24px; }
|
|
119
|
+
h1 { font-size: 20px; font-weight: 700; }
|
|
120
|
+
h1 span { color: var(--accent); }
|
|
121
|
+
.status { font-size: 13px; color: var(--dim); }
|
|
122
|
+
.status .dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--green); margin-right: 6px; }
|
|
123
|
+
|
|
124
|
+
.tabs { display: flex; gap: 4px; margin-bottom: 20px; }
|
|
125
|
+
.tab { padding: 8px 16px; border-radius: 8px; cursor: pointer; font-size: 14px; color: var(--dim); border: 1px solid transparent; transition: all .15s; }
|
|
126
|
+
.tab:hover { color: var(--text); }
|
|
127
|
+
.tab.active { color: var(--text); background: var(--card); border-color: var(--border); }
|
|
128
|
+
|
|
129
|
+
.panel { display: none; }
|
|
130
|
+
.panel.active { display: block; }
|
|
131
|
+
|
|
132
|
+
.card { background: var(--card); border: 1px solid var(--border); border-radius: 10px; padding: 16px; margin-bottom: 12px; }
|
|
133
|
+
.card-title { font-size: 13px; font-weight: 600; color: var(--dim); text-transform: uppercase; letter-spacing: .05em; margin-bottom: 12px; }
|
|
134
|
+
|
|
135
|
+
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin-bottom: 20px; }
|
|
136
|
+
.stat { background: var(--card); border: 1px solid var(--border); border-radius: 10px; padding: 16px; }
|
|
137
|
+
.stat-value { font-size: 28px; font-weight: 700; color: var(--accent); }
|
|
138
|
+
.stat-label { font-size: 13px; color: var(--dim); margin-top: 2px; }
|
|
139
|
+
|
|
140
|
+
table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
|
141
|
+
th { text-align: left; color: var(--dim); font-weight: 500; padding: 8px 12px; border-bottom: 1px solid var(--border); }
|
|
142
|
+
td { padding: 8px 12px; border-bottom: 1px solid var(--border); }
|
|
143
|
+
tr:hover { background: rgba(255,255,255,.02); }
|
|
144
|
+
.pr { color: var(--accent); font-weight: 600; font-size: 12px; }
|
|
145
|
+
|
|
146
|
+
.search-box { width: 100%; padding: 12px 16px; background: var(--card); border: 1px solid var(--border); border-radius: 8px; color: var(--text); font-size: 15px; outline: none; margin-bottom: 16px; }
|
|
147
|
+
.search-box:focus { border-color: var(--accent); }
|
|
148
|
+
.search-box::placeholder { color: var(--dim); }
|
|
149
|
+
|
|
150
|
+
.result { background: var(--card); border: 1px solid var(--border); border-radius: 8px; padding: 12px 16px; margin-bottom: 8px; }
|
|
151
|
+
.result-header { display: flex; justify-content: space-between; margin-bottom: 6px; }
|
|
152
|
+
.result-path { font-size: 13px; color: var(--accent); font-weight: 500; }
|
|
153
|
+
.result-score { font-size: 12px; color: var(--dim); }
|
|
154
|
+
.result pre { font-size: 12px; color: var(--dim); overflow-x: auto; white-space: pre-wrap; max-height: 200px; }
|
|
155
|
+
|
|
156
|
+
.memory { background: var(--card); border: 1px solid var(--border); border-radius: 8px; padding: 12px 16px; margin-bottom: 8px; }
|
|
157
|
+
.memory-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; }
|
|
158
|
+
.memory-cat { font-size: 11px; padding: 2px 8px; border-radius: 4px; background: var(--accent); color: white; font-weight: 600; text-transform: uppercase; }
|
|
159
|
+
.memory-meta { font-size: 12px; color: var(--dim); margin-top: 6px; }
|
|
160
|
+
.memory-content { font-size: 14px; }
|
|
161
|
+
.stale { opacity: 0.5; border-color: var(--red); }
|
|
162
|
+
.tag { font-size: 11px; padding: 1px 6px; border-radius: 3px; background: rgba(124,106,239,.15); color: var(--accent); margin-right: 4px; }
|
|
163
|
+
|
|
164
|
+
.empty { text-align: center; padding: 40px; color: var(--dim); }
|
|
165
|
+
|
|
166
|
+
.graph-container { width: 100%; height: 500px; background: var(--card); border: 1px solid var(--border); border-radius: 10px; position: relative; overflow: hidden; }
|
|
167
|
+
canvas { width: 100%; height: 100%; }
|
|
168
|
+
</style>
|
|
169
|
+
</head>
|
|
170
|
+
<body>
|
|
171
|
+
<div class="container">
|
|
172
|
+
<header>
|
|
173
|
+
<h1><span>⚡</span> Sverklo</h1>
|
|
174
|
+
<div class="status" id="status"><span class="dot"></span>Loading...</div>
|
|
175
|
+
</header>
|
|
176
|
+
|
|
177
|
+
<div class="stat-grid" id="stats"></div>
|
|
178
|
+
|
|
179
|
+
<div class="tabs">
|
|
180
|
+
<div class="tab active" data-panel="files">Files</div>
|
|
181
|
+
<div class="tab" data-panel="memories">Memories</div>
|
|
182
|
+
<div class="tab" data-panel="search">Search</div>
|
|
183
|
+
<div class="tab" data-panel="graph">Dependencies</div>
|
|
184
|
+
</div>
|
|
185
|
+
|
|
186
|
+
<div class="panel active" id="files">
|
|
187
|
+
<table>
|
|
188
|
+
<thead><tr><th>File</th><th>Language</th><th>PageRank</th><th>Symbols</th></tr></thead>
|
|
189
|
+
<tbody id="files-body"></tbody>
|
|
190
|
+
</table>
|
|
191
|
+
</div>
|
|
192
|
+
|
|
193
|
+
<div class="panel" id="memories">
|
|
194
|
+
<div id="memories-list"></div>
|
|
195
|
+
</div>
|
|
196
|
+
|
|
197
|
+
<div class="panel" id="search">
|
|
198
|
+
<input class="search-box" id="search-input" placeholder="Search your codebase semantically..." />
|
|
199
|
+
<div id="search-results"></div>
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<div class="panel" id="graph">
|
|
203
|
+
<div class="graph-container">
|
|
204
|
+
<canvas id="graph-canvas"></canvas>
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
</div>
|
|
208
|
+
|
|
209
|
+
<script>
|
|
210
|
+
const API = '';
|
|
211
|
+
|
|
212
|
+
// Tabs
|
|
213
|
+
document.querySelectorAll('.tab').forEach(t => {
|
|
214
|
+
t.addEventListener('click', () => {
|
|
215
|
+
document.querySelectorAll('.tab').forEach(x => x.classList.remove('active'));
|
|
216
|
+
document.querySelectorAll('.panel').forEach(x => x.classList.remove('active'));
|
|
217
|
+
t.classList.add('active');
|
|
218
|
+
document.getElementById(t.dataset.panel).classList.add('active');
|
|
219
|
+
if (t.dataset.panel === 'graph') drawGraph();
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Load status
|
|
224
|
+
async function loadStatus() {
|
|
225
|
+
const s = await (await fetch(API + '/api/status')).json();
|
|
226
|
+
document.getElementById('status').innerHTML = '<span class="dot"></span>' + s.projectName + ' — ' + s.fileCount + ' files, ' + s.chunkCount + ' chunks';
|
|
227
|
+
document.getElementById('stats').innerHTML = [
|
|
228
|
+
stat(s.fileCount, 'Files'),
|
|
229
|
+
stat(s.chunkCount, 'Code Chunks'),
|
|
230
|
+
stat(s.languages.length, 'Languages'),
|
|
231
|
+
stat(s.languages.join(', ') || '-', 'Detected'),
|
|
232
|
+
].join('');
|
|
233
|
+
}
|
|
234
|
+
function stat(v, l) { return '<div class="stat"><div class="stat-value">' + v + '</div><div class="stat-label">' + l + '</div></div>'; }
|
|
235
|
+
|
|
236
|
+
// Load files
|
|
237
|
+
async function loadFiles() {
|
|
238
|
+
const data = await (await fetch(API + '/api/overview')).json();
|
|
239
|
+
const tbody = document.getElementById('files-body');
|
|
240
|
+
tbody.innerHTML = data.map(f => {
|
|
241
|
+
const symbols = f.chunks.filter(c => c.name).map(c => c.name).slice(0, 5).join(', ');
|
|
242
|
+
return '<tr><td>' + f.path + '</td><td>' + (f.language || '-') + '</td><td><span class="pr">' + f.pagerank.toFixed(2) + '</span></td><td style="color:var(--dim);font-size:12px">' + (symbols || '-') + '</td></tr>';
|
|
243
|
+
}).join('');
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Load memories
|
|
247
|
+
async function loadMemories() {
|
|
248
|
+
const data = await (await fetch(API + '/api/memories')).json();
|
|
249
|
+
const el = document.getElementById('memories-list');
|
|
250
|
+
if (data.length === 0) { el.innerHTML = '<div class="empty">No memories yet. Use sverklo_remember to save decisions.</div>'; return; }
|
|
251
|
+
el.innerHTML = data.map(m => {
|
|
252
|
+
const tags = (m.tags || []).map(t => '<span class="tag">' + t + '</span>').join('');
|
|
253
|
+
const age = formatAge(m.created_at);
|
|
254
|
+
return '<div class="memory' + (m.is_stale ? ' stale' : '') + '"><div class="memory-header"><span class="memory-cat">' + m.category + '</span><span style="font-size:12px;color:var(--dim)">#' + m.id + '</span></div><div class="memory-content">' + m.content + '</div><div class="memory-meta">' + age + ' ago · conf: ' + m.confidence + ' · used: ' + m.access_count + 'x' + (m.git_sha ? ' · ' + (m.git_branch||'?') + '@' + m.git_sha.slice(0,7) : '') + ' ' + tags + '</div></div>';
|
|
255
|
+
}).join('');
|
|
256
|
+
}
|
|
257
|
+
function formatAge(ts) { const m = Math.floor((Date.now()-ts)/60000); if(m<60) return m+'m'; const h=Math.floor(m/60); if(h<24) return h+'h'; return Math.floor(h/24)+'d'; }
|
|
258
|
+
|
|
259
|
+
// Search
|
|
260
|
+
let searchTimeout;
|
|
261
|
+
document.getElementById('search-input').addEventListener('input', (e) => {
|
|
262
|
+
clearTimeout(searchTimeout);
|
|
263
|
+
searchTimeout = setTimeout(() => doSearch(e.target.value), 300);
|
|
264
|
+
});
|
|
265
|
+
async function doSearch(q) {
|
|
266
|
+
if (!q || q.length < 2) { document.getElementById('search-results').innerHTML = ''; return; }
|
|
267
|
+
const data = await (await fetch(API + '/api/search?q=' + encodeURIComponent(q))).json();
|
|
268
|
+
document.getElementById('search-results').innerHTML = data.map(r =>
|
|
269
|
+
'<div class="result"><div class="result-header"><span class="result-path">' + r.file + ':' + r.startLine + '</span><span class="result-score">' + (r.name ? r.type + ': ' + r.name : r.type) + ' · score: ' + r.score.toFixed(4) + '</span></div><pre>' + escHtml(r.content.slice(0, 500)) + '</pre></div>'
|
|
270
|
+
).join('') || '<div class="empty">No results</div>';
|
|
271
|
+
}
|
|
272
|
+
function escHtml(s) { return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }
|
|
273
|
+
|
|
274
|
+
// Graph
|
|
275
|
+
let graphData;
|
|
276
|
+
async function drawGraph() {
|
|
277
|
+
if (!graphData) graphData = await (await fetch(API + '/api/deps')).json();
|
|
278
|
+
const canvas = document.getElementById('graph-canvas');
|
|
279
|
+
const ctx = canvas.getContext('2d');
|
|
280
|
+
canvas.width = canvas.parentElement.clientWidth;
|
|
281
|
+
canvas.height = canvas.parentElement.clientHeight;
|
|
282
|
+
|
|
283
|
+
const nodes = graphData.nodes.map((n, i) => {
|
|
284
|
+
const angle = (i / graphData.nodes.length) * Math.PI * 2;
|
|
285
|
+
const r = Math.min(canvas.width, canvas.height) * 0.35;
|
|
286
|
+
return { ...n, x: canvas.width/2 + Math.cos(angle) * r, y: canvas.height/2 + Math.sin(angle) * r, label: n.path.split('/').pop() };
|
|
287
|
+
});
|
|
288
|
+
const nodeMap = {};
|
|
289
|
+
nodes.forEach(n => nodeMap[n.path] = n);
|
|
290
|
+
|
|
291
|
+
// Simple force simulation (5 iterations)
|
|
292
|
+
for (let iter = 0; iter < 50; iter++) {
|
|
293
|
+
// Repulsion
|
|
294
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
295
|
+
for (let j = i+1; j < nodes.length; j++) {
|
|
296
|
+
const dx = nodes[j].x - nodes[i].x;
|
|
297
|
+
const dy = nodes[j].y - nodes[i].y;
|
|
298
|
+
const d = Math.max(Math.sqrt(dx*dx + dy*dy), 1);
|
|
299
|
+
const f = 5000 / (d * d);
|
|
300
|
+
nodes[i].x -= dx/d * f;
|
|
301
|
+
nodes[i].y -= dy/d * f;
|
|
302
|
+
nodes[j].x += dx/d * f;
|
|
303
|
+
nodes[j].y += dy/d * f;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// Attraction (edges)
|
|
307
|
+
for (const e of graphData.edges) {
|
|
308
|
+
const a = nodeMap[e.source], b = nodeMap[e.target];
|
|
309
|
+
if (!a || !b) continue;
|
|
310
|
+
const dx = b.x - a.x, dy = b.y - a.y;
|
|
311
|
+
const d = Math.max(Math.sqrt(dx*dx + dy*dy), 1);
|
|
312
|
+
const f = (d - 120) * 0.01;
|
|
313
|
+
a.x += dx/d * f; a.y += dy/d * f;
|
|
314
|
+
b.x -= dx/d * f; b.y -= dy/d * f;
|
|
315
|
+
}
|
|
316
|
+
// Center gravity
|
|
317
|
+
nodes.forEach(n => {
|
|
318
|
+
n.x += (canvas.width/2 - n.x) * 0.01;
|
|
319
|
+
n.y += (canvas.height/2 - n.y) * 0.01;
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Draw
|
|
324
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
325
|
+
|
|
326
|
+
// Edges
|
|
327
|
+
ctx.strokeStyle = '#333';
|
|
328
|
+
ctx.lineWidth = 1;
|
|
329
|
+
for (const e of graphData.edges) {
|
|
330
|
+
const a = nodeMap[e.source], b = nodeMap[e.target];
|
|
331
|
+
if (!a || !b) continue;
|
|
332
|
+
ctx.beginPath(); ctx.moveTo(a.x, a.y); ctx.lineTo(b.x, b.y); ctx.stroke();
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Nodes
|
|
336
|
+
for (const n of nodes) {
|
|
337
|
+
const size = 4 + n.pagerank * 12;
|
|
338
|
+
ctx.beginPath(); ctx.arc(n.x, n.y, size, 0, Math.PI*2);
|
|
339
|
+
ctx.fillStyle = n.pagerank > 0.5 ? '#7c6aef' : '#444';
|
|
340
|
+
ctx.fill();
|
|
341
|
+
ctx.fillStyle = '#aaa';
|
|
342
|
+
ctx.font = '11px -apple-system, sans-serif';
|
|
343
|
+
ctx.fillText(n.label, n.x + size + 4, n.y + 4);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Init
|
|
348
|
+
loadStatus();
|
|
349
|
+
loadFiles();
|
|
350
|
+
loadMemories();
|
|
351
|
+
</script>
|
|
352
|
+
</body>
|
|
353
|
+
</html>`;
|
|
354
|
+
}
|
|
355
|
+
//# sourceMappingURL=http-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-server.js","sourceRoot":"","sources":["../../../src/server/http-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
-
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
4
|
import { Indexer } from "../indexer/indexer.js";
|
|
5
5
|
import { startWatcher } from "../indexer/watcher.js";
|
|
6
6
|
import { getProjectConfig } from "../utils/config.js";
|
|
@@ -15,6 +15,7 @@ import { rememberTool, handleRemember } from "./tools/remember.js";
|
|
|
15
15
|
import { recallTool, handleRecall } from "./tools/recall.js";
|
|
16
16
|
import { forgetTool, handleForget } from "./tools/forget.js";
|
|
17
17
|
import { memoriesTool, handleMemories } from "./tools/memories.js";
|
|
18
|
+
import { startHttpServer } from "./http-server.js";
|
|
18
19
|
export async function startMcpServer(rootPath) {
|
|
19
20
|
const config = getProjectConfig(rootPath);
|
|
20
21
|
const indexer = new Indexer(config);
|
|
@@ -22,6 +23,8 @@ export async function startMcpServer(rootPath) {
|
|
|
22
23
|
const indexPromise = indexer.index().catch((err) => {
|
|
23
24
|
logError("Initial indexing failed", err);
|
|
24
25
|
});
|
|
26
|
+
// Start dashboard HTTP server alongside MCP
|
|
27
|
+
startHttpServer(indexer);
|
|
25
28
|
// Start file watcher
|
|
26
29
|
startWatcher(indexer, rootPath);
|
|
27
30
|
const server = new Server({
|
|
@@ -30,8 +33,54 @@ export async function startMcpServer(rootPath) {
|
|
|
30
33
|
}, {
|
|
31
34
|
capabilities: {
|
|
32
35
|
tools: {},
|
|
36
|
+
resources: {},
|
|
33
37
|
},
|
|
34
38
|
});
|
|
39
|
+
// Resources — auto-injected context at session start
|
|
40
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
41
|
+
resources: [
|
|
42
|
+
{
|
|
43
|
+
uri: "sverklo://context",
|
|
44
|
+
name: "Sverklo Project Context",
|
|
45
|
+
description: "Key memories and codebase overview. Read this at session start to understand the project.",
|
|
46
|
+
mimeType: "text/plain",
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
}));
|
|
50
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
51
|
+
if (request.params.uri === "sverklo://context") {
|
|
52
|
+
await indexPromise;
|
|
53
|
+
const parts = [];
|
|
54
|
+
// Top memories (most accessed, highest confidence)
|
|
55
|
+
const memories = indexer.memoryStore.getAll(5);
|
|
56
|
+
if (memories.length > 0) {
|
|
57
|
+
parts.push("## Key Memories");
|
|
58
|
+
for (const m of memories) {
|
|
59
|
+
const stale = m.is_stale ? " [STALE]" : "";
|
|
60
|
+
parts.push(`- [${m.category}]${stale} ${m.content}`);
|
|
61
|
+
}
|
|
62
|
+
parts.push("");
|
|
63
|
+
}
|
|
64
|
+
// Index summary
|
|
65
|
+
const status = indexer.getStatus();
|
|
66
|
+
parts.push(`## Codebase: ${status.projectName}`);
|
|
67
|
+
parts.push(`${status.fileCount} files, ${status.chunkCount} chunks indexed`);
|
|
68
|
+
parts.push(`Languages: ${status.languages.join(", ") || "none"}`);
|
|
69
|
+
parts.push("");
|
|
70
|
+
parts.push("Use sverklo_search for semantic code search (preferred over grep).");
|
|
71
|
+
parts.push("Use sverklo_remember to save important decisions.");
|
|
72
|
+
return {
|
|
73
|
+
contents: [
|
|
74
|
+
{
|
|
75
|
+
uri: "sverklo://context",
|
|
76
|
+
mimeType: "text/plain",
|
|
77
|
+
text: parts.join("\n"),
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return { contents: [] };
|
|
83
|
+
});
|
|
35
84
|
// List tools
|
|
36
85
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
37
86
|
tools: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../../../src/server/mcp-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,
|
|
1
|
+
{"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../../../src/server/mcp-server.ts"],"names":[],"mappings":"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"}
|