Skip to content

Commit 1ad06d1

Browse files
committed
feat: JSDoc extraction and type_members table
Add doc_comment column to symbols (3,084 documented symbols on benchmark repo) and new type_members table for interface/type-alias properties (12,052 members extracted). Agents can now query type shapes and symbol documentation without reading files. SCHEMA_VERSION bumped to 2.
1 parent 6739c8a commit 1ad06d1

13 files changed

Lines changed: 330 additions & 8 deletions

File tree

.agents/rules/codemap.mdc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ If the question looks like any of these → use the index:
5656
| "How many files/symbols/components are there?" | any table with `COUNT(*)` |
5757
| "What are the CSS classes in X?" | `css_classes` |
5858
| "What keyframe animations exist?" | `css_keyframes` |
59+
| "What fields does interface/type X have?" | `type_members` |
60+
| "Is symbol X deprecated?" / "What does X do?" | `symbols` (`doc_comment`) |
5961

6062
## When Grep / Read IS appropriate
6163

@@ -93,6 +95,9 @@ bun src/index.ts query --json "<SQL>"
9395
| CSS design tokens | `SELECT name, value, scope FROM css_variables WHERE name LIKE '--%...'` |
9496
| CSS module classes | `SELECT name, file_path FROM css_classes WHERE is_module = 1` |
9597
| CSS keyframes | `SELECT name, file_path FROM css_keyframes` |
98+
| Type/interface shape | `SELECT name, type, is_optional, is_readonly FROM type_members WHERE symbol_name = '...'` |
99+
| Deprecated symbols | `SELECT name, kind, file_path, doc_comment FROM symbols WHERE doc_comment LIKE '%@deprecated%'` |
100+
| Symbol docs | `SELECT name, signature, doc_comment FROM symbols WHERE name = '...' AND doc_comment IS NOT NULL` |
96101

97102
**Use `DISTINCT`** on dependency and import queries — a file importing multiple specifiers from the same module produces duplicate rows.
98103

.agents/skills/codemap/SKILL.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,23 @@ LIMIT 10
8585
| kind | TEXT | `function`, `class`, `type`, `interface`, `enum`, `const` |
8686
| line_start | INTEGER | Start line (1-based) |
8787
| line_end | INTEGER | End line (1-based) |
88-
| signature | TEXT | e.g. `createHandler()`, `type UserProps` |
88+
| signature | TEXT | Reconstructed signature with generics and return types |
8989
| is_exported | INTEGER | 1 if exported |
9090
| is_default_export | INTEGER | 1 if default export |
91+
| members | TEXT | JSON enum members (NULL for non-enums) |
92+
| doc_comment | TEXT | Leading JSDoc text (cleaned), NULL when absent |
93+
94+
### `type_members` — Properties of interfaces and object-literal type aliases
95+
96+
| Column | Type | Description |
97+
| ----------- | ---------- | -------------------------------------------------- |
98+
| id | INTEGER PK | Auto-increment ID |
99+
| file_path | TEXT FK | References `files(path)` |
100+
| symbol_name | TEXT | Parent interface / type alias name |
101+
| name | TEXT | Property or method name |
102+
| type | TEXT | Type annotation (e.g. `string`, `(key) => number`) |
103+
| is_optional | INTEGER | 1 if `?` modifier |
104+
| is_readonly | INTEGER | 1 if `readonly` modifier |
91105

92106
### `imports` — Import statements
93107

@@ -194,6 +208,18 @@ FROM symbols WHERE file_path LIKE '%settings-provider%' AND is_exported = 1;
194208
SELECT name, members FROM symbols
195209
WHERE kind = 'enum' AND name = 'TransactionStatus';
196210

211+
-- Interface / type shape (what fields does a type have?)
212+
SELECT name, type, is_optional, is_readonly FROM type_members
213+
WHERE symbol_name = 'UserSession';
214+
215+
-- Deprecated symbols (find @deprecated via JSDoc)
216+
SELECT name, kind, file_path, doc_comment FROM symbols
217+
WHERE doc_comment LIKE '%@deprecated%';
218+
219+
-- Symbol documentation
220+
SELECT name, signature, doc_comment FROM symbols
221+
WHERE name = 'formatCurrency' AND doc_comment IS NOT NULL;
222+
197223
-- File overview (imports + exports)
198224
SELECT 'import' as dir, source as name, specifiers as detail
199225
FROM imports WHERE file_path LIKE '%OrderRow%'

