@aprimediet/codewalker 1.1.0 → 1.3.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 +28 -3
- package/package.json +1 -1
- package/prompts/codewalker.md +3 -1
- package/skills/codewalker/SKILL.md +118 -28
- package/src/cards.test.ts +123 -1
- package/src/cards.ts +53 -0
- package/src/db.test.ts +405 -3
- package/src/db.ts +402 -29
- package/src/enrich.test.ts +102 -0
- package/src/enrich.ts +107 -0
- package/src/format.test.ts +103 -0
- package/src/format.ts +11 -0
- package/src/index.contract.test.ts +77 -1
- package/src/index.ts +273 -19
- package/src/indexer.heal.test.ts +90 -0
- package/src/indexer.ts +9 -1
- package/src/libs/cards.test.ts +86 -0
- package/src/libs/cards.ts +53 -0
- package/src/libs/dts.test.ts +269 -0
- package/src/libs/dts.ts +213 -0
- package/src/libs/indexer.test.ts +236 -0
- package/src/libs/indexer.ts +291 -0
- package/src/libs/resolve.test.ts +218 -0
- package/src/libs/resolve.ts +120 -0
- package/src/notes-cards.test.ts +99 -0
- package/src/notes-cards.ts +92 -0
- package/src/notes.test.ts +172 -0
- package/src/notes.ts +145 -0
- package/src/project.test.ts +12 -1
- package/src/project.ts +7 -1
- package/src/query.test.ts +148 -1
- package/src/query.ts +28 -8
- package/src/types.ts +44 -0
package/src/db.test.ts
CHANGED
|
@@ -3,7 +3,7 @@ import * as fs from 'node:fs';
|
|
|
3
3
|
import * as path from 'node:path';
|
|
4
4
|
import * as os from 'node:os';
|
|
5
5
|
import Database from 'better-sqlite3';
|
|
6
|
-
import { openDb, bootstrapDb, upsertSymbol, searchSymbols, getMeta, setMeta, deleteFileSymbols } from './db.ts';
|
|
6
|
+
import { openDb, bootstrapDb, upsertSymbol, searchSymbols, getMeta, setMeta, deleteFileSymbols, upsertLibrary, upsertLibSymbol, deleteLibrary, searchLibSymbols, upsertNote, searchNotes, deleteNote, updateSymbolSummary, selectUnenrichedSymbols } from './db.ts';
|
|
7
7
|
|
|
8
8
|
describe('db.ts', () => {
|
|
9
9
|
let tmpDir: string;
|
|
@@ -47,11 +47,48 @@ describe('db.ts', () => {
|
|
|
47
47
|
// No error = idempotent
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
it('sets user_version to
|
|
50
|
+
it('sets user_version to 3 (v1.3 schema)', () => {
|
|
51
51
|
const db = new Database(dbPath);
|
|
52
52
|
bootstrapDb(db);
|
|
53
53
|
const version = db.pragma('user_version', { simple: true }) as number;
|
|
54
|
-
expect(version).toBe(
|
|
54
|
+
expect(version).toBe(3);
|
|
55
|
+
db.close();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('creates libraries, lib_symbols, and lib_symbols_fts tables', () => {
|
|
59
|
+
const db = openDb(dbPath);
|
|
60
|
+
const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all() as { name: string }[];
|
|
61
|
+
const tableNames = tables.map(t => t.name);
|
|
62
|
+
expect(tableNames).toContain('libraries');
|
|
63
|
+
expect(tableNames).toContain('lib_symbols');
|
|
64
|
+
|
|
65
|
+
const ftsTables = db.prepare("SELECT name FROM sqlite_master WHERE name='lib_symbols_fts'").all() as { name: string }[];
|
|
66
|
+
expect(ftsTables.length).toBe(1);
|
|
67
|
+
db.close();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('does not destroy existing tables (additive upgrade, v2 → notes in v3)', () => {
|
|
71
|
+
// Bootstrap then re-bootstrap (simulate upgrade)
|
|
72
|
+
const db = new Database(dbPath);
|
|
73
|
+
bootstrapDb(db);
|
|
74
|
+
upsertSymbol(db, {
|
|
75
|
+
name: 'keep', kind: 'function', file_path: 'src/a.ts',
|
|
76
|
+
line_start: 1, line_end: 1, signature: '', doc: '', summary: '', card_path: '',
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Call bootstrapDb again (simulates upgrade to v3 adds notes tables)
|
|
80
|
+
bootstrapDb(db);
|
|
81
|
+
|
|
82
|
+
// Symbol still there
|
|
83
|
+
const symbols = searchSymbols(db, 'keep', undefined, 10);
|
|
84
|
+
expect(symbols).toHaveLength(1);
|
|
85
|
+
|
|
86
|
+
// Notes tables exist
|
|
87
|
+
const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all() as { name: string }[];
|
|
88
|
+
expect(tables.map(t => t.name)).toContain('notes');
|
|
89
|
+
|
|
90
|
+
const ftsTables = db.prepare("SELECT name FROM sqlite_master WHERE name='notes_fts'").all() as { name: string }[];
|
|
91
|
+
expect(ftsTables.length).toBe(1);
|
|
55
92
|
db.close();
|
|
56
93
|
});
|
|
57
94
|
});
|
|
@@ -173,6 +210,371 @@ describe('db.ts', () => {
|
|
|
173
210
|
});
|
|
174
211
|
});
|
|
175
212
|
|
|
213
|
+
describe('library CRUD', () => {
|
|
214
|
+
it('upsertLibrary inserts or updates a library record', () => {
|
|
215
|
+
const db = openDb(dbPath);
|
|
216
|
+
upsertLibrary(db, { name: 'hono', version: '4.6.3', source: 'node_modules', dts_path: '/a.d.ts', readme: 'Hono web framework' });
|
|
217
|
+
|
|
218
|
+
const row = db.prepare("SELECT * FROM libraries WHERE name = ?").get('hono') as any;
|
|
219
|
+
expect(row).not.toBeUndefined();
|
|
220
|
+
expect(row.version).toBe('4.6.3');
|
|
221
|
+
expect(row.source).toBe('node_modules');
|
|
222
|
+
db.close();
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('upsertLibSymbol inserts a lib symbol and it is FTS-searchable', () => {
|
|
226
|
+
const db = openDb(dbPath);
|
|
227
|
+
upsertLibSymbol(db, {
|
|
228
|
+
lib: 'hono', version: '4.6.3', name: 'createMiddleware',
|
|
229
|
+
kind: 'function', signature: 'export declare function createMiddleware(...)',
|
|
230
|
+
doc: 'Define a typed middleware handler.', summary: 'Define a typed middleware handler.',
|
|
231
|
+
card_path: '/cards/hono/createMiddleware.md',
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
const results = searchLibSymbols(db, 'createMiddleware', undefined, 10);
|
|
235
|
+
expect(results).toHaveLength(1);
|
|
236
|
+
expect(results[0]!.name).toBe('createMiddleware');
|
|
237
|
+
expect(results[0]!.lib).toBe('hono');
|
|
238
|
+
expect(results[0]!.version).toBe('4.6.3');
|
|
239
|
+
expect(results[0]!.source).toBe('lib');
|
|
240
|
+
|
|
241
|
+
db.close();
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('searchLibSymbols empty query returns all symbols ordered by name', () => {
|
|
245
|
+
const db = openDb(dbPath);
|
|
246
|
+
upsertLibSymbol(db, {
|
|
247
|
+
lib: 'hono', version: '4.6.3', name: 'zMiddleware',
|
|
248
|
+
kind: 'function', signature: '', doc: '', summary: '', card_path: '',
|
|
249
|
+
});
|
|
250
|
+
upsertLibSymbol(db, {
|
|
251
|
+
lib: 'hono', version: '4.6.3', name: 'aRouter',
|
|
252
|
+
kind: 'function', signature: '', doc: '', summary: '', card_path: '',
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
const results = searchLibSymbols(db, '', undefined, 10);
|
|
256
|
+
expect(results).toHaveLength(2);
|
|
257
|
+
expect(results[0]!.name).toBe('aRouter');
|
|
258
|
+
expect(results[1]!.name).toBe('zMiddleware');
|
|
259
|
+
db.close();
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('deleteLibrary removes symbols and FTS rows for all versions of a lib', () => {
|
|
263
|
+
const db = openDb(dbPath);
|
|
264
|
+
|
|
265
|
+
upsertLibSymbol(db, {
|
|
266
|
+
lib: 'hono', version: '4.6.3', name: 'createMiddleware',
|
|
267
|
+
kind: 'function', signature: '', doc: '', summary: '', card_path: '',
|
|
268
|
+
});
|
|
269
|
+
upsertLibSymbol(db, {
|
|
270
|
+
lib: 'hono', version: '4.5.0', name: 'oldFunc',
|
|
271
|
+
kind: 'function', signature: '', doc: '', summary: '', card_path: '',
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
deleteLibrary(db, 'hono');
|
|
275
|
+
|
|
276
|
+
const results = searchLibSymbols(db, '', undefined, 10);
|
|
277
|
+
expect(results).toHaveLength(0);
|
|
278
|
+
|
|
279
|
+
const libRow = db.prepare("SELECT * FROM libraries WHERE name = ?").get('hono') as any;
|
|
280
|
+
expect(libRow).toBeUndefined();
|
|
281
|
+
|
|
282
|
+
db.close();
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('re-inserting same (lib, name) does not create duplicates', () => {
|
|
286
|
+
const db = openDb(dbPath);
|
|
287
|
+
|
|
288
|
+
upsertLibSymbol(db, {
|
|
289
|
+
lib: 'hono', version: '4.6.3', name: 'createMiddleware',
|
|
290
|
+
kind: 'function', signature: 'v1', doc: '', summary: '', card_path: '',
|
|
291
|
+
});
|
|
292
|
+
upsertLibSymbol(db, {
|
|
293
|
+
lib: 'hono', version: '4.6.3', name: 'createMiddleware',
|
|
294
|
+
kind: 'function', signature: 'v2', doc: '', summary: '', card_path: '',
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
const results = searchLibSymbols(db, 'createMiddleware', undefined, 10);
|
|
298
|
+
expect(results).toHaveLength(1);
|
|
299
|
+
// Latest signature
|
|
300
|
+
expect(results[0]!.signature).toBe('v2');
|
|
301
|
+
|
|
302
|
+
db.close();
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('kind filter narrows lib symbol results', () => {
|
|
306
|
+
const db = openDb(dbPath);
|
|
307
|
+
|
|
308
|
+
upsertLibSymbol(db, {
|
|
309
|
+
lib: 'hono', version: '4.6.3', name: 'myFunc',
|
|
310
|
+
kind: 'function', signature: '', doc: '', summary: '', card_path: '',
|
|
311
|
+
});
|
|
312
|
+
upsertLibSymbol(db, {
|
|
313
|
+
lib: 'hono', version: '4.6.3', name: 'MyType',
|
|
314
|
+
kind: 'type', signature: '', doc: '', summary: '', card_path: '',
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
const funcs = searchLibSymbols(db, '', 'function', 10);
|
|
318
|
+
expect(funcs).toHaveLength(1);
|
|
319
|
+
expect(funcs[0]!.name).toBe('myFunc');
|
|
320
|
+
|
|
321
|
+
db.close();
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
describe('notes CRUD + FTS via triggers', () => {
|
|
326
|
+
it('creates notes + notes_fts on bootstrap', () => {
|
|
327
|
+
const db = openDb(dbPath);
|
|
328
|
+
const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all() as { name: string }[];
|
|
329
|
+
expect(tables.map(t => t.name)).toContain('notes');
|
|
330
|
+
|
|
331
|
+
const ftsTables = db.prepare("SELECT name FROM sqlite_master WHERE name='notes_fts'").all() as { name: string }[];
|
|
332
|
+
expect(ftsTables.length).toBe(1);
|
|
333
|
+
|
|
334
|
+
// Triggers exist
|
|
335
|
+
const triggers = db.prepare("SELECT name FROM sqlite_master WHERE type='trigger'").all() as { name: string }[];
|
|
336
|
+
const triggerNames = triggers.map(t => t.name);
|
|
337
|
+
expect(triggerNames).toContain('notes_ai');
|
|
338
|
+
expect(triggerNames).toContain('notes_ad');
|
|
339
|
+
expect(triggerNames).toContain('notes_au');
|
|
340
|
+
db.close();
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it('upsertNote inserts a note and FTS MATCH finds it', () => {
|
|
344
|
+
const db = openDb(dbPath);
|
|
345
|
+
|
|
346
|
+
const id = upsertNote(db, {
|
|
347
|
+
note_kind: 'glossary',
|
|
348
|
+
title: 'Idempotency Key',
|
|
349
|
+
body: 'A client-supplied key that makes a retried POST safe to replay.',
|
|
350
|
+
tags: 'api,payments',
|
|
351
|
+
related: 'createCharge',
|
|
352
|
+
card_path: '/entries/glossary/idempotency-key.md',
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
expect(typeof id).toBe('number');
|
|
356
|
+
expect(id).toBeGreaterThan(0);
|
|
357
|
+
|
|
358
|
+
// FTS search
|
|
359
|
+
const results = searchNotes(db, 'idempotency');
|
|
360
|
+
expect(results).toHaveLength(1);
|
|
361
|
+
expect(results[0]!.name).toBe('Idempotency Key');
|
|
362
|
+
expect(results[0]!.note_kind).toBe('glossary');
|
|
363
|
+
expect(results[0]!.source).toBe('note');
|
|
364
|
+
|
|
365
|
+
db.close();
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it('upsertNote upserts on (note_kind, title) — updating a note refreshes FTS via notes_au', () => {
|
|
369
|
+
const db = openDb(dbPath);
|
|
370
|
+
|
|
371
|
+
upsertNote(db, {
|
|
372
|
+
note_kind: 'glossary',
|
|
373
|
+
title: 'Retry Key',
|
|
374
|
+
body: 'Old description',
|
|
375
|
+
tags: '',
|
|
376
|
+
related: '',
|
|
377
|
+
card_path: '/entries/glossary/retry-key.md',
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
// Update the body
|
|
381
|
+
upsertNote(db, {
|
|
382
|
+
note_kind: 'glossary',
|
|
383
|
+
title: 'Retry Key',
|
|
384
|
+
body: 'New improved description with uniqueTermXYZ',
|
|
385
|
+
tags: 'updated',
|
|
386
|
+
related: '',
|
|
387
|
+
card_path: '/entries/glossary/retry-key.md',
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
// Should find by new text (proves notes_au fired the reindex)
|
|
391
|
+
const results = searchNotes(db, 'uniqueTermXYZ');
|
|
392
|
+
expect(results).toHaveLength(1);
|
|
393
|
+
expect(results[0]!.summary).toContain('uniqueTermXYZ');
|
|
394
|
+
|
|
395
|
+
// Should NOT have duplicates (upsert behavior)
|
|
396
|
+
const all = searchNotes(db, '');
|
|
397
|
+
expect(all).toHaveLength(1);
|
|
398
|
+
|
|
399
|
+
db.close();
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it('deleteNote removes a note from both base table and FTS', () => {
|
|
403
|
+
const db = openDb(dbPath);
|
|
404
|
+
|
|
405
|
+
upsertNote(db, {
|
|
406
|
+
note_kind: 'glossary',
|
|
407
|
+
title: 'Temp Term',
|
|
408
|
+
body: 'Will be deleted',
|
|
409
|
+
tags: '',
|
|
410
|
+
related: '',
|
|
411
|
+
card_path: '',
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
deleteNote(db, 'glossary', 'Temp Term');
|
|
415
|
+
|
|
416
|
+
const results = searchNotes(db, 'Temp');
|
|
417
|
+
expect(results).toHaveLength(0);
|
|
418
|
+
|
|
419
|
+
// Verify base table is also empty
|
|
420
|
+
const row = db.prepare("SELECT * FROM notes WHERE title = ?").get('Temp Term');
|
|
421
|
+
expect(row).toBeUndefined();
|
|
422
|
+
|
|
423
|
+
db.close();
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
it('searchNotes empty query returns all notes ordered by title', () => {
|
|
427
|
+
const db = openDb(dbPath);
|
|
428
|
+
|
|
429
|
+
upsertNote(db, {
|
|
430
|
+
note_kind: 'glossary', title: 'Zebra', body: '', tags: '', related: '', card_path: '',
|
|
431
|
+
});
|
|
432
|
+
upsertNote(db, {
|
|
433
|
+
note_kind: 'decision', title: 'Alpha decision', body: '', tags: '', related: '', card_path: '',
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
const results = searchNotes(db, '');
|
|
437
|
+
expect(results).toHaveLength(2);
|
|
438
|
+
expect(results[0]!.name).toBe('Alpha decision');
|
|
439
|
+
expect(results[1]!.name).toBe('Zebra');
|
|
440
|
+
|
|
441
|
+
db.close();
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it('searchNotes with kindFilter narrows by note_kind', () => {
|
|
445
|
+
const db = openDb(dbPath);
|
|
446
|
+
|
|
447
|
+
upsertNote(db, {
|
|
448
|
+
note_kind: 'glossary', title: 'Term', body: '', tags: '', related: '', card_path: '',
|
|
449
|
+
});
|
|
450
|
+
upsertNote(db, {
|
|
451
|
+
note_kind: 'decision', title: 'Decide', body: '', tags: '', related: '', card_path: '',
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
const glossaryResults = searchNotes(db, '', 'glossary');
|
|
455
|
+
expect(glossaryResults).toHaveLength(1);
|
|
456
|
+
expect(glossaryResults[0]!.name).toBe('Term');
|
|
457
|
+
|
|
458
|
+
const decisionResults = searchNotes(db, '', 'decision');
|
|
459
|
+
expect(decisionResults).toHaveLength(1);
|
|
460
|
+
expect(decisionResults[0]!.name).toBe('Decide');
|
|
461
|
+
|
|
462
|
+
db.close();
|
|
463
|
+
});
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
describe('updateSymbolSummary', () => {
|
|
467
|
+
it('sets symbols.summary for a matching card_path and symbols_au reindexes FTS', () => {
|
|
468
|
+
const db = openDb(dbPath);
|
|
469
|
+
|
|
470
|
+
upsertSymbol(db, {
|
|
471
|
+
name: 'myFunc', kind: 'function', file_path: 'src/a.ts',
|
|
472
|
+
line_start: 1, line_end: 10, signature: '', doc: 'Old doc', summary: '',
|
|
473
|
+
card_path: '/cards/myFunc.md',
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
const result = updateSymbolSummary(db, '/cards/myFunc.md', 'A function that does X');
|
|
477
|
+
expect(result).toBe(true);
|
|
478
|
+
|
|
479
|
+
// Query FTS for the summary word — proves symbols_au trigger reindexed
|
|
480
|
+
const syms = searchSymbols(db, 'does X', undefined, 10);
|
|
481
|
+
expect(syms).toHaveLength(1);
|
|
482
|
+
expect(syms[0]!.name).toBe('myFunc');
|
|
483
|
+
expect(syms[0]!.summary).toBe('A function that does X');
|
|
484
|
+
|
|
485
|
+
db.close();
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
it('returns false when no symbol matches card_path', () => {
|
|
489
|
+
const db = openDb(dbPath);
|
|
490
|
+
const result = updateSymbolSummary(db, '/nonexistent.md', 'summary');
|
|
491
|
+
expect(result).toBe(false);
|
|
492
|
+
db.close();
|
|
493
|
+
});
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
describe('selectUnenrichedSymbols', () => {
|
|
497
|
+
it('selects symbols with empty summary under a path prefix', () => {
|
|
498
|
+
const db = openDb(dbPath);
|
|
499
|
+
|
|
500
|
+
upsertSymbol(db, {
|
|
501
|
+
name: 'enriched', kind: 'function', file_path: 'src/auth/token.ts',
|
|
502
|
+
line_start: 1, line_end: 5, signature: '', doc: '', summary: 'Already done',
|
|
503
|
+
card_path: '/cards/enriched.md',
|
|
504
|
+
});
|
|
505
|
+
upsertSymbol(db, {
|
|
506
|
+
name: 'unenriched', kind: 'function', file_path: 'src/auth/token.ts',
|
|
507
|
+
line_start: 10, line_end: 20, signature: '', doc: '', summary: '',
|
|
508
|
+
card_path: '/cards/unenriched.md',
|
|
509
|
+
});
|
|
510
|
+
upsertSymbol(db, {
|
|
511
|
+
name: 'other', kind: 'function', file_path: 'src/other/util.ts',
|
|
512
|
+
line_start: 1, line_end: 1, signature: '', doc: '', summary: '',
|
|
513
|
+
card_path: '/cards/other.md',
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
const results = selectUnenrichedSymbols(db, 'src/auth/', 10);
|
|
517
|
+
expect(results).toHaveLength(1);
|
|
518
|
+
expect(results[0]!.name).toBe('unenriched');
|
|
519
|
+
expect(results[0]!.card_path).toBe('/cards/unenriched.md');
|
|
520
|
+
|
|
521
|
+
db.close();
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
it('returns empty for a prefix with no unenriched symbols', () => {
|
|
525
|
+
const db = openDb(dbPath);
|
|
526
|
+
|
|
527
|
+
upsertSymbol(db, {
|
|
528
|
+
name: 'done', kind: 'function', file_path: 'src/done.ts',
|
|
529
|
+
line_start: 1, line_end: 1, signature: '', doc: '', summary: 'Has summary',
|
|
530
|
+
card_path: '',
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
const results = selectUnenrichedSymbols(db, 'src/done.ts', 10);
|
|
534
|
+
expect(results).toHaveLength(0);
|
|
535
|
+
|
|
536
|
+
db.close();
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
it('respects limit parameter', () => {
|
|
540
|
+
const db = openDb(dbPath);
|
|
541
|
+
|
|
542
|
+
for (let i = 0; i < 5; i++) {
|
|
543
|
+
upsertSymbol(db, {
|
|
544
|
+
name: `sym${i}`, kind: 'function', file_path: 'src/a.ts',
|
|
545
|
+
line_start: i, line_end: i, signature: '', doc: '', summary: '',
|
|
546
|
+
card_path: '',
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
const results = selectUnenrichedSymbols(db, 'src/', 3);
|
|
551
|
+
expect(results).toHaveLength(3);
|
|
552
|
+
|
|
553
|
+
db.close();
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
it('returns card_path, name, kind, file_path, line_start, line_end for each result', () => {
|
|
557
|
+
const db = openDb(dbPath);
|
|
558
|
+
|
|
559
|
+
upsertSymbol(db, {
|
|
560
|
+
name: 'myFunc', kind: 'function', file_path: 'src/test.ts',
|
|
561
|
+
line_start: 10, line_end: 20, signature: '', doc: '', summary: '',
|
|
562
|
+
card_path: '/cards/test.md',
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
const results = selectUnenrichedSymbols(db, 'src/', 10);
|
|
566
|
+
expect(results).toHaveLength(1);
|
|
567
|
+
expect(results[0]!.name).toBe('myFunc');
|
|
568
|
+
expect(results[0]!.kind).toBe('function');
|
|
569
|
+
expect(results[0]!.file_path).toBe('src/test.ts');
|
|
570
|
+
expect(results[0]!.line_start).toBe(10);
|
|
571
|
+
expect(results[0]!.line_end).toBe(20);
|
|
572
|
+
expect(results[0]!.card_path).toBe('/cards/test.md');
|
|
573
|
+
|
|
574
|
+
db.close();
|
|
575
|
+
});
|
|
576
|
+
});
|
|
577
|
+
|
|
176
578
|
describe('meta', () => {
|
|
177
579
|
it('setMeta and getMeta round-trip values', () => {
|
|
178
580
|
const db = openDb(dbPath);
|