carto-md 1.0.8 → 1.0.10
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 +108 -0
- package/README.md +267 -0
- package/package.json +1 -1
- package/src/cli/init.js +1 -0
- package/src/sync.js +8 -41
package/CONTRIBUTING.md
ADDED
|
@@ -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/README.md
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# carto
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/carto-md)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://www.npmjs.com/package/carto-md)
|
|
6
|
+
|
|
7
|
+
**Your code changes. AGENTS.md updates. Every AI always knows.**
|
|
8
|
+
|
|
9
|
+
Carto auto-generates and auto-maintains your `AGENTS.md` file. Every time you save, your routes, models, functions, and dependencies are extracted and written into the standard file every AI coding tool already reads.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Origin
|
|
14
|
+
|
|
15
|
+
I was building [Emfirge](https://emfirge.com) — a cloud security advisor for AWS.
|
|
16
|
+
|
|
17
|
+
To make the AI inside Emfirge understand infrastructure, I wrote a module called `cartography.py`. It scanned 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 about the actual infrastructure — not guesses.
|
|
18
|
+
|
|
19
|
+
Halfway through, I switched AI tools. Opened a new session. Had to explain everything again from scratch.
|
|
20
|
+
|
|
21
|
+
I thought: *I just built a cartography system so AI can understand infrastructure. Why doesn't this exist for codebases?*
|
|
22
|
+
|
|
23
|
+
Carto is that thing. Same insight, different domain. Map your codebase once. Every AI session starts with accurate facts. You never explain your project again.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## The problem
|
|
28
|
+
|
|
29
|
+
AI coding tools are blind to your actual project. Every session starts from zero.
|
|
30
|
+
|
|
31
|
+
- Claude hallucinates your schema
|
|
32
|
+
- Copilot suggests the wrong field names
|
|
33
|
+
- Kiro asks what framework you're using
|
|
34
|
+
- You rebuild context manually, every time
|
|
35
|
+
|
|
36
|
+
`AGENTS.md` is the standard that fixes this — a file in your project root that every AI tool reads for project context. But it's static. You write it manually. It gets stale the moment your code changes.
|
|
37
|
+
|
|
38
|
+
**Carto makes it live.**
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Why not just paste your code?
|
|
43
|
+
|
|
44
|
+
Context windows are large now. But pasting code means:
|
|
45
|
+
|
|
46
|
+
- You decide what's relevant — you're often wrong
|
|
47
|
+
- AI sees a snapshot, not your live state
|
|
48
|
+
- Bigger context ≠ better context
|
|
49
|
+
|
|
50
|
+
Carto gives AI the map. You give AI the problem. Different jobs.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## How it works
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
You save a file
|
|
58
|
+
↓
|
|
59
|
+
Carto extracts routes, models, functions, env vars
|
|
60
|
+
↓
|
|
61
|
+
AGENTS.md updated in 300ms
|
|
62
|
+
↓
|
|
63
|
+
Cursor, Copilot, Kiro, Codex, Claude — all read current truth
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Install
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npm install -g carto-md
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Or run without installing:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npx carto-md init
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Usage
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# 1. Go to your project
|
|
86
|
+
cd your-project
|
|
87
|
+
|
|
88
|
+
# 2. Generate AGENTS.md (run once)
|
|
89
|
+
carto init
|
|
90
|
+
|
|
91
|
+
# 3. Keep it live while you work
|
|
92
|
+
carto watch
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Leave `carto watch` running in a background terminal. Every file save updates AGENTS.md automatically.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Commands
|
|
100
|
+
|
|
101
|
+
| Command | What it does |
|
|
102
|
+
|---------|-------------|
|
|
103
|
+
| `carto init` | Detect stack, generate AGENTS.md, install git hook |
|
|
104
|
+
| `carto watch` | Watch files, update AGENTS.md on every save |
|
|
105
|
+
| `carto sync` | One-time refresh, no watcher |
|
|
106
|
+
| `carto impact <file>` | Show blast radius before touching a file |
|
|
107
|
+
| `carto --version` | Show version |
|
|
108
|
+
|
|
109
|
+
**When to use each:**
|
|
110
|
+
- `init` — once, when you add Carto to a project
|
|
111
|
+
- `watch` — every work session, leave it running
|
|
112
|
+
- `sync` — if you skipped watch and just want a fresh snapshot
|
|
113
|
+
- `impact` — before editing anything critical
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## What gets extracted automatically
|
|
118
|
+
|
|
119
|
+
- API routes — FastAPI, Express, Next.js App Router
|
|
120
|
+
- Data models — Pydantic, Prisma
|
|
121
|
+
- Function signatures — across all files
|
|
122
|
+
- Dependencies — from `package.json` / `requirements.txt`
|
|
123
|
+
- Environment variable names — never values
|
|
124
|
+
- Frontend API calls — from `fetch()` patterns
|
|
125
|
+
- Import graph — which files depend on which
|
|
126
|
+
- Database tables
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## What Carto never touches
|
|
131
|
+
|
|
132
|
+
The manual sections you write directly into `AGENTS.md` — architecture decisions, active bugs, business rules, coding conventions — stay yours forever. Carto only rewrites content between its own markers:
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
<!-- CARTO:AUTO:START -->
|
|
136
|
+
... auto-generated content ...
|
|
137
|
+
<!-- CARTO:AUTO:END -->
|
|
138
|
+
|
|
139
|
+
Your manual notes here. Never touched.
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## carto impact
|
|
145
|
+
|
|
146
|
+
Before touching any file, know the blast radius:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
carto impact app/models.py
|
|
150
|
+
|
|
151
|
+
# Impact analysis: app/models.py
|
|
152
|
+
#
|
|
153
|
+
# Imported by:
|
|
154
|
+
# → app/main.py
|
|
155
|
+
# → app/rules.py
|
|
156
|
+
# → app/scoring.py
|
|
157
|
+
# → app/aws_collector.py
|
|
158
|
+
# → tests/conftest.py
|
|
159
|
+
#
|
|
160
|
+
# Routes affected:
|
|
161
|
+
# → POST /analyze
|
|
162
|
+
# → GET /history
|
|
163
|
+
# → POST /simulate
|
|
164
|
+
# → ... 12 more
|
|
165
|
+
#
|
|
166
|
+
# Risk: HIGH — 5 files depend on this
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Most production bugs aren't logic errors. They're *"I didn't know X depended on Y."* Carto makes that invisible knowledge visible before you break something.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## What Carto fixes
|
|
174
|
+
|
|
175
|
+
Carto fixes **factual hallucination about your own project**:
|
|
176
|
+
|
|
177
|
+
- AI guessing wrong routes → fixed
|
|
178
|
+
- AI guessing wrong field names → fixed
|
|
179
|
+
- AI assuming wrong framework → fixed
|
|
180
|
+
- AI guessing wrong DB schema → fixed
|
|
181
|
+
|
|
182
|
+
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.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Real test — cal.com (800k lines)
|
|
187
|
+
|
|
188
|
+
We ran the same task in two Claude sessions: *"Add a `notes` field to the booking model."*
|
|
189
|
+
|
|
190
|
+
**Without AGENTS.md:**
|
|
191
|
+
- Wrong API route: suggested `POST /api/bookings` → actual is `POST /v2/bookings`
|
|
192
|
+
- Wrong handler: suggested `handleNewBooking.ts` → not the creation path
|
|
193
|
+
- Wrong file paths: pointed to v1 API (`apps/api/v1/...`) → v1 is legacy
|
|
194
|
+
- Wrong tRPC file: `bookings.tsx` → actual is `bookings/_router.tsx`
|
|
195
|
+
- Field list: ~15 fields guessed → missing 20+ real fields
|
|
196
|
+
- Couldn't proceed without follow-up: *"Want me to write the exact diffs once you confirm the codebase location?"*
|
|
197
|
+
|
|
198
|
+
**With AGENTS.md (generated by Carto):**
|
|
199
|
+
- Correct API route: `POST /v2/bookings` ✅
|
|
200
|
+
- Correct controller path ✅
|
|
201
|
+
- Correct tRPC file ✅
|
|
202
|
+
- All 35+ booking fields returned accurately ✅
|
|
203
|
+
- Answered in one shot. No follow-up needed.
|
|
204
|
+
|
|
205
|
+
**4 wrong file paths → 0. 20 missing fields → 0. Zero follow-up clarifications.**
|
|
206
|
+
|
|
207
|
+
This is what Carto does. Not smarter AI. The same AI with accurate facts.
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## AI tools that read AGENTS.md
|
|
212
|
+
|
|
213
|
+
Drop the file in your project root. Each tool picks it up via its own context config:
|
|
214
|
+
|
|
215
|
+
- **Cursor** — via context rules
|
|
216
|
+
- **GitHub Copilot** — via workspace instructions
|
|
217
|
+
- **Kiro** — natively
|
|
218
|
+
- **Codex** — natively
|
|
219
|
+
- **VS Code** — via workspace context
|
|
220
|
+
- **Gemini CLI** — natively
|
|
221
|
+
- **Devin** — natively
|
|
222
|
+
- **Jules** — natively
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Tested on
|
|
227
|
+
|
|
228
|
+
- FastAPI + Python projects
|
|
229
|
+
- Next.js App Router
|
|
230
|
+
- Next.js + Prisma
|
|
231
|
+
- React + FastAPI monorepos
|
|
232
|
+
- Large monorepos (5000+ files — tested on Supabase and cal.com for stability, caps at 50 most important files on projects this scale)
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## What it does NOT do
|
|
237
|
+
|
|
238
|
+
- No cloud. No servers. No telemetry. No tracking.
|
|
239
|
+
- Your code never leaves your machine.
|
|
240
|
+
- No paid tiers. Free forever. MIT license.
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Security
|
|
245
|
+
|
|
246
|
+
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.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Contributing
|
|
251
|
+
|
|
252
|
+
Python and JS/TS are supported today. Community adds more.
|
|
253
|
+
|
|
254
|
+
- **Add a language**: `src/ast/languages/`
|
|
255
|
+
- **Add a framework**: `src/extractors/`
|
|
256
|
+
|
|
257
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## License
|
|
262
|
+
|
|
263
|
+
MIT — free forever.
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
*Built because AGENTS.md won. Someone had to keep it alive.*
|
package/package.json
CHANGED
package/src/cli/init.js
CHANGED
package/src/sync.js
CHANGED
|
@@ -56,30 +56,21 @@ async function scanStructure(basePath) {
|
|
|
56
56
|
*/
|
|
57
57
|
async function runFullSync(config) {
|
|
58
58
|
const warnings = [];
|
|
59
|
+
const projectRoot = config.projectRoot || process.cwd();
|
|
59
60
|
|
|
60
61
|
const allRouteFiles = config.watch.routeFiles || [];
|
|
61
62
|
const allModelFiles = config.watch.modelFiles || [];
|
|
62
63
|
const allFrontendFiles = config.watch.frontendFiles || [];
|
|
63
64
|
|
|
64
|
-
// Aggregate data
|
|
65
65
|
let allRoutes = [];
|
|
66
66
|
let allModels = [];
|
|
67
67
|
let allFetches = [];
|
|
68
68
|
let allStorageKeys = [];
|
|
69
|
-
|
|
70
|
-
// Functions: { filename: [{ name, params, returnType }] }
|
|
71
69
|
const functionsMap = {};
|
|
72
|
-
// Routes per file for file map
|
|
73
70
|
const routeCountMap = {};
|
|
74
|
-
// Env vars: { varName: Set([filename, ...]) }
|
|
75
71
|
const envVarMap = new Map();
|
|
76
|
-
// DB tables: [{ tableName, modelName, file }]
|
|
77
72
|
const dbTableList = [];
|
|
78
|
-
|
|
79
|
-
// Deduplicate files
|
|
80
73
|
const processedFiles = new Set();
|
|
81
|
-
|
|
82
|
-
// Process all code files (route + model files, deduplicated)
|
|
83
74
|
const allCodeFiles = [...new Set([...allRouteFiles, ...allModelFiles])];
|
|
84
75
|
|
|
85
76
|
for (const filePath of allCodeFiles) {
|
|
@@ -90,45 +81,35 @@ async function runFullSync(config) {
|
|
|
90
81
|
if (!content) continue;
|
|
91
82
|
|
|
92
83
|
const basename = path.basename(filePath);
|
|
93
|
-
const relPath = path.relative(
|
|
84
|
+
const relPath = path.relative(projectRoot, filePath);
|
|
94
85
|
const plugin = getPluginForFile(plugins, filePath);
|
|
95
86
|
|
|
96
|
-
if (!plugin)
|
|
97
|
-
// No plugin for this file type — skip silently
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
87
|
+
if (!plugin) continue;
|
|
100
88
|
|
|
101
89
|
const result = plugin.extract(content, relPath);
|
|
102
90
|
|
|
103
|
-
// Routes
|
|
104
91
|
allRoutes = allRoutes.concat(result.routes);
|
|
105
92
|
routeCountMap[filePath] = result.routes.length;
|
|
106
93
|
|
|
107
|
-
// Models
|
|
108
94
|
allModels = allModels.concat(result.models);
|
|
109
95
|
|
|
110
|
-
// Functions
|
|
111
96
|
if (result.functions.length > 0 && basename !== '__init__.py') {
|
|
112
97
|
functionsMap[basename] = result.functions;
|
|
113
98
|
}
|
|
114
99
|
|
|
115
|
-
// Env vars
|
|
116
100
|
for (const varName of result.envVars) {
|
|
117
101
|
if (!envVarMap.has(varName)) envVarMap.set(varName, new Set());
|
|
118
102
|
envVarMap.get(varName).add(basename);
|
|
119
103
|
}
|
|
120
104
|
|
|
121
|
-
// DB tables
|
|
122
105
|
for (const t of result.dbTables) {
|
|
123
106
|
dbTableList.push({ tableName: t.tableName, modelName: t.modelName, file: basename });
|
|
124
107
|
}
|
|
125
108
|
|
|
126
|
-
// Fetches and storage keys (from JS/HTML plugins)
|
|
127
109
|
allFetches = allFetches.concat(result.fetches);
|
|
128
110
|
allStorageKeys = allStorageKeys.concat(result.storageKeys);
|
|
129
111
|
}
|
|
130
112
|
|
|
131
|
-
// Process frontend files separately (may overlap with code files)
|
|
132
113
|
for (const filePath of allFrontendFiles) {
|
|
133
114
|
if (processedFiles.has(filePath)) continue;
|
|
134
115
|
processedFiles.add(filePath);
|
|
@@ -146,12 +127,10 @@ async function runFullSync(config) {
|
|
|
146
127
|
allStorageKeys = allStorageKeys.concat(result.storageKeys);
|
|
147
128
|
}
|
|
148
129
|
|
|
149
|
-
// Global dedup: collapse dynamic fetches across all files into one summary row
|
|
150
130
|
const staticFetches = allFetches.filter(f => f.url !== '[dynamic]' && !f.url.startsWith('dynamic calls detected'));
|
|
151
131
|
let totalDynamic = 0;
|
|
152
132
|
for (const f of allFetches) {
|
|
153
133
|
if (f.url === '[dynamic]') totalDynamic++;
|
|
154
|
-
// Also count already-collapsed per-file rows
|
|
155
134
|
const m = f.url.match(/^dynamic calls detected \((\d+) unresolved\)$/);
|
|
156
135
|
if (m) totalDynamic += parseInt(m[1], 10);
|
|
157
136
|
}
|
|
@@ -160,7 +139,6 @@ async function runFullSync(config) {
|
|
|
160
139
|
}
|
|
161
140
|
allFetches = staticFetches;
|
|
162
141
|
|
|
163
|
-
// Global dedup: storage keys across all files
|
|
164
142
|
const skSeen = new Set();
|
|
165
143
|
allStorageKeys = allStorageKeys.filter(({ operation, key }) => {
|
|
166
144
|
const id = `${operation}::${key}`;
|
|
@@ -169,10 +147,8 @@ async function runFullSync(config) {
|
|
|
169
147
|
return true;
|
|
170
148
|
});
|
|
171
149
|
|
|
172
|
-
// Build import graph from all processed files
|
|
173
150
|
const fileContentsForImports = [];
|
|
174
151
|
const allProcessedPaths = [...new Set([...allCodeFiles, ...allFrontendFiles])];
|
|
175
|
-
// Re-read is avoided — collect during processing. Use a second pass for simplicity.
|
|
176
152
|
for (const filePath of allProcessedPaths) {
|
|
177
153
|
try {
|
|
178
154
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
@@ -181,21 +157,17 @@ async function runFullSync(config) {
|
|
|
181
157
|
// skip — already warned during extraction
|
|
182
158
|
}
|
|
183
159
|
}
|
|
184
|
-
const importGraph = buildImportGraph(fileContentsForImports,
|
|
160
|
+
const importGraph = buildImportGraph(fileContentsForImports, projectRoot);
|
|
185
161
|
|
|
186
|
-
|
|
187
|
-
const stackItems = buildStackLine(fileContentsForImports, config.projectRoot);
|
|
162
|
+
const stackItems = buildStackLine(fileContentsForImports, projectRoot);
|
|
188
163
|
|
|
189
|
-
// Compute entry points and high impact files from import graph
|
|
190
164
|
const allValues = new Set();
|
|
191
165
|
for (const deps of Object.values(importGraph)) {
|
|
192
166
|
for (const dep of deps) allValues.add(dep);
|
|
193
167
|
}
|
|
194
|
-
// Entry points: files that import 3+ others but nothing imports them
|
|
195
168
|
const entryPoints = Object.keys(importGraph)
|
|
196
169
|
.filter(f => !allValues.has(f) && importGraph[f].length >= 3)
|
|
197
170
|
.sort();
|
|
198
|
-
// High impact: files imported by 3+ others, sorted descending by count
|
|
199
171
|
const depCount = {};
|
|
200
172
|
for (const deps of Object.values(importGraph)) {
|
|
201
173
|
for (const dep of deps) {
|
|
@@ -207,11 +179,10 @@ async function runFullSync(config) {
|
|
|
207
179
|
.sort((a, b) => b[1] - a[1])
|
|
208
180
|
.map(([file, count]) => ({ file, count }));
|
|
209
181
|
|
|
210
|
-
// Build file map
|
|
211
182
|
const fileMap = [];
|
|
212
183
|
for (const filePath of allCodeFiles) {
|
|
213
184
|
const basename = path.basename(filePath);
|
|
214
|
-
const relPath = path.relative(
|
|
185
|
+
const relPath = path.relative(projectRoot, filePath);
|
|
215
186
|
const funcCount = (functionsMap[basename] || []).length;
|
|
216
187
|
const routeCount = routeCountMap[filePath] || 0;
|
|
217
188
|
const responsibility = inferResponsibility(basename, funcCount, routeCount);
|
|
@@ -220,15 +191,12 @@ async function runFullSync(config) {
|
|
|
220
191
|
}
|
|
221
192
|
}
|
|
222
193
|
|
|
223
|
-
// Aggregate env vars into sorted array
|
|
224
194
|
const envVars = [...envVarMap.keys()]
|
|
225
195
|
.sort()
|
|
226
196
|
.map(name => ({ name, files: [...envVarMap.get(name)].sort() }));
|
|
227
197
|
|
|
228
|
-
|
|
229
|
-
const structure = await scanStructure(config.projectRoot);
|
|
198
|
+
const structure = await scanStructure(projectRoot);
|
|
230
199
|
|
|
231
|
-
// Validate extracted data — drop anything malformed
|
|
232
200
|
const validated = validateExtracted({
|
|
233
201
|
routes: allRoutes,
|
|
234
202
|
models: allModels,
|
|
@@ -255,8 +223,7 @@ async function runFullSync(config) {
|
|
|
255
223
|
|
|
256
224
|
mergeIntoAgentsMd(config.output, autoContent);
|
|
257
225
|
|
|
258
|
-
|
|
259
|
-
const cartoDir = path.join(config.projectRoot, '.carto');
|
|
226
|
+
const cartoDir = path.join(projectRoot, '.carto');
|
|
260
227
|
const mapData = {
|
|
261
228
|
version: '1',
|
|
262
229
|
generated: new Date().toISOString(),
|