carto-md 1.0.9 → 1.0.12

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.
@@ -0,0 +1,108 @@
1
+ # Contributing to Carto
2
+
3
+ Carto is free, open source, and community-maintained. The core team owns the merger logic, AST engine, and CLI. The community owns language and framework extractors.
4
+
5
+ ---
6
+
7
+ ## What to contribute
8
+
9
+ ### Tier 1 — Languages (safe to add, easy to review)
10
+
11
+ New language support lives in `src/ast/languages/`. Each language is an isolated module.
12
+
13
+ Currently supported: JavaScript/TypeScript, Python.
14
+
15
+ Wanted: Go, Rust, Ruby, Java, PHP, C#.
16
+
17
+ ### Tier 2 — Framework extractors (safe to add, easy to review)
18
+
19
+ Framework-specific route and model extraction lives in `src/extractors/`. Each framework is an isolated module.
20
+
21
+ Currently supported: FastAPI, Express, Next.js App Router, Prisma, HTML fetch().
22
+
23
+ Wanted: Django, Rails, Laravel, NestJS, Hono, Gin, Spring.
24
+
25
+ ### Tier 3 — Core (review carefully before merging)
26
+
27
+ - `src/agents/merger.js` — merger logic. One bad merge = developer loses manual notes = project dies. Changes here need strong justification and full test coverage.
28
+ - `src/ast/` — AST engine. Wrong extraction = wrong AGENTS.md = AI gets confident with wrong facts. Worse than no AGENTS.md.
29
+ - `src/detector/` — framework detection logic.
30
+ - `src/cli/` — CLI commands.
31
+
32
+ ---
33
+
34
+ ## How to add a language
35
+
36
+ 1. Create `src/ast/languages/yourlanguage.js`
37
+ 2. Export a single function: `extractFromFile(filePath, fileContent)`
38
+ 3. Return:
39
+ ```js
40
+ {
41
+ functions: [{ name, params, returns }],
42
+ classes: [{ name, fields }],
43
+ imports: [{ from, symbols }],
44
+ exports: [{ name }]
45
+ }
46
+ ```
47
+ 4. Add it to `src/ast/parser.js` language map
48
+ 5. Test on at least 3 real open-source projects
49
+ 6. Open a PR with before/after AGENTS.md examples
50
+
51
+ ---
52
+
53
+ ## How to add a framework extractor
54
+
55
+ 1. Create `src/extractors/yourframework.js`
56
+ 2. Export:
57
+ ```js
58
+ {
59
+ detect(projectRoot, files) → boolean,
60
+ extractRoutes(filePath, fileContent) → [{ method, path, functionName }],
61
+ extractModels(filePath, fileContent) → [{ name, fields: [{ name, type }] }]
62
+ }
63
+ ```
64
+ 3. Add detection logic to `src/detector/framework.js`
65
+ 4. Test on at least 2 real projects using that framework
66
+ 5. Open a PR with before/after AGENTS.md examples
67
+
68
+ ---
69
+
70
+ ## Ground rules
71
+
72
+ - **Never break the merger.** Manual sections in AGENTS.md are sacred. If your change could corrupt them, it needs a full merger test suite pass.
73
+ - **Wrong output is worse than no output.** If your extractor produces incorrect routes or models, AI gets confident with wrong facts. Only ship when accurate on real projects.
74
+ - **Test on unknown repos.** Don't just test on projects you wrote. Find a real open-source repo using the framework and verify the output is correct.
75
+ - **No cloud, no telemetry, no tracking.** Carto is local only. Forever. Don't add any network calls.
76
+ - **No paid features.** Free forever. MIT. Don't propose monetization.
77
+
78
+ ---
79
+
80
+ ## Development setup
81
+
82
+ ```bash
83
+ git clone https://github.com/anshsonkar/carto-ansh
84
+ cd carto-ansh
85
+ npm install
86
+ node src/cli/index.js init # test in any project
87
+ ```
88
+
89
+ ---
90
+
91
+ ## PR checklist
92
+
93
+ - [ ] Tested on at least 2-3 real open-source projects
94
+ - [ ] Before/after AGENTS.md included in PR description
95
+ - [ ] No changes to merger logic (unless explicitly fixing a merger bug)
96
+ - [ ] No network calls added
97
+ - [ ] `carto --version` still works
98
+ - [ ] Existing tests pass
99
+
100
+ ---
101
+
102
+ ## Issues
103
+
104
+ - **Bug**: Open an issue with the project type, command run, and what AGENTS.md produced vs what you expected.
105
+ - **Language request**: Open an issue titled "Language: [name]" — someone from the community will pick it up.
106
+ - **Framework request**: Open an issue titled "Framework: [name]".
107
+
108
+ All issues acknowledged within 24 hours.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ansh Sonkar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,273 @@
1
+ # carto
2
+
3
+ [![npm version](https://img.shields.io/npm/v/carto-md)](https://www.npmjs.com/package/carto-md)
4
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
5
+ [![npm downloads](https://img.shields.io/npm/dm/carto-md)](https://www.npmjs.com/package/carto-md)
6
+
7
+ **Your code changes. AGENTS.md updates. Every AI always knows.**
8
+
9
+ ```bash
10
+ npm install -g carto-md
11
+ ```
12
+
13
+ Carto auto-generates and maintains your `AGENTS.md` — the standard file every AI coding tool reads for project context. Every time you save, your routes, models, functions, and dependencies are extracted and kept current.
14
+
15
+ ---
16
+
17
+ ## The problem
18
+
19
+ AI coding tools are blind to your actual project. Every session starts from zero.
20
+
21
+ - Claude hallucinates your schema
22
+ - Copilot suggests the wrong field names
23
+ - Kiro asks what framework you're using
24
+ - You rebuild context manually, every time
25
+
26
+ `AGENTS.md` fixes this — a file in your project root that every AI tool reads. But it's static. You write it manually. It gets stale the moment your code changes.
27
+
28
+ **Carto makes it live.**
29
+
30
+ ---
31
+
32
+ ## Proof — cal.com (800k lines)
33
+
34
+ Same task, two Claude sessions: *"Add a `notes` field to the booking model."*
35
+
36
+ **Without AGENTS.md:**
37
+ - Wrong API route: suggested `POST /api/bookings` → actual is `POST /v2/bookings`
38
+ - Wrong handler: suggested `handleNewBooking.ts` → not the creation path
39
+ - Wrong file paths: pointed to v1 API (`apps/api/v1/...`) → v1 is legacy
40
+ - Wrong tRPC file: `bookings.tsx` → actual is `bookings/_router.tsx`
41
+ - Field list: ~15 fields guessed → missing 20+ real fields
42
+ - Couldn't proceed without follow-up: *"Want me to write the exact diffs once you confirm the codebase location?"*
43
+
44
+ **With AGENTS.md (generated by Carto):**
45
+ - Correct API route: `POST /v2/bookings` ✅
46
+ - Correct controller path ✅
47
+ - Correct tRPC file ✅
48
+ - All 35+ booking fields returned accurately ✅
49
+ - Answered in one shot. No follow-up needed.
50
+
51
+ **4 wrong file paths → 0. 20 missing fields → 0. Zero follow-up clarifications.**
52
+
53
+ Not smarter AI. The same AI with accurate facts.
54
+
55
+ ---
56
+
57
+ ## Know what breaks before you break it
58
+
59
+ Most production bugs aren't logic errors. They're *"I didn't know X depended on Y."*
60
+
61
+ `carto impact` makes that invisible knowledge visible — before you touch anything.
62
+
63
+ ```bash
64
+ carto impact app/models.py
65
+
66
+ # Impact analysis: app/models.py
67
+ #
68
+ # Imported by:
69
+ # → app/main.py
70
+ # → app/rules.py
71
+ # → app/scoring.py
72
+ # → app/aws_collector.py
73
+ # → tests/conftest.py
74
+ #
75
+ # Routes affected:
76
+ # → POST /analyze
77
+ # → GET /history
78
+ # → POST /simulate
79
+ # → ... 12 more
80
+ #
81
+ # Risk: HIGH — 5 files depend on this
82
+ ```
83
+
84
+ No AI. No cloud. Runs in under a second. Locally, from your import graph.
85
+
86
+ Make it a habit: before touching any file, run `carto impact` first. 10 seconds. Could save hours.
87
+
88
+ ---
89
+
90
+ ## Why not just paste your code?
91
+
92
+ Context windows are large now. But pasting code means:
93
+
94
+ - You decide what's relevant — you're often wrong
95
+ - AI sees a snapshot, not your live state
96
+ - Bigger context ≠ better context
97
+
98
+ Carto gives AI the map. You give AI the problem. Different jobs.
99
+
100
+ ---
101
+
102
+ ## How it works
103
+
104
+ ```
105
+ You save a file
106
+
107
+ Carto extracts routes, models, functions, env vars
108
+
109
+ AGENTS.md updated in 300ms
110
+
111
+ Cursor, Copilot, Kiro, Codex, Claude — all read current truth
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Install
117
+
118
+ ```bash
119
+ npm install -g carto-md
120
+ ```
121
+
122
+ Or run without installing:
123
+
124
+ ```bash
125
+ npx carto-md init
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Usage
131
+
132
+ ```bash
133
+ # 1. Go to your project
134
+ cd your-project
135
+
136
+ # 2. Generate AGENTS.md (run once)
137
+ carto init
138
+
139
+ # 3. Keep it live while you work
140
+ carto watch
141
+ ```
142
+
143
+ Leave `carto watch` running in a background terminal. Every file save updates AGENTS.md automatically.
144
+
145
+ ---
146
+
147
+ ## Commands
148
+
149
+ | Command | What it does |
150
+ |---------|-------------|
151
+ | `carto init` | Detect stack, generate AGENTS.md, install git hook |
152
+ | `carto watch` | Watch files, update AGENTS.md on every save |
153
+ | `carto sync` | One-time refresh, no watcher |
154
+ | `carto impact <file>` | Show blast radius before touching a file |
155
+ | `carto --version` | Show version |
156
+
157
+ **When to use each:**
158
+ - `init` — once, when you add Carto to a project
159
+ - `watch` — every work session, leave it running
160
+ - `sync` — skipped watch and need a fresh snapshot
161
+ - `impact` — before editing anything critical
162
+
163
+ ---
164
+
165
+ ## Works with
166
+
167
+ | Language | Frameworks |
168
+ |----------|------------|
169
+ | Python | FastAPI, Pydantic |
170
+ | JavaScript | Express, Next.js |
171
+ | TypeScript | Express, Next.js, Prisma |
172
+ | HTML | fetch() calls |
173
+
174
+ More languages via community — open an issue or see [CONTRIBUTING.md](CONTRIBUTING.md).
175
+
176
+ ---
177
+
178
+ ## What gets extracted automatically
179
+
180
+ - API routes — FastAPI, Express, Next.js App Router
181
+ - Data models — Pydantic, Prisma
182
+ - Function signatures — across all files
183
+ - Dependencies — from `package.json` / `requirements.txt`
184
+ - Environment variable names — never values
185
+ - Frontend API calls — from `fetch()` patterns
186
+ - Import graph — which files depend on which
187
+ - Database tables
188
+
189
+ ---
190
+
191
+ ## What Carto never touches
192
+
193
+ Your manual sections — architecture decisions, active bugs, business rules, coding conventions — stay yours forever. Carto only rewrites between its own markers:
194
+
195
+ ```
196
+ <!-- CARTO:AUTO:START -->
197
+ ... auto-generated content ...
198
+ <!-- CARTO:AUTO:END -->
199
+
200
+ Your manual notes here. Never touched.
201
+ ```
202
+
203
+ ---
204
+
205
+ ## What Carto fixes
206
+
207
+ Carto fixes **factual hallucination about your own project**:
208
+
209
+ - AI guessing wrong routes → fixed
210
+ - AI guessing wrong field names → fixed
211
+ - AI assuming wrong framework → fixed
212
+ - AI guessing wrong DB schema → fixed
213
+
214
+ 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.
215
+
216
+ ---
217
+
218
+ ## AI tools that read AGENTS.md
219
+
220
+ Drop the file in your project root. Each tool picks it up via its own context config:
221
+
222
+ - **Cursor** — via context rules
223
+ - **GitHub Copilot** — via workspace instructions
224
+ - **Kiro** — natively
225
+ - **Codex** — natively
226
+ - **VS Code** — via workspace context
227
+ - **Gemini CLI** — natively
228
+ - **Devin** — natively
229
+ - **Jules** — natively
230
+
231
+ ---
232
+
233
+ ## What it does NOT do
234
+
235
+ - No cloud. No servers. No telemetry. No tracking.
236
+ - Your code never leaves your machine.
237
+ - No paid tiers. Free forever. MIT license.
238
+
239
+ ---
240
+
241
+ ## Security
242
+
243
+ 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.
244
+
245
+ ---
246
+
247
+ ## Contributing
248
+
249
+ Python and JS/TS today. Want Go, Ruby, Django, Rails? Open an issue — or read [CONTRIBUTING.md](CONTRIBUTING.md) to add it yourself.
250
+
251
+ ---
252
+
253
+ ## Origin
254
+
255
+ I was building [Emfirge](https://emfirge.cloud) — a cloud security agent for AWS.
256
+
257
+ 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.
258
+
259
+ Halfway through, I switched AI tools. Opened a new session. Had to explain everything again from scratch.
260
+
261
+ I thought: *I just built a cartography system so AI can understand infrastructure. Why doesn't this exist for codebases?*
262
+
263
+ Carto is that. Same insight, different domain.
264
+
265
+ ---
266
+
267
+ ## License
268
+
269
+ MIT — free forever.
270
+
271
+ ---
272
+
273
+ *Built because AGENTS.md won. Someone had to keep it alive.*
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "carto-md",
3
- "version": "1.0.9",
3
+ "version": "1.0.12",
4
4
  "description": "The context layer for AI-native development.",
5
5
  "bin": {
6
6
  "carto": "src/cli/index.js"
package/src/sync.js CHANGED
@@ -62,25 +62,15 @@ async function runFullSync(config) {
62
62
  const allModelFiles = config.watch.modelFiles || [];
63
63
  const allFrontendFiles = config.watch.frontendFiles || [];
64
64
 
65
- // Aggregate data
66
65
  let allRoutes = [];
67
66
  let allModels = [];
68
67
  let allFetches = [];
69
68
  let allStorageKeys = [];
70
-
71
- // Functions: { filename: [{ name, params, returnType }] }
72
69
  const functionsMap = {};
73
- // Routes per file for file map
74
70
  const routeCountMap = {};
75
- // Env vars: { varName: Set([filename, ...]) }
76
71
  const envVarMap = new Map();
77
- // DB tables: [{ tableName, modelName, file }]
78
72
  const dbTableList = [];
79
-
80
- // Deduplicate files
81
73
  const processedFiles = new Set();
82
-
83
- // Process all code files (route + model files, deduplicated)
84
74
  const allCodeFiles = [...new Set([...allRouteFiles, ...allModelFiles])];
85
75
 
86
76
  for (const filePath of allCodeFiles) {
@@ -94,42 +84,32 @@ async function runFullSync(config) {
94
84
  const relPath = path.relative(projectRoot, filePath);
95
85
  const plugin = getPluginForFile(plugins, filePath);
96
86
 
97
- if (!plugin) {
98
- // No plugin for this file type — skip silently
99
- continue;
100
- }
87
+ if (!plugin) continue;
101
88
 
102
89
  const result = plugin.extract(content, relPath);
103
90
 
104
- // Routes
105
91
  allRoutes = allRoutes.concat(result.routes);
106
92
  routeCountMap[filePath] = result.routes.length;
107
93
 
108
- // Models
109
94
  allModels = allModels.concat(result.models);
110
95
 
111
- // Functions
112
96
  if (result.functions.length > 0 && basename !== '__init__.py') {
113
97
  functionsMap[basename] = result.functions;
114
98
  }
115
99
 
116
- // Env vars
117
100
  for (const varName of result.envVars) {
118
101
  if (!envVarMap.has(varName)) envVarMap.set(varName, new Set());
119
102
  envVarMap.get(varName).add(basename);
120
103
  }
121
104
 
122
- // DB tables
123
105
  for (const t of result.dbTables) {
124
106
  dbTableList.push({ tableName: t.tableName, modelName: t.modelName, file: basename });
125
107
  }
126
108
 
127
- // Fetches and storage keys (from JS/HTML plugins)
128
109
  allFetches = allFetches.concat(result.fetches);
129
110
  allStorageKeys = allStorageKeys.concat(result.storageKeys);
130
111
  }
131
112
 
132
- // Process frontend files separately (may overlap with code files)
133
113
  for (const filePath of allFrontendFiles) {
134
114
  if (processedFiles.has(filePath)) continue;
135
115
  processedFiles.add(filePath);
@@ -147,12 +127,10 @@ async function runFullSync(config) {
147
127
  allStorageKeys = allStorageKeys.concat(result.storageKeys);
148
128
  }
149
129
 
150
- // Global dedup: collapse dynamic fetches across all files into one summary row
151
130
  const staticFetches = allFetches.filter(f => f.url !== '[dynamic]' && !f.url.startsWith('dynamic calls detected'));
152
131
  let totalDynamic = 0;
153
132
  for (const f of allFetches) {
154
133
  if (f.url === '[dynamic]') totalDynamic++;
155
- // Also count already-collapsed per-file rows
156
134
  const m = f.url.match(/^dynamic calls detected \((\d+) unresolved\)$/);
157
135
  if (m) totalDynamic += parseInt(m[1], 10);
158
136
  }
@@ -161,7 +139,6 @@ async function runFullSync(config) {
161
139
  }
162
140
  allFetches = staticFetches;
163
141
 
164
- // Global dedup: storage keys across all files
165
142
  const skSeen = new Set();
166
143
  allStorageKeys = allStorageKeys.filter(({ operation, key }) => {
167
144
  const id = `${operation}::${key}`;
@@ -170,10 +147,8 @@ async function runFullSync(config) {
170
147
  return true;
171
148
  });
172
149
 
173
- // Build import graph from all processed files
174
150
  const fileContentsForImports = [];
175
151
  const allProcessedPaths = [...new Set([...allCodeFiles, ...allFrontendFiles])];
176
- // Re-read is avoided — collect during processing. Use a second pass for simplicity.
177
152
  for (const filePath of allProcessedPaths) {
178
153
  try {
179
154
  const content = fs.readFileSync(filePath, 'utf-8');
@@ -184,19 +159,15 @@ async function runFullSync(config) {
184
159
  }
185
160
  const importGraph = buildImportGraph(fileContentsForImports, projectRoot);
186
161
 
187
- // Detect tech stack from watched files + manifests
188
162
  const stackItems = buildStackLine(fileContentsForImports, projectRoot);
189
163
 
190
- // Compute entry points and high impact files from import graph
191
164
  const allValues = new Set();
192
165
  for (const deps of Object.values(importGraph)) {
193
166
  for (const dep of deps) allValues.add(dep);
194
167
  }
195
- // Entry points: files that import 3+ others but nothing imports them
196
168
  const entryPoints = Object.keys(importGraph)
197
169
  .filter(f => !allValues.has(f) && importGraph[f].length >= 3)
198
170
  .sort();
199
- // High impact: files imported by 3+ others, sorted descending by count
200
171
  const depCount = {};
201
172
  for (const deps of Object.values(importGraph)) {
202
173
  for (const dep of deps) {
@@ -208,7 +179,6 @@ async function runFullSync(config) {
208
179
  .sort((a, b) => b[1] - a[1])
209
180
  .map(([file, count]) => ({ file, count }));
210
181
 
211
- // Build file map
212
182
  const fileMap = [];
213
183
  for (const filePath of allCodeFiles) {
214
184
  const basename = path.basename(filePath);
@@ -221,15 +191,12 @@ async function runFullSync(config) {
221
191
  }
222
192
  }
223
193
 
224
- // Aggregate env vars into sorted array
225
194
  const envVars = [...envVarMap.keys()]
226
195
  .sort()
227
196
  .map(name => ({ name, files: [...envVarMap.get(name)].sort() }));
228
197
 
229
- // Scan project structure
230
198
  const structure = await scanStructure(projectRoot);
231
199
 
232
- // Validate extracted data — drop anything malformed
233
200
  const validated = validateExtracted({
234
201
  routes: allRoutes,
235
202
  models: allModels,
@@ -256,7 +223,6 @@ async function runFullSync(config) {
256
223
 
257
224
  mergeIntoAgentsMd(config.output, autoContent);
258
225
 
259
- // Save graph to .carto/map.json (atomic write)
260
226
  const cartoDir = path.join(projectRoot, '.carto');
261
227
  const mapData = {
262
228
  version: '1',