@fodx/codelens 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -18
- package/adapters/pi/codelens.extension.ts +4 -1
- package/adapters/pi/extension.json +1 -1
- package/build/src/index/indexer.js +2 -0
- package/build/src/index/indexer.js.map +1 -1
- package/build/src/index/reindex.js +6 -2
- package/build/src/index/reindex.js.map +1 -1
- package/build/src/index/staleness.js +16 -0
- package/build/src/index/staleness.js.map +1 -0
- package/build/src/installer/agents.js +8 -4
- package/build/src/installer/agents.js.map +1 -1
- package/build/src/obs/usage.js +33 -5
- package/build/src/obs/usage.js.map +1 -1
- package/build/src/server.js +2 -2
- package/build/src/tools/explore.js +147 -0
- package/build/src/tools/explore.js.map +1 -0
- package/build/src/tools/impact.js +176 -0
- package/build/src/tools/impact.js.map +1 -0
- package/build/src/tools/map.js +12 -2
- package/build/src/tools/map.js.map +1 -1
- package/build/src/tools/registry.js +54 -3
- package/build/src/tools/registry.js.map +1 -1
- package/build/src/tools/related.js +30 -10
- package/build/src/tools/related.js.map +1 -1
- package/build/src/tools/search.js +20 -3
- package/build/src/tools/search.js.map +1 -1
- package/docs/agent-guide.md +23 -1
- package/docs/how-it-works.md +154 -0
- package/docs/routing.md +12 -6
- package/docs/tools.md +17 -4
- package/docs/usage-metrics.md +89 -0
- package/package.json +5 -2
package/docs/agent-guide.md
CHANGED
|
@@ -11,7 +11,13 @@ End-to-end workflow for a coding agent using codelens.
|
|
|
11
11
|
2. cl_refresh
|
|
12
12
|
→ { indexedFiles: 812, totalChunks: 2400, status: "ready" }
|
|
13
13
|
|
|
14
|
-
3.
|
|
14
|
+
3. cl_explore(query: "session validation flow")
|
|
15
|
+
→ { query: "session validation flow", count: 3,
|
|
16
|
+
files: [{ path: "src/auth/session.ts", results: [{ handle: "chk_…", lines: "12-58", preview: "export function validateSession…" }] }],
|
|
17
|
+
related: [{ sourcePath: "src/auth/session.ts", path: "src/routes/login.ts", edgeType: "imported_by", hops: 1 }] }
|
|
18
|
+
|
|
19
|
+
# If you only need to locate a handle, use the leaner search tool:
|
|
20
|
+
cl_search(query: "session validation")
|
|
15
21
|
→ { query: "session validation", count: 1, results: [
|
|
16
22
|
{ handle: "chk_…", path: "src/auth/session.ts", lines: "12-58",
|
|
17
23
|
score: 0.92, why: "fts,symbol,graph", preview: "export function validateSession(token: string): boolean" }
|
|
@@ -24,6 +30,10 @@ End-to-end workflow for a coding agent using codelens.
|
|
|
24
30
|
]
|
|
25
31
|
# TS/JS also populate `calls`/`references` and resolve dynamic import().
|
|
26
32
|
|
|
33
|
+
# Before changing shared code, ask for the blast radius.
|
|
34
|
+
cl_impact(symbol: "validateSession", path: "src/auth/session.ts")
|
|
35
|
+
→ { callers: [...], callees: [...], affectedFiles: [...], affectedTests: [...], confidenceNote: "…" }
|
|
36
|
+
|
|
27
37
|
# Orientation (optional): outline a file or directory without reading it.
|
|
28
38
|
cl_map(path: "src/auth")
|
|
29
39
|
→ files: [{ path: "src/auth/session.ts", symbols: [{ name: "validateSession", kind: "function", signature: "export function validateSession(token: string): boolean" }] }]
|
|
@@ -37,6 +47,14 @@ End-to-end workflow for a coding agent using codelens.
|
|
|
37
47
|
8. cl_load(name: "auth-investigation") # after compaction
|
|
38
48
|
```
|
|
39
49
|
|
|
50
|
+
## Tool choice
|
|
51
|
+
|
|
52
|
+
- Use `cl_explore` for broad questions: "how does X work?", "show the flow around Y", or surveying an unfamiliar area. It combines search, compact source previews, and relationships in one call.
|
|
53
|
+
- Use `cl_search` when you only need ranked handles/locations.
|
|
54
|
+
- Use `cl_related` to expand from a known file.
|
|
55
|
+
- Use `cl_impact` before editing shared code to see callers/callees/affected tests.
|
|
56
|
+
- Use `cl_expand` (or raw read) for exact current file content before editing.
|
|
57
|
+
|
|
40
58
|
## Branch switch
|
|
41
59
|
|
|
42
60
|
```
|
|
@@ -46,6 +64,10 @@ cl_refresh → builds feature-b index; main's results no longer leak in
|
|
|
46
64
|
```
|
|
47
65
|
|
|
48
66
|
|
|
67
|
+
## Freshness and stale results
|
|
68
|
+
|
|
69
|
+
Query tools reconcile changed files before answering. If a response includes `freshness: "partial"`, `pendingFiles`, or per-result `stale:true`, read those stale files directly (`cl_expand` or raw read) before relying on indexed previews/edges.
|
|
70
|
+
|
|
49
71
|
## Saving context across compaction
|
|
50
72
|
|
|
51
73
|
`cl_save` / `cl_load` persist named handle sets in a **separate** DB that
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# How CodeLens works
|
|
2
|
+
|
|
3
|
+
CodeLens is a **branch-aware local code knowledge retrieval engine**. It indexes
|
|
4
|
+
the current repo/branch into SQLite and lets coding agents ask "what code is
|
|
5
|
+
relevant to X?" via compact ranked handles — instead of grep/find/read flooding
|
|
6
|
+
their context window. No chat LLM is used anywhere in the core path.
|
|
7
|
+
|
|
8
|
+
## Architecture
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
Agent (Pi / Claude Code / Cursor / Gemini / opencode / Codex)
|
|
12
|
+
│ MCP stdio
|
|
13
|
+
▼
|
|
14
|
+
CodeLens MCP server ──▶ CLI (codelens <subcommand>)
|
|
15
|
+
│
|
|
16
|
+
├── Git scope detector (repo / worktree / branch / HEAD / dirty)
|
|
17
|
+
├── Index manager (per-branch index identity)
|
|
18
|
+
├── File scanner (.gitignore-aware, binary/size filters)
|
|
19
|
+
├── FTS5 indexer (structure-aware chunks + content hash)
|
|
20
|
+
├── Tree-sitter symbol extractor (11 grammars, text fallback)
|
|
21
|
+
├── Source graph builder (imports / defines / tests / belongs_to)
|
|
22
|
+
├── Graph query (recursive CTE + bounded BFS)
|
|
23
|
+
├── Freshness checker (mtime/size fast → hash on suspicion)
|
|
24
|
+
├── TTL pruner (never-delete guards)
|
|
25
|
+
├── Saved-context store (separate DB, survives rebuilds)
|
|
26
|
+
└── Usage tracker (global, actual-file-size savings)
|
|
27
|
+
│
|
|
28
|
+
▼
|
|
29
|
+
SQLite
|
|
30
|
+
├── per-branch index DB (~/.codelens/indexes/index-<repoId>.db)
|
|
31
|
+
├── saved-contexts DB (~/.codelens/contexts/contexts-<repoId>.db)
|
|
32
|
+
└── global usage DB (~/.codelens/usage.db)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## The index layers
|
|
36
|
+
|
|
37
|
+
1. **Files**: path, language, size, mtime, content hash.
|
|
38
|
+
2. **FTS5 lexical**: structure-aware code chunks with line-based fallback and
|
|
39
|
+
Porter-stemming/BM25 ranking. Code files with extractable symbols are chunked
|
|
40
|
+
around outermost functions/classes/methods/types; oversized symbols and non-structural
|
|
41
|
+
gaps (imports, top-level statements, inter-symbol regions) are line-chunked.
|
|
42
|
+
Leading comment/decorator blocks are attached to the following symbol chunk.
|
|
43
|
+
Line fallback uses small overlap to reduce boundary loss. Chunks carry
|
|
44
|
+
`code` vs `prose`, `chunker`, `chunker_version`, and `symbol_id` when aligned
|
|
45
|
+
to a symbol. The match-only FTS text preserves the original chunk and appends
|
|
46
|
+
bounded, deduplicated identifier subtokens (for example `validateSession` →
|
|
47
|
+
`validate session`) so code-identifier queries can match without changing
|
|
48
|
+
snippets or `cl_expand` content.
|
|
49
|
+
3. **Symbols** (tree-sitter): functions/classes/methods/types/exports/imports
|
|
50
|
+
with line ranges + signatures + exported flag. Parser-eligible files are
|
|
51
|
+
parsed once and the same tree-sitter tree is reused for symbols and edges;
|
|
52
|
+
structure-aware chunking consumes those extracted symbol ranges. 11 grammars
|
|
53
|
+
shipped; unknown languages fall back to text-only FTS.
|
|
54
|
+
4. **Source graph**: edges `imports`, `defines`, `belongs_to`, `exports`,
|
|
55
|
+
`tests` (filename heuristics). Resolution handles TS ESM `.js`→`.ts`
|
|
56
|
+
substitution. Unresolved imports emit no edge (no wrong edges).
|
|
57
|
+
5. *(No vector/semantic layer — removed; ranking is FTS + symbol + graph.)*
|
|
58
|
+
|
|
59
|
+
## Branch isolation (the core idea)
|
|
60
|
+
|
|
61
|
+
Every index is scoped to `repoRoot + worktreePath + branch + HEAD`:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
index_id = sha256(repoRoot | worktreePath | branch | headSha)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Every table row carries `index_id`; every query filters by it. So `git checkout`
|
|
68
|
+
activates/creates a different index automatically — results from `main` never
|
|
69
|
+
leak into `feature-b`. `cl_current` reports which index is active.
|
|
70
|
+
|
|
71
|
+
## Freshness (local files are source of truth)
|
|
72
|
+
|
|
73
|
+
Before query tools answer (`cl_search`, `cl_explore`, `cl_related`, `cl_impact`, `cl_map`), `ensureFreshIndex` runs:
|
|
74
|
+
1. Detect current git scope → activate/create the branch index.
|
|
75
|
+
2. Scan files, diff `mtime`+`size` vs indexed rows (fast); hash only
|
|
76
|
+
changed/suspicious files.
|
|
77
|
+
3. Reindex changed/new files in per-file transactions; drop deleted. Files with
|
|
78
|
+
chunks from an older/unknown `chunker_version` are also treated as changed, so
|
|
79
|
+
chunker improvements roll forward on the next refresh.
|
|
80
|
+
4. Budget-bounded (default 500ms); if incomplete, surface
|
|
81
|
+
`freshness:"partial"` + `pendingFiles`. A chunker-version bump can require a
|
|
82
|
+
full budget-bounded re-chunk over several refresh cycles.
|
|
83
|
+
|
|
84
|
+
`cl_expand` **always reads current disk** (never stale stored text). A file
|
|
85
|
+
watcher (server mode) short-circuits the scan when nothing changed, with a 5s
|
|
86
|
+
periodic full-scan backstop.
|
|
87
|
+
|
|
88
|
+
**Edit behavior:** when the agent edits a file, the *next query tool call*
|
|
89
|
+
auto-refreshes and reindexes it (lazy, not eager at edit time). If the refresh
|
|
90
|
+
budget is exhausted, known-stale paths are surfaced with `stale:true` and
|
|
91
|
+
`freshness:"partial"` so the agent can read those files directly.
|
|
92
|
+
|
|
93
|
+
## Ranking (hybrid)
|
|
94
|
+
|
|
95
|
+
`cl_search` fuses lexical, structural, graph, path, and code/prose signals with
|
|
96
|
+
weights from `src/search/rank.ts`:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
score = fts×0.34 + symbol×0.18 + graph×0.22 + code×0.08 + path×0.08 + exact×0.10
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
- `fts`: BM25 normalized. Queries are expanded with the same bounded identifier
|
|
103
|
+
splitter used at index time, then quoted through the FTS path; expansion is
|
|
104
|
+
capped to avoid broadening queries or hurting latency.
|
|
105
|
+
- `symbol`: full signal when the chunk's `symbol_id` name partially matches a
|
|
106
|
+
query term; lower-strength file-level symbol match is kept as fallback.
|
|
107
|
+
- `exact`: full signal when the chunk's `symbol_id` name exactly matches a query
|
|
108
|
+
term; lower-strength file-level exact match is kept as fallback.
|
|
109
|
+
- `graph`: 1 if the file is graph-proximate to top lexical results.
|
|
110
|
+
- `path`: query term appears in the file path/name.
|
|
111
|
+
- `code`: modest boost for `code` chunks over `prose` (docs), so code discovery
|
|
112
|
+
isn't drowned out by markdown. Pass `contentType:"code"` to filter to source
|
|
113
|
+
only.
|
|
114
|
+
|
|
115
|
+
`cl_related` runs a recursive-CTE BFS over the `edges` table (direction-aware,
|
|
116
|
+
cycle-guarded, depth-capped at 3).
|
|
117
|
+
|
|
118
|
+
## Concurrency & recovery
|
|
119
|
+
|
|
120
|
+
- **WAL mode** + a single-writer queue + a cross-process advisory lease
|
|
121
|
+
(`index_locks`) so two agent processes on the same repo never corrupt rows;
|
|
122
|
+
readers see the prior committed snapshot.
|
|
123
|
+
- **Corruption recovery:** `PRAGMA quick_check` on startup + on query error →
|
|
124
|
+
rebuild the core index. Saved contexts live in a **separate DB** and survive.
|
|
125
|
+
- **Schema versioning:** version guard refuses a newer-than-code DB; migrations
|
|
126
|
+
are transactional with a pre-migration backup.
|
|
127
|
+
|
|
128
|
+
## TTL
|
|
129
|
+
|
|
130
|
+
Inactive indexes are pruned automatically (startup + periodic): inactive branch
|
|
131
|
+
14d, detached 3d, worktree 48h. **Never** deletes the active index, pinned
|
|
132
|
+
indexes, locked indexes, or recently-accessed ones. `cl_prune` (manual),
|
|
133
|
+
`cl_drop` (explicit, refuses active/pinned).
|
|
134
|
+
|
|
135
|
+
## Saved contexts
|
|
136
|
+
|
|
137
|
+
`cl_save`/`cl_load` persist named handle sets + notes in a **separate**
|
|
138
|
+
contexts DB (`~/.codelens/contexts/`), keyed by repo. They survive core-index
|
|
139
|
+
rebuilds. Items reference path+symbol (stable across reindex), not chunk ids.
|
|
140
|
+
Pin to prevent TTL deletion.
|
|
141
|
+
|
|
142
|
+
## Usage tracking
|
|
143
|
+
|
|
144
|
+
Global (`~/.codelens/usage.db`): per-tool calls, bytes served, and an estimated
|
|
145
|
+
context saving computed from **actual indexed file sizes** of the result files.
|
|
146
|
+
Discovery tools (`cl_search`, `cl_explore`, `cl_related`, `cl_impact`) accrue savings. See
|
|
147
|
+
[`docs/usage-metrics.md`](usage-metrics.md).
|
|
148
|
+
|
|
149
|
+
## Distribution
|
|
150
|
+
|
|
151
|
+
Currently builds from source (Node ≥ 22.5) via `install.sh`/`install.ps1`,
|
|
152
|
+
which also wires agent configs + slash commands. A self-contained bundle
|
|
153
|
+
(vendored Node) like some other tools is future work once published. The `cl_*`
|
|
154
|
+
MCP tools are the same surface across every host.
|
package/docs/routing.md
CHANGED
|
@@ -14,14 +14,16 @@ right tool for the job.
|
|
|
14
14
|
|
|
15
15
|
Prefer codelens when:
|
|
16
16
|
- you don't know the exact name/string (semantic or conceptual search via `cl_search`)
|
|
17
|
-
- you need
|
|
18
|
-
|
|
17
|
+
- you need broad orientation in one call (`cl_explore`) — "how does X work?", flows, or unfamiliar areas
|
|
18
|
+
- you need relationships — importers, tests, callers (`cl_related`) — or blast radius before edits (`cl_impact`)
|
|
19
|
+
- you need a per-file outline / repo map (`cl_map`)
|
|
19
20
|
- the repo is large or unfamiliar, or you'd otherwise grep + read many files
|
|
20
21
|
- branch-scoped correctness matters (results won't leak across branches)
|
|
21
22
|
|
|
22
23
|
Then use `cl_expand` to read the exact current content of a chosen target (it
|
|
23
24
|
reads from disk — never stale), and `cl_save`/`cl_load` to persist working
|
|
24
|
-
context across compaction.
|
|
25
|
+
context across compaction. If a query result has `stale:true`, read that file
|
|
26
|
+
from disk before relying on indexed snippets/edges.
|
|
25
27
|
|
|
26
28
|
## When raw grep/find/read is fine (or better)
|
|
27
29
|
|
|
@@ -44,9 +46,11 @@ branch's index automatically.
|
|
|
44
46
|
|
|
45
47
|
## Freshness
|
|
46
48
|
|
|
47
|
-
|
|
48
|
-
the response carries `freshness: "partial"
|
|
49
|
-
recent edits
|
|
49
|
+
Query tools auto-refresh changed files before returning (budget-bounded). If
|
|
50
|
+
the response carries `freshness: "partial"`, `pendingFiles > 0`, or per-result
|
|
51
|
+
`stale:true`, some recent edits were not reindexed within the budget. Read those
|
|
52
|
+
files directly with `cl_expand`/raw read, then call `cl_refresh` or re-query when
|
|
53
|
+
you need indexed relationships to catch up.
|
|
50
54
|
|
|
51
55
|
## Tool quick reference
|
|
52
56
|
|
|
@@ -55,7 +59,9 @@ recent edits may not yet be reflected — call `cl_refresh` or re-query.
|
|
|
55
59
|
| `cl_current` | repo/branch/index status + freshness |
|
|
56
60
|
| `cl_refresh` | build/update the current branch index |
|
|
57
61
|
| `cl_search` | hybrid ranked search → compact handles |
|
|
62
|
+
| `cl_explore` | one-call grouped search + previews + relationship map |
|
|
58
63
|
| `cl_related` | graph neighbors (imports/tests/callers) |
|
|
64
|
+
| `cl_impact` | callers/callees/affected files/tests before edits |
|
|
59
65
|
| `cl_map` | per-file symbol outline (repo map) |
|
|
60
66
|
| `cl_expand` | exact current file content by path/range |
|
|
61
67
|
| `cl_save` / `cl_load` | persist + reload working context |
|
package/docs/tools.md
CHANGED
|
@@ -15,24 +15,37 @@ JSON-serialized text content.
|
|
|
15
15
|
|
|
16
16
|
## cl_search
|
|
17
17
|
- **Input**: `{ query: string, limit?: number=5, cursor?: string, contentType?: "code"|"prose", related?: boolean, snippet?: "none"|"headline"|"compact"|"full" }`
|
|
18
|
-
- **Returns**: `{ indexId, query, count, results:[{handle,path,lines,score,why,preview}], freshness, nextCursor?, pendingFiles?, related? }`
|
|
19
|
-
- `lines` is `"start-end"`; `why` is a comma-joined signal string; `preview` is a short highlighted snippet (empty when `snippet:"none"`). Use `handle` with `cl_expand`/`cl_save`. Pagination uses the top-level `nextCursor` (no per-result cursor).
|
|
18
|
+
- **Returns**: `{ indexId, query, count, results:[{handle,path,lines,score,why,preview,stale?}], freshness, nextCursor?, pendingFiles?, related? }`
|
|
19
|
+
- `lines` is `"start-end"`; `why` is a comma-joined signal string; `preview` is a short highlighted snippet (empty when `snippet:"none"`). Use `handle` with `cl_expand`/`cl_save`. Pagination uses the top-level `nextCursor` (no per-result cursor). If `stale:true`, read that file from disk (`cl_expand`/raw read) before relying on indexed content.
|
|
20
20
|
- **Use**: intent-level code discovery.
|
|
21
21
|
- **Identifier matching**: code identifiers are indexed with bounded subtokens (`validateSession` also matches `session`) in the match-only FTS text. Query expansion uses the same bounded splitter and preserves snippets/handles from stored chunk content.
|
|
22
22
|
- **`snippet` (preview verbosity)**: default is signature-first `headline` (richer `compact` for the top ~3 results), which keeps payloads small. `none` returns path+lines only (fetch with `cl_expand`); `compact`/`full` return larger code windows. Explicitly setting `snippet` applies that mode to all results.
|
|
23
23
|
- **`why` signals**: `fts|symbol|exact|graph|path|code` (exact = exact symbol-name match; path = query term in the file path). Ranking is deterministic with a stable tie-break.
|
|
24
24
|
- **Line-range format**: `cl_search` reports `lines` as a `"start-end"` string for compact display; `cl_map` and `cl_expand` use numeric `startLine`/`endLine` for programmatic use. This difference is intentional.
|
|
25
25
|
|
|
26
|
+
## cl_explore
|
|
27
|
+
- **Input**: `{ query: string, limit?: number=8, cursor?: string, contentType?: "code"|"prose", snippet?: "none"|"headline"|"compact"|"full", relatedDepth?: number=1, maxFiles?: number=6, maxResultsPerFile?: number=3, maxRelated?: number=20 }`
|
|
28
|
+
- **Returns**: `{ indexId, query, count, files:[{path, stale?, results:[{handle,lines,score,why,preview,signature?,collapsed?,stale?}]}], related:[{sourcePath,path,edgeType,hops,confidence,stale?}], freshness, pendingFiles?, nextCursor?, truncated? }`
|
|
29
|
+
- **Use**: broad orientation in one call — "how does X work?", "show the flow around Y", or surveying an unfamiliar area. It fuses `cl_search` + grouped previews + graph relationships; use `cl_search` when you only need to locate handles.
|
|
30
|
+
|
|
26
31
|
## cl_related
|
|
27
32
|
- **Input**: `{ path: string, types?: string[], depth?: number=2, direction?: "out"|"in"|"both" }`
|
|
28
|
-
- **Returns**: `{ indexId, results:[{handle,path,edgeType,hops,confidence}] }`
|
|
33
|
+
- **Returns**: `{ indexId, results:[{handle,path,edgeType,hops,confidence,stale?}], freshness?, pendingFiles? }`
|
|
29
34
|
- **Edge types**: `imports|imported_by|tests|calls|references|defines|exports|belongs_to` (TS/JS populate `calls`/`references` and resolve dynamic `import()`).
|
|
30
35
|
|
|
36
|
+
## cl_impact
|
|
37
|
+
- **Input**: `{ symbol?: string, path?: string, depth?: number=2, includeTests?: boolean=true }`
|
|
38
|
+
- **Returns**: `{ indexId, target?, candidates?, callers, callees, affectedFiles, affectedTests, depth, summary?, confidenceNote, freshness?, pendingFiles? }`
|
|
39
|
+
- **Use**: before changing shared code. Pass `symbol` plus `path` when possible; if a symbol is ambiguous, CodeLens returns `candidates` instead of guessing. Impact is edge-derived and includes confidence/hop counts plus provenance labels (`graph` or conservative `path-heuristic`); use `cl_expand` to inspect exact current code before editing.
|
|
40
|
+
|
|
31
41
|
## cl_map
|
|
32
42
|
- **Input**: `{ path?: string, limit?: number=50, all?: boolean }`
|
|
33
|
-
- **Returns**: `{ indexId, files:[{path, symbols:[{name,kind,signature,startLine,endLine,exported}]}], fileCount, truncated }`
|
|
43
|
+
- **Returns**: `{ indexId, files:[{path, stale?, symbols:[{name,kind,signature,startLine,endLine,exported}]}], fileCount, truncated, freshness?, pendingFiles? }`
|
|
34
44
|
- **Use**: outline / repo-map for orientation — per-file symbol signatures from the index (no file re-read). Defaults to exported symbols; pass `all:true` for everything. File-capped (default 50, max 200) with a `truncated` flag.
|
|
35
45
|
|
|
46
|
+
## Freshness and stale flags
|
|
47
|
+
Query tools perform a budget-bounded reconciliation before answering. If the response has `freshness:"partial"` and `pendingFiles > 0`, some changed files were not reindexed within the budget. Results/files with `stale:true` point at those known-stale paths; read them directly with `cl_expand` or a raw read before depending on indexed previews/edges.
|
|
48
|
+
|
|
36
49
|
## cl_expand
|
|
37
50
|
- **Input**: `{ path?: string, handle?: string, startLine?: number, endLine?: number, budget?: number=4000 }`
|
|
38
51
|
- **Returns**: `{ path, startLine, endLine, content, truncated, chars }`
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Usage metrics — how "saved" is calculated
|
|
2
|
+
|
|
3
|
+
`cl_usage` reports per-tool call counts, bytes served, and an **estimated**
|
|
4
|
+
context-window saving. The saving is an *estimate*, not a measurement — this
|
|
5
|
+
page explains exactly how it's computed and its limits.
|
|
6
|
+
|
|
7
|
+
## Which tools are tracked
|
|
8
|
+
|
|
9
|
+
Only the agent's **retrieval + context-management** tools are tracked as
|
|
10
|
+
"usage" — the ones that represent the agent actually using CodeLens to
|
|
11
|
+
find/read/save code:
|
|
12
|
+
|
|
13
|
+
| Tool | tracked | calls | bytes_served | bytes_saved |
|
|
14
|
+
|------|---------|-------|--------------|-------------|
|
|
15
|
+
| `cl_search` | ✅ | ✅ | ✅ | ✅ (discovery) |
|
|
16
|
+
| `cl_explore` | ✅ | ✅ | ✅ | ✅ (discovery) |
|
|
17
|
+
| `cl_related` | ✅ | ✅ | ✅ | ✅ (discovery) |
|
|
18
|
+
| `cl_impact` | ✅ | ✅ | ✅ | ✅ (discovery) |
|
|
19
|
+
| `cl_expand` | ✅ | ✅ | ✅ | 0 (it *is* the scoped read step) |
|
|
20
|
+
| `cl_save` / `cl_load` | ✅ | ✅ | ✅ | 0 (context management) |
|
|
21
|
+
|
|
22
|
+
**Operational tools are NOT tracked** (they're maintenance, not usage):
|
|
23
|
+
`cl_refresh`, `cl_doctor`, `cl_stats`, `cl_prune`, `cl_drop`, `cl_current`,
|
|
24
|
+
`cl_usage`. So `cl_refresh` (building the index) does not appear in the usage
|
|
25
|
+
report, and checking `cl_usage` never inflates the numbers.
|
|
26
|
+
|
|
27
|
+
Only the **discovery** tools (`cl_search`, `cl_explore`, `cl_related`, `cl_impact`) accrue `bytes_saved` —
|
|
28
|
+
the ones that replace "grep + read a bunch of files" with compact indexed context.
|
|
29
|
+
|
|
30
|
+
## The formula (refined — actual file sizes)
|
|
31
|
+
|
|
32
|
+
For a discovery call, CodeLens computes savings from the **real sizes of the
|
|
33
|
+
files in the results**, which it already indexes (`files.size`):
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
saved = max(0, Σ(distinct result files' indexed sizes) − bytesServed)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- `distinct result files` = the unique paths among the returned handles/files
|
|
40
|
+
(`cl_search`/`cl_related` carry `results[].path`; `cl_explore` carries
|
|
41
|
+
`files[].path`; `cl_impact` carries paths in target/candidates/callers/callees/affected lists).
|
|
42
|
+
- `indexed sizes` = `files.size` for those paths in the current branch index.
|
|
43
|
+
- `bytesServed` = bytes of the JSON actually sent to the model.
|
|
44
|
+
- **Capped at 50 distinct files** so a `cl_related` result returning 100
|
|
45
|
+
importers doesn't inflate the total (the agent wouldn't read all 100
|
|
46
|
+
without the tool).
|
|
47
|
+
|
|
48
|
+
**Counterfactual being modeled:** without the index, the agent would `grep` +
|
|
49
|
+
`read` the relevant files (whole-file reads at their real sizes); with the tool
|
|
50
|
+
it got compact handles. The difference is the saved context.
|
|
51
|
+
|
|
52
|
+
### Fallback (flat proxy)
|
|
53
|
+
|
|
54
|
+
If the size lookup fails (e.g. index not ready, paths not yet indexed), CodeLens
|
|
55
|
+
falls back to a flat proxy:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
saved = max(0, handles × 4096 − bytesServed)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
where `4096` is a rough average file size and `handles` = number of result
|
|
62
|
+
entries. This is the older, less-accurate estimate, kept only as a graceful
|
|
63
|
+
fallback.
|
|
64
|
+
|
|
65
|
+
## Honest limits
|
|
66
|
+
|
|
67
|
+
1. **It's a counterfactual estimate, not a measurement.** We don't know exactly
|
|
68
|
+
which files the agent *would have* read without the tool — we assume the
|
|
69
|
+
result files, whole.
|
|
70
|
+
2. **Whole-file assumption.** We credit the full file size, but a raw `read`
|
|
71
|
+
might be partial, or the agent might read only the relevant part — so the
|
|
72
|
+
estimate can overstate for large files with small relevant regions.
|
|
73
|
+
3. **No grep-output cost.** It ignores the bytes `grep` itself would have dumped
|
|
74
|
+
into context, which would make the real saving *larger*.
|
|
75
|
+
4. **No multi-round exploration.** It counts one call, not the exploration
|
|
76
|
+
rounds the agent avoids.
|
|
77
|
+
5. The 50-file cap is a judgment call to curb broad relationship/impact results.
|
|
78
|
+
|
|
79
|
+
Net: treat `saved(est)` as an order-of-magnitude indicator of how much context
|
|
80
|
+
the index tools are sparing, not an audited figure.
|
|
81
|
+
|
|
82
|
+
## Storage
|
|
83
|
+
|
|
84
|
+
Usage is **global** (`~/.codelens/usage.db`), aggregated across all repos, with
|
|
85
|
+
a `(tool, repo_id)` key so `cl_usage` can break it down per repo. It survives
|
|
86
|
+
restarts and core-index rebuilds (it's separate from the per-branch index DBs).
|
|
87
|
+
|
|
88
|
+
Reset with `cl_usage` is not exposed as a tool; from code, `UsageTracker.reset()`
|
|
89
|
+
clears it.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fodx/codelens",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Local, branch-aware code search & relation graph. SQLite FTS5 + tree-sitter symbols + source-graph edges, exposed as an MCP server and CLI. No chat LLM required.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -39,8 +39,10 @@
|
|
|
39
39
|
"adapters/pi",
|
|
40
40
|
"README.md",
|
|
41
41
|
"docs/agent-guide.md",
|
|
42
|
+
"docs/how-it-works.md",
|
|
42
43
|
"docs/routing.md",
|
|
43
44
|
"docs/tools.md",
|
|
45
|
+
"docs/usage-metrics.md",
|
|
44
46
|
"docs/codelens-preview.png"
|
|
45
47
|
],
|
|
46
48
|
"pi": {
|
|
@@ -58,7 +60,8 @@
|
|
|
58
60
|
"mcp:smoke": "node build/src/server.js --smoke",
|
|
59
61
|
"start": "node build/src/server.js",
|
|
60
62
|
"benchmark": "npx tsx bench/run.ts",
|
|
61
|
-
"quality": "npx tsx bench/quality.ts"
|
|
63
|
+
"quality": "npx tsx bench/quality.ts",
|
|
64
|
+
"eval:agent": "npx tsx bench/agent-eval.ts"
|
|
62
65
|
},
|
|
63
66
|
"dependencies": {
|
|
64
67
|
"@modelcontextprotocol/sdk": "^1.26.0",
|