carto-md 2.0.1 → 2.0.2

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,34 @@
1
+ # Carto V2 Benchmark Results
2
+
3
+ Generated: 2026-05-28T17:58:28.019Z
4
+ Platform: Node v20.20.1 · 8 CPUs · 8192.0 MB RAM · darwin arm64
5
+
6
+ | Repo | Source Files | Indexed | First Run | Second Run | DB Size | Routes | Import Edges |
7
+ |------|-------------|---------|-----------|------------|---------|--------|--------------|
8
+ | prisma | 3303 | 3303 | 1.6s | 178ms | 2.2 MB | 10 | 3590 |
9
+ | supabase | 6818 | 6746 | 4.9s | 725ms | 4.3 MB | 90 | 5754 |
10
+ | vscode | 10565 | 10565 | 9.7s | 1.2s | 10.6 MB | 11 | 19769 |
11
+ | zed | 1837 | 1837 | 2.7s | 83ms | 4.7 MB | 12 | 2176 |
12
+
13
+ ## Domains Detected
14
+
15
+ **prisma:** DATABASE(1044) · CORE(752) · EVENTS(36) · AUTH(8)
16
+ **supabase:** CORE(4243) · AUTH(412) · DATABASE(387) · PAYMENTS(51) · EVENTS(17) · NOTIFICATIONS(12) · TRPC(3)
17
+ **vscode:** CORE(5330) · AUTH(850) · EVENTS(446) · DATABASE(419) · NOTIFICATIONS(5)
18
+ **zed:** CORE(1424) · DATABASE(110) · AUTH(75) · EVENTS(55) · PAYMENTS(53) · NOTIFICATIONS(10) · TRPC(2)
19
+
20
+ ## MCP Query Latency
21
+
22
+ | Repo | get_structure | get_routes | get_domains_list |
23
+ |------|--------------|------------|-----------------|
24
+ | prisma | 0ms | 0ms | 1ms |
25
+ | supabase | 1ms | 0ms | 3ms |
26
+ | vscode | 1ms | 0ms | 3ms |
27
+ | zed | 0ms | 0ms | 1ms |
28
+
29
+ ## Target Assessment
30
+
31
+ - **prisma**: ✅ All targets met
32
+ - **supabase**: ✅ All targets met
33
+ - **vscode**: ✅ All targets met
34
+ - **zed**: ✅ All targets met
package/CONTRIBUTING.md CHANGED
@@ -215,7 +215,7 @@ cd carto
215
215
  npm install
216
216
  node src/cli/index.js init # test in any project
217
217
  node src/cli/index.js serve # test MCP server
218
- npm test # run test suite (30 tests)
218
+ npm test # run test suite (35 tests)
219
219
  node test/correctness.js # run correctness tests (31 tests)
220
220
  node test/benchmark.js # run benchmarks against real repos
