carto-md 1.1.2 → 1.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/CONTRIBUTING.md CHANGED
@@ -10,23 +10,32 @@ Carto is free, open source, and community-maintained. The core team owns the mer
10
10
 
11
11
  New language support lives in `src/extractors/languages/`. Each language is an isolated module.
12
12
 
13
- Currently supported: JavaScript/TypeScript, Python, R.
13
+ Currently supported: JavaScript/TypeScript, Python, Go, R.
14
14
 
15
- Wanted: Go, Rust, Ruby, Java, PHP, C#.
15
+ Wanted: Rust, Ruby, Java, PHP, C#, Swift, Kotlin.
16
16
 
17
17
  ### Tier 2 — Framework extractors (safe to add, easy to review)
18
18
 
19
19
  Framework-specific route and model extraction lives in `src/extractors/`. Each framework is an isolated module.
20
20
 
21
- Currently supported: FastAPI, Express, Next.js App Router, Prisma, tRPC, HTML fetch(), Plumber, Shiny.
21
+ Currently supported:
22
+ - **JS/TS**: Express, Next.js (App + Pages Router), tRPC, Drizzle, Zod
23
+ - **Python**: FastAPI, Pydantic, SQLAlchemy, Django (models + URLs)
24
+ - **Go**: Gin, Echo, Chi, net/http
25
+ - **Schema**: Prisma
26
+ - **Frontend**: HTML fetch()
27
+ - **R**: Plumber, Shiny, R6, S7
22
28
 
23
- Wanted: Django, Rails, Laravel, NestJS, Hono, Gin, Spring.
29
+ Wanted: Rails, Laravel, NestJS, Hono, Spring, Flask, Fastify.
24
30
 
25
31
  ### Tier 3 — Core (review carefully before merging)
26
32
 
27
33
  - `src/agents/merger.js` — merger logic. One bad merge = developer loses manual notes = project dies.
28
34
  - `src/agents/domains.js` — graph-based domain clustering. Wrong clusters = wrong context files.
35
+ - `src/engine/carto.js` — programmatic module API. Breaking changes affect tools that import Carto.
29
36
  - `src/mcp/server.js` — MCP server tools. Breaking changes affect Kiro/Cursor/Claude integration.
37
+ - `src/engine/incremental.js` — incremental graph update engine. Bugs here cause stale graphs.
38
+ - `src/cache/` — file hash + graph cache. Bugs here cause wrong re-index behavior.
30
39
  - `src/detector/` — framework detection logic.
31
40
  - `src/cli/` — CLI commands.
32
41
 
@@ -36,23 +45,27 @@ Wanted: Django, Rails, Laravel, NestJS, Hono, Gin, Spring.
36
45
 
37
46
  1. Create `src/extractors/languages/yourlanguage.js`
38
47
  2. Export a plugin object:
48
+
39
49
  ```js
40
50
  module.exports = {
41
51
  name: 'yourlanguage',
42
52
  extensions: ['.ext'],
43
53
  extract(content, relPath) {
44
54
  return {
45
- routes: [{ method, path, functionName }],
46
- models: [{ className, fields: [{ name, type }] }],
47
- functions: [{ name, params, returnType }],
48
- envVars: ['VAR_NAME'],
49
- dbTables: [{ tableName, modelName }],
50
- fetches: [],
51
- storageKeys: []
55
+ routes: [{ method, path, functionName }],
56
+ models: [{ className, fields: [{ name, type }], kind: 'yourlanguage' }],
57
+ functions: [{ name, params, returnType }],
58
+ envVars: ['VAR_NAME'],
59
+ dbTables: [{ tableName, modelName }],
60
+ fetches: [],
61
+ storageKeys: [],
62
+ events: [{ type: 'listener'|'emitter', event: 'event.name' }],
63
+ jobs: [{ type: 'cron'|'queue'|'interval', expression?: '* * * * *', name?: 'job-name' }],
52
64
  };
53
65
  }
54
66
  };
55
67
  ```
68
+
56
69
  3. The loader auto-discovers it — no changes to `loader.js` needed
57
70
  4. Test on at least 3 real open-source projects
58
71
  5. Open a PR with before/after AGENTS.md examples
@@ -96,7 +109,7 @@ cd carto
96
109
  npm install
97
110
  node src/cli/index.js init # test in any project
98
111
  node src/cli/index.js serve # test MCP server
99
- npm test # run test suite
112
+ npm test # run test suite (30 tests)
100
113
  ```
101
114
 
102
115
  ---
@@ -105,6 +118,7 @@ npm test # run test suite
105
118
 
106
119
  - [ ] Tested on at least 2-3 real open-source projects
107
120
  - [ ] Before/after AGENTS.md included in PR description
121
+ - [ ] Plugin returns all fields including `events` and `jobs` (can be empty arrays)
108
122
  - [ ] No changes to merger logic (unless explicitly fixing a merger bug)
109
123
  - [ ] No network calls added
110
124
  - [ ] `carto --version` still works
package/README.md CHANGED
@@ -4,293 +4,332 @@
4
4
  [![MIT License](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
5
5
  [![npm downloads](https://img.shields.io/npm/dm/carto-md)](https://www.npmjs.com/package/carto-md)
6
6
 
7
- **The codebase intelligence layer every AI tool queries instead of guessing.**
7
+ **The structural intelligence layer for AI coding tools.**
8
8
 
9
9
  ```bash