docs/architecture.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ A local SQLite database (`.codemap.db`) indexes the project tree and stores stru
100100
| `application/` | Indexing use cases and engine (`run-index`, `index-engine`, types) |
101101
| `worker-pool.ts` | Parallel parse workers (Bun / Node) |
102102
| `db.ts` | SQLite adapter — schema DDL, typed CRUD, connection management |
103-
| `parser.ts` | TS/TSX/JS/JSX extraction via `oxc-parser` — symbols, imports, exports, components, markers |
103+
| `parser.ts` | TS/TSX/JS/JSX extraction via `oxc-parser` — symbols (with JSDoc + generics + return types), type members, imports, exports, components, markers |
104104
| `css-parser.ts` | CSS extraction via `lightningcss` — custom properties, classes, keyframes, `@theme` blocks |
105105
| `resolver.ts` | Import path resolution via `oxc-resolver` — respects `tsconfig` aliases, builds dependency graph |
106106
| `constants.ts` | Shared constants — e.g. `LANG_MAP` |
@@ -185,6 +185,19 @@ All tables use `STRICT` mode. Tables marked with `WITHOUT ROWID` store data dire
185185
| is_exported | INTEGER | 1 if exported |
186186
| is_default_export | INTEGER | 1 if default export |
187187
| members | TEXT | JSON array of enum members (NULL for non-enums). Each entry: `{"name":"…","value":"…"}` (value omitted for implicit-value enums) |
188+
| doc_comment | TEXT | Leading JSDoc comment text (cleaned: `*` prefixes stripped, trimmed). NULL when absent. Preserves `@deprecated`, `@param`, etc. tags |
189+
190+
### `type_members` — Properties and methods of interfaces and object-literal types (`STRICT`)
191+
192+
| Column | Type | Description |
193+
| ----------- | ---------- | --------------------------------------------------------- |
194+
| id | INTEGER PK | Auto-increment row id |
195+
| file_path | TEXT FK | References `files(path)` ON DELETE CASCADE |
196+
| symbol_name | TEXT | Name of the parent interface or type alias |
197+
| name | TEXT | Property or method name |
198+
| type | TEXT | Type annotation string (e.g. `string`, `(key) => number`) |
199+
| is_optional | INTEGER | 1 if `?` modifier present |
200+
| is_readonly | INTEGER | 1 if `readonly` modifier present |
188201

189202
### `imports` — Import statements (`STRICT`)
190203

src/adapters/builtin.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ function parseTsJs(ctx: ParseContext): ParsedFilePayload {
2323
exports: data.exports,
2424
components: data.components,
2525
markers: data.markers,
26+
typeMembers: data.typeMembers,
2627
};
2728
}
2829

src/adapters/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export type ParsedFilePayload = Pick<
2727
| "exports"
2828
| "components"
2929
| "markers"
30+
| "typeMembers"
3031
| "cssVariables"
3132
| "cssClasses"
3233
| "cssKeyframes"

