@voidwire/lore 1.0.5 → 1.0.7
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 +9 -2
- package/lib/db.ts +15 -4
- package/lib/indexer.ts +30 -0
- package/lib/indexers/blogs.ts +10 -6
- 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/package.json +1 -1
package/lib/config.ts
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* Usage:
|
|
8
8
|
* import { getConfig } from "./config";
|
|
9
9
|
* const config = getConfig();
|
|
10
|
-
* console.log(config.paths.data); //
|
|
11
|
-
* console.log(config.database.sqlite); //
|
|
10
|
+
* console.log(config.paths.data); // ~/.local/share/lore
|
|
11
|
+
* console.log(config.database.sqlite); // ~/.local/share/lore/lore.db
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { readFileSync } from "fs";
|
|
@@ -27,9 +27,11 @@ export interface LoreConfig {
|
|
|
27
27
|
sable_events?: string;
|
|
28
28
|
flux?: string;
|
|
29
29
|
flux_projects?: string;
|
|
30
|
+
blog_url?: string;
|
|
30
31
|
};
|
|
31
32
|
database: {
|
|
32
33
|
sqlite: string;
|
|
34
|
+
custom_sqlite?: string;
|
|
33
35
|
};
|
|
34
36
|
}
|
|
35
37
|
|
|
@@ -129,9 +131,14 @@ export function getConfig(): LoreConfig {
|
|
|
129
131
|
typeof paths.flux_projects === "string"
|
|
130
132
|
? resolvePath(paths.flux_projects)
|
|
131
133
|
: undefined,
|
|
134
|
+
blog_url: typeof paths.blog_url === "string" ? paths.blog_url : undefined,
|
|
132
135
|
},
|
|
133
136
|
database: {
|
|
134
137
|
sqlite: resolvePath(database.sqlite as string),
|
|
138
|
+
custom_sqlite:
|
|
139
|
+
typeof database.custom_sqlite === "string"
|
|
140
|
+
? resolvePath(database.custom_sqlite)
|
|
141
|
+
: undefined,
|
|
135
142
|
},
|
|
136
143
|
};
|
|
137
144
|
|
package/lib/db.ts
CHANGED
|
@@ -9,11 +9,22 @@ import { Database } from "bun:sqlite";
|
|
|
9
9
|
import { existsSync } from "fs";
|
|
10
10
|
import { getConfig } from "./config";
|
|
11
11
|
|
|
12
|
-
//
|
|
12
|
+
// Load custom SQLite from config to enable extension loading
|
|
13
13
|
// Must be called before any Database instances are created
|
|
14
|
-
const
|
|
15
|
-
if (
|
|
16
|
-
|
|
14
|
+
const config = getConfig();
|
|
15
|
+
if (config.database.custom_sqlite) {
|
|
16
|
+
if (!existsSync(config.database.custom_sqlite)) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
`database.custom_sqlite path does not exist: ${config.database.custom_sqlite}`,
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
Database.setCustomSQLite(config.database.custom_sqlite);
|
|
22
|
+
} else {
|
|
23
|
+
throw new Error(
|
|
24
|
+
"database.custom_sqlite not set in ~/.config/lore/config.toml.\n" +
|
|
25
|
+
"Required for sqlite-vec extension loading.\n" +
|
|
26
|
+
'macOS: custom_sqlite = "/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib"',
|
|
27
|
+
);
|
|
17
28
|
}
|
|
18
29
|
|
|
19
30
|
/**
|
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,9 +37,12 @@ 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
|
-
|
|
42
|
-
|
|
40
|
+
if (!checkPath("blogs", "content/posts", postsDir)) return;
|
|
41
|
+
|
|
42
|
+
if (!ctx.config.paths.blog_url) {
|
|
43
|
+
console.warn(
|
|
44
|
+
"WARNING: paths.blog_url not set in config.toml — blog post URLs will not be generated",
|
|
45
|
+
);
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
const files = walkMarkdownFiles(postsDir);
|
|
@@ -117,9 +120,10 @@ export async function indexBlogs(ctx: IndexerContext): Promise<void> {
|
|
|
117
120
|
// Topic from categories
|
|
118
121
|
const topic = categories.length > 0 ? categories.join(" ") : "";
|
|
119
122
|
|
|
120
|
-
// URL from slug or filename
|
|
123
|
+
// URL from slug or filename — requires blog_url in config
|
|
124
|
+
const blogUrl = ctx.config.paths.blog_url;
|
|
121
125
|
const urlSlug = slug || basename(filePath, ".md");
|
|
122
|
-
const url =
|
|
126
|
+
const url = blogUrl ? `${blogUrl}/posts/${urlSlug}/` : "";
|
|
123
127
|
|
|
124
128
|
// Word count
|
|
125
129
|
const wordCount = content.split(/\s+/).filter(Boolean).length;
|
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
|
|