10
10
  npm install -g carto-md
11
11
  ```
12
12
 
13
- Carto maps your codebase routes, models, import graph, domain context and exposes it as a live MCP server that Kiro, Cursor, and Claude can query mid-task. No hallucinations about your own project. No rebuilding context every session.
13
+ Carto indexes your codebase (routes, models, import graph, blast radius, domain clusters) and keeps it live. Every AI tool you use gets accurate structural facts about your project instead of guessing.
14
14
 
15
15
  ---
16
16
 
17
- ## The problem
17
+ ## What it does in one sentence
18
18
 
19
- AI coding tools are blind to your actual project. Every session starts from zero.
19
+ **Your file changes. Carto re-indexes in ~130ms. Every AI tool instantly knows what broke.**
20
20
 
21
- - Claude hallucinates your schema
22
- - Copilot suggests wrong field names
23
- - Kiro asks what framework you're using
24
- - You rebuild context manually, every time
21
+ ---
22
+
23
+ ## Proof: Supabase repo (6,335 files)
25
24
 
26
- `AGENTS.md` fixes this a standard file every AI tool reads. But it's static. You write it manually. It gets stale the moment your code changes.
25
+ Fresh `carto init`. No prior knowledge of the repo.
27
26
 
28
- **Carto makes it live. And queryable.**
27
+ ```
28
+ Detected: nextjs (javascript)
29
+ Indexed: 310 files in 422ms
30
+ Routes: 104 API endpoints
31
+ Models: 3,598 extracted
32
+ Domains: AUTH · DATABASE · PAYMENTS · EVENTS · NOTIFICATIONS · TRPC · CORE
33
+ Import edges: 5,248
34
+ ```
35
+
36
+ Then a file changes:
37
+
38
+ ```
39
+ packages/ui/src/lib/utils/cn.ts updated
29
40
 
30
- | | Without Carto | With Carto |
31
- |---|---|---|
32
- | Knows blast radius before editing | Never | Always, instantly |
33
- | Knows which routes break | Never | Exact list |
34
- | Plans multi-file changes | Guesses | Fully informed |
35
- | Hallucinates field names | Often | Never |
36
- | Understands codebase on session start | 10–20 min | 0 |
37
- | Works across Kiro, Cursor, Claude, Copilot | Separately | One shared graph |
38
- | Stays current as code changes | Goes stale | Live on every save |
41
+ Re-indexed in 129ms.
42
+
43
+ Risk: 🔴 HIGH
44
+ Directly affected: 55 files
45
+ Potentially affected: 83 files total
46
+
47
+ Files that depend on this:
48
+ packages/ui/src/components/Button/Button.tsx
49
+ packages/ui/src/components/Modal/Modal.tsx
50
+ → packages/ui/src/components/shadcn/ui/button.tsx
51
+ → ...83 more
52
+ ```
53
+
54
+ One file changed. Carto told you exactly what broke. In 129ms. On a 6,335 file monorepo.
39
55
 
40
56
  ---
41
57
 
42
- ## Proof cal.com (800k lines)
58
+ ## Proof: cal.com (800k lines)
43
59
 
44
60
  Same task, two Claude sessions: *"Add a `notes` field to the booking model."*
45
61
 
46
62
  **Without Carto:**
47
- - Wrong API route: suggested `POST /api/bookings` → actual is `POST /v2/bookings`
48
- - Wrong handler: suggested `handleNewBooking.ts` → not the creation path
49
- - Wrong file paths: pointed to v1 API → v1 is legacy
50
- - Wrong tRPC file: `bookings.tsx` → actual is `bookings/_router.tsx`
51
- - Field list: ~15 fields guessed → missing 20+ real fields
52
63
 
53
- **With Carto:**
54
- - Correct API route ✅
55
- - Correct controller path
56
- - Correct tRPC file
57
- - All 35+ booking fields returned accurately
58
- - Answered in one shot. No follow-up needed.
64
+ | | What AI suggested | Reality |
65
+ |--|---|---|
66
+ | API route | `POST /api/bookings` | `POST /v2/bookings` |
67
+ | Handler | `handleNewBooking.ts` | Not the creation path |
68
+ | File path | v1 API files | v1 is legacy |
69
+ | tRPC file | `bookings.tsx` | `bookings/_router.tsx` |
70
+ | Fields found | ~15 guessed | 35+ actual fields |
71
+
72
+ **With Carto:** Correct route, correct handler, correct file, all 35+ fields. One shot. Zero follow-ups.
59
73
 
60
- **4 wrong file paths → 0. 20 missing fields → 0. Zero follow-up clarifications.**
74
+ **4 wrong paths → 0. 20 missing fields → 0.**
61
75
 
62
76
  Not smarter AI. The same AI with accurate facts.
63
77
 
64
78
  ---
65
79
 
80
+ ## Performance
81
+
82
+ Tested on Supabase (6,335 files, 310 watched):
83
+
84
+ | Operation | Time |
85
+ |-----------|------|
86
+ | Cold start (first `carto init`) | 422ms |
87
+ | Warm start (files cached) | 66ms |
88
+ | One file change (incremental re-index) | ~130ms |
89
+ | Blast radius lookup | <5ms |
90
+ | Route search | <1ms |
91
+
92
+ The secret: file hashes. On warm runs, Carto skips every file whose content hasn't changed. On a file save, only that one file gets re-parsed. The rest loads from disk cache in milliseconds.
93
+
94
+ ---
95
+
66
96
  ## How it works
67
97
 
68
98
  ```