src/application/index-engine.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
insertCssVariables,
2525
insertCssClasses,
2626
insertCssKeyframes,
27+
insertTypeMembers,
2728
getAllFileHashes,
2829
SCHEMA_VERSION,
2930
type CodemapDatabase,
@@ -220,6 +221,9 @@ function insertParsedResults(
220221
insertComponents(db, parsed.components);
221222
}
222223
if (parsed.markers?.length) insertMarkers(db, parsed.markers);
224+
if (parsed.typeMembers?.length) {
225+
insertTypeMembers(db, parsed.typeMembers);
226+
}
223227
}
224228
} catch (err) {
225229
console.error(
@@ -246,6 +250,7 @@ export function fetchTableStats(db: CodemapDatabase): IndexTableStats {
246250
(SELECT COUNT(*) FROM components) as components,
247251
(SELECT COUNT(*) FROM dependencies) as dependencies,
248252
(SELECT COUNT(*) FROM markers) as markers,
253+
(SELECT COUNT(*) FROM type_members) as type_members,
249254
(SELECT COUNT(*) FROM css_variables) as css_vars,
250255
(SELECT COUNT(*) FROM css_classes) as css_classes,
251256
(SELECT COUNT(*) FROM css_keyframes) as css_keyframes`,
@@ -360,6 +365,8 @@ export async function indexFiles(
360365
if (data.exports.length) insertExports(db, data.exports);
361366
if (data.components.length) insertComponents(db, data.components);
362367
if (data.markers.length) insertMarkers(db, data.markers);
368+
if (data.typeMembers.length)
369+
insertTypeMembers(db, data.typeMembers);
363370
}
364371
} catch (err) {
365372
console.error(

src/application/run-index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ function emptyStats(): IndexTableStats {
2020
components: 0,
2121
dependencies: 0,
2222
markers: 0,
23+
type_members: 0,
2324
css_vars: 0,
2425
css_classes: 0,
2526
css_keyframes: 0,

src/application/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export interface IndexTableStats extends Record<string, number> {
99
components: number;
1010
dependencies: number;
1111
markers: number;
12+
type_members: number;
1213
css_vars: number;
1314
css_classes: number;
1415
css_keyframes: number;

src/db.ts

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
* tweaks; run `--full` locally after pulling. After v1.0, bump in lockstep with
1010
* `createTables` / `createIndexes` when the on-disk schema changes.
1111
*/
12-
export const SCHEMA_VERSION = 1;
12+
export const SCHEMA_VERSION = 2;
1313

1414
export type { CodemapDatabase };
1515

@@ -50,7 +50,8 @@ export function createTables(db: CodemapDatabase) {
5050
signature TEXT,
5151
is_exported INTEGER DEFAULT 0,
5252
is_default_export INTEGER DEFAULT 0,
53-
members TEXT
53+
members TEXT,
54+
doc_comment TEXT
5455
) STRICT;
5556
5657
CREATE TABLE IF NOT EXISTS imports (
@@ -119,6 +120,16 @@ export function createTables(db: CodemapDatabase) {
119120
line_number INTEGER
120121
) STRICT;
121122
123+
CREATE TABLE IF NOT EXISTS type_members (
124+
id INTEGER PRIMARY KEY AUTOINCREMENT,
125+
file_path TEXT NOT NULL REFERENCES files(path) ON DELETE CASCADE,
126+
symbol_name TEXT NOT NULL,
127+
name TEXT NOT NULL,
128+
type TEXT,
129+
is_optional INTEGER DEFAULT 0,
130+
is_readonly INTEGER DEFAULT 0
131+
) STRICT;
132+
122133
CREATE TABLE IF NOT EXISTS meta (
123134
key TEXT PRIMARY KEY,
124135
value TEXT
@@ -160,6 +171,9 @@ export function createIndexes(db: CodemapDatabase) {
160171
CREATE INDEX IF NOT EXISTS idx_css_classes_name ON css_classes(name, file_path, is_module);
161172
CREATE INDEX IF NOT EXISTS idx_css_classes_file ON css_classes(file_path);
162173
CREATE INDEX IF NOT EXISTS idx_css_keyframes_name ON css_keyframes(name, file_path);
174+
175+
CREATE INDEX IF NOT EXISTS idx_type_members_symbol ON type_members(symbol_name, file_path, name, type, is_optional, is_readonly);
176+
CREATE INDEX IF NOT EXISTS idx_type_members_file ON type_members(file_path);
163177
`);
164178
}
165179

@@ -188,6 +202,7 @@ export function createSchema(db: CodemapDatabase) {
188202

189203
export function dropAll(db: CodemapDatabase) {
190204
db.run(`
205+
DROP TABLE IF EXISTS type_members;
191206
DROP TABLE IF EXISTS dependencies;
192207
DROP TABLE IF EXISTS markers;
193208
DROP TABLE IF EXISTS components;
@@ -256,6 +271,7 @@ export interface SymbolRow {
256271
is_exported: number;
257272
is_default_export: number;
258273
members: string | null;
274+
doc_comment: string | null;
259275
}
260276

261277
const BATCH_SIZE = 100;
@@ -288,8 +304,8 @@ export function insertSymbols(db: CodemapDatabase, symbols: SymbolRow[]) {
288304
batchInsert(
289305
db,
290306
symbols,
291-
"INSERT INTO symbols (file_path, name, kind, line_start, line_end, signature, is_exported, is_default_export, members)",
292-
"(?,?,?,?,?,?,?,?,?)",
307+
"INSERT INTO symbols (file_path, name, kind, line_start, line_end, signature, is_exported, is_default_export, members, doc_comment)",
308+
"(?,?,?,?,?,?,?,?,?,?)",
293309
(s, v) =>
294310
v.push(
295311
s.file_path,
@@ -301,6 +317,7 @@ export function insertSymbols(db: CodemapDatabase, symbols: SymbolRow[]) {
301317
s.is_exported,
302318
s.is_default_export,
303319
s.members,
320+
s.doc_comment,
304321
),
305322
);
306323
}
@@ -469,6 +486,36 @@ export function insertCssKeyframes(
469486
);
470487
}
471488

489+
export interface TypeMemberRow {
490+
file_path: string;
491+
symbol_name: string;
492+
name: string;
493+
type: string | null;
494+
is_optional: number;
495+
is_readonly: number;
496+
}
497+
498+
export function insertTypeMembers(
499+
db: CodemapDatabase,
500+
members: TypeMemberRow[],
501+
) {
502+
batchInsert(
503+
db,
504+
members,
505+
"INSERT INTO type_members (file_path, symbol_name, name, type, is_optional, is_readonly)",
506+
"(?,?,?,?,?,?)",
507+
(m, v) =>
508+
v.push(
509+
m.file_path,
510+
m.symbol_name,
511+
m.name,
512+
m.type,
513+
m.is_optional,
514+
m.is_readonly,
515+
),
516+
);
517+
}
518+
472519
export function getAllFileHashes(db: CodemapDatabase): Map<string, string> {
473520
const rows = db
474521
.query<{ path: string; content_hash: string }>(

src/parsed-types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {
88
CssVariableRow,
99
CssClassRow,
1010
CssKeyframeRow,
11+
TypeMemberRow,
1112
} from "./db";
1213

1314
/**
@@ -24,6 +25,7 @@ export interface ParsedFile {
2425
exports?: ExportRow[];
2526
components?: ComponentRow[];
2627
markers?: MarkerRow[];
28+
typeMembers?: TypeMemberRow[];
2729
cssVariables?: CssVariableRow[];
2830
cssClasses?: CssClassRow[];
2931
cssKeyframes?: CssKeyframeRow[];

0 commit comments

Comments
 (0)