@voidwire/lore 0.3.0 → 0.5.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 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 <domain> List domain entries
13
+ * lore list <source> List source entries
14
14
  * lore capture task|knowledge|note|teaching Capture knowledge
15
15
  *
16
16
  * Exit codes:
@@ -25,7 +25,6 @@ import {
25
25
  searchAtuin,
26
26
  listSources,
27
27
  list,
28
- listDomains,
29
28
  formatBriefList,
30
29
  info,
31
30
  formatInfoHuman,
@@ -39,11 +38,11 @@ import {
39
38
  semanticSearch,
40
39
  formatBriefSearch,
41
40
  hasEmbeddings,
42
- DOMAINS,
41
+ SOURCES,
43
42
  type SearchResult,
44
43
  type ListResult,
45
44
  type ListEntry,
46
- type Domain,
45
+ type Source,
47
46
  type TaskInput,
48
47
  type KnowledgeInput,
49
48
  type NoteInput,
@@ -289,7 +288,7 @@ async function handleSearch(args: string[]): Promise<void> {
289
288
  // ============================================================================
290
289
 
291
290
  function formatHumanOutput(result: ListResult): string {
292
- const lines: string[] = [`${result.domain} (${result.count} entries):`, ""];
291
+ const lines: string[] = [`${result.source} (${result.count} entries):`, ""];
293
292
 
294
293
  for (const entry of result.entries) {
295
294
  lines.push(` ${entry.title}`);
@@ -309,21 +308,23 @@ function handleList(args: string[]): void {
309
308
  const parsed = parseArgs(args);
310
309
  const positional = getPositionalArgs(args);
311
310
 
312
- // Handle --domains flag
311
+ // Handle --domains flag (deprecated)
313
312
  if (hasFlag(args, "domains")) {
314
- output({ success: true, domains: listDomains() });
315
- console.error(`✅ ${DOMAINS.length} domains available`);
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`);
316
317
  process.exit(0);
317
318
  }
318
319
 
319
320
  if (positional.length === 0) {
320
- fail(`Missing domain. Available: ${DOMAINS.join(", ")}`);
321
+ fail(`Missing source. Available: ${SOURCES.join(", ")}`);
321
322
  }
322
323
 
323
- const domain = positional[0] as Domain;
324
+ const source = positional[0] as Source;
324
325
 
325
- if (!DOMAINS.includes(domain)) {
326
- fail(`Invalid domain: ${domain}. Available: ${DOMAINS.join(", ")}`);
326
+ if (!SOURCES.includes(source)) {
327
+ fail(`Invalid source: ${source}. Available: ${SOURCES.join(", ")}`);
327
328
  }
328
329
 
329
330
  const limit = parsed.has("limit")
@@ -331,10 +332,11 @@ function handleList(args: string[]): void {
331
332
  : undefined;
332
333
  const format = parsed.get("format") || "json";
333
334
  const project = parsed.get("project");
335
+ const type = parsed.get("type");
334
336
  const brief = hasFlag(args, "brief");
335
337
 
336
338
  try {
337
- const result = list(domain, { limit, project });
339
+ const result = list(source, { limit, project, type });
338
340
 
339
341
  if (brief) {
340
342
  console.log(formatBriefList(result));
@@ -347,13 +349,13 @@ function handleList(args: string[]): void {
347
349
  } else {
348
350
  output({
349
351
  success: true,
350
- domain: result.domain,
352
+ source: result.source,
351
353
  entries: result.entries,
352
354
  count: result.count,
353
355
  });
354
356
  }
355
357
 
356
- console.error(`✅ ${result.count} entries in ${domain}`);
358
+ console.error(`✅ ${result.count} entries in ${source}`);
357
359
  process.exit(0);
358
360
  } catch (error) {
359
361
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -468,6 +470,50 @@ function handleAbout(args: string[]): void {
468
470
  }
469
471
  }
470
472
 
473
+ // ============================================================================
474
+ // Sources Command
475
+ // ============================================================================
476
+
477
+ function handleSources(args: string[]): void {
478
+ if (hasFlag(args, "help")) {
479
+ showSourcesHelp();
480
+ }
481
+
482
+ try {
483
+ const sources = listSources();
484
+ output({
485
+ success: true,
486
+ sources,
487
+ });
488
+ console.error(`✅ ${sources.length} sources indexed`);
489
+ process.exit(0);
490
+ } catch (error) {
491
+ const message = error instanceof Error ? error.message : "Unknown error";
492
+ fail(message, 2);
493
+ }
494
+ }
495
+
496
+ function showSourcesHelp(): void {
497
+ console.log(`
498
+ lore sources - List all indexed sources with counts
499
+
500
+ Usage:
501
+ lore sources List all sources with entry counts
502
+
503
+ Options:
504
+ --help Show this help
505
+
506
+ Output:
507
+ JSON array of {source, count} objects, sorted by count descending.
508
+
509
+ Examples:
510
+ lore sources
511
+ lore sources | jq '.[0]'
512
+ lore sources | jq -r '.[] | "\\(.source): \\(.count)"'
513
+ `);
514
+ process.exit(0);
515
+ }
516
+
471
517
  // ============================================================================
472
518
  // Capture Command
473
519
  // ============================================================================
@@ -635,9 +681,8 @@ Philosophy:
635
681
  Usage:
636
682
  lore search <query> Search all sources
637
683
  lore search <source> <query> Search specific source
638
- lore search --sources List indexed sources
639
- lore list <domain> List domain entries
640
- lore list --domains List available domains
684
+ lore sources List indexed sources with counts
685
+ lore list <source> List source entries
641
686
  lore info Show indexed sources and counts
642
687
  lore info --human Human-readable info
643
688
  lore about <project> Aggregate view of project knowledge
@@ -650,7 +695,6 @@ Search Options:
650
695
  --project <name> Filter results by project
651
696
  --brief Compact output (titles only)
652
697
  --since <date> Filter by date (today, yesterday, this-week, YYYY-MM-DD)
653
- --sources List indexed sources with counts
654
698
 
655
699
  Passthrough Sources:
656
700
  prismis Semantic search via prismis daemon (requires prismis-daemon running)
@@ -660,7 +704,7 @@ List Options:
660
704
  --limit <n> Maximum entries
661
705
  --format <fmt> Output format: json (default), jsonl, human
662
706
  --brief Compact output (titles only)
663
- --domains List available domains
707
+ --project <name> Filter by project name
664
708
 
665
709
  Capture Types:
666
710
  task Log task completion
@@ -688,6 +732,7 @@ Capture Types:
688
732
  Examples:
689
733
  lore search "authentication"
690
734
  lore search blogs "typescript patterns"
735
+ lore sources
691
736
  lore list development
692
737
  lore list commits --limit 10 --format human
693
738
  lore capture knowledge --context=lore --text="Unified CLI works" --type=learning
@@ -702,7 +747,6 @@ lore search - Search indexed knowledge
702
747
  Usage:
703
748
  lore search <query> Search all sources
704
749
  lore search <source> <query> Search specific source
705
- lore search --sources List indexed sources
706
750
 
707
751
  Options:
708
752
  --exact Use FTS5 text search (bypasses semantic search)
@@ -710,7 +754,6 @@ Options:
710
754
  --project <name> Filter results by project (post-filters KNN results)
711
755
  --brief Compact output (titles only)
712
756
  --since <date> Filter by date (today, yesterday, this-week, YYYY-MM-DD)
713
- --sources List indexed sources with counts
714
757
  --help Show this help
715
758
 
716
759
  Indexed Sources:
@@ -725,6 +768,7 @@ Indexed Sources:
725
768
  readmes Project README files
726
769
  sessions Claude Code session transcripts
727
770
  tasks Logged development tasks
771
+ teachings Teaching moments
728
772
 
729
773
  Passthrough Sources:
730
774
  prismis Semantic search via prismis daemon
@@ -732,6 +776,9 @@ Passthrough Sources:
732
776
  atuin Shell history search
733
777
  (queries ~/.local/share/atuin/history.db directly)
734
778
 
779
+ See also:
780
+ lore sources List all sources with entry counts
781
+
735
782
  Examples:
736
783
  lore search "authentication"
737
784
  lore search blogs "typescript patterns"
@@ -746,21 +793,20 @@ Examples:
746
793
 
747
794
  function showListHelp(): void {
748
795
  console.log(`
749
- lore list - List domain entries
796
+ lore list - List source entries
750
797
 
751
798
  Usage:
752
- lore list <domain> List entries in domain
753
- lore list --domains List available domains
799
+ lore list <source> List entries in source
754
800
 
755
801
  Options:
756
802
  --limit <n> Maximum entries (default: all)
757
803
  --format <fmt> Output format: json (default), jsonl, human
758
804
  --project <name> Filter by project name
805
+ --type <type> Filter captures by type (learning, gotcha, preference, decision)
759
806
  --brief Compact output (titles only)
760
- --domains List available domains
761
807
  --help Show this help
762
808
 
763
- Available Domains:
809
+ Available Sources:
764
810
  blogs Blog posts
765
811
  books Books read
766
812
  captures Quick captures
@@ -773,16 +819,20 @@ Available Domains:
773
819
  movies Movies watched
774
820
  obsidian Obsidian notes
775
821
  people People/contacts
776
- personal Personal data aggregate
777
822
  podcasts Podcasts listened
778
823
  readmes Project READMEs
779
824
  sessions Claude Code sessions
780
825
  tasks Development tasks
826
+ teachings Teaching moments
827
+
828
+ See also:
829
+ lore sources List all sources with entry counts
781
830
 
782
831
  Examples:
783
832
  lore list development
784
833
  lore list commits --limit 10 --format human
785
834
  lore list commits --project=momentum --limit 5
835
+ lore list captures --type=learning --limit 5
786
836
  lore list books --format jsonl
787
837
  `);
788
838
  process.exit(0);
@@ -963,6 +1013,9 @@ function main(): void {
963
1013
  case "list":
964
1014
  handleList(commandArgs);
965
1015
  break;
1016
+ case "sources":
1017
+ handleSources(commandArgs);
1018
+ break;
966
1019
  case "info":
967
1020
  handleInfo(commandArgs);
968
1021
  break;
@@ -977,7 +1030,7 @@ function main(): void {
977
1030
  break;
978
1031
  default:
979
1032
  fail(
980
- `Unknown command: ${command}. Use: search, list, info, projects, about, or capture`,
1033
+ `Unknown command: ${command}. Use: search, list, sources, info, projects, about, or capture`,
981
1034
  );
982
1035
  }
983
1036
  }
package/index.ts CHANGED
@@ -19,10 +19,9 @@ export {
19
19
  // List
20
20
  export {
21
21
  list,
22
- listDomains,
23
22
  formatBriefList,
24
- DOMAINS,
25
- type Domain,
23
+ SOURCES,
24
+ type Source,
26
25
  type ListOptions,
27
26
  type ListEntry,
28
27
  type ListResult,
package/lib/about.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * Uses parallel queries via Promise.all for performance.
6
6
  */
7
7
 
8
- import { list, formatBriefList, type ListResult, type Domain } from "./list";
8
+ import { list, formatBriefList, type ListResult, type Source } from "./list";
9
9
 
10
10
  export interface AboutOptions {
11
11
  brief?: boolean;
@@ -26,7 +26,7 @@ export interface AboutResult {
26
26
  * Each source has a different field for project mapping (handled by list.ts)
27
27
  * Note: "insights" will be added when task 2.1 is complete
28
28
  */
29
- const ABOUT_SOURCES: Domain[] = [
29
+ const ABOUT_SOURCES: Source[] = [
30
30
  "commits",
31
31
  "captures",
32
32
  "tasks",
@@ -48,13 +48,13 @@ export function about(
48
48
  const limit = options.limit ?? 10;
49
49
 
50
50
  // Query all sources in parallel
51
- const results = ABOUT_SOURCES.map((source) => {
51
+ const results = ABOUT_SOURCES.map((src) => {
52
52
  try {
53
- return list(source, { project, limit });
53
+ return list(src, { project, limit });
54
54
  } catch {
55
55
  // Source doesn't exist or has no data - return empty result
56
56
  return {
57
- domain: source,
57
+ source: src,
58
58
  entries: [],
59
59
  count: 0,
60
60
  } as ListResult;
package/lib/list.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
- * lib/list.ts - Domain listing functions
2
+ * lib/list.ts - Source listing functions
3
3
  *
4
- * Browse indexed domains without search queries.
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
- // Domain types - sources that can be listed
13
- export type Domain =
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 DOMAINS: Domain[] = [
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
- // Domains that query the 'personal' source with type filter
53
- const PERSONAL_SUBTYPES: Partial<Record<Domain, string>> = {
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",
@@ -71,6 +71,7 @@ const PROJECT_FIELD: Record<string, string> = {
71
71
  export interface ListOptions {
72
72
  limit?: number;
73
73
  project?: string;
74
+ type?: string;
74
75
  }
75
76
 
76
77
  export interface ListEntry {
@@ -80,7 +81,7 @@ export interface ListEntry {
80
81
  }
81
82
 
82
83
  export interface ListResult {
83
- domain: Domain;
84
+ source: Source;
84
85
  entries: ListEntry[];
85
86
  count: number;
86
87
  }
@@ -104,6 +105,7 @@ function queryBySource(
104
105
  source: string,
105
106
  limit?: number,
106
107
  project?: string,
108
+ type?: string,
107
109
  ): ListEntry[] {
108
110
  let sql = "SELECT title, content, metadata FROM search WHERE source = ?";
109
111
  const params: (string | number)[] = [source];
@@ -117,6 +119,12 @@ function queryBySource(
117
119
  }
118
120
  }
119
121
 
122
+ // Add type filter if provided (captures source only)
123
+ if (type && source === "captures") {
124
+ sql += ` AND json_extract(metadata, '$.type') = ?`;
125
+ params.push(type);
126
+ }
127
+
120
128
  if (limit) {
121
129
  sql += " LIMIT ?";
122
130
  params.push(limit);
@@ -160,17 +168,17 @@ function queryPersonalType(
160
168
  }
161
169
 
162
170
  /**
163
- * List all entries in a domain
171
+ * List all entries in a source
164
172
  *
165
- * @param domain - The domain to list (development, tasks, blogs, etc.)
173
+ * @param source - The source to list (development, tasks, blogs, etc.)
166
174
  * @param options - Optional limit
167
175
  * @returns ListResult with entries and count
168
- * @throws Error if database doesn't exist or domain is invalid
176
+ * @throws Error if database doesn't exist or source is invalid
169
177
  */
170
- export function list(domain: Domain, options: ListOptions = {}): ListResult {
171
- if (!DOMAINS.includes(domain)) {
178
+ export function list(source: Source, options: ListOptions = {}): ListResult {
179
+ if (!SOURCES.includes(source)) {
172
180
  throw new Error(
173
- `Invalid domain: ${domain}. Valid domains: ${DOMAINS.join(", ")}`,
181
+ `Invalid source: ${source}. Valid sources: ${SOURCES.join(", ")}`,
174
182
  );
175
183
  }
176
184
 
@@ -185,16 +193,22 @@ export function list(domain: Domain, options: ListOptions = {}): ListResult {
185
193
  try {
186
194
  let entries: ListEntry[];
187
195
 
188
- // Check if this is a personal subtype domain
189
- const personalType = PERSONAL_SUBTYPES[domain];
196
+ // Check if this is a personal subtype source
197
+ const personalType = PERSONAL_SUBTYPES[source];
190
198
  if (personalType) {
191
199
  entries = queryPersonalType(db, personalType, options.limit);
192
200
  } else {
193
- entries = queryBySource(db, domain, options.limit, options.project);
201
+ entries = queryBySource(
202
+ db,
203
+ source,
204
+ options.limit,
205
+ options.project,
206
+ options.type,
207
+ );
194
208
  }
195
209
 
196
210
  return {
197
- domain,
211
+ source,
198
212
  entries,
199
213
  count: entries.length,
200
214
  };
@@ -204,28 +218,28 @@ export function list(domain: Domain, options: ListOptions = {}): ListResult {
204
218
  }
205
219
 
206
220
  /**
207
- * Get available domains
221
+ * Get available sources
208
222
  */
209
- export function listDomains(): Domain[] {
210
- return [...DOMAINS];
223
+ export function listSources(): Source[] {
224
+ return [...SOURCES];
211
225
  }
212
226
 
213
227
  /**
214
228
  * Extract project name from entry metadata
215
229
  */
216
- function extractProjectFromEntry(entry: ListEntry, domain: string): string {
217
- const field = PROJECT_FIELD[domain];
230
+ function extractProjectFromEntry(entry: ListEntry, source: string): string {
231
+ const field = PROJECT_FIELD[source];
218
232
  if (!field) return "unknown";
219
233
  return (entry.metadata[field] as string) || "unknown";
220
234
  }
221
235
 
222
236
  /**
223
- * Extract identifier from entry based on domain type
237
+ * Extract identifier from entry based on source type
224
238
  */
225
- function extractIdentifier(entry: ListEntry, domain: string): string {
239
+ function extractIdentifier(entry: ListEntry, source: string): string {
226
240
  const metadata = entry.metadata;
227
241
 
228
- switch (domain) {
242
+ switch (source) {
229
243
  case "commits":
230
244
  return (metadata.sha as string)?.substring(0, 7) || "";
231
245
  case "sessions":
@@ -239,8 +253,8 @@ function extractIdentifier(entry: ListEntry, domain: string): string {
239
253
  * Get the best display text for an entry
240
254
  * Commits use content (commit message), others use title
241
255
  */
242
- function getDisplayText(entry: ListEntry, domain: string): string {
243
- if (domain === "commits") {
256
+ function getDisplayText(entry: ListEntry, source: string): string {
257
+ if (source === "commits") {
244
258
  return entry.content || entry.title;
245
259
  }
246
260
  return entry.title;
@@ -251,12 +265,12 @@ function getDisplayText(entry: ListEntry, domain: string): string {
251
265
  * One line per entry: " project: identifier - title"
252
266
  */
253
267
  export function formatBriefList(result: ListResult): string {
254
- const lines = [`${result.domain} (${result.count}):`];
268
+ const lines = [`${result.source} (${result.count}):`];
255
269
 
256
270
  result.entries.forEach((entry) => {
257
- const project = extractProjectFromEntry(entry, result.domain);
258
- const identifier = extractIdentifier(entry, result.domain);
259
- const displayText = getDisplayText(entry, result.domain);
271
+ const project = extractProjectFromEntry(entry, result.source);
272
+ const identifier = extractIdentifier(entry, result.source);
273
+ const displayText = getDisplayText(entry, result.source);
260
274
 
261
275
  const line = identifier
262
276
  ? ` ${project}: ${identifier} - ${displayText}`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voidwire/lore",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Unified knowledge CLI - Search, list, and capture your indexed knowledge",
5
5
  "type": "module",
6
6
  "main": "./index.ts",