69
99
  carto init
70
-
71
- Carto maps your codebase
72
- AGENTS.md (79 lines lean map every AI reads)
73
- .carto/context/AUTH.md, PAYMENTS.md, TRPC.md, DATABASE.md
74
- .carto/map.json (import graph, routes, blast radius)
75
- MCP server auto-wired into Kiro, Cursor, Claude Desktop
76
-
77
- carto watch (keeps everything live on every file save)
78
- carto serve (MCP server — AI tools query graph mid-task)
100
+
101
+ Hashes every file → skips unchanged on re-runs
102
+ Builds import graph knows who depends on who
103
+ Extracts routes, models, functions, env vars
104
+ Clusters into domains (AUTH, PAYMENTS, DATABASE...)
105
+ Calculates blast radius for every file
106
+ Writes AGENTS.md + .carto/context/*.md
107
+ Auto-wires MCP into Kiro, Cursor, Claude Desktop
108
+
109
+ carto watch
110
+
111
+ File saved → re-parse 1 file → update graph → ~130ms
79
112
  ```
80
113
 
81
114
  ---
82
115
 
83
- ## MCP AI queries your codebase live
116
+ ## 12 MCP tools: AI queries your codebase live
117
+
118
+ `carto serve` exposes a local MCP server. Kiro, Cursor, and Claude query it mid-task instead of guessing.
119
+
120
+ | Tool | What it returns |
121
+ |------|----------------|
122
+ | `get_blast_radius(file)` | Risk level, all affected files, routes at risk per domain |
123
+ | `get_context(file)` | Everything about a file in one call: domain, blast radius, neighbors, routes, models |
124
+ | `get_routes()` | All API endpoints with file mapping |
125
+ | `get_structure()` | Import graph, entry points, high-impact files, tech stack |
126
+ | `get_domain(name)` | All routes, models, functions for AUTH / PAYMENTS / DATABASE / etc. |
127
+ | `get_neighbors(file, hops)` | Import graph neighbors: nodes and edges |
128
+ | `get_cross_domain()` | Import edges that cross domain boundaries |
129
+ | `search_routes(query)` | Search API routes by path or method |
130
+ | `get_models(domain?)` | All data models, optionally filtered by domain |
131
+ | `get_high_impact_files(n)` | Top N files by blast radius, highest-risk to change |
132
+ | `get_env_vars(domain?)` | All env vars with domain mapping |
133
+ | `get_domains_list()` | All detected domains with file, route, model counts |
84
134
 
85
- `carto init` auto-wires the MCP config into Kiro, Cursor, and Claude Desktop automatically. When Kiro or Cursor is mid-task, it can call Carto directly instead of guessing:
135
+ ---
86
136
 
87
- **`get_blast_radius("src/lib/payments.ts")`**
88
- ```
89
- Files affected:
90
- → apps/web/app/api/checkout/route.ts
91
- → apps/web/app/api/webhook/route.ts
92
- → packages/trpc/routers/billing.ts
93
-
94
- Routes at risk:
95
- → POST /api/checkout
96
- → POST /api/webhook
97
- → POST /trpc/createSubscription
98
- ```
137
+ ## What gets extracted
99
138
 
100
- **`get_routes()`**
101
- ```
102
- | Method | Path | Handler |
103
- |--------|-----------------------------|---------------------|
104
- | POST | /api/auth/signup | POST |
105
- | GET | /api/auth/oauth/me | GET |
106
- | POST | /trpc/createBooking | createBooking |
107
- | GET | /trpc/getAvailability | getAvailability |
108
- | ... | ... | ... |
109
- ```
139
+ | Category | What Carto finds |
140
+ |----------|-----------------|
141
+ | **Routes** | FastAPI, Express, Next.js App/Pages Router, React Router (JSX + createBrowserRouter), tRPC procedures, Django URLs, Gin/Echo (Go) |
142
+ | **Models** | Prisma, Pydantic, SQLAlchemy, Django ORM, TypeScript interfaces/types, Zod schemas, Drizzle tables |
143
+ | **Graph** | Full import graph: who imports what, transitive dependencies up to 5 hops |
144
+ | **Blast radius** | Risk level (HIGH/MEDIUM/LOW) per file and per route |
145
+ | **Domains** | AUTH, PAYMENTS, DATABASE, EVENTS, TRPC, NOTIFICATIONS, CORE, auto-clustered from imports |
146
+ | **Events** | EventEmitter listeners, webhook handlers, queue jobs, cron schedules |
147
+ | **Env vars** | Every `process.env` / `os.Getenv` call (names only, never values) |
148
+ | **Functions** | Signatures with param names and return types |
110
149
 
111
- **`get_domain("AUTH")`**
112
- Returns `AUTH.md` — all auth routes, session models, JWT functions, env vars.
150
+ ---
113
151
 
114
- **`get_structure()`**
115
- Returns import graph, entry points, high impact files, tech stack.
152
+ ## Languages and frameworks
116
153
 
117
- ### Manual MCP config (if auto-wire didn't detect your IDE)
154
+ | Language | Frameworks |
155
+ |----------|------------|
156
+ | TypeScript / JavaScript | Express, Next.js (App + Pages Router), React Router, tRPC, Drizzle, Zod |
157
+ | Python | FastAPI, Pydantic, SQLAlchemy, Django |
158
+ | Go | Gin, Echo, Chi, net/http |
159
+ | R | Plumber, Shiny, R6, S7 |
160
+ | Schema | Prisma |
161
+ | HTML | fetch() calls |
118
162
 
119
- **Kiro** add to `~/.kiro/settings/mcp.json`:
120
- ```json
121
- {
122
- "mcpServers": {
123
- "carto": {
124
- "command": "carto",
125
- "args": ["serve"],
126
- "cwd": "/path/to/your/project"
127
- }
128
- }
129
- }
130
- ```
163
+ More via community. See [CONTRIBUTING.md](CONTRIBUTING.md).
131
164
 
132
- **Cursor** — add to `~/.cursor/mcp.json`:
133
- ```json
134
- {
135
- "mcpServers": {
136
- "carto": {
137
- "command": "carto",
138
- "args": ["serve"],
139
- "cwd": "/path/to/your/project"
140
- }
141
- }
142
- }
143
- ```
165
+ ---
144
166
 
145
- **Claude Desktop** — add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
146
- ```json
147
- {
148
- "mcpServers": {
149
- "carto": {
150
- "command": "carto",
151
- "args": ["serve"],
152
- "cwd": "/path/to/your/project"
153
- }
154
- }
155
- }
156
- ```
167
+ ## Commands
157
168
 
158
- Then run `carto serve` in your project directory alongside `carto watch`.
169
+ | Command | What it does |
170
+ |---------|-------------|
171
+ | `carto init` | Detect project, index codebase, generate AGENTS.md, wire MCP |
172
+ | `carto watch` | Incremental live re-index on every file save (~130ms) |
173
+ | `carto sync` | One-time manual re-index |
174
+ | `carto impact <file>` | Blast radius: risk level, affected files, routes at risk |
175
+ | `carto check` | Cross-domain violations, high-risk uncommitted changes, domain health |
176
+ | `carto serve` | Start MCP server for Kiro / Cursor / Claude |
177
+ | `carto remove` | Remove AGENTS.md and .carto/ from project |
178
+ | `carto --version` | Show version |
159
179
 
160
180
  ---
161
181
 
162
- ## Domain context files
163
-
164
- Large codebases kill AI accuracy. A 2900-line AGENTS.md means AI reads 500 lines and guesses the rest.
182
+ ## `carto check`
165
183
 
166
- Carto splits context by domain automatically:
184
+ Run before committing. Tells you what's risky before you push.
167
185
 
168
186
  ```
169
- AGENTS.md → 79 lines, always loaded
170
- .carto/context/
171
- AUTH.md → auth routes, session models, JWT functions
172
- PAYMENTS.md → Stripe routes, billing models
173
- TRPC.md → all tRPC procedures
174
- DATABASE.md → every model, schema, table
175
- EVENTS.md → webhooks, queues, cron jobs
176
- CORE.md → shared utilities
177
- ```
187
+ ── Carto Check ───────────────────────────────────────
178
188
 
179
- AI reads AGENTS.md always. Then reads only the relevant domain file for the current task. 400 lines of exact context instead of 2900 lines of everything.
189
+ Files indexed : 310
190
+ Routes found : 104
191
+ Import edges : 5,248
192
+ Domains : AUTH · DATABASE · PAYMENTS · EVENTS · CORE
180
193
 
181
- Domain assignment uses your import graph — files that import each other cluster together, regardless of folder names.
194
+ ⚠️ High-risk uncommitted changes (1):
195
+ 🔴 src/lib/auth.service.ts
196
+ 11 files depend on this, blast risk: HIGH
182
197
 
183
- ---
184
-
185
- ## Know what breaks before you break it
198
+ ✅ No cross-domain dependency violations
186
199
 
187
- ```bash
188
- carto impact apps/web/app/api/auth/signup/route.ts
189
-
190
- # Impact analysis: apps/web/app/api/auth/signup/route.ts
191
- #
192
- # Imported by:
193
- # → apps/web/app/api/auth/signup/handlers/calcomSignupHandler.ts
194
- # → apps/web/app/api/auth/signup/handlers/selfHostedHandler.ts
195
- #
196
- # Routes at risk:
197
- # → POST /api/auth/signup
198
- # → ALL /api/auth/signup/handlers
199
- #
200
- # Risk: MEDIUM
200
+ 🔥 Top high-impact files:
201
+ 83 dependents - packages/ui/src/lib/utils/cn.ts
202
+ 35 dependents - packages/pg-meta/src/pg-format/index.ts
203
+ 34 dependents - packages/icons/src/createSupabaseIcon.ts
201
204
  ```
202
205
 
203
- No AI. No cloud. Runs in under a second. From your live import graph.
204
-
205
206
  ---
206
207
 
207
- ## Install
208
+ ## `carto impact`
208
209
 
209
210
  ```bash
210
- npm install -g carto-md
211
- ```
211
+ carto impact src/middleware.ts
212
212
 
213
- Or without installing:
213
+ Impact analysis: src/middleware.ts
214
214
 
215
- ```bash
216
- npx carto-md init
215
+ Risk: 🔴 HIGH
216
+ Directly affected: 11 files across 2 domain(s)
217
+ Domains impacted: AUTH, PAYMENTS
218
+
219
+ Files that depend on this (11):
220
+ → src/routes/auth.ts
221
+ → src/routes/billing.ts
222
+ → ...
223
+
224
+ Routes at risk (4):
225
+ 🔴 POST /api/auth/login
226
+ 🔴 POST /api/billing/checkout
227
+ 🟡 GET /api/users/me
228
+ 🟢 GET /api/health
217
229
  ```
218
230
 
219
231
  ---
220
232
 
221
- ## Usage
233
+ ## Programmatic API
222
234
 
223
- ```bash
224
- cd your-project
225
- carto init
226
- ```
235
+ Use Carto as a module, no CLI required. This is how tools embed it.
227
236
 
228
- That's it. Carto:
229
- - Maps your codebase
230
- - Generates AGENTS.md + domain context files
231
- - Auto-wires MCP into Kiro, Cursor, Claude Desktop
232
- - Installs a git hook — syncs on every commit
237
+ ```js
238
+ const { Carto } = require('carto-md');
233
239
 
234
- Run `carto watch` in background for live updates on every file save.
235
- Run `carto serve` to start the MCP server manually if needed.
240
+ const carto = new Carto();
241
+ await carto.index('/path/to/project');
236
242
 
237
- ---
243
+ // Everything about a file in one call
244
+ const ctx = carto.getContextForFile('src/auth/auth.service.ts');
245
+ // {
246
+ // domain: 'AUTH',
247
+ // routes: ['POST /api/auth/login', 'GET /api/auth/me'],
248
+ // models: ['User', 'Session'],
249
+ // blastRadius: { risk: 'HIGH', directlyAffected: { files: 8, domains: 2 } },
250
+ // neighbors: { nodes: [...], edges: [...] }, // React Flow compatible
251
+ // crossDomainDeps: [...],
252
+ // domainContext: '...AUTH.md content...'
253
+ // }
238
254
 
239
- ## Commands
255
+ // Live updates
256
+ carto.on('updated', ({ file, blastRadius }) => {
257
+ console.log(`${file} changed, blast risk: ${blastRadius.risk}`);
258
+ });
240
259
 
241
- | Command | What it does |
242
- |---------|-------------|
243
- | `carto init` | Map codebase, generate context files, wire MCP into IDEs |
244
- | `carto watch` | Live updates on every file save |
245
- | `carto sync` | One-time manual refresh |
246
- | `carto serve` | Start MCP server for Kiro/Cursor/Claude queries |
247
- | `carto impact <file>` | Show blast radius before touching a file |
248
- | `carto remove` | Remove AGENTS.md and .carto/ from this project |
249
- | `carto --version` | Show version |
260
+ await carto.reindex('src/auth/auth.service.ts'); // ~130ms
261
+ ```
262
+
263
+ **Full API:**
264
+
265
+ ```js
266
+ carto.getBlastRadius(file) // Risk + affected files + routes
267
+ carto.getNeighbors(file, hops) // Import graph, React Flow nodes/edges
268
+ carto.getCrossDomainDeps() // Cross-boundary import edges
269
+ carto.getHighImpactFiles(n) // Top N by blast radius
270
+ carto.searchRoutes(query) // Route search
271
+ carto.getRoutes() // All API routes
272
+ carto.getDomain(name) // Domain cluster + context file
273
+ carto.getDomainsList() // All domains with counts
274
+ carto.getModels(domain?) // All models
275
+ carto.getEnvVars(domain?) // Env vars with domain mapping
276
+ carto.getMeta() // Index stats
277
+ ```
278
+
279
+ Events: `status` · `indexed` · `updated`
250
280
 
251
281
  ---
252
282
 
253
- ## Works with
283
+ ## Domain context files
254
284
 
255
- | Language | Frameworks |
256
- |----------|------------|
257
- | Python | FastAPI, Pydantic |
258
- | JavaScript | Express, Next.js |
259
- | TypeScript | Express, Next.js, Prisma, tRPC |
260
- | R | Plumber, Shiny, R6, S7 |
261
- | HTML | fetch() calls |
285
+ Large codebases kill AI accuracy. A 2,900-line AGENTS.md means the AI reads 500 lines and guesses the rest.
262
286
 
263
- More languages via community open an issue or see [CONTRIBUTING.md](CONTRIBUTING.md).
287
+ Carto splits context by domain automatically:
264
288
 
265
- ---
289
+ ```
290
+ AGENTS.md → lean map, always loaded by every AI
291
+ .carto/context/
292
+ AUTH.md → auth routes, session models, JWT functions, middleware
293
+ PAYMENTS.md → Stripe routes, billing models, webhook handlers
294
+ DATABASE.md → every model, schema, table, migration pattern
295
+ EVENTS.md → webhooks, queues, cron jobs, event emitters
296
+ TRPC.md → all procedures with input/output schemas
297
+ CORE.md → shared utilities
298
+ ```
266
299
 
267
- ## What gets extracted
300
+ AI reads AGENTS.md always. Then fetches only the domain file relevant to the task. 400 lines of exact context instead of 2,900 lines of everything.
268
301
 
269
- - API routes FastAPI, Express, Next.js App Router, tRPC procedures
270
- - Data models — Pydantic, Prisma, TypeScript interfaces
271
- - Function signatures — across all files
272
- - Import graph — which files depend on which
273
- - Domain clusters — AUTH, PAYMENTS, TRPC, DATABASE, EVENTS
274
- - Blast radius — what breaks if you change a file
275
- - Environment variable names — never values
276
- - Database tables — SQLAlchemy, Django ORM, Prisma
302
+ Domain assignment runs on your import graph: files that import each other cluster together, regardless of folder names.
277
303
 
278
304
  ---
279
305
 
280
- ## What Carto never touches
306
+ ## MCP config (if auto-wire missed your IDE)
281
307
 
282
- Your manual sections stay yours forever. Carto only rewrites between its own markers:
308
+ **Kiro**: `~/.kiro/settings/mcp.json`
309
+ ```json
310
+ { "mcpServers": { "carto": { "command": "carto", "args": ["serve"], "cwd": "/your/project" } } }
311
+ ```
283
312
 
313
+ **Cursor**: `~/.cursor/mcp.json`
314
+ ```json
315
+ { "mcpServers": { "carto": { "command": "carto", "args": ["serve"], "cwd": "/your/project" } } }
284
316
  ```
285
- <!-- CARTO:AUTO:START -->
286
- ... auto-generated content ...
287
- <!-- CARTO:AUTO:END -->
288
317
 
289
- Your manual notes here. Never touched.
318
+ **Claude Desktop**: `~/Library/Application Support/Claude/claude_desktop_config.json`
319
+ ```json
320
+ { "mcpServers": { "carto": { "command": "carto", "args": ["serve"], "cwd": "/your/project" } } }
290
321
  ```
291
322
 
292
323
  ---
293
324
 
325
+ ## AI tools that read AGENTS.md natively
326
+
327
+ Cursor · GitHub Copilot · Kiro · Claude Desktop · Claude Code · Codex · VS Code · Gemini CLI · Devin · Jules
328
+
329
+ Carto generates the file they all read. One source of truth. Every tool stays accurate.
330
+
331
+ ---
332
+
294
333
  ## What Carto fixes
295
334
 
296
335
  Carto fixes **factual hallucination about your own project**:
@@ -298,66 +337,56 @@ Carto fixes **factual hallucination about your own project**:
298
337
  - AI guessing wrong routes → fixed
299
338
  - AI guessing wrong field names → fixed
300
339
  - AI assuming wrong framework → fixed
301
- - AI guessing wrong DB schema → fixed
302
340
  - AI not knowing blast radius → fixed
341
+ - AI losing context between sessions → fixed
342
+ - Rebuilding project context every session → gone
303
343
 
304
- What Carto does not fix: AI reasoning badly, wrong implementation logic, misunderstanding what you want. Carto makes AI **accurate** about your project. Not smarter. Accurate. Different thing.
344
+ What Carto does not fix: AI reasoning badly, wrong implementation logic, misunderstanding requirements. Carto makes AI **accurate** about your project. Not smarter. Accurate. Different thing.
305
345
 
306
346
  ---
307
347
 
308
- ## AI tools that read AGENTS.md
348
+ ## What Carto never does
309
349
 
310
- - **Cursor** via context rules + MCP
311
- - **GitHub Copilot** via workspace instructions
312
- - **Kiro** natively + MCP
313
- - **Claude Desktop** via MCP
314
- - **Claude Code** — natively
315
- - **Codex** — natively
316
- - **VS Code** — via workspace context
317
- - **Gemini CLI** — natively
318
- - **Devin** — natively
319
- - **Jules** — natively
350
+ - Sends your code anywhere. Local only.
351
+ - Writes secrets into AGENTS.md. `.cartoignore` blocks `.env` and credential files by default.
352
+ - Touches your manual notes. It writes only between `<!-- CARTO:AUTO:START -->` and `<!-- CARTO:AUTO:END -->`.
353
+ - Costs money. MIT license. Free forever.
320
354
 
321
355
  ---
322
356
 
323
- ## What it does NOT do
324
-
325
- - No cloud. No servers. No telemetry. No tracking.
326
- - Your code never leaves your machine.
327
- - No paid tiers. Free forever. MIT license.
328
-
329
- ---
330
-
331
- ## Security
332
-
333
- Carto never writes secrets into AGENTS.md. `.cartoignore` blocks `.env` files, secret files, key files, and credential files by default. The sanitizer strips API key patterns from extracted code.
357
+ ## Install
334
358
 
335
- ---
359
+ ```bash
360
+ npm install -g carto-md
361
+ ```
336
362
 
337
- ## Contributing
363
+ ```bash
364
+ cd your-project
365
+ carto init
366
+ ```
338
367
 
339
- Python, JS/TS, and R today. Want Go, Ruby, Django, Rails? Open an issue — or read [CONTRIBUTING.md](CONTRIBUTING.md) to add it yourself.
368
+ That's it.
340
369
 
341
370
  ---
342
371
 
343
372
  ## Origin
344
373
 
345
- I was building [Emfirge](https://emfirge.cloud) a cloud security agent for AWS.
374
+ I was building Emfirge, a cloud security agent for AWS.
346
375
 
347
- 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 accurate facts — not guesses.
376
+ To make the AI understand infrastructure, I built a module that mapped AWS resources into a graph. The AI stopped hallucinating. It worked with facts.
348
377
 
349
- Halfway through, I switched AI tools. Opened a new session. Had to explain everything again from scratch.
378
+ Then I switched AI tools. New session. Had to explain the whole project again from scratch.
350
379
 
351
- I thought: *I just built a cartography system so AI can understand infrastructure. Why doesn't this exist for codebases?*
380
+ I thought: *I just built a cartography system for infrastructure. Why doesn't this exist for codebases?*
352
381
 
353
- Carto is that. Same insight, different domain.
382
+ Carto is that.
354
383
 
355
384
  ---
356
385
 
357
386
  ## License
358
387
 
359
- MIT free forever.
388
+ MIT. Free forever.
360
389
 
361
390
  ---
362
391
 
363
- *Built because AGENTS.md won. Someone had to keep it alive — and make it queryable.*
392
+ *Your code changes. Carto knows. Every AI you use knows.*
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "carto-md",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "The context layer for AI-native development.",
5
5
  "bin": {
6
6
  "carto": "src/cli/index.js"
@@ -38,6 +38,7 @@ module.exports = {
38
38
 
39
39
  // Expose internals for typescript.js to reuse
40
40
  _extractExpressRoutes: extractExpressRoutes,
41
+ _extractReactRouterRoutes: extractReactRouterRoutes,
41
42
  _extractProcessEnv: extractProcessEnv,
42
43
  _extractJSFetches: extractJSFetches,
43
44
  _extractJSFunctions: extractJSFunctions,
@@ -77,6 +78,10 @@ function extractExpressRoutes(ast, filename) {
77
78
  const nextRoutes = extractNextJSPagesRoutes(ast, filename);
78
79
  routes.push(...nextRoutes);
79
80
 
81
+ // React Router (JSX <Route> + createBrowserRouter/createHashRouter/createMemoryRouter)
82
+ const reactRoutes = extractReactRouterRoutes(ast);
83
+ routes.push(...reactRoutes);
84
+
80
85
  walk(ast, (node) => {
81
86
  if (node.type !== 'CallExpression') return;
82
87
  if (!node.callee || node.callee.type !== 'MemberExpression') return;
@@ -134,6 +139,120 @@ function extractExpressRoutes(ast, filename) {
134
139
  });
135
140
  }
136
141
 
142
+ // ---------------------------------------------------------------------------
143
+ // React Router extraction
144
+ // ---------------------------------------------------------------------------
145
+
146
+ const REACT_ROUTER_CREATORS = new Set(['createBrowserRouter', 'createHashRouter', 'createMemoryRouter', 'useRoutes']);
147
+
148
+ function extractReactRouterRoutes(ast) {
149
+ const routes = [];
150
+
151
+ // 1. JSX <Route path="..." component={X} /> or element={<X />}
152
+ walk(ast, (node) => {
153
+ if (node.type !== 'JSXOpeningElement') return;
154
+ if (!node.name || node.name.name !== 'Route') return;
155
+
156
+ let routePath = null;
157
+ let componentName = '[anonymous]';
158
+
159
+ for (const attr of (node.attributes || [])) {
160
+ if (attr.type !== 'JSXAttribute' || !attr.name) continue;
161
+ const attrName = attr.name.name;
162
+
163
+ if (attrName === 'path' && attr.value) {
164
+ if (attr.value.type === 'StringLiteral') {
165
+ routePath = attr.value.value;
166
+ }
167
+ }
168
+
169
+ if ((attrName === 'component' || attrName === 'element') && attr.value) {
170
+ if (attr.value.type === 'JSXExpressionContainer' && attr.value.expression) {
171
+ const expr = attr.value.expression;
172
+ if (expr.type === 'Identifier') {
173
+ componentName = expr.name;
174
+ } else if (expr.type === 'JSXElement' && expr.openingElement && expr.openingElement.name) {
175
+ componentName = expr.openingElement.name.name || '[anonymous]';
176
+ }
177
+ }
178
+ }
179
+ }
180
+
181
+ if (routePath !== null) {
182
+ routes.push({ method: 'VIEW', path: routePath, functionName: componentName });
183
+ }
184
+ });
185
+
186
+ // 2. createBrowserRouter / createHashRouter / createMemoryRouter / useRoutes
187
+ walk(ast, (node) => {
188
+ if (node.type !== 'CallExpression') return;
189
+ const callee = node.callee;
190
+ if (!callee) return;
191
+ const calleeName = callee.type === 'Identifier' ? callee.name
192
+ : (callee.type === 'MemberExpression' && callee.property) ? callee.property.name
193
+ : null;
194
+ if (!calleeName || !REACT_ROUTER_CREATORS.has(calleeName)) return;
195
+ if (!node.arguments || node.arguments.length === 0) return;
196
+
197
+ const firstArg = node.arguments[0];
198
+ if (firstArg.type === 'ArrayExpression') {
199
+ extractRoutesFromRouteArray(firstArg, routes);
200
+ }
201
+ });
202
+
203
+ const seen = new Set();
204
+ return routes.filter(r => {
205
+ const key = `VIEW::${r.path}`;
206
+ if (seen.has(key)) return false;
207
+ seen.add(key);
208
+ return true;
209
+ });
210
+ }
211
+
212
+ function extractRoutesFromRouteArray(arrayNode, routes) {
213
+ for (const element of (arrayNode.elements || [])) {
214
+ if (!element || element.type !== 'ObjectExpression') continue;
215
+ extractRouteFromRouteObject(element, routes);
216
+ }
217
+ }
218
+
219
+ function extractRouteFromRouteObject(objNode, routes) {
220
+ let routePath = null;
221
+ let componentName = '[anonymous]';
222
+ let childrenNode = null;
223
+
224
+ for (const prop of (objNode.properties || [])) {
225
+ if (prop.type !== 'ObjectProperty' && prop.type !== 'Property') continue;
226
+ if (!prop.key) continue;
227
+ const key = prop.key.name || prop.key.value;
228
+
229
+ if (key === 'path' && prop.value && prop.value.type === 'StringLiteral') {
230
+ routePath = prop.value.value;
231
+ }
232
+
233
+ if ((key === 'element' || key === 'component') && prop.value) {
234
+ const val = prop.value;
235
+ if (val.type === 'Identifier') {
236
+ componentName = val.name;
237
+ } else if (val.type === 'JSXElement' && val.openingElement && val.openingElement.name) {
238
+ componentName = val.openingElement.name.name || '[anonymous]';
239
+ }
240
+ }
241
+
242
+ if (key === 'children' && prop.value && prop.value.type === 'ArrayExpression') {
243
+ childrenNode = prop.value;
244
+ }
245
+ }
246
+
247
+ if (routePath !== null) {
248
+ routes.push({ method: 'VIEW', path: routePath, functionName: componentName });
249
+ }
250
+
251
+ if (childrenNode) {
252
+ extractRoutesFromRouteArray(childrenNode, routes);
253
+ }
254
+ }
255
+
137
256
  /**
138
257
  * Detects Next.js Pages Router pattern:
139
258
  * export default function handler(req, res) in files under pages/api/