carto-md 2.0.6 → 2.0.8
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 +297 -130
- package/docs/screenshots/claude-code-supabase.png +0 -0
- package/package.json +7 -3
- package/scripts/postinstall.js +46 -0
- package/src/acp/agent.js +6 -13
- package/src/acp/providers/index.js +4 -12
- package/src/agents/leiden.js +7 -13
- package/src/bitmap/bitset.js +190 -0
- package/src/bitmap/index.js +121 -0
- package/src/bitmap/sidecar.js +545 -0
- package/src/bitmap/tools.js +310 -0
- package/src/cli/check.js +57 -0
- package/src/cli/impact.js +6 -1
- package/src/cli/index.js +14 -2
- package/src/cli/init.js +297 -50
- package/src/cli/inspect.js +295 -0
- package/src/cli/serve.js +1 -1
- package/src/cli/watch.js +6 -0
- package/src/engine/worker.js +24 -4
- package/src/extractors/imports.js +181 -1
- package/src/extractors/languages/html.js +4 -1
- package/src/extractors/languages/javascript.js +5 -0
- package/src/extractors/languages/prisma.js +4 -1
- package/src/extractors/languages/python.js +5 -1
- package/src/extractors/languages/r.js +4 -1
- package/src/extractors/languages/typescript.js +2 -0
- package/src/extractors/tree-sitter-parser.js +15 -0
- package/src/mcp/diff-parser.js +246 -0
- package/src/mcp/server-v2.js +535 -8
- package/src/mcp/validate.js +304 -0
- package/src/security/ignore.js +56 -1
- package/src/store/config-loader.js +77 -0
- package/src/store/path-utils.js +50 -0
- package/src/store/sqlite-store.js +419 -8
- package/src/store/sync-v2.js +422 -96
- package/BENCHMARK_RESULTS.md +0 -34
package/README.md
CHANGED
|
@@ -7,93 +7,147 @@
|
|
|
7
7
|
|
|
8
8
|
**The structural intelligence layer for AI coding tools.**
|
|
9
9
|
|
|
10
|
+
Your AI coding tool sees files. Carto sees architecture — domains, blast radius, import graph, routes. Every AI tool you use stops guessing about your codebase and starts knowing it.
|
|
11
|
+
|
|
10
12
|
```bash
|
|
11
13
|
npm install -g carto-md
|
|
12
14
|
cd your-project
|
|
13
15
|
carto init
|
|
14
16
|
```
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
That's it. Carto auto-wires itself into every AI tool you have installed. Restart your AI tool and it now knows your codebase architecturally.
|
|
19
|
+
|
|
20
|
+
**Works with:** Cursor · Claude Code · Codex · Kiro · Claude Desktop · Windsurf · VS Code Copilot · JetBrains · Zed
|
|
17
21
|
|
|
18
22
|
---
|
|
19
23
|
|
|
20
|
-
##
|
|
24
|
+
## Use it with your AI tool
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
`carto init` auto-wires the MCP server into every AI tool it detects. If yours wasn't detected, here's the manual config — **one block, copy-paste, done.**
|
|
23
27
|
|
|
24
|
-
|
|
25
|
-
|------|----------|-------------|---------|-----------|------------|---------|--------------|
|
|
26
|
-
| [prisma/prisma](https://github.com/prisma/prisma) | TypeScript | 3,303 | 3,303 | **1.6s** | **178ms** | 2.2 MB | 3,590 |
|
|
27
|
-
| [supabase/supabase](https://github.com/supabase/supabase) | TypeScript | 6,818 | 6,746 | **4.9s** | **725ms** | 4.3 MB | 5,754 |
|
|
28
|
-
| [microsoft/vscode](https://github.com/microsoft/vscode) | TypeScript | 10,565 | 10,565 | **9.7s** | **1.2s** | 10.6 MB | 19,769 |
|
|
29
|
-
| [zed-industries/zed](https://github.com/zed-industries/zed) | Rust | 1,837 | 1,837 | **2.7s** | **83ms** | 4.7 MB | 2,176 |
|
|
28
|
+
### Cursor
|
|
30
29
|
|
|
31
|
-
|
|
30
|
+
`carto init` writes this for you. Manual: `~/.cursor/mcp.json`
|
|
31
|
+
```json
|
|
32
|
+
{ "mcpServers": { "carto": { "command": "carto", "args": ["serve"], "cwd": "/your/project" } } }
|
|
33
|
+
```
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
### Claude Code (CLI)
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
`carto init` writes `<project>/.mcp.json` for you when Claude Code is detected (`claude` binary on PATH or `~/.claude/` exists). Manual:
|
|
38
|
+
```bash
|
|
39
|
+
claude mcp add carto -- carto serve
|
|
40
|
+
```
|
|
41
|
+
Or create `.mcp.json` at the project root:
|
|
42
|
+
```json
|
|
43
|
+
{ "mcpServers": { "carto": { "command": "carto", "args": ["serve"] } } }
|
|
44
|
+
```
|
|
36
45
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
### Codex (CLI)
|
|
47
|
+
|
|
48
|
+
`carto init` writes `~/.codex/config.toml` for you when Codex is detected (`codex` binary on PATH or `~/.codex/` exists). Manual:
|
|
49
|
+
```bash
|
|
50
|
+
codex mcp add carto -- carto serve
|
|
51
|
+
```
|
|
52
|
+
Or edit `~/.codex/config.toml`:
|
|
53
|
+
```toml
|
|
54
|
+
[mcp_servers.carto]
|
|
55
|
+
command = "carto"
|
|
56
|
+
args = ["serve"]
|
|
57
|
+
cwd = "/your/project"
|
|
58
|
+
enabled = true
|
|
59
|
+
```
|
|
43
60
|
|
|
44
|
-
|
|
61
|
+
### Kiro
|
|
45
62
|
|
|
46
|
-
|
|
63
|
+
`carto init` writes this for you. Manual: `~/.kiro/settings/mcp.json`
|
|
64
|
+
```json
|
|
65
|
+
{ "mcpServers": { "carto": { "command": "carto", "args": ["serve"], "cwd": "/your/project" } } }
|
|
66
|
+
```
|
|
47
67
|
|
|
48
|
-
|
|
68
|
+
### Claude Desktop
|
|
49
69
|
|
|
70
|
+
`carto init` writes this for you (cross-platform). Manual paths:
|
|
71
|
+
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
72
|
+
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
73
|
+
- Linux: `~/.config/Claude/claude_desktop_config.json` (community Linux builds)
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{ "mcpServers": { "carto": { "command": "carto", "args": ["serve"], "cwd": "/your/project" } } }
|
|
50
77
|
```
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
Leiden+CPM graph clustering → auto-detects domains
|
|
58
|
-
Computes reverse deps → blast radius for every file
|
|
59
|
-
Writes AGENTS.md + .carto/context/*.md (lazy, on-demand)
|
|
60
|
-
Auto-wires MCP into Kiro, Cursor, Claude Desktop
|
|
61
|
-
↓
|
|
62
|
-
carto watch
|
|
63
|
-
↓
|
|
64
|
-
File saved → debounce 50ms → re-parse 1 file → SQLite write → <50ms
|
|
78
|
+
|
|
79
|
+
### VS Code Copilot
|
|
80
|
+
|
|
81
|
+
`carto init` writes `<project>/.vscode/mcp.json` for you when the `code` binary is on PATH. Manual: `.vscode/mcp.json` in your project root, or Command Palette → `MCP: Add Server`. Note: VS Code uses `servers` (not `mcpServers`) and requires `"type": "stdio"`.
|
|
82
|
+
```json
|
|
83
|
+
{ "servers": { "carto": { "type": "stdio", "command": "carto", "args": ["serve"] } } }
|
|
65
84
|
```
|
|
66
85
|
|
|
86
|
+
### Windsurf
|
|
87
|
+
|
|
88
|
+
`carto init` writes this for you when Windsurf is detected. Manual: `~/.codeium/windsurf/mcp_config.json`
|
|
89
|
+
```json
|
|
90
|
+
{ "mcpServers": { "carto": { "command": "carto", "args": ["serve"], "cwd": "/your/project" } } }
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Zed / JetBrains / VS Code (full agent mode)
|
|
94
|
+
|
|
95
|
+
Carto also runs as a full **ACP agent** — not just a passive MCP server, but an active coding agent with architectural awareness. See [ACP Agent](#acp-agent-zed--jetbrains--vs-code) below.
|
|
96
|
+
|
|
67
97
|
---
|
|
68
98
|
|
|
69
|
-
##
|
|
99
|
+
## What changes for your AI
|
|
70
100
|
|
|
71
|
-
|
|
101
|
+
Without carto, your AI greps text and guesses. With carto, it queries structural facts:
|
|
72
102
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
103
|
+
> *"Add rate limiting to /api/users"*
|
|
104
|
+
|
|
105
|
+
**Without carto:** AI grep-searches your codebase, finds 12 files mentioning "users", picks 3 at random, hopes for the best.
|
|
106
|
+
|
|
107
|
+
**With carto:** AI calls `get_change_plan("add rate limiting to /api/users")` and gets back:
|
|
108
|
+
- The exact route handler file
|
|
109
|
+
- All 7 files that import it (blast radius)
|
|
110
|
+
- The AUTH domain it lives in
|
|
111
|
+
- Similar middleware patterns already in the codebase
|
|
112
|
+
- Cross-domain dependencies that could break
|
|
113
|
+
|
|
114
|
+
Your AI's response goes from "here's a guess" to "here's the right change with the architectural context."
|
|
115
|
+
|
|
116
|
+
### Catching bad changes before save
|
|
117
|
+
|
|
118
|
+
The harder problem isn't finding the right file — it's stopping a confident-sounding AI from writing a refactor that breaks the rest of the repo. Carto exposes a diff-shaped query the AI can call before the user accepts a change:
|
|
119
|
+
|
|
120
|
+
> *AI proposes a 12-line patch to `packages/pg-meta/src/pg-format/index.ts`. Before showing the diff, it calls `validate_diff(patch)`.*
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
# Diff Validation
|
|
124
|
+
|
|
125
|
+
**Risk:** 🔴 HIGH
|
|
126
|
+
**Files changed:** 1
|
|
127
|
+
**Union blast radius:** 83 transitive dependents
|
|
128
|
+
|
|
129
|
+
## Violations (1)
|
|
130
|
+
|
|
131
|
+
| Severity | Kind | File | Detail |
|
|
132
|
+
|----------|-------------|----------------------------------------|-----------------------------------------------------------------|
|
|
133
|
+
| HIGH | high_blast | `packages/pg-meta/src/pg-format/index.ts` | Modifying this file affects 83 transitive dependents (>50). |
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The AI sees this *before* it proposes the diff. It revises its plan, splits the change, or asks the user. The bad refactor never makes it to the screen. Sub-millisecond on a 7,000-file repo — see the **Benchmarks** section below.
|
|
137
|
+
|
|
138
|
+
Every `validate_diff` call is also written to a local SQLite log, so a session that runs five hours later can ask `did_we_discuss_this("snake_case naming")` and get back the prior decision. The AI stops re-deciding settled questions.
|
|
139
|
+
|
|
140
|
+
### In the wild
|
|
141
|
+
|
|
142
|
+

|
|
143
|
+
|
|
144
|
+
*Claude Code analyzing the [supabase](https://github.com/supabase/supabase) repo via carto. Real session, no editing — 5,974 files indexed in ~780ms, 86 routes, 4,839 import edges, 7 domains. The agent's own verdict at the bottom: "useful, especially for a large codebase like supabase. The blast radius + cross-domain tools are the most valuable."*
|
|
91
145
|
|
|
92
146
|
---
|
|
93
147
|
|
|
94
148
|
## Languages and frameworks
|
|
95
149
|
|
|
96
|
-
### Import graph + symbols (
|
|
150
|
+
### Import graph + symbols (any repo)
|
|
97
151
|
|
|
98
152
|
| Language | Extensions |
|
|
99
153
|
|----------|-----------|
|
|
@@ -106,16 +160,16 @@ File saved → debounce 50ms → re-parse 1 file → SQLite write → <50ms
|
|
|
106
160
|
| C# | `.cs` |
|
|
107
161
|
| Ruby | `.rb` |
|
|
108
162
|
|
|
109
|
-
### Route extraction (framework-
|
|
163
|
+
### Route extraction (framework-aware)
|
|
110
164
|
|
|
111
165
|
| Framework | Language |
|
|
112
166
|
|-----------|---------|
|
|
113
|
-
| Express, Next.js (App + Pages
|
|
167
|
+
| Express, Next.js (App + Pages), tRPC, React Router | TypeScript / JavaScript |
|
|
114
168
|
| FastAPI, Flask, Django | Python |
|
|
115
169
|
| Gin, Echo, Chi, net/http | Go |
|
|
116
170
|
| Actix-web, Axum, Rocket | Rust |
|
|
117
171
|
| Spring MVC / Boot, JAX-RS | Java |
|
|
118
|
-
| ASP.NET Core
|
|
172
|
+
| ASP.NET Core | C# |
|
|
119
173
|
| Rails, Sinatra | Ruby |
|
|
120
174
|
|
|
121
175
|
### Model extraction
|
|
@@ -127,52 +181,18 @@ File saved → debounce 50ms → re-parse 1 file → SQLite write → <50ms
|
|
|
127
181
|
| Go structs | Go |
|
|
128
182
|
| Rust structs | Rust |
|
|
129
183
|
| JPA `@Entity`, Java records | Java |
|
|
130
|
-
| EF Core
|
|
184
|
+
| EF Core, C# records | C# |
|
|
131
185
|
| ActiveRecord | Ruby |
|
|
132
186
|
|
|
133
187
|
### TypeScript path aliases
|
|
134
188
|
|
|
135
|
-
Reads `tsconfig.json` / `jsconfig.json`
|
|
136
|
-
|
|
137
|
-
---
|
|
138
|
-
|
|
139
|
-
## Domain detection
|
|
140
|
-
|
|
141
|
-
Carto uses **Leiden+CPM graph clustering** — files that import each other heavily cluster together. Domain names are inferred from path tokens, with keyword hints for well-known patterns (AUTH, PAYMENTS, DATABASE, etc.).
|
|
142
|
-
|
|
143
|
-
Works on any repo — not just SaaS apps. vscode gets AUTH/EVENTS/DATABASE. zed (Rust) gets DATABASE/AUTH/EVENTS. A game engine would get RENDERER/PHYSICS/AUDIO.
|
|
144
|
-
|
|
145
|
-
Custom domains via `carto.config.json`:
|
|
146
|
-
|
|
147
|
-
```json
|
|
148
|
-
{
|
|
149
|
-
"domains": {
|
|
150
|
-
"EDITOR": ["editor", "monaco", "text"],
|
|
151
|
-
"WORKBENCH": ["workbench", "layout", "panel"]
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
---
|
|
157
|
-
|
|
158
|
-
## Commands
|
|
159
|
-
|
|
160
|
-
| Command | What it does |
|
|
161
|
-
|---------|-------------|
|
|
162
|
-
| `carto init` | Detect project, index codebase, generate AGENTS.md, wire MCP |
|
|
163
|
-
| `carto sync` | Full re-index (skips unchanged files via mtime+size cache) |
|
|
164
|
-
| `carto watch` | Incremental live re-index on every file save (<50ms) |
|
|
165
|
-
| `carto serve` | Start MCP server for Kiro / Cursor / Claude |
|
|
166
|
-
| `carto agent` | Start ACP agent mode (for Zed / JetBrains / VS Code) |
|
|
167
|
-
| `carto impact <file>` | Blast radius: risk level, affected files, routes at risk |
|
|
168
|
-
| `carto check` | Cross-domain violations, high-risk uncommitted changes, domain health |
|
|
169
|
-
| `carto remove` | Remove AGENTS.md and .carto/ from project |
|
|
189
|
+
Reads `tsconfig.json` / `jsconfig.json` `paths` config. `@/components/Button` resolves to the actual file in the import graph — blast radius works correctly for Next.js and Vite projects.
|
|
170
190
|
|
|
171
191
|
---
|
|
172
192
|
|
|
173
193
|
## ACP Agent (Zed / JetBrains / VS Code)
|
|
174
194
|
|
|
175
|
-
Carto
|
|
195
|
+
Beyond MCP, Carto runs as a full **ACP agent** — an active coding agent with built-in architectural awareness.
|
|
176
196
|
|
|
177
197
|
```
|
|
178
198
|
User: "Add rate limiting to /api/users"
|
|
@@ -189,20 +209,16 @@ Sends to LLM (your API key) → streams answer + diffs back to editor
|
|
|
189
209
|
|
|
190
210
|
### Setup in Zed
|
|
191
211
|
|
|
192
|
-
|
|
193
|
-
|
|
212
|
+
`~/.config/zed/settings.json`:
|
|
194
213
|
```json
|
|
195
214
|
{
|
|
196
215
|
"agent_servers": {
|
|
197
|
-
"Carto": {
|
|
198
|
-
"command": "carto",
|
|
199
|
-
"args": ["agent"]
|
|
200
|
-
}
|
|
216
|
+
"Carto": { "command": "carto", "args": ["agent"] }
|
|
201
217
|
}
|
|
202
218
|
}
|
|
203
219
|
```
|
|
204
220
|
|
|
205
|
-
###
|
|
221
|
+
### Bring Your Own Key
|
|
206
222
|
|
|
207
223
|
Carto supports any LLM provider — configure in your editor:
|
|
208
224
|
|
|
@@ -217,53 +233,204 @@ Carto supports any LLM provider — configure in your editor:
|
|
|
217
233
|
| Together AI | Open-source models |
|
|
218
234
|
| Azure OpenAI | Enterprise deployments |
|
|
219
235
|
|
|
220
|
-
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## The 22 MCP tools
|
|
221
239
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
-
|
|
240
|
+
Once carto is wired in, your AI tool can call any of these mid-task. You don't need to memorize them — your AI will pick the right ones.
|
|
241
|
+
|
|
242
|
+
| Tool | What it returns |
|
|
243
|
+
|------|----------------|
|
|
244
|
+
| `get_architecture()` | 500-word project overview: domains, entry points, stack, key patterns. **Use this first.** |
|
|
245
|
+
| `get_change_plan(intent)` | Given "add rate limiting to /api/users" → files to touch, domains affected, blast radius, similar patterns |
|
|
246
|
+
| `get_blast_radius(file)` | All files affected by changing a given file, with hop distance |
|
|
247
|
+
| `simulate_change_impact(files)` | Union of all files transitively affected by changing **multiple** files at once. Powered by the bitmap engine — sub-millisecond on 7K-file repos. |
|
|
248
|
+
| `validate_diff(diff)` | Given a unified diff: violations (cross-domain imports, high-blast files), blast radius per file, risk level (SAFE/LOW/MEDIUM/HIGH), suggestions. Each call is recorded in the **episodic memory** log so other tools can ask "did we discuss this?". Sub-15ms p99. |
|
|
249
|
+
| `get_context(file)` | Everything about a file: domain, blast radius, neighbors, routes, models |
|
|
250
|
+
| `get_file_summary(file)` | What a file does, its role, key deps and dependents |
|
|
251
|
+
| `get_similar_patterns(file)` | Files with same domain, route shape, or shared deps — find conventions before writing new code |
|
|
252
|
+
| `get_routes()` | All API endpoints with file mapping |
|
|
253
|
+
| `get_structure()` | Import graph, entry points, high-impact files, tech stack |
|
|
254
|
+
| `get_domain(name)` | All routes, models, functions for a domain. Lazily regenerated when stale. |
|
|
255
|
+
| `get_neighbors(file, hops)` | Import graph neighbors: nodes and edges |
|
|
256
|
+
| `get_cross_domain()` | Import edges that cross domain boundaries |
|
|
257
|
+
| `search_routes(query)` | Search API routes by path or method |
|
|
258
|
+
| `get_models(domain?)` | All data models, optionally filtered by domain |
|
|
259
|
+
| `get_high_impact_files(n)` | Top N files by blast radius |
|
|
260
|
+
| `get_env_vars(domain?)` | All env vars with domain mapping |
|
|
261
|
+
| `get_domains_list()` | All detected domains with file, route, model counts |
|
|
262
|
+
| `get_recent_decisions(time_range, kind?)` | Recent validation decisions and architectural choices the AI has made in this project |
|
|
263
|
+
| `get_session_context(session_id?)` | Full context for an AI session: every decision and intervention, ordered chronologically |
|
|
264
|
+
| `did_we_discuss_this(topic)` | Substring search over the episodic memory log — avoid re-deciding settled questions |
|
|
265
|
+
| `get_intervention_history(file?)` | Past Carto-issued violations and suggestions, optionally filtered by file |
|
|
266
|
+
|
|
267
|
+
**All MCP queries:** **<5ms** on every benchmarked repo.
|
|
268
|
+
|
|
269
|
+
## Episodic Memory
|
|
270
|
+
|
|
271
|
+
Carto remembers every diff it validates. The `validate_diff` tool writes one row per call into a local SQLite log (`ai_sessions`/`decisions`/`interventions` tables) — so a session that runs five hours later can still ask `did_we_discuss_this("snake_case naming")` and get back the prior decision. The log lives next to the index in `.carto/carto.db` — never sent over the network, never shared between projects.
|
|
227
272
|
|
|
228
273
|
---
|
|
229
274
|
|
|
230
|
-
##
|
|
275
|
+
## Domain detection
|
|
231
276
|
|
|
232
|
-
|
|
277
|
+
Carto uses **Leiden+CPM graph clustering** — files that import each other heavily cluster together. Domain names are inferred from path tokens, with keyword hints for well-known patterns (AUTH, PAYMENTS, DATABASE, etc.).
|
|
233
278
|
|
|
234
|
-
**
|
|
235
|
-
```json
|
|
236
|
-
{ "mcpServers": { "carto": { "command": "carto", "args": ["serve"], "cwd": "/your/project" } } }
|
|
237
|
-
```
|
|
279
|
+
**Adaptive strategy:** Repos under 100 files use keyword-only clustering (avoids over-fragmentation). Larger repos with dense import graphs get graph-based clustering with a gamma that scales continuously with repo size.
|
|
238
280
|
|
|
239
|
-
|
|
240
|
-
```json
|
|
241
|
-
{ "mcpServers": { "carto": { "command": "carto", "args": ["serve"], "cwd": "/your/project" } } }
|
|
242
|
-
```
|
|
281
|
+
Works on any repo — not just SaaS apps. vscode gets AUTH/EVENTS/DATABASE. zed (Rust) gets DATABASE/AUTH/EVENTS. A game engine would get RENDERER/PHYSICS/AUDIO.
|
|
243
282
|
|
|
244
|
-
|
|
283
|
+
Custom domains via `carto.config.json`:
|
|
245
284
|
```json
|
|
246
|
-
{
|
|
285
|
+
{
|
|
286
|
+
"domains": {
|
|
287
|
+
"EDITOR": ["editor", "monaco", "text"],
|
|
288
|
+
"WORKBENCH": ["workbench", "layout", "panel"]
|
|
289
|
+
}
|
|
290
|
+
}
|
|
247
291
|
```
|
|
248
292
|
|
|
249
|
-
|
|
293
|
+
Full schema with anchor pinning (forces files into a domain regardless of clustering):
|
|
250
294
|
```json
|
|
251
|
-
{
|
|
295
|
+
{
|
|
296
|
+
"domains": {
|
|
297
|
+
"AUTH": {
|
|
298
|
+
"keywords": ["auth", "login", "session"],
|
|
299
|
+
"anchor": ["src/auth/session.ts", "src/auth/middleware.ts"]
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
252
303
|
```
|
|
253
304
|
|
|
254
|
-
**
|
|
255
|
-
|
|
256
|
-
|
|
305
|
+
**Stability tracking:** Carto tracks domain assignments across syncs. If >5% of files change domain, `carto check` flags it as unstable. Two consecutive syncs with no code changes always produce 0% drift.
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## CLI commands
|
|
310
|
+
|
|
311
|
+
| Command | What it does |
|
|
312
|
+
|---------|-------------|
|
|
313
|
+
| `carto init` | Detect project, index codebase, generate AGENTS.md, install git hooks (pre-commit, post-checkout, post-merge, post-rewrite), auto-wire MCP into every AI tool found |
|
|
314
|
+
| `carto sync` | Full re-index (skips unchanged files via mtime+size cache). Called automatically by git hooks on commit/checkout/merge/rebase. |
|
|
315
|
+
| `carto serve` | Start MCP server (called by your AI tool — usually you don't run this directly). On every file-aware query the server mtime+size-checks the file and re-parses inline if stale. |
|
|
316
|
+
| `carto watch` | **Optional.** Live re-index on every file save. Not required — git hooks + lazy MCP re-parse keep the index fresh by default. Use only for AI-heavy workflows that write 50+ files between commits. |
|
|
317
|
+
| `carto agent` | Start ACP agent mode (for Zed / JetBrains / VS Code) |
|
|
318
|
+
| `carto impact <file>` | Blast radius: risk level, affected files, routes at risk |
|
|
319
|
+
| `carto check` | Cross-domain violations, high-risk uncommitted changes, domain health |
|
|
320
|
+
| `carto inspect` | Read-only diagnostic: index paths, sizes, freshness, bitmap sidecar shape, top-impact files, schema version, sync timestamps. `--json` for piping into `jq`. Never triggers a rebuild. |
|
|
321
|
+
| `carto remove` | Remove AGENTS.md and .carto/ from project |
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Benchmarks
|
|
326
|
+
|
|
327
|
+
Measured on real open-source repos. Apple M-series, 8 CPUs, 8GB RAM. SHAs pinned in `~/carto-test-repos`. Reproducible via `~/carto-test-repos/run-bench.sh`.
|
|
328
|
+
|
|
329
|
+
### Indexing speed
|
|
330
|
+
|
|
331
|
+
| Repo | Language | Indexed Files | First Run | Second Run | DB Size | Import Edges |
|
|
332
|
+
|------|----------|---------------|-----------|------------|---------|--------------|
|
|
333
|
+
| [prisma/prisma](https://github.com/prisma/prisma) | TypeScript | 961 | **961ms** | **431ms** | 0.7 MB | 1,387 |
|
|
334
|
+
| [supabase/supabase](https://github.com/supabase/supabase) | TypeScript | 6,330 | **5.4s** | **1.2s** | 4.0 MB | 5,321 |
|
|
335
|
+
| [microsoft/vscode](https://github.com/microsoft/vscode) | TypeScript | 7,567 | **7.7s** | **1.0s** | 6.7 MB | 13,420 |
|
|
336
|
+
| [zed-industries/zed](https://github.com/zed-industries/zed) | Rust | 1,752 | **3.0s** | **491ms** | 4.4 MB | 2,113 |
|
|
337
|
+
|
|
338
|
+
**Indexed Files** counts what Carto actually parses — `.ts/.js/.py/.go/.rs/...` after excluding `node_modules`, build output, and per-file `*.test.*` / `*.spec.*` / `*.stories.*` / `test_*.py` patterns. The on-disk file count of the repo is larger.
|
|
339
|
+
|
|
340
|
+
**Second Run** = `carto sync` after no changes. mtime+size checked before reading content — if nothing changed, nothing is re-parsed.
|
|
341
|
+
|
|
342
|
+
### MCP query latency (bitmap engine vs SQLite)
|
|
343
|
+
|
|
344
|
+
Carto's MCP query path is bitmap-backed on five tools, plus a sixth (`simulate_change_impact`) that's only feasible with bitmap OR-aggregation. Speedups measured against the same data, same DB, same machine — bitmap path vs the equivalent SQLite path.
|
|
345
|
+
|
|
346
|
+
| Tool | vscode (7,567 files) | Speedup vs SQLite |
|
|
347
|
+
|------|---------------------:|------------------:|
|
|
348
|
+
| `get_blast_radius` | sub-ms | **10.7×** |
|
|
349
|
+
| `get_cross_domain` | 2.1ms | **6.2×** |
|
|
350
|
+
| `get_high_impact_files` | sub-ms | **559×** |
|
|
351
|
+
| `get_similar_patterns` | sub-ms | **73×** |
|
|
352
|
+
| `simulate_change_impact` | sub-ms | **6.5×** (no SQLite equivalent at this latency) |
|
|
353
|
+
|
|
354
|
+
Median speedup across all five tools on vscode: **10.7×**. Smaller repos hit higher peaks — laravel-framework `get_high_impact_files` clocks at 155× on the smaller graph. Reproducible via `npm run bench:bitmap -- --repo <path>`.
|
|
355
|
+
|
|
356
|
+
### `validate_diff` latency
|
|
357
|
+
|
|
358
|
+
The new diff-shaped query that lets the AI ask "is this patch safe?" before showing it to the user. Profiled with a representative 20-line diff against 20 random mid-blast-radius files per repo, 1000 calls each.
|
|
359
|
+
|
|
360
|
+
| Repo | Files | p50 | p99 |
|
|
361
|
+
|------|-------|----:|----:|
|
|
362
|
+
| supabase | 6,259 | **0.082ms** | **0.298ms** |
|
|
363
|
+
| vscode | 7,567 | **0.084ms** | **0.489ms** |
|
|
364
|
+
|
|
365
|
+
Budget was p50 ≤ 5ms, p99 ≤ 15ms. Both targets are cleared by 30-60×. The bitmap engine handles every blast-radius and cross-domain query in microseconds; what's left is diff parsing + result aggregation. Reproducible via `node bench/validation-perf/index.js --repo <path>`.
|
|
366
|
+
|
|
367
|
+
### Domains detected
|
|
368
|
+
|
|
369
|
+
| Repo | Domains |
|
|
370
|
+
|------|---------|
|
|
371
|
+
| prisma | CORE · DATABASE · AUTH · EVENTS |
|
|
372
|
+
| supabase | CORE · AUTH · DATABASE · PAYMENTS · NOTIFICATIONS · EVENTS · TRPC |
|
|
373
|
+
| vscode | EXTENSIONS · AUTH · EVENTS · DATABASE · EXTENSION · CLI · CORE |
|
|
374
|
+
| zed (Rust) | CORE · DATABASE · AUTH · EVENTS · PAYMENTS · TRPC · NOTIFICATIONS |
|
|
375
|
+
|
|
376
|
+
vscode at 7,567 indexed files in under 8 seconds. Rust import graph working on zed (2,113 edges from `mod` declarations and `use crate::` paths).
|
|
377
|
+
|
|
378
|
+
### Accuracy
|
|
379
|
+
|
|
380
|
+
12 corpus repos pass `node test/accuracy-corpus.js --samples 100` — full parity between the bitmap path and the SQLite path on `blastRadius`, `crossDomain`, `highImpactFiles`, and `simulateChangeImpact`. The bitmap layer is a speedup, never a behavior change.
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
384
|
+
## How it works
|
|
385
|
+
|
|
386
|
+
```
|
|
387
|
+
carto init
|
|
388
|
+
↓
|
|
389
|
+
Discovers all files (no cap — SQLite handles the volume)
|
|
390
|
+
mtime+size check → skip unchanged files
|
|
391
|
+
tree-sitter parse → imports + symbols (0.05–0.2ms/file)
|
|
392
|
+
Babel deep parse → routes + models (API handler files only)
|
|
393
|
+
Leiden+CPM graph clustering → auto-detects domains
|
|
394
|
+
Computes reverse deps → blast radius for every file
|
|
395
|
+
Writes AGENTS.md + .carto/context/*.md (lazy, on-demand)
|
|
396
|
+
Auto-wires MCP into every AI tool found
|
|
397
|
+
Installs 4 git hooks: pre-commit, post-checkout, post-merge, post-rewrite
|
|
398
|
+
↓
|
|
399
|
+
[no daemon, no watcher, no background process]
|
|
400
|
+
↓
|
|
401
|
+
─── Freshness mechanism 1: git hooks (90% of cases) ───
|
|
402
|
+
You commit / pull / switch branches / rebase
|
|
403
|
+
→ hook runs `carto sync` quietly in <1s
|
|
404
|
+
→ only changed files re-parsed
|
|
405
|
+
↓
|
|
406
|
+
─── Freshness mechanism 2: lazy mtime check (the gap) ───
|
|
407
|
+
You edit files between commits, AI asks "blast radius of db.ts?"
|
|
408
|
+
→ MCP server stats db.ts (mtime+size vs DB row)
|
|
409
|
+
→ if stale, re-parses just that file inline (<50ms)
|
|
410
|
+
→ returns fresh answer
|
|
411
|
+
↓
|
|
412
|
+
─── Optional: carto watch (AI-heavy workflows only) ───
|
|
413
|
+
File saved → debounce 50ms → re-parse 1 file → SQLite write → <50ms
|
|
257
414
|
```
|
|
258
415
|
|
|
259
416
|
---
|
|
260
417
|
|
|
261
418
|
## What Carto never does
|
|
262
419
|
|
|
263
|
-
- Sends your code anywhere
|
|
264
|
-
- Writes secrets into AGENTS.md
|
|
265
|
-
- Touches your manual notes
|
|
266
|
-
- Costs money
|
|
420
|
+
- **Sends your code anywhere.** Local only. SQLite on disk.
|
|
421
|
+
- **Writes secrets into AGENTS.md.** `.cartoignore` blocks `.env` and credential files by default.
|
|
422
|
+
- **Touches your manual notes.** Writes only between `<!-- CARTO:AUTO:START -->` and `<!-- CARTO:AUTO:END -->`.
|
|
423
|
+
- **Costs money.** MIT license. Free forever.
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
## Origin
|
|
428
|
+
|
|
429
|
+
I was building [Emfirge](https://www.emfirge.cloud) — a cloud security agent that maps AWS infrastructure into a graph and simulates the blast radius of every change.
|
|
430
|
+
|
|
431
|
+
To make the AI inside Emfirge understand infrastructure, I wrote a module called `cartography.py`. It mapped AWS resources, built a graph of how they connected, and wrote it into a structured map. The AI stopped hallucinating. It worked with facts, not guesses.
|
|
432
|
+
|
|
433
|
+
Carto is the same idea, applied to source code. Same insight: AI agents stop guessing once they can query the architecture.
|
|
267
434
|
|
|
268
435
|
---
|
|
269
436
|
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "carto-md",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.8",
|
|
4
4
|
"description": "Structural intelligence layer for AI coding tools. Indexes your codebase into SQLite — routes, models, import graph, blast radius, domains — and exposes 16 MCP tools for Kiro, Cursor, and Claude.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"carto": "src/cli/index.js"
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
"test": "node test/test.js",
|
|
11
11
|
"test:correctness": "node test/correctness.js",
|
|
12
12
|
"test:benchmark": "node test/benchmark.js",
|
|
13
|
-
"test:bench-ci": "node test/bench-ci.js"
|
|
13
|
+
"test:bench-ci": "node test/bench-ci.js",
|
|
14
|
+
"postinstall": "node scripts/postinstall.js",
|
|
15
|
+
"bench:bitmap": "node bench/bitmap-validation/index.js"
|
|
14
16
|
},
|
|
15
17
|
"dependencies": {
|
|
16
18
|
"@agentclientprotocol/sdk": "^0.22.1",
|
|
@@ -18,7 +20,9 @@
|
|
|
18
20
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
19
21
|
"better-sqlite3": "11.7.0",
|
|
20
22
|
"chokidar": "^3.6.0",
|
|
21
|
-
"tree-sitter": "0.25.0"
|
|
23
|
+
"tree-sitter": "0.25.0"
|
|
24
|
+
},
|
|
25
|
+
"optionalDependencies": {
|
|
22
26
|
"tree-sitter-c-sharp": "0.21.3",
|
|
23
27
|
"tree-sitter-cpp": "0.23.4",
|
|
24
28
|
"tree-sitter-go": "0.25.0",
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// Postinstall script — checks tree-sitter grammar availability and prints
|
|
5
|
+
// actionable guidance if any failed to install. Always exits 0.
|
|
6
|
+
|
|
7
|
+
if (process.env.CARTO_NO_POSTINSTALL === '1') process.exit(0);
|
|
8
|
+
|
|
9
|
+
const GRAMMARS = [
|
|
10
|
+
{ pkg: 'tree-sitter-javascript', langs: 'JavaScript' },
|
|
11
|
+
{ pkg: 'tree-sitter-typescript', langs: 'TypeScript' },
|
|
12
|
+
{ pkg: 'tree-sitter-python', langs: 'Python' },
|
|
13
|
+
{ pkg: 'tree-sitter-go', langs: 'Go' },
|
|
14
|
+
{ pkg: 'tree-sitter-rust', langs: 'Rust' },
|
|
15
|
+
{ pkg: 'tree-sitter-java', langs: 'Java' },
|
|
16
|
+
{ pkg: 'tree-sitter-cpp', langs: 'C/C++' },
|
|
17
|
+
{ pkg: 'tree-sitter-c-sharp', langs: 'C#' },
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const failed = [];
|
|
21
|
+
for (const g of GRAMMARS) {
|
|
22
|
+
try { require(g.pkg); } catch { failed.push(g); }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (failed.length === 0) process.exit(0);
|
|
26
|
+
|
|
27
|
+
// Print guidance
|
|
28
|
+
const langs = failed.map(g => g.langs).join(', ');
|
|
29
|
+
const isWin = process.platform === 'win32';
|
|
30
|
+
const isMac = process.platform === 'darwin';
|
|
31
|
+
|
|
32
|
+
let fix;
|
|
33
|
+
if (isWin) {
|
|
34
|
+
fix = 'Install "Desktop development with C++" from https://aka.ms/vs/17/release/vs_BuildTools.exe then re-run: npm rebuild';
|
|
35
|
+
} else if (isMac) {
|
|
36
|
+
fix = 'Run: xcode-select --install && npm rebuild';
|
|
37
|
+
} else {
|
|
38
|
+
fix = 'Run: sudo apt-get install -y build-essential && npm rebuild (or equivalent for your distro)';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.log('');
|
|
42
|
+
console.log('[CARTO] ⚠️ Some tree-sitter grammars failed to install.');
|
|
43
|
+
console.log(`[CARTO] Affected languages: ${langs}`);
|
|
44
|
+
console.log('[CARTO] These languages will use regex-only extraction (less accurate).');
|
|
45
|
+
console.log(`[CARTO] To fix: ${fix}`);
|
|
46
|
+
console.log('');
|
package/src/acp/agent.js
CHANGED
|
@@ -70,19 +70,12 @@ class CartoAgent {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
// Provider methods
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return {};
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async unstable_disableProvider(_params) {
|
|
83
|
-
this.providers.disable();
|
|
84
|
-
return {};
|
|
85
|
-
}
|
|
73
|
+
// The custom `unstable_*` provider-management methods (list / set / disable)
|
|
74
|
+
// were removed. The ACP SDK only dispatches the methods declared in
|
|
75
|
+
// its AGENT_METHODS constant, so those custom routes returned -32601
|
|
76
|
+
// "Method not found" anyway. Provider config flows through env vars /
|
|
77
|
+
// editor settings; ProviderRegistry remains as the internal
|
|
78
|
+
// configuration carrier.
|
|
86
79
|
|
|
87
80
|
// Session list/load stubs
|
|
88
81
|
async listSessions(_params) { return { sessions: [] }; }
|