@voidwire/lore 1.0.6 → 1.0.8
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/lib/config.ts +27 -1
- package/lib/indexer.ts +30 -0
- package/lib/indexers/blogs.ts +2 -5
- package/lib/indexers/captures.ts +10 -5
- package/lib/indexers/commits.ts +2 -6
- package/lib/indexers/development.ts +2 -6
- package/lib/indexers/events.ts +10 -6
- package/lib/indexers/explorations.ts +3 -6
- package/lib/indexers/flux.ts +4 -6
- package/lib/indexers/insights.ts +10 -5
- package/lib/indexers/learnings.ts +10 -6
- package/lib/indexers/observations.ts +10 -5
- package/lib/indexers/obsidian.ts +3 -6
- package/lib/indexers/personal.ts +2 -5
- package/lib/indexers/readmes.ts +2 -6
- package/lib/indexers/sessions.ts +13 -11
- package/lib/indexers/teachings.ts +10 -5
- package/lib/semantic.ts +2 -2
- package/package.json +1 -1
package/lib/config.ts
CHANGED
|
@@ -33,6 +33,10 @@ export interface LoreConfig {
|
|
|
33
33
|
sqlite: string;
|
|
34
34
|
custom_sqlite?: string;
|
|
35
35
|
};
|
|
36
|
+
embedding: {
|
|
37
|
+
model: string;
|
|
38
|
+
dimensions: number;
|
|
39
|
+
};
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
let cachedConfig: LoreConfig | null = null;
|
|
@@ -81,9 +85,27 @@ export function getConfig(): LoreConfig {
|
|
|
81
85
|
"Invalid config: missing [database] section in config.toml",
|
|
82
86
|
);
|
|
83
87
|
}
|
|
88
|
+
if (!parsed.embedding || typeof parsed.embedding !== "object") {
|
|
89
|
+
throw new Error(
|
|
90
|
+
"Invalid config: missing [embedding] section in config.toml",
|
|
91
|
+
);
|
|
92
|
+
}
|
|
84
93
|
|
|
85
94
|
const paths = parsed.paths as Record<string, unknown>;
|
|
86
95
|
const database = parsed.database as Record<string, unknown>;
|
|
96
|
+
const embedding = parsed.embedding as Record<string, unknown>;
|
|
97
|
+
|
|
98
|
+
// Validate required embedding fields
|
|
99
|
+
if (typeof embedding.model !== "string") {
|
|
100
|
+
throw new Error(
|
|
101
|
+
"Invalid config: embedding.model is missing or not a string",
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
if (typeof embedding.dimensions !== "number") {
|
|
105
|
+
throw new Error(
|
|
106
|
+
"Invalid config: embedding.dimensions is missing or not a number",
|
|
107
|
+
);
|
|
108
|
+
}
|
|
87
109
|
|
|
88
110
|
// Validate required path fields
|
|
89
111
|
const requiredPaths = [
|
|
@@ -140,7 +162,11 @@ export function getConfig(): LoreConfig {
|
|
|
140
162
|
? resolvePath(database.custom_sqlite)
|
|
141
163
|
: undefined,
|
|
142
164
|
},
|
|
165
|
+
embedding: {
|
|
166
|
+
model: embedding.model as string,
|
|
167
|
+
dimensions: embedding.dimensions as number,
|
|
168
|
+
},
|
|
143
169
|
};
|
|
144
170
|
|
|
145
|
-
return cachedConfig
|
|
171
|
+
return cachedConfig!;
|
|
146
172
|
}
|
package/lib/indexer.ts
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
import { Database } from "bun:sqlite";
|
|
20
20
|
import { createHash } from "crypto";
|
|
21
|
+
import { existsSync } from "fs";
|
|
21
22
|
import { getConfig, type LoreConfig } from "./config";
|
|
22
23
|
|
|
23
24
|
export interface IndexEntry {
|
|
@@ -39,6 +40,35 @@ export interface IndexerContext {
|
|
|
39
40
|
|
|
40
41
|
export type IndexerFunction = (ctx: IndexerContext) => Promise<void>;
|
|
41
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Check if a path is configured and exists on disk.
|
|
45
|
+
* Logs a specific reason when the check fails:
|
|
46
|
+
* - "not configured" when path is undefined
|
|
47
|
+
* - "not found: /path" or "not found — hint" when path doesn't exist
|
|
48
|
+
*
|
|
49
|
+
* Returns true (with type narrowing) if path exists.
|
|
50
|
+
*/
|
|
51
|
+
export function checkPath(
|
|
52
|
+
source: string,
|
|
53
|
+
name: string,
|
|
54
|
+
path: string | undefined,
|
|
55
|
+
hint?: string,
|
|
56
|
+
): path is string {
|
|
57
|
+
if (!path) {
|
|
58
|
+
console.log(`${source}: ${name} not configured`);
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
if (!existsSync(path)) {
|
|
62
|
+
if (hint) {
|
|
63
|
+
console.log(`${source}: ${name} not found — ${hint}`);
|
|
64
|
+
} else {
|
|
65
|
+
console.log(`${source}: ${name} not found: ${path}`);
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
|
|
42
72
|
/**
|
|
43
73
|
* Content chunking with overlap.
|
|
44
74
|
* Splits content at sentence boundaries when possible.
|
package/lib/indexers/blogs.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import { readdirSync, readFileSync, statSync, existsSync } from "fs";
|
|
15
15
|
import { join, basename } from "path";
|
|
16
|
-
import type
|
|
16
|
+
import { checkPath, type IndexerContext } from "../indexer";
|
|
17
17
|
|
|
18
18
|
function walkMarkdownFiles(dir: string, files: string[] = []): string[] {
|
|
19
19
|
if (!existsSync(dir)) return files;
|
|
@@ -37,10 +37,7 @@ export async function indexBlogs(ctx: IndexerContext): Promise<void> {
|
|
|
37
37
|
const blogsDir = ctx.config.paths.blogs;
|
|
38
38
|
const postsDir = join(blogsDir, "content", "posts");
|
|
39
39
|
|
|
40
|
-
if (!
|
|
41
|
-
console.log(`Blog posts directory not found: ${postsDir}`);
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
40
|
+
if (!checkPath("blogs", "content/posts", postsDir)) return;
|
|
44
41
|
|
|
45
42
|
if (!ctx.config.paths.blog_url) {
|
|
46
43
|
console.warn(
|
package/lib/indexers/captures.ts
CHANGED
|
@@ -10,15 +10,20 @@
|
|
|
10
10
|
* Timestamp: event timestamp
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { readFileSync
|
|
14
|
-
import type
|
|
13
|
+
import { readFileSync } from "fs";
|
|
14
|
+
import { checkPath, type IndexerContext } from "../indexer";
|
|
15
15
|
|
|
16
16
|
export async function indexCaptures(ctx: IndexerContext): Promise<void> {
|
|
17
17
|
const logPath = `${ctx.config.paths.data}/log.jsonl`;
|
|
18
|
-
if (
|
|
19
|
-
|
|
18
|
+
if (
|
|
19
|
+
!checkPath(
|
|
20
|
+
"captures",
|
|
21
|
+
"log.jsonl",
|
|
22
|
+
logPath,
|
|
23
|
+
"populated by Sable session hooks",
|
|
24
|
+
)
|
|
25
|
+
)
|
|
20
26
|
return;
|
|
21
|
-
}
|
|
22
27
|
|
|
23
28
|
const lines = readFileSync(logPath, "utf-8").split("\n").filter(Boolean);
|
|
24
29
|
|
package/lib/indexers/commits.ts
CHANGED
|
@@ -13,15 +13,11 @@
|
|
|
13
13
|
import { readdirSync, existsSync } from "fs";
|
|
14
14
|
import { join } from "path";
|
|
15
15
|
import { spawnSync } from "child_process";
|
|
16
|
-
import type
|
|
16
|
+
import { checkPath, type IndexerContext } from "../indexer";
|
|
17
17
|
|
|
18
18
|
export async function indexCommits(ctx: IndexerContext): Promise<void> {
|
|
19
19
|
const projectsDir = ctx.config.paths.projects;
|
|
20
|
-
|
|
21
|
-
if (!existsSync(projectsDir)) {
|
|
22
|
-
console.log(`Projects directory not found: ${projectsDir}`);
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
20
|
+
if (!checkPath("commits", "paths.projects", projectsDir)) return;
|
|
25
21
|
|
|
26
22
|
const projects = readdirSync(projectsDir, { withFileTypes: true })
|
|
27
23
|
.filter((dirent) => dirent.isDirectory())
|
|
@@ -10,15 +10,11 @@
|
|
|
10
10
|
|
|
11
11
|
import { readdirSync, readFileSync, statSync, existsSync } from "fs";
|
|
12
12
|
import { join } from "path";
|
|
13
|
-
import type
|
|
13
|
+
import { checkPath, type IndexerContext } from "../indexer";
|
|
14
14
|
|
|
15
15
|
export async function indexDevelopment(ctx: IndexerContext): Promise<void> {
|
|
16
16
|
const projectsDir = ctx.config.paths.projects;
|
|
17
|
-
|
|
18
|
-
if (!existsSync(projectsDir)) {
|
|
19
|
-
console.log(`Projects directory not found: ${projectsDir}`);
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
17
|
+
if (!checkPath("development", "paths.projects", projectsDir)) return;
|
|
22
18
|
|
|
23
19
|
const projects = readdirSync(projectsDir, { withFileTypes: true })
|
|
24
20
|
.filter((dirent) => dirent.isDirectory())
|
package/lib/indexers/events.ts
CHANGED
|
@@ -10,16 +10,20 @@
|
|
|
10
10
|
* Timestamp: last event timestamp per project
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { readFileSync
|
|
14
|
-
import type
|
|
13
|
+
import { readFileSync } from "fs";
|
|
14
|
+
import { checkPath, type IndexerContext } from "../indexer";
|
|
15
15
|
|
|
16
16
|
export async function indexEvents(ctx: IndexerContext): Promise<void> {
|
|
17
17
|
const logPath = `${ctx.config.paths.data}/log.jsonl`;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
if (
|
|
19
|
+
!checkPath(
|
|
20
|
+
"events",
|
|
21
|
+
"log.jsonl",
|
|
22
|
+
logPath,
|
|
23
|
+
"populated by Sable session hooks",
|
|
24
|
+
)
|
|
25
|
+
)
|
|
21
26
|
return;
|
|
22
|
-
}
|
|
23
27
|
|
|
24
28
|
const lines = readFileSync(logPath, "utf-8").split("\n").filter(Boolean);
|
|
25
29
|
const projectData = new Map<
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
* Timestamp: file mtime as ISO 8601
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { readdirSync, readFileSync, statSync
|
|
13
|
+
import { readdirSync, readFileSync, statSync } from "fs";
|
|
14
14
|
import { join, basename, dirname } from "path";
|
|
15
|
-
import type
|
|
15
|
+
import { checkPath, type IndexerContext } from "../indexer";
|
|
16
16
|
|
|
17
17
|
function walkMarkdownFiles(dir: string, files: string[] = []): string[] {
|
|
18
18
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
@@ -33,10 +33,7 @@ function walkMarkdownFiles(dir: string, files: string[] = []): string[] {
|
|
|
33
33
|
export async function indexExplorations(ctx: IndexerContext): Promise<void> {
|
|
34
34
|
const explorationsDir = ctx.config.paths.explorations;
|
|
35
35
|
|
|
36
|
-
if (!
|
|
37
|
-
console.log(`Explorations directory not found: ${explorationsDir}`);
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
36
|
+
if (!checkPath("explorations", "paths.explorations", explorationsDir)) return;
|
|
40
37
|
|
|
41
38
|
const files = walkMarkdownFiles(explorationsDir);
|
|
42
39
|
|
package/lib/indexers/flux.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import { readdirSync, readFileSync, existsSync, statSync } from "fs";
|
|
16
16
|
import { join, basename } from "path";
|
|
17
|
-
import type
|
|
17
|
+
import { checkPath, type IndexerContext } from "../indexer";
|
|
18
18
|
|
|
19
19
|
export async function indexFlux(ctx: IndexerContext): Promise<void> {
|
|
20
20
|
const fluxDir = ctx.config.paths.flux;
|
|
@@ -22,7 +22,7 @@ export async function indexFlux(ctx: IndexerContext): Promise<void> {
|
|
|
22
22
|
let found = false;
|
|
23
23
|
|
|
24
24
|
// Pass 1: General flux files (no project association)
|
|
25
|
-
if (
|
|
25
|
+
if (checkPath("flux", "paths.flux", fluxDir)) {
|
|
26
26
|
found = true;
|
|
27
27
|
const files = readdirSync(fluxDir).filter((f) => f.endsWith(".md"));
|
|
28
28
|
for (const file of files) {
|
|
@@ -37,7 +37,7 @@ export async function indexFlux(ctx: IndexerContext): Promise<void> {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
// Pass 2: Per-project flux files (active.md, later.md)
|
|
40
|
-
if (
|
|
40
|
+
if (checkPath("flux", "paths.flux_projects", fluxProjectsDir)) {
|
|
41
41
|
found = true;
|
|
42
42
|
const projects = readdirSync(fluxProjectsDir, { withFileTypes: true })
|
|
43
43
|
.filter((d) => d.isDirectory())
|
|
@@ -57,9 +57,7 @@ export async function indexFlux(ctx: IndexerContext): Promise<void> {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
if (!found)
|
|
61
|
-
console.log("No flux directories found, skipping flux");
|
|
62
|
-
}
|
|
60
|
+
if (!found) return;
|
|
63
61
|
}
|
|
64
62
|
|
|
65
63
|
function statusFromFilename(name: string): string {
|
package/lib/indexers/insights.ts
CHANGED
|
@@ -10,15 +10,20 @@
|
|
|
10
10
|
* Timestamp: event timestamp
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { readFileSync
|
|
14
|
-
import type
|
|
13
|
+
import { readFileSync } from "fs";
|
|
14
|
+
import { checkPath, type IndexerContext } from "../indexer";
|
|
15
15
|
|
|
16
16
|
export async function indexInsights(ctx: IndexerContext): Promise<void> {
|
|
17
17
|
const logPath = `${ctx.config.paths.data}/log.jsonl`;
|
|
18
|
-
if (
|
|
19
|
-
|
|
18
|
+
if (
|
|
19
|
+
!checkPath(
|
|
20
|
+
"insights",
|
|
21
|
+
"log.jsonl",
|
|
22
|
+
logPath,
|
|
23
|
+
"populated by Sable session hooks",
|
|
24
|
+
)
|
|
25
|
+
)
|
|
20
26
|
return;
|
|
21
|
-
}
|
|
22
27
|
|
|
23
28
|
const lines = readFileSync(logPath, "utf-8").split("\n").filter(Boolean);
|
|
24
29
|
|
|
@@ -10,16 +10,20 @@
|
|
|
10
10
|
* Timestamp: event timestamp
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { readFileSync
|
|
14
|
-
import type
|
|
13
|
+
import { readFileSync } from "fs";
|
|
14
|
+
import { checkPath, type IndexerContext } from "../indexer";
|
|
15
15
|
|
|
16
16
|
export async function indexLearnings(ctx: IndexerContext): Promise<void> {
|
|
17
17
|
const logPath = `${ctx.config.paths.data}/log.jsonl`;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
if (
|
|
19
|
+
!checkPath(
|
|
20
|
+
"learnings",
|
|
21
|
+
"log.jsonl",
|
|
22
|
+
logPath,
|
|
23
|
+
"populated by Sable session hooks",
|
|
24
|
+
)
|
|
25
|
+
)
|
|
21
26
|
return;
|
|
22
|
-
}
|
|
23
27
|
|
|
24
28
|
const lines = readFileSync(logPath, "utf-8").split("\n").filter(Boolean);
|
|
25
29
|
|
|
@@ -10,15 +10,20 @@
|
|
|
10
10
|
* Timestamp: event timestamp
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { readFileSync
|
|
14
|
-
import type
|
|
13
|
+
import { readFileSync } from "fs";
|
|
14
|
+
import { checkPath, type IndexerContext } from "../indexer";
|
|
15
15
|
|
|
16
16
|
export async function indexObservations(ctx: IndexerContext): Promise<void> {
|
|
17
17
|
const logPath = `${ctx.config.paths.data}/log.jsonl`;
|
|
18
|
-
if (
|
|
19
|
-
|
|
18
|
+
if (
|
|
19
|
+
!checkPath(
|
|
20
|
+
"observations",
|
|
21
|
+
"log.jsonl",
|
|
22
|
+
logPath,
|
|
23
|
+
"populated by Sable session hooks",
|
|
24
|
+
)
|
|
25
|
+
)
|
|
20
26
|
return;
|
|
21
|
-
}
|
|
22
27
|
|
|
23
28
|
const lines = readFileSync(logPath, "utf-8").split("\n").filter(Boolean);
|
|
24
29
|
|
package/lib/indexers/obsidian.ts
CHANGED
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
* Timestamp: file mtime as ISO 8601
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { readdirSync, readFileSync, statSync
|
|
14
|
+
import { readdirSync, readFileSync, statSync } from "fs";
|
|
15
15
|
import { join, basename, dirname } from "path";
|
|
16
|
-
import type
|
|
16
|
+
import { checkPath, type IndexerContext } from "../indexer";
|
|
17
17
|
|
|
18
18
|
function walkMarkdownFiles(
|
|
19
19
|
dir: string,
|
|
@@ -41,10 +41,7 @@ function walkMarkdownFiles(
|
|
|
41
41
|
export async function indexObsidian(ctx: IndexerContext): Promise<void> {
|
|
42
42
|
const obsidianDir = ctx.config.paths.obsidian;
|
|
43
43
|
|
|
44
|
-
if (!
|
|
45
|
-
console.log(`Obsidian directory not found: ${obsidianDir}`);
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
44
|
+
if (!checkPath("obsidian", "paths.obsidian", obsidianDir)) return;
|
|
48
45
|
|
|
49
46
|
const files = walkMarkdownFiles(obsidianDir, obsidianDir);
|
|
50
47
|
|
package/lib/indexers/personal.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { readFileSync, statSync, existsSync } from "fs";
|
|
14
14
|
import { join } from "path";
|
|
15
|
-
import type
|
|
15
|
+
import { checkPath, type IndexerContext } from "../indexer";
|
|
16
16
|
|
|
17
17
|
function fileMtime(path: string): string {
|
|
18
18
|
return statSync(path).mtime.toISOString();
|
|
@@ -27,10 +27,7 @@ function toISO(dateStr: string, fallback: string): string {
|
|
|
27
27
|
export async function indexPersonal(ctx: IndexerContext): Promise<void> {
|
|
28
28
|
const personalDir = ctx.config.paths.personal;
|
|
29
29
|
|
|
30
|
-
if (!
|
|
31
|
-
console.log(`Personal data directory not found: ${personalDir}`);
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
30
|
+
if (!checkPath("personal", "paths.personal", personalDir)) return;
|
|
34
31
|
|
|
35
32
|
// Books
|
|
36
33
|
const booksPath = join(personalDir, "books.json");
|
package/lib/indexers/readmes.ts
CHANGED
|
@@ -10,15 +10,11 @@
|
|
|
10
10
|
|
|
11
11
|
import { readdirSync, readFileSync, statSync, existsSync } from "fs";
|
|
12
12
|
import { join } from "path";
|
|
13
|
-
import type
|
|
13
|
+
import { checkPath, type IndexerContext } from "../indexer";
|
|
14
14
|
|
|
15
15
|
export async function indexReadmes(ctx: IndexerContext): Promise<void> {
|
|
16
16
|
const projectsDir = ctx.config.paths.projects;
|
|
17
|
-
|
|
18
|
-
if (!existsSync(projectsDir)) {
|
|
19
|
-
console.log(`Projects directory not found: ${projectsDir}`);
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
17
|
+
if (!checkPath("readmes", "paths.projects", projectsDir)) return;
|
|
22
18
|
|
|
23
19
|
const projects = readdirSync(projectsDir, { withFileTypes: true })
|
|
24
20
|
.filter((dirent) => dirent.isDirectory())
|
package/lib/indexers/sessions.ts
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
* Timestamp: first event timestamp per session
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { readdirSync, readFileSync
|
|
13
|
+
import { readdirSync, readFileSync } from "fs";
|
|
14
14
|
import { join } from "path";
|
|
15
|
-
import type
|
|
15
|
+
import { checkPath, type IndexerContext } from "../indexer";
|
|
16
16
|
|
|
17
17
|
interface SessionData {
|
|
18
18
|
project: string;
|
|
@@ -27,17 +27,19 @@ interface SessionData {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export async function indexSessions(ctx: IndexerContext): Promise<void> {
|
|
30
|
-
//
|
|
31
|
-
const eventDirs = [
|
|
32
|
-
|
|
33
|
-
ctx.config.paths.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
// Check each event directory individually for clear diagnostics
|
|
31
|
+
const eventDirs: string[] = [];
|
|
32
|
+
for (const { name, path } of [
|
|
33
|
+
{ name: "session_events", path: ctx.config.paths.session_events },
|
|
34
|
+
{ name: "sable_events", path: ctx.config.paths.sable_events },
|
|
35
|
+
]) {
|
|
36
|
+
if (checkPath("sessions", name, path)) {
|
|
37
|
+
eventDirs.push(path);
|
|
38
|
+
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
if (eventDirs.length === 0) return;
|
|
42
|
+
|
|
41
43
|
const eventFiles: string[] = [];
|
|
42
44
|
for (const dir of eventDirs) {
|
|
43
45
|
const files = readdirSync(dir)
|
|
@@ -10,15 +10,20 @@
|
|
|
10
10
|
* Timestamp: event timestamp
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { readFileSync
|
|
14
|
-
import type
|
|
13
|
+
import { readFileSync } from "fs";
|
|
14
|
+
import { checkPath, type IndexerContext } from "../indexer";
|
|
15
15
|
|
|
16
16
|
export async function indexTeachings(ctx: IndexerContext): Promise<void> {
|
|
17
17
|
const logPath = `${ctx.config.paths.data}/log.jsonl`;
|
|
18
|
-
if (
|
|
19
|
-
|
|
18
|
+
if (
|
|
19
|
+
!checkPath(
|
|
20
|
+
"teachings",
|
|
21
|
+
"log.jsonl",
|
|
22
|
+
logPath,
|
|
23
|
+
"populated by Sable session hooks",
|
|
24
|
+
)
|
|
25
|
+
)
|
|
20
26
|
return;
|
|
21
|
-
}
|
|
22
27
|
|
|
23
28
|
const lines = readFileSync(logPath, "utf-8").split("\n").filter(Boolean);
|
|
24
29
|
|
package/lib/semantic.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { existsSync } from "fs";
|
|
|
11
11
|
import { pipeline } from "@huggingface/transformers";
|
|
12
12
|
import { getDatabasePath, openDatabase } from "./db.js";
|
|
13
13
|
import { search as keywordSearch, type SearchResult } from "./search.js";
|
|
14
|
+
import { getConfig } from "./config.js";
|
|
14
15
|
|
|
15
16
|
export interface SemanticResult {
|
|
16
17
|
rowid: number;
|
|
@@ -30,8 +31,7 @@ export interface SemanticSearchOptions {
|
|
|
30
31
|
since?: string;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
const MODEL_NAME =
|
|
34
|
-
const EMBEDDING_DIM = 768;
|
|
34
|
+
const { model: MODEL_NAME, dimensions: EMBEDDING_DIM } = getConfig().embedding;
|
|
35
35
|
|
|
36
36
|
interface EmbeddingPipeline {
|
|
37
37
|
(
|