221
221
  ```
@@ -232,7 +232,7 @@ node test/benchmark.js # run benchmarks against real repos
232
232
  - [ ] Extension added to `CODE_EXTS` and `detectLanguage()` in `sync-v2.js`
233
233
  - [ ] No changes to merger logic (unless explicitly fixing a merger bug)
234
234
  - [ ] No network calls added
235
- - [ ] `npm test` passes (30/30)
235
+ - [ ] `npm test` passes (35/35)
236
236
  - [ ] `node test/correctness.js` passes (31/31)
237
237
 
238
238
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "carto-md",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
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"
@@ -16,6 +16,9 @@ function formatSections({ routes, models, frontend, structure, warnings, fileMap
16
16
  sections.push(`- ${icon} ${entry.name}${suffix}`);
17
17
  }
18
18
  } else {
19
+ if (process.env.CARTO_DEBUG) {
20
+ console.warn('[CARTO] Warning: structure data was empty when formatting AGENTS.md');
21
+ }
19
22
  sections.push('_No structure data available._');
20
23
  }
21
24
 
@@ -0,0 +1,69 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+
5
+ /**
6
+ * IGNORE_DIRS — names skipped at the top level when listing project structure.
7
+ *
8
+ * This is intentionally a *small* set tuned for the "Project Structure (auto)"
9
+ * block in AGENTS.md. It is NOT the same as the recursive file-discovery
10
+ * ignore lists (e.g. JS_IGNORE / PYTHON_IGNORE in src/detector/files.js or
11
+ * IGNORE_DIRS in src/store/sync-v2.js) — those filter what gets indexed.
12
+ *
13
+ * Top-level structure should still surface things like `dist/`, `build/`,
14
+ * `coverage/` (so users see what their project actually contains), but it
15
+ * should hide noise (`node_modules`, `.git`, `.carto`, etc.) and the file
16
+ * the structure block is about to be merged into (`AGENTS.md`).
17
+ *
18
+ * Anchored on the original V1 set in src/sync.js so existing AGENTS.md
19
+ * outputs do not drift after the V1 → V2 cleanup.
20
+ */
21
+ const IGNORE_DIRS = new Set([
22
+ 'node_modules',
23
+ '.git',
24
+ '__pycache__',
25
+ '.venv',
26
+ 'venv',
27
+ '.idea',
28
+ '.vscode',
29
+ '.carto',
30
+ 'AGENTS.md'
31
+ ]);
32
+
33
+ /**
34
+ * scanStructure(basePath) → Array<{ name: string, type: 'dir' | 'file' }>
35
+ *
36
+ * Lists the immediate children of `basePath` (one level deep, no recursion).
37
+ * Filters out IGNORE_DIRS. Sorts: directories before files; alphabetical
38
+ * within each group.
39
+ *
40
+ * Symlinks are reported by Dirent as neither dir nor file → treated as 'file'.
41
+ * Failures (missing dir, EACCES, etc.) return an empty array silently — the
42
+ * formatter handles the empty case.
43
+ */
44
+ async function scanStructure(basePath) {
45
+ const entries = [];
46
+ let items;
47
+ try {
48
+ items = await fs.promises.readdir(basePath, { withFileTypes: true });
49
+ } catch {
50
+ return entries;
51
+ }
52
+
53
+ for (const item of items) {
54
+ if (IGNORE_DIRS.has(item.name)) continue;
55
+ entries.push({
56
+ name: item.name,
57
+ type: item.isDirectory() ? 'dir' : 'file'
58
+ });
59
+ }
60
+
61
+ entries.sort((a, b) => {
62
+ if (a.type !== b.type) return a.type === 'dir' ? -1 : 1;
63
+ return a.name.localeCompare(b.name);
64
+ });
65
+
66
+ return entries;
67
+ }
68
+
69
+ module.exports = { scanStructure, IGNORE_DIRS };
@@ -13,6 +13,7 @@ const { clusterByDomain } = require('../agents/domains');
13
13
  const { clusterByGraph } = require('../agents/leiden');
14
14
  const { formatSections, formatDomainFile } = require('../agents/formatter');
15
15
  const { mergeIntoAgentsMd } = require('../agents/merger');
16
+ const { scanStructure } = require('../agents/scan-structure');
16
17
  const { WorkerPool, POOL_SIZE } = require('../engine/worker-pool');
17
18
  const { parseCartoIgnore } = require('../security/ignore');
18
19
 
@@ -420,7 +421,7 @@ async function runSyncV2(config) {
420
421
 
421
422
  // 8. Generate outputs (only if files changed)
422
423
  if (toProcess.length > 0) {
423
- generateOutputs(store, config, projectRoot, store.getImportGraph());
424
+ await generateOutputs(store, config, projectRoot, store.getImportGraph());
424
425
  }
425
426
 
426
427
  console.log(`[CARTO] Indexed ${toProcess.length} files (${cached} cached) in ${elapsed}ms`);
@@ -461,12 +462,13 @@ function buildFileDataFromStore(store) {
461
462
  /**
462
463
  * Generate backward-compatible outputs (AGENTS.md, context files, map.json)
463
464
  */
464
- function generateOutputs(store, config, projectRoot, importGraph) {
465
+ async function generateOutputs(store, config, projectRoot, importGraph) {
465
466
  const structure = store.getStructure();
466
467
  const routes = store.getRoutes();
467
468
  const models = store.getModels();
468
469
  const envVars = store.getEnvVars();
469
470
  const domains = store.getDomainsList();
471
+ const topLevelStructure = await scanStructure(projectRoot);
470
472
 
471
473
  // Write domain context files — lazy: only write if stale or missing
472
474
  const contextDir = path.join(projectRoot, '.carto', 'context');
@@ -518,7 +520,7 @@ function generateOutputs(store, config, projectRoot, importGraph) {
518
520
  routes,
519
521
  models,
520
522
  frontend: { fetches: [], storageKeys: [] },
521
- structure: [],
523
+ structure: topLevelStructure,
522
524
  warnings: [],
523
525
  fileMap: [],
524
526
  functions: {},
package/src/sync.js CHANGED
@@ -13,8 +13,7 @@ const { buildStackLine } = require('./extractors/stack');
13
13
  const { loadHashes, saveHashes, computeChangedFiles } = require('./cache/file-hash');
14
14
  const { loadGraphCache, saveGraphCache, buildEmptyCache } = require('./cache/graph-cache');
15
15
  const { applyIncrementalUpdate, recomputeGraphMetrics } = require('./engine/incremental');
16
-
17
- const IGNORE_DIRS = new Set(['node_modules', '.git', '__pycache__', '.venv', 'venv', '.idea', '.vscode', '.carto', 'AGENTS.md']);
16
+ const { scanStructure } = require('./agents/scan-structure');
18
17
 
19
18
  // Load plugins once at module load
20
19
  const plugins = loadLanguagePlugins();
@@ -29,22 +28,6 @@ async function safeReadFile(filePath, warnings) {
29
28
  }
30
29
  }
31
30
 
32
- async function scanStructure(basePath) {
33
- const entries = [];
34
- try {
35
- const items = await fs.promises.readdir(basePath, { withFileTypes: true });
36
- for (const item of items) {
37
- if (IGNORE_DIRS.has(item.name)) continue;
38
- entries.push({ name: item.name, type: item.isDirectory() ? 'dir' : 'file' });
39
- }
40
- entries.sort((a, b) => {
41
- if (a.type !== b.type) return a.type === 'dir' ? -1 : 1;
42
- return a.name.localeCompare(b.name);
43
- });
44
- } catch {}
45
- return entries;
46
- }
47
-
48
31
  /**
49
32
  * runFullSync(config)
50
33
  *
package/acp-strategy.md DELETED
@@ -1,480 +0,0 @@
1
- # Carto ACP Agent — Strategy & Implementation Plan
2
-
3
- > Build the brain, not the editor. Ship to every IDE in weeks.
4
-
5
- ---
6
-
7
- ## What is ACP?
8
-
9
- **Agent Client Protocol (ACP)** is an open standard created by Zed Industries that lets any AI coding agent work inside any compatible editor — without custom integrations.
10
-
11
- - **LSP** = one standard so any editor supports any programming language
12
- - **ACP** = one standard so any editor supports any AI coding agent
13
-
14
- **Transport:** JSON-RPC 2.0 over stdin/stdout (local) or HTTP/WebSocket (remote)
15
- **Editors:** Zed, JetBrains (IntelliJ, WebStorm, etc.), VS Code (in progress)
16
- **SDK:** `@agentclientprotocol/sdk` (TypeScript/JS, Python, Rust, Kotlin, Java)
17
-
18
- ---
19
-
20
- ## The Core Idea
21
-
22
- Instead of building a standalone editor, build Carto as an **ACP agent** that plugs into existing editors. Carto already has the structural intelligence — ACP gives it hands.
23
-
24
- ```
25
- ┌─────────────────────────────────────────┐
26
- │ Zed / JetBrains / VS Code │
27
- │ ┌───────────────────────────────────┐ │
28
- │ │ Agent Panel → "Carto" thread │ │
29
- │ │ │ │
30
- │ │ User: "Add rate limiting to │ │
31
- │ │ /api/users" │ │
32
- │ │ │ │
33
- │ │ Carto: I see AUTH domain has │ │
34
- │ │ 12 files, 3 routes. Highest │ │
35
- │ │ impact: auth/session.ts │ │
36
- │ │ (8 dependents). Here's my plan: │ │
37
- │ │ [shows file diffs inline] │ │
38
- │ └───────────────────────────────────┘ │
39
- └─────────────────────────────────────────┘
40
- ↕ ACP (stdin/stdout)
41
- ┌─────────────────────────────────────────┐
42
- │ carto agent (Node.js process) │
43
- │ ├── Carto structural intelligence │
44
- │ ├── LLM calls (user's own API key) │
45
- │ └── File edits streamed back │
46
- └─────────────────────────────────────────┘
47
- ```
48
-
49
- ---
50
-
51
- ## How Data Flows (MCP vs ACP)
52
-
53
- ### Current: MCP (passive tool server)
54
-
55
- ```
56
- External AI (Claude/Cursor) decides to call a tool
57
-
58
- get_blast_radius("auth.service.ts")
59
-
60
- Carto returns a SUMMARY → "3 files affected, risk HIGH"
61
-
62
- AI uses summary to answer (may miss context, can't read files through Carto)
63
- ```
64
-
65
- **Problem:** LLM gets summaries, not full context. It has to guess which tools to call. It can't read actual file contents through Carto.
66
-
67
- ### New: ACP (active intelligent agent)
68
-
69
- ```
70
- User asks: "Add rate limiting to /api/users"
71
-
72
- Carto agent receives message
73
-
74
- Carto INTERNALLY queries its own SQLite:
75
- - Blast radius of relevant files
76
- - Domain context (AUTH)
77
- - Similar patterns in codebase
78
- - Import graph neighbors
79
-
80
- Carto reads ACTUAL file contents via ACP fileSystem
81
-
82
- Carto builds a RICH prompt with all context + file contents
83
-
84
- Sends to LLM (user's API key) → gets response
85
-
86
- Streams answer + file diffs back to editor
87
- ```
88
-
89
- **Key difference:** Carto controls the LLM call. It proactively provides the right context instead of waiting for the LLM to ask.
90
-
91
- ### What the LLM receives (built by Carto):
92
-
93
- | Data | How it gets to the LLM |
94
- |------|----------------------|
95
- | Structural facts (blast radius, domains, routes) | Carto queries SQLite internally → injects into system prompt |
96
- | Relevant file contents (actual code) | Carto reads via ACP fileSystem → includes in prompt |
97
- | Domain context (`.carto/context/AUTH.md` etc.) | Carto reads internally → includes relevant domain |
98
- | Import graph | Carto summarizes relevant portion → injects |
99
- | Similar patterns | Carto finds matching files → includes as examples |
100
-
101
- Carto acts as a **smart filter** — only sends what's relevant. No token waste.
102
-
103
- ---
104
-
105
- ## Auto-Index on First Session
106
-
107
- When a user opens a Carto agent thread in any project:
108
-
109
- ```
110
- User opens Carto agent thread in Zed
111
-
112
- Carto checks: does .carto/carto.db exist?
113
-
114
- NO → automatically runs full indexing
115
-
116
- • Discovers all source files
117
- • tree-sitter parse → imports + symbols (0.05–0.2ms/file)
118
- • Babel deep parse → routes + models (API files only)
119
- • Leiden+CPM graph clustering → domain detection
120
- • Computes reverse deps → blast radius
121
- • Writes .carto/carto.db + context/*.md
122
-
123
- Takes 1-10 seconds depending on repo size:
124
- • Prisma (3,303 files): 1.6s
125
- • Supabase (6,818 files): 4.9s
126
- • VS Code (10,565 files): 9.7s
127
-
128
- Agent responds: "Indexed! Found X files, Y routes, Z domains. How can I help?"
129
-
130
- YES → opens SQLite instantly (<10ms), ready to go
131
- ```
132
-
133
- **On subsequent sessions:** No re-indexing. SQLite opens in <10ms.
134
-
135
- **On file changes:** If `carto watch` is running, DB stays live. If not, agent can do a quick mtime check on session start (178ms–1.2s for unchanged repos).
136
-
137
- ### What gets created on disk:
138
-
139
- ```
140
- project/
141
- ├── .carto/
142
- │ ├── carto.db ← SQLite (import graph, routes, models, domains, blast radius)
143
- │ ├── config.json ← project settings
144
- │ └── context/
145
- │ ├── AUTH.md ← domain context (auto-generated)
146
- │ ├── CORE.md
147
- │ ├── DATABASE.md
148
- │ └── PAYMENTS.md
149
- ├── AGENTS.md ← still generated (useful for other MCP tools)
150
- └── src/...
151
- ```
152
-
153
- Same as today. ACP doesn't change what Carto creates — just who reads it.
154
-
155
- ---
156
-
157
- ## Supported LLM Providers (BYOK)
158
-
159
- Carto currently has **zero LLM integration** — it's purely structural intelligence. The ACP agent adds LLM calls. We support any provider the user brings:
160
-
161
- ### Tier 1 — Full support (streaming, tool use, all models)
162
-
163
- | Provider | Protocol | Models |
164
- |----------|----------|--------|
165
- | **OpenAI** | `openai` | GPT-4o, GPT-4o-mini, o1, o3 |
166
- | **Anthropic** | `anthropic` | Claude Sonnet 4, Claude Haiku |
167
- | **Google Gemini** | `openai` (compatible) | Gemini 2.5 Pro, 2.5 Flash |
168
-
169
- ### Tier 2 — Works via OpenAI-compatible API
170
-
171
- | Provider | Protocol | Notes |
172
- |----------|----------|-------|
173
- | **Azure OpenAI** | `azure` | Enterprise deployments |
174
- | **AWS Bedrock** | `bedrock` | Claude/Titan via AWS |
175
- | **Ollama** | `openai` | Local models (Llama, Mistral, Qwen, DeepSeek) |
176
- | **OpenRouter** | `openai` | Any model via single API |
177
- | **Together AI** | `openai` | Open-source models hosted |
178
- | **Groq** | `openai` | Ultra-fast inference |
179
- | **LM Studio** | `openai` | Local GUI + API |
180
- | **vLLM** | `openai` | Self-hosted inference |
181
-
182
- ### How BYOK works in ACP:
183
-
184
- ```
185
- 1. User installs Carto agent in Zed/JetBrains
186
- 2. Editor calls providers/list → Carto returns supported providers
187
- 3. User configures their key in editor settings:
188
- - API key (stored securely by editor)
189
- - Base URL (for self-hosted/proxy)
190
- - Model name
191
- 4. Editor calls providers/set → Carto stores config
192
- 5. All LLM calls use user's key — zero cost to us
193
- ```
194
-
195
- ### Provider config stored in:
196
-
197
- ```json
198
- // .carto/agent-config.json (gitignored)
199
- {
200
- "provider": {
201
- "apiType": "anthropic",
202
- "baseUrl": "https://api.anthropic.com",
203
- "model": "claude-sonnet-4-20250514"
204
- }
205
- }
206
- ```
207
-
208
- API keys are NEVER stored by Carto — they're passed via ACP `providers/set` headers at runtime and held in memory only.
209
-
210
- ### Cost to user:
211
-
212
- | Usage Level | Tokens/day | Claude Sonnet | Gemini Flash (free) | Ollama (local) |
213
- |-------------|-----------|---------------|--------------------|----|
214
- | Light (5 tasks) | ~50K | ~$0.15 | $0.00 | $0.00 |
215
- | Medium (15 tasks) | ~150K | ~$0.50 | $0.00 | $0.00 |
216
- | Heavy (30+ tasks) | ~400K | ~$1.20 | $0.00 (rate limits) | $0.00 |
217
- | Power user | ~1M | ~$3.00 | Needs paid tier | $0.00 |
218
-
219
- ---
220
-
221
- ## What ACP Gives You For Free
222
-
223
- Everything the editor handles natively — zero code from us:
224
-
225
- | Feature | ACP / Editor Handles It |
226
- |---------|------------------------|
227
- | Chat panel UI | ✅ Zed/JetBrains Agent Panel |
228
- | Multiple chats / threads | ✅ Built-in threads sidebar |
229
- | Conversation persistence & resume | ✅ Editor manages sessions |
230
- | Stop / Interrupt button | ✅ Native editor stop |
231
- | Inline diffs with Accept / Reject | ✅ ACP native diff content type |
232
- | "Open in Editor" for large diffs | ✅ Native editor behavior |
233
- | Tool call visibility (collapsed) | ✅ Editor renders agent plan |
234
- | File read / write / create / delete | ✅ ACP `fileSystem` capability |
235
- | Terminal / command execution | ✅ ACP `terminals` capability |
236
- | Streaming token responses | ✅ ACP streams natively |
237
- | BYOK (Bring Your Own Key) | ✅ ACP `providers/set` |
238
- | Session list / resume old chats | ✅ ACP `session/list`, `session/resume` |
239
- | Redirect mid-task | ✅ Editor supports new message while agent runs |
240
- | Keyboard shortcuts | ✅ Editor-native |
241
-
242
- **~75-80% of the chat.md spec is free from ACP.**
243
-
244
- ---
245
-
246
- ## What We Build (The Actual Product)
247
-
248
- ### 1. Auto-Context Injection
249
-
250
- Every user message gets enriched with structural context before hitting the LLM:
251
-
252
- ```javascript
253
- // What we inject into the system prompt (user doesn't see this)
254
- const context = carto.getContextForFile(session.currentFile);
255
-
256
- // Becomes:
257
- `Current file: src/auth/auth.service.ts
258
- Domain: AUTH
259
- Blast radius: 3 routes, 2 services affected
260
- - Directly affected: auth.routes.ts, auth.controller.ts, auth.middleware.ts
261
- - Routes impacted: POST /auth/login, GET /auth/me, POST /auth/register
262
- Import graph: session.service.ts, user.repository.ts
263
- Imported by: auth.routes.ts, auth.middleware.ts`
264
- ```
265
-
266
- ### 2. Carto Intelligence as Internal Tools
267
-
268
- The LLM can call these during reasoning (Carto handles them internally):
269
-
270
- ```
271
- get_blast_radius(file) → full impact analysis
272
- get_structure() → project architecture
273
- get_domain(name) → domain routes, models, files
274
- get_routes() → all API endpoints
275
- get_change_plan(intent) → files to touch, similar patterns
276
- get_context(file) → everything about a file
277
- get_similar_patterns(file) → conventions to follow
278
- ```
279
-
280
- ### 3. The Agent Loop
281
-
282
- ```javascript
283
- async function handlePrompt(userMessage, session) {
284
- // 1. Auto-index if needed
285
- if (!dbExists(session.workingDir)) {
286
- await indexProject(session.workingDir);
287
- stream("Indexed! Found X files, Y routes, Z domains.\n\n");
288
- }
289
-
290
- // 2. Get structural context for current file
291
- const context = carto.getContextForFile(session.currentFile);
292
-
293
- // 3. Build messages with rich context
294
- const messages = [
295
- { role: "system", content: buildSystemPrompt(context) },
296
- ...session.history,
297
- { role: "user", content: userMessage }
298
- ];
299
-
300
- // 4. Agent loop
301
- let iterations = 0;
302
- while (iterations++ < 25) {
303
- const response = await callLLM(messages, {
304
- provider: session.provider,
305
- tools: [...CARTO_TOOLS, ...FILE_TOOLS],
306
- stream: true
307
- });
308
-
309
- for (const block of response) {
310
- if (block.type === "text") stream(block.text);
311
- if (block.type === "tool_use") {
312
- const result = await executeTool(block, session);
313
- messages.push(toolResult(block.id, result));
314
- }
315
- }
316
-
317
- if (noMoreToolCalls(response)) break;
318
- }
319
- }
320
- ```
321
-
322
- ### 4. System Prompt
323
-
324
- ```
325
- You are Carto, an AI coding agent with deep architectural awareness.
326
-
327
- You understand this project's structure before writing a single line:
328
- - Every file's blast radius (what breaks if this changes)
329
- - All API routes and which files define them
330
- - Domain clusters (AUTH, PAYMENTS, DATABASE, etc.)
331
- - The full import graph and cross-domain dependencies
332
-
333
- RULES:
334
- 1. Always check blast radius before making changes to high-impact files
335
- 2. Reference real file names and routes — never hallucinate paths
336
- 3. Make minimal, focused changes — read first, then write
337
- 4. Warn the user if a change crosses domain boundaries
338
- 5. After changes, confirm what was changed and what it affects
339
- 6. Use existing patterns (check get_similar_patterns before writing new code)
340
- 7. If unsure, read the relevant files — don't guess
341
-
342
- CONTEXT (auto-provided every message):
343
- Current file: {filePath}
344
- Domain: {domain}
345
- Blast radius: {blastRadius}
346
- Imports: {imports}
347
- Imported by: {importedBy}
348
- ```
349
-
350
- ---
351
-
352
- ## What You Lose vs Standalone Editor
353
-
354
- | Spec Feature | Reality with ACP |
355
- |---|---|
356
- | Custom UI layout (exact mockup) | ❌ Editor's standard agent panel |
357
- | `📖 🎯 ⚡` icons per tool type | ❌ Editor decides tool call rendering |
358
- | Auto-context badge visible to user | ❌ Context is invisible (sent to LLM only) |
359
- | Custom storage format | ❌ Editor manages persistence |
360
- | Custom keyboard shortcuts | ❌ Editor controls shortcuts |
361
-
362
- **All cosmetic.** The intelligence differentiators are fully preserved.
363
-
364
- ---
365
-
366
- ## ACP vs Building Standalone
367
-
368
- | | Standalone Editor | ACP Agent |
369
- |---|---|---|
370
- | Time to ship V1 | 6-12 months | 4-6 weeks |
371
- | UI control | Full | Limited (editor's panel) |
372
- | Editor reach | Only ours | Zed + JetBrains + VS Code |
373
- | Core intelligence | ✅ | ✅ (same) |
374
- | Blast radius / domain awareness | ✅ | ✅ (same) |
375
- | BYOK | Build it | Free from ACP |
376
- | Diff viewer | Build it | Free from ACP |
377
- | Terminal integration | Build it | Free from ACP |
378
- | Session persistence | Build it | Free from ACP |
379
- | LLM provider switching | Build it | Free from ACP |
380
-
381
- ---
382
-
383
- ## Implementation Plan
384
-
385
- ### Phase 1 — Core Agent (Week 1-2)
386
-
387
- - [ ] Add `carto agent` CLI command (starts ACP mode via stdin/stdout)
388
- - [ ] Implement `AgentSideConnection` with capabilities: `fileSystem`, `terminals`
389
- - [ ] Auto-index on first session (detect missing `.carto/carto.db` → run sync)
390
- - [ ] Wire Carto tools as internal agent tools
391
- - [ ] Build agent loop (context injection → LLM call → tool routing → stream back)
392
- - [ ] Write system prompt with auto-context
393
-
394
- ### Phase 2 — LLM Providers / BYOK (Week 2-3)
395
-
396
- - [ ] Implement `providers/list` — advertise supported providers
397
- - [ ] Implement `providers/set` — accept API key + base URL + model
398
- - [ ] OpenAI provider (GPT-4o, GPT-4o-mini)
399
- - [ ] Anthropic provider (Claude Sonnet, Haiku)
400
- - [ ] Gemini provider (via OpenAI-compatible endpoint)
401
- - [ ] Generic OpenAI-compatible provider (Ollama, OpenRouter, Together, Groq, LM Studio, vLLM)
402
- - [ ] Azure OpenAI provider
403
- - [ ] Store model config in `.carto/agent-config.json` (gitignored, no secrets)
404
-
405
- ### Phase 3 — Polish (Week 3-4)
406
-
407
- - [ ] 25-iteration safety cap with pause + user prompt
408
- - [ ] Context overflow handling (auto-summarize old messages)
409
- - [ ] Blast radius check before AND after file writes
410
- - [ ] Cross-domain change warnings
411
- - [ ] Register in ACP Registry (discoverable from Zed/JetBrains)
412
-
413
- ### Phase 4 — Advanced (Week 5+)
414
-
415
- - [ ] Parallel agent support (multiple Carto threads on different tasks)
416
- - [ ] Slash commands (`/blast`, `/domain`, `/routes`)
417
- - [ ] Agent plan display (show what Carto intends to do before doing it)
418
- - [ ] If traction warrants: standalone editor with full UI control
419
-
420
- ---
421
-
422
- ## Technical Setup
423
-
424
- ```bash
425
- # Install ACP SDK
426
- npm install @agentclientprotocol/sdk
427
-
428
- # CLI commands
429
- carto serve # existing — MCP mode (passive tools, keep as-is)
430
- carto agent # new — ACP mode (active agent, stdin/stdout)
431
- ```
432
-
433
- ### File structure (new code):
434
-
435
- ```
436
- src/
437
- ├── acp/
438
- │ ├── agent.js ← ACP AgentSideConnection setup
439
- │ ├── session.js ← Session management + auto-index
440
- │ ├── prompt.js ← System prompt builder + context injection
441
- │ ├── tools.js ← Carto tools exposed to LLM
442
- │ └── providers/
443
- │ ├── index.js ← Provider registry + BYOK logic
444
- │ ├── openai.js ← OpenAI/compatible provider
445
- │ ├── anthropic.js ← Anthropic provider
446
- │ └── types.js ← Shared types
447
- ├── cli/
448
- │ ├── index.js ← add "agent" command
449
- │ └── agent.js ← new: starts ACP mode
450
- ├── mcp/ ← existing MCP server (unchanged)
451
- └── ... ← existing Carto intelligence (unchanged)
452
- ```
453
-
454
- ---
455
-
456
- ## References
457
-
458
- - **ACP TypeScript SDK:** `@agentclientprotocol/sdk` ([npm](https://www.npmjs.com/package/@agentclientprotocol/sdk))
459
- - **ACP Spec:** https://agentclientprotocol.com
460
- - **Reference impl:** [Gemini CLI ACP](https://github.com/google-gemini/gemini-cli/blob/main/packages/cli/src/zed-integration/zedIntegration.ts)
461
- - **ACP Registry:** https://zed.dev/blog/acp-registry
462
- - **Configurable LLM Providers RFD:** https://agentclientprotocol.com/rfds/custom-llm-endpoint
463
-
464
- ---
465
-
466
- ## Why This Makes Sense
467
-
468
- Carto already has the hard part — understanding codebases at scale:
469
- - 10,565 files indexed in <10 seconds
470
- - Blast radius computed in <5ms
471
- - Domain detection via graph clustering
472
- - Route/model extraction across 8 languages
473
-
474
- What it's missing: the ability to **act** on that knowledge. ACP gives it hands (file edits, terminal) and a voice (LLM reasoning). The combination — structural intelligence + LLM + editor integration — is what makes this better than every other AI coding agent.
475
-
476
- Other agents are smart but blind. Carto sees the architecture. ACP lets it act on what it sees.
477
-
478
- ---
479
-
480
- *Decision: Go ACP. Auto-index on first session. Support all major LLM providers via BYOK. Ship to Zed + JetBrains in 4-6 weeks.*