@voidwire/lore 0.2.0 → 0.4.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/cli.ts +180 -29
- package/index.ts +10 -3
- package/lib/about.ts +103 -0
- package/lib/list.ts +32 -32
- package/package.json +1 -1
package/cli.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* Usage:
|
|
11
11
|
* lore search <query> Search all sources
|
|
12
12
|
* lore search <source> <query> Search specific source
|
|
13
|
-
* lore list <
|
|
13
|
+
* lore list <source> List source entries
|
|
14
14
|
* lore capture task|knowledge|note|teaching Capture knowledge
|
|
15
15
|
*
|
|
16
16
|
* Exit codes:
|
|
@@ -25,11 +25,12 @@ import {
|
|
|
25
25
|
searchAtuin,
|
|
26
26
|
listSources,
|
|
27
27
|
list,
|
|
28
|
-
listDomains,
|
|
29
28
|
formatBriefList,
|
|
30
29
|
info,
|
|
31
30
|
formatInfoHuman,
|
|
32
31
|
projects,
|
|
32
|
+
about,
|
|
33
|
+
formatBriefAbout,
|
|
33
34
|
captureTask,
|
|
34
35
|
captureKnowledge,
|
|
35
36
|
captureNote,
|
|
@@ -37,11 +38,11 @@ import {
|
|
|
37
38
|
semanticSearch,
|
|
38
39
|
formatBriefSearch,
|
|
39
40
|
hasEmbeddings,
|
|
40
|
-
|
|
41
|
+
SOURCES,
|
|
41
42
|
type SearchResult,
|
|
42
43
|
type ListResult,
|
|
43
44
|
type ListEntry,
|
|
44
|
-
type
|
|
45
|
+
type Source,
|
|
45
46
|
type TaskInput,
|
|
46
47
|
type KnowledgeInput,
|
|
47
48
|
type NoteInput,
|
|
@@ -287,7 +288,7 @@ async function handleSearch(args: string[]): Promise<void> {
|
|
|
287
288
|
// ============================================================================
|
|
288
289
|
|
|
289
290
|
function formatHumanOutput(result: ListResult): string {
|
|
290
|
-
const lines: string[] = [`${result.
|
|
291
|
+
const lines: string[] = [`${result.source} (${result.count} entries):`, ""];
|
|
291
292
|
|
|
292
293
|
for (const entry of result.entries) {
|
|
293
294
|
lines.push(` ${entry.title}`);
|
|
@@ -307,21 +308,23 @@ function handleList(args: string[]): void {
|
|
|
307
308
|
const parsed = parseArgs(args);
|
|
308
309
|
const positional = getPositionalArgs(args);
|
|
309
310
|
|
|
310
|
-
// Handle --domains flag
|
|
311
|
+
// Handle --domains flag (deprecated)
|
|
311
312
|
if (hasFlag(args, "domains")) {
|
|
312
|
-
|
|
313
|
-
|
|
313
|
+
console.error("⚠️ --domains is deprecated. Use 'lore sources' instead.");
|
|
314
|
+
const sources = listSources();
|
|
315
|
+
output({ success: true, sources });
|
|
316
|
+
console.error(`✅ ${SOURCES.length} sources available`);
|
|
314
317
|
process.exit(0);
|
|
315
318
|
}
|
|
316
319
|
|
|
317
320
|
if (positional.length === 0) {
|
|
318
|
-
fail(`Missing
|
|
321
|
+
fail(`Missing source. Available: ${SOURCES.join(", ")}`);
|
|
319
322
|
}
|
|
320
323
|
|
|
321
|
-
const
|
|
324
|
+
const source = positional[0] as Source;
|
|
322
325
|
|
|
323
|
-
if (!
|
|
324
|
-
fail(`Invalid
|
|
326
|
+
if (!SOURCES.includes(source)) {
|
|
327
|
+
fail(`Invalid source: ${source}. Available: ${SOURCES.join(", ")}`);
|
|
325
328
|
}
|
|
326
329
|
|
|
327
330
|
const limit = parsed.has("limit")
|
|
@@ -332,7 +335,7 @@ function handleList(args: string[]): void {
|
|
|
332
335
|
const brief = hasFlag(args, "brief");
|
|
333
336
|
|
|
334
337
|
try {
|
|
335
|
-
const result = list(
|
|
338
|
+
const result = list(source, { limit, project });
|
|
336
339
|
|
|
337
340
|
if (brief) {
|
|
338
341
|
console.log(formatBriefList(result));
|
|
@@ -345,13 +348,13 @@ function handleList(args: string[]): void {
|
|
|
345
348
|
} else {
|
|
346
349
|
output({
|
|
347
350
|
success: true,
|
|
348
|
-
|
|
351
|
+
source: result.source,
|
|
349
352
|
entries: result.entries,
|
|
350
353
|
count: result.count,
|
|
351
354
|
});
|
|
352
355
|
}
|
|
353
356
|
|
|
354
|
-
console.error(`✅ ${result.count} entries in ${
|
|
357
|
+
console.error(`✅ ${result.count} entries in ${source}`);
|
|
355
358
|
process.exit(0);
|
|
356
359
|
} catch (error) {
|
|
357
360
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -417,6 +420,99 @@ function handleProjects(args: string[]): void {
|
|
|
417
420
|
}
|
|
418
421
|
}
|
|
419
422
|
|
|
423
|
+
// ============================================================================
|
|
424
|
+
// About Command
|
|
425
|
+
// ============================================================================
|
|
426
|
+
|
|
427
|
+
function handleAbout(args: string[]): void {
|
|
428
|
+
if (hasFlag(args, "help")) {
|
|
429
|
+
showAboutHelp();
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const parsed = parseArgs(args);
|
|
433
|
+
const positional = getPositionalArgs(args);
|
|
434
|
+
|
|
435
|
+
if (positional.length === 0) {
|
|
436
|
+
fail("Missing project name. Use: lore about <project>");
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const project = positional[0];
|
|
440
|
+
const brief = hasFlag(args, "brief");
|
|
441
|
+
const limit = parsed.has("limit")
|
|
442
|
+
? parseInt(parsed.get("limit")!, 10)
|
|
443
|
+
: undefined;
|
|
444
|
+
|
|
445
|
+
try {
|
|
446
|
+
const result = about(project, { brief, limit });
|
|
447
|
+
|
|
448
|
+
if (brief) {
|
|
449
|
+
console.log(formatBriefAbout(result));
|
|
450
|
+
} else {
|
|
451
|
+
output({
|
|
452
|
+
success: true,
|
|
453
|
+
...result,
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const totalCount =
|
|
458
|
+
result.commits.count +
|
|
459
|
+
result.captures.count +
|
|
460
|
+
result.tasks.count +
|
|
461
|
+
result.teachings.count +
|
|
462
|
+
result.sessions.count;
|
|
463
|
+
|
|
464
|
+
console.error(`✅ ${totalCount} entries for project: ${project}`);
|
|
465
|
+
process.exit(0);
|
|
466
|
+
} catch (error) {
|
|
467
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
468
|
+
fail(message, 2);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// ============================================================================
|
|
473
|
+
// Sources Command
|
|
474
|
+
// ============================================================================
|
|
475
|
+
|
|
476
|
+
function handleSources(args: string[]): void {
|
|
477
|
+
if (hasFlag(args, "help")) {
|
|
478
|
+
showSourcesHelp();
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
try {
|
|
482
|
+
const sources = listSources();
|
|
483
|
+
output({
|
|
484
|
+
success: true,
|
|
485
|
+
sources,
|
|
486
|
+
});
|
|
487
|
+
console.error(`✅ ${sources.length} sources indexed`);
|
|
488
|
+
process.exit(0);
|
|
489
|
+
} catch (error) {
|
|
490
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
491
|
+
fail(message, 2);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function showSourcesHelp(): void {
|
|
496
|
+
console.log(`
|
|
497
|
+
lore sources - List all indexed sources with counts
|
|
498
|
+
|
|
499
|
+
Usage:
|
|
500
|
+
lore sources List all sources with entry counts
|
|
501
|
+
|
|
502
|
+
Options:
|
|
503
|
+
--help Show this help
|
|
504
|
+
|
|
505
|
+
Output:
|
|
506
|
+
JSON array of {source, count} objects, sorted by count descending.
|
|
507
|
+
|
|
508
|
+
Examples:
|
|
509
|
+
lore sources
|
|
510
|
+
lore sources | jq '.[0]'
|
|
511
|
+
lore sources | jq -r '.[] | "\\(.source): \\(.count)"'
|
|
512
|
+
`);
|
|
513
|
+
process.exit(0);
|
|
514
|
+
}
|
|
515
|
+
|
|
420
516
|
// ============================================================================
|
|
421
517
|
// Capture Command
|
|
422
518
|
// ============================================================================
|
|
@@ -584,11 +680,12 @@ Philosophy:
|
|
|
584
680
|
Usage:
|
|
585
681
|
lore search <query> Search all sources
|
|
586
682
|
lore search <source> <query> Search specific source
|
|
587
|
-
lore
|
|
588
|
-
lore list <
|
|
589
|
-
lore list --domains List available domains
|
|
683
|
+
lore sources List indexed sources with counts
|
|
684
|
+
lore list <source> List source entries
|
|
590
685
|
lore info Show indexed sources and counts
|
|
591
686
|
lore info --human Human-readable info
|
|
687
|
+
lore about <project> Aggregate view of project knowledge
|
|
688
|
+
lore about <project> --brief Compact project summary
|
|
592
689
|
lore capture task|knowledge|note|teaching Capture knowledge
|
|
593
690
|
|
|
594
691
|
Search Options:
|
|
@@ -597,7 +694,6 @@ Search Options:
|
|
|
597
694
|
--project <name> Filter results by project
|
|
598
695
|
--brief Compact output (titles only)
|
|
599
696
|
--since <date> Filter by date (today, yesterday, this-week, YYYY-MM-DD)
|
|
600
|
-
--sources List indexed sources with counts
|
|
601
697
|
|
|
602
698
|
Passthrough Sources:
|
|
603
699
|
prismis Semantic search via prismis daemon (requires prismis-daemon running)
|
|
@@ -607,7 +703,7 @@ List Options:
|
|
|
607
703
|
--limit <n> Maximum entries
|
|
608
704
|
--format <fmt> Output format: json (default), jsonl, human
|
|
609
705
|
--brief Compact output (titles only)
|
|
610
|
-
--
|
|
706
|
+
--project <name> Filter by project name
|
|
611
707
|
|
|
612
708
|
Capture Types:
|
|
613
709
|
task Log task completion
|
|
@@ -635,6 +731,7 @@ Capture Types:
|
|
|
635
731
|
Examples:
|
|
636
732
|
lore search "authentication"
|
|
637
733
|
lore search blogs "typescript patterns"
|
|
734
|
+
lore sources
|
|
638
735
|
lore list development
|
|
639
736
|
lore list commits --limit 10 --format human
|
|
640
737
|
lore capture knowledge --context=lore --text="Unified CLI works" --type=learning
|
|
@@ -649,7 +746,6 @@ lore search - Search indexed knowledge
|
|
|
649
746
|
Usage:
|
|
650
747
|
lore search <query> Search all sources
|
|
651
748
|
lore search <source> <query> Search specific source
|
|
652
|
-
lore search --sources List indexed sources
|
|
653
749
|
|
|
654
750
|
Options:
|
|
655
751
|
--exact Use FTS5 text search (bypasses semantic search)
|
|
@@ -657,7 +753,6 @@ Options:
|
|
|
657
753
|
--project <name> Filter results by project (post-filters KNN results)
|
|
658
754
|
--brief Compact output (titles only)
|
|
659
755
|
--since <date> Filter by date (today, yesterday, this-week, YYYY-MM-DD)
|
|
660
|
-
--sources List indexed sources with counts
|
|
661
756
|
--help Show this help
|
|
662
757
|
|
|
663
758
|
Indexed Sources:
|
|
@@ -672,6 +767,7 @@ Indexed Sources:
|
|
|
672
767
|
readmes Project README files
|
|
673
768
|
sessions Claude Code session transcripts
|
|
674
769
|
tasks Logged development tasks
|
|
770
|
+
teachings Teaching moments
|
|
675
771
|
|
|
676
772
|
Passthrough Sources:
|
|
677
773
|
prismis Semantic search via prismis daemon
|
|
@@ -679,6 +775,9 @@ Passthrough Sources:
|
|
|
679
775
|
atuin Shell history search
|
|
680
776
|
(queries ~/.local/share/atuin/history.db directly)
|
|
681
777
|
|
|
778
|
+
See also:
|
|
779
|
+
lore sources List all sources with entry counts
|
|
780
|
+
|
|
682
781
|
Examples:
|
|
683
782
|
lore search "authentication"
|
|
684
783
|
lore search blogs "typescript patterns"
|
|
@@ -693,21 +792,19 @@ Examples:
|
|
|
693
792
|
|
|
694
793
|
function showListHelp(): void {
|
|
695
794
|
console.log(`
|
|
696
|
-
lore list - List
|
|
795
|
+
lore list - List source entries
|
|
697
796
|
|
|
698
797
|
Usage:
|
|
699
|
-
lore list <
|
|
700
|
-
lore list --domains List available domains
|
|
798
|
+
lore list <source> List entries in source
|
|
701
799
|
|
|
702
800
|
Options:
|
|
703
801
|
--limit <n> Maximum entries (default: all)
|
|
704
802
|
--format <fmt> Output format: json (default), jsonl, human
|
|
705
803
|
--project <name> Filter by project name
|
|
706
804
|
--brief Compact output (titles only)
|
|
707
|
-
--domains List available domains
|
|
708
805
|
--help Show this help
|
|
709
806
|
|
|
710
|
-
Available
|
|
807
|
+
Available Sources:
|
|
711
808
|
blogs Blog posts
|
|
712
809
|
books Books read
|
|
713
810
|
captures Quick captures
|
|
@@ -720,11 +817,14 @@ Available Domains:
|
|
|
720
817
|
movies Movies watched
|
|
721
818
|
obsidian Obsidian notes
|
|
722
819
|
people People/contacts
|
|
723
|
-
personal Personal data aggregate
|
|
724
820
|
podcasts Podcasts listened
|
|
725
821
|
readmes Project READMEs
|
|
726
822
|
sessions Claude Code sessions
|
|
727
823
|
tasks Development tasks
|
|
824
|
+
teachings Teaching moments
|
|
825
|
+
|
|
826
|
+
See also:
|
|
827
|
+
lore sources List all sources with entry counts
|
|
728
828
|
|
|
729
829
|
Examples:
|
|
730
830
|
lore list development
|
|
@@ -790,6 +890,51 @@ Examples:
|
|
|
790
890
|
process.exit(0);
|
|
791
891
|
}
|
|
792
892
|
|
|
893
|
+
function showAboutHelp(): void {
|
|
894
|
+
console.log(`
|
|
895
|
+
lore about - Show everything about a project
|
|
896
|
+
|
|
897
|
+
Usage:
|
|
898
|
+
lore about <project> Aggregate view of project knowledge
|
|
899
|
+
lore about <project> --brief Compact output
|
|
900
|
+
|
|
901
|
+
Options:
|
|
902
|
+
--brief Compact output (titles only)
|
|
903
|
+
--limit <n> Results per source (default: 10)
|
|
904
|
+
--help Show this help
|
|
905
|
+
|
|
906
|
+
Sources queried:
|
|
907
|
+
commits Git commits for project
|
|
908
|
+
captures Quick captures in project context
|
|
909
|
+
tasks Development tasks for project
|
|
910
|
+
teachings Teachings from project
|
|
911
|
+
sessions Claude Code sessions for project
|
|
912
|
+
|
|
913
|
+
Output (JSON):
|
|
914
|
+
{
|
|
915
|
+
"project": "name",
|
|
916
|
+
"commits": [...],
|
|
917
|
+
"captures": [...],
|
|
918
|
+
"tasks": [...],
|
|
919
|
+
"teachings": [...],
|
|
920
|
+
"sessions": [...]
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
Output (--brief):
|
|
924
|
+
commits (3):
|
|
925
|
+
project: hash - commit message
|
|
926
|
+
|
|
927
|
+
captures (2):
|
|
928
|
+
project: insight text
|
|
929
|
+
|
|
930
|
+
Examples:
|
|
931
|
+
lore about momentum --brief
|
|
932
|
+
lore about lore | jq '.commits | length'
|
|
933
|
+
lore about momentum --limit 5
|
|
934
|
+
`);
|
|
935
|
+
process.exit(0);
|
|
936
|
+
}
|
|
937
|
+
|
|
793
938
|
function showCaptureHelp(): void {
|
|
794
939
|
console.log(`
|
|
795
940
|
lore capture - Capture knowledge
|
|
@@ -865,18 +1010,24 @@ function main(): void {
|
|
|
865
1010
|
case "list":
|
|
866
1011
|
handleList(commandArgs);
|
|
867
1012
|
break;
|
|
1013
|
+
case "sources":
|
|
1014
|
+
handleSources(commandArgs);
|
|
1015
|
+
break;
|
|
868
1016
|
case "info":
|
|
869
1017
|
handleInfo(commandArgs);
|
|
870
1018
|
break;
|
|
871
1019
|
case "projects":
|
|
872
1020
|
handleProjects(commandArgs);
|
|
873
1021
|
break;
|
|
1022
|
+
case "about":
|
|
1023
|
+
handleAbout(commandArgs);
|
|
1024
|
+
break;
|
|
874
1025
|
case "capture":
|
|
875
1026
|
handleCapture(commandArgs);
|
|
876
1027
|
break;
|
|
877
1028
|
default:
|
|
878
1029
|
fail(
|
|
879
|
-
`Unknown command: ${command}. Use: search, list, info, projects, or capture`,
|
|
1030
|
+
`Unknown command: ${command}. Use: search, list, sources, info, projects, about, or capture`,
|
|
880
1031
|
);
|
|
881
1032
|
}
|
|
882
1033
|
}
|
package/index.ts
CHANGED
|
@@ -19,10 +19,9 @@ export {
|
|
|
19
19
|
// List
|
|
20
20
|
export {
|
|
21
21
|
list,
|
|
22
|
-
listDomains,
|
|
23
22
|
formatBriefList,
|
|
24
|
-
|
|
25
|
-
type
|
|
23
|
+
SOURCES,
|
|
24
|
+
type Source,
|
|
26
25
|
type ListOptions,
|
|
27
26
|
type ListEntry,
|
|
28
27
|
type ListResult,
|
|
@@ -39,6 +38,14 @@ export {
|
|
|
39
38
|
// Projects
|
|
40
39
|
export { projects } from "./lib/projects";
|
|
41
40
|
|
|
41
|
+
// About
|
|
42
|
+
export {
|
|
43
|
+
about,
|
|
44
|
+
formatBriefAbout,
|
|
45
|
+
type AboutResult,
|
|
46
|
+
type AboutOptions,
|
|
47
|
+
} from "./lib/about";
|
|
48
|
+
|
|
42
49
|
// Prismis integration
|
|
43
50
|
export {
|
|
44
51
|
searchPrismis,
|
package/lib/about.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/about.ts - Project knowledge aggregation
|
|
3
|
+
*
|
|
4
|
+
* Aggregates all knowledge sources for a given project.
|
|
5
|
+
* Uses parallel queries via Promise.all for performance.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { list, formatBriefList, type ListResult, type Source } from "./list";
|
|
9
|
+
|
|
10
|
+
export interface AboutOptions {
|
|
11
|
+
brief?: boolean;
|
|
12
|
+
limit?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface AboutResult {
|
|
16
|
+
project: string;
|
|
17
|
+
commits: ListResult;
|
|
18
|
+
captures: ListResult;
|
|
19
|
+
tasks: ListResult;
|
|
20
|
+
teachings: ListResult;
|
|
21
|
+
sessions: ListResult;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Sources to query for project knowledge
|
|
26
|
+
* Each source has a different field for project mapping (handled by list.ts)
|
|
27
|
+
* Note: "insights" will be added when task 2.1 is complete
|
|
28
|
+
*/
|
|
29
|
+
const ABOUT_SOURCES: Source[] = [
|
|
30
|
+
"commits",
|
|
31
|
+
"captures",
|
|
32
|
+
"tasks",
|
|
33
|
+
"teachings",
|
|
34
|
+
"sessions",
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get aggregated knowledge about a project across all sources
|
|
39
|
+
*
|
|
40
|
+
* @param project - Project name to query
|
|
41
|
+
* @param options - Optional brief flag and limit
|
|
42
|
+
* @returns AboutResult with data from all sources, or formatted string if brief
|
|
43
|
+
*/
|
|
44
|
+
export function about(
|
|
45
|
+
project: string,
|
|
46
|
+
options: AboutOptions = {},
|
|
47
|
+
): AboutResult {
|
|
48
|
+
const limit = options.limit ?? 10;
|
|
49
|
+
|
|
50
|
+
// Query all sources in parallel
|
|
51
|
+
const results = ABOUT_SOURCES.map((src) => {
|
|
52
|
+
try {
|
|
53
|
+
return list(src, { project, limit });
|
|
54
|
+
} catch {
|
|
55
|
+
// Source doesn't exist or has no data - return empty result
|
|
56
|
+
return {
|
|
57
|
+
source: src,
|
|
58
|
+
entries: [],
|
|
59
|
+
count: 0,
|
|
60
|
+
} as ListResult;
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
project,
|
|
66
|
+
commits: results[0],
|
|
67
|
+
captures: results[1],
|
|
68
|
+
tasks: results[2],
|
|
69
|
+
teachings: results[3],
|
|
70
|
+
sessions: results[4],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Format about result as brief, compact output
|
|
76
|
+
* Groups by source, skips empty sources
|
|
77
|
+
*/
|
|
78
|
+
export function formatBriefAbout(result: AboutResult): string {
|
|
79
|
+
const sections: string[] = [];
|
|
80
|
+
|
|
81
|
+
// Format each non-empty source
|
|
82
|
+
if (result.commits.count > 0) {
|
|
83
|
+
sections.push(formatBriefList(result.commits));
|
|
84
|
+
}
|
|
85
|
+
if (result.captures.count > 0) {
|
|
86
|
+
sections.push(formatBriefList(result.captures));
|
|
87
|
+
}
|
|
88
|
+
if (result.tasks.count > 0) {
|
|
89
|
+
sections.push(formatBriefList(result.tasks));
|
|
90
|
+
}
|
|
91
|
+
if (result.teachings.count > 0) {
|
|
92
|
+
sections.push(formatBriefList(result.teachings));
|
|
93
|
+
}
|
|
94
|
+
if (result.sessions.count > 0) {
|
|
95
|
+
sections.push(formatBriefList(result.sessions));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (sections.length === 0) {
|
|
99
|
+
return `(no results for project: ${result.project})`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return sections.join("\n\n");
|
|
103
|
+
}
|
package/lib/list.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* lib/list.ts -
|
|
2
|
+
* lib/list.ts - Source listing functions
|
|
3
3
|
*
|
|
4
|
-
* Browse indexed
|
|
4
|
+
* Browse indexed sources without search queries.
|
|
5
5
|
* Uses Bun's built-in SQLite for zero external dependencies.
|
|
6
6
|
*/
|
|
7
7
|
|
|
@@ -9,8 +9,8 @@ import { Database } from "bun:sqlite";
|
|
|
9
9
|
import { homedir } from "os";
|
|
10
10
|
import { existsSync } from "fs";
|
|
11
11
|
|
|
12
|
-
//
|
|
13
|
-
export type
|
|
12
|
+
// Source types - data sources that can be listed
|
|
13
|
+
export type Source =
|
|
14
14
|
| "development"
|
|
15
15
|
| "tasks"
|
|
16
16
|
| "events"
|
|
@@ -29,7 +29,7 @@ export type Domain =
|
|
|
29
29
|
| "teachings"
|
|
30
30
|
| "sessions";
|
|
31
31
|
|
|
32
|
-
export const
|
|
32
|
+
export const SOURCES: Source[] = [
|
|
33
33
|
"development",
|
|
34
34
|
"tasks",
|
|
35
35
|
"events",
|
|
@@ -49,8 +49,8 @@ export const DOMAINS: Domain[] = [
|
|
|
49
49
|
"sessions",
|
|
50
50
|
];
|
|
51
51
|
|
|
52
|
-
//
|
|
53
|
-
const PERSONAL_SUBTYPES: Partial<Record<
|
|
52
|
+
// Sources that query the 'personal' source with type filter
|
|
53
|
+
const PERSONAL_SUBTYPES: Partial<Record<Source, string>> = {
|
|
54
54
|
books: "book",
|
|
55
55
|
movies: "movie",
|
|
56
56
|
podcasts: "podcast",
|
|
@@ -80,7 +80,7 @@ export interface ListEntry {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
export interface ListResult {
|
|
83
|
-
|
|
83
|
+
source: Source;
|
|
84
84
|
entries: ListEntry[];
|
|
85
85
|
count: number;
|
|
86
86
|
}
|
|
@@ -160,17 +160,17 @@ function queryPersonalType(
|
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
/**
|
|
163
|
-
* List all entries in a
|
|
163
|
+
* List all entries in a source
|
|
164
164
|
*
|
|
165
|
-
* @param
|
|
165
|
+
* @param source - The source to list (development, tasks, blogs, etc.)
|
|
166
166
|
* @param options - Optional limit
|
|
167
167
|
* @returns ListResult with entries and count
|
|
168
|
-
* @throws Error if database doesn't exist or
|
|
168
|
+
* @throws Error if database doesn't exist or source is invalid
|
|
169
169
|
*/
|
|
170
|
-
export function list(
|
|
171
|
-
if (!
|
|
170
|
+
export function list(source: Source, options: ListOptions = {}): ListResult {
|
|
171
|
+
if (!SOURCES.includes(source)) {
|
|
172
172
|
throw new Error(
|
|
173
|
-
`Invalid
|
|
173
|
+
`Invalid source: ${source}. Valid sources: ${SOURCES.join(", ")}`,
|
|
174
174
|
);
|
|
175
175
|
}
|
|
176
176
|
|
|
@@ -185,16 +185,16 @@ export function list(domain: Domain, options: ListOptions = {}): ListResult {
|
|
|
185
185
|
try {
|
|
186
186
|
let entries: ListEntry[];
|
|
187
187
|
|
|
188
|
-
// Check if this is a personal subtype
|
|
189
|
-
const personalType = PERSONAL_SUBTYPES[
|
|
188
|
+
// Check if this is a personal subtype source
|
|
189
|
+
const personalType = PERSONAL_SUBTYPES[source];
|
|
190
190
|
if (personalType) {
|
|
191
191
|
entries = queryPersonalType(db, personalType, options.limit);
|
|
192
192
|
} else {
|
|
193
|
-
entries = queryBySource(db,
|
|
193
|
+
entries = queryBySource(db, source, options.limit, options.project);
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
return {
|
|
197
|
-
|
|
197
|
+
source,
|
|
198
198
|
entries,
|
|
199
199
|
count: entries.length,
|
|
200
200
|
};
|
|
@@ -204,28 +204,28 @@ export function list(domain: Domain, options: ListOptions = {}): ListResult {
|
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
/**
|
|
207
|
-
* Get available
|
|
207
|
+
* Get available sources
|
|
208
208
|
*/
|
|
209
|
-
export function
|
|
210
|
-
return [...
|
|
209
|
+
export function listSources(): Source[] {
|
|
210
|
+
return [...SOURCES];
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
/**
|
|
214
214
|
* Extract project name from entry metadata
|
|
215
215
|
*/
|
|
216
|
-
function extractProjectFromEntry(entry: ListEntry,
|
|
217
|
-
const field = PROJECT_FIELD[
|
|
216
|
+
function extractProjectFromEntry(entry: ListEntry, source: string): string {
|
|
217
|
+
const field = PROJECT_FIELD[source];
|
|
218
218
|
if (!field) return "unknown";
|
|
219
219
|
return (entry.metadata[field] as string) || "unknown";
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
/**
|
|
223
|
-
* Extract identifier from entry based on
|
|
223
|
+
* Extract identifier from entry based on source type
|
|
224
224
|
*/
|
|
225
|
-
function extractIdentifier(entry: ListEntry,
|
|
225
|
+
function extractIdentifier(entry: ListEntry, source: string): string {
|
|
226
226
|
const metadata = entry.metadata;
|
|
227
227
|
|
|
228
|
-
switch (
|
|
228
|
+
switch (source) {
|
|
229
229
|
case "commits":
|
|
230
230
|
return (metadata.sha as string)?.substring(0, 7) || "";
|
|
231
231
|
case "sessions":
|
|
@@ -239,8 +239,8 @@ function extractIdentifier(entry: ListEntry, domain: string): string {
|
|
|
239
239
|
* Get the best display text for an entry
|
|
240
240
|
* Commits use content (commit message), others use title
|
|
241
241
|
*/
|
|
242
|
-
function getDisplayText(entry: ListEntry,
|
|
243
|
-
if (
|
|
242
|
+
function getDisplayText(entry: ListEntry, source: string): string {
|
|
243
|
+
if (source === "commits") {
|
|
244
244
|
return entry.content || entry.title;
|
|
245
245
|
}
|
|
246
246
|
return entry.title;
|
|
@@ -251,12 +251,12 @@ function getDisplayText(entry: ListEntry, domain: string): string {
|
|
|
251
251
|
* One line per entry: " project: identifier - title"
|
|
252
252
|
*/
|
|
253
253
|
export function formatBriefList(result: ListResult): string {
|
|
254
|
-
const lines = [`${result.
|
|
254
|
+
const lines = [`${result.source} (${result.count}):`];
|
|
255
255
|
|
|
256
256
|
result.entries.forEach((entry) => {
|
|
257
|
-
const project = extractProjectFromEntry(entry, result.
|
|
258
|
-
const identifier = extractIdentifier(entry, result.
|
|
259
|
-
const displayText = getDisplayText(entry, result.
|
|
257
|
+
const project = extractProjectFromEntry(entry, result.source);
|
|
258
|
+
const identifier = extractIdentifier(entry, result.source);
|
|
259
|
+
const displayText = getDisplayText(entry, result.source);
|
|
260
260
|
|
|
261
261
|
const line = identifier
|
|
262
262
|
? ` ${project}: ${identifier} - ${displayText}`
|