gnosys 4.0.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/LICENSE +21 -0
- package/README.md +1387 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +3753 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2267 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/archive.d.ts +95 -0
- package/dist/lib/archive.d.ts.map +1 -0
- package/dist/lib/archive.js +311 -0
- package/dist/lib/archive.js.map +1 -0
- package/dist/lib/ask.d.ts +77 -0
- package/dist/lib/ask.d.ts.map +1 -0
- package/dist/lib/ask.js +316 -0
- package/dist/lib/ask.js.map +1 -0
- package/dist/lib/audit.d.ts +47 -0
- package/dist/lib/audit.d.ts.map +1 -0
- package/dist/lib/audit.js +136 -0
- package/dist/lib/audit.js.map +1 -0
- package/dist/lib/bootstrap.d.ts +56 -0
- package/dist/lib/bootstrap.d.ts.map +1 -0
- package/dist/lib/bootstrap.js +163 -0
- package/dist/lib/bootstrap.js.map +1 -0
- package/dist/lib/config.d.ts +239 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +371 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/dashboard.d.ts +81 -0
- package/dist/lib/dashboard.d.ts.map +1 -0
- package/dist/lib/dashboard.js +314 -0
- package/dist/lib/dashboard.js.map +1 -0
- package/dist/lib/db.d.ts +182 -0
- package/dist/lib/db.d.ts.map +1 -0
- package/dist/lib/db.js +620 -0
- package/dist/lib/db.js.map +1 -0
- package/dist/lib/dbSearch.d.ts +65 -0
- package/dist/lib/dbSearch.d.ts.map +1 -0
- package/dist/lib/dbSearch.js +239 -0
- package/dist/lib/dbSearch.js.map +1 -0
- package/dist/lib/dbWrite.d.ts +56 -0
- package/dist/lib/dbWrite.d.ts.map +1 -0
- package/dist/lib/dbWrite.js +171 -0
- package/dist/lib/dbWrite.js.map +1 -0
- package/dist/lib/dream.d.ts +170 -0
- package/dist/lib/dream.d.ts.map +1 -0
- package/dist/lib/dream.js +706 -0
- package/dist/lib/dream.js.map +1 -0
- package/dist/lib/embeddings.d.ts +84 -0
- package/dist/lib/embeddings.d.ts.map +1 -0
- package/dist/lib/embeddings.js +226 -0
- package/dist/lib/embeddings.js.map +1 -0
- package/dist/lib/export.d.ts +92 -0
- package/dist/lib/export.d.ts.map +1 -0
- package/dist/lib/export.js +362 -0
- package/dist/lib/export.js.map +1 -0
- package/dist/lib/federated.d.ts +113 -0
- package/dist/lib/federated.d.ts.map +1 -0
- package/dist/lib/federated.js +346 -0
- package/dist/lib/federated.js.map +1 -0
- package/dist/lib/graph.d.ts +50 -0
- package/dist/lib/graph.d.ts.map +1 -0
- package/dist/lib/graph.js +118 -0
- package/dist/lib/graph.js.map +1 -0
- package/dist/lib/history.d.ts +39 -0
- package/dist/lib/history.d.ts.map +1 -0
- package/dist/lib/history.js +112 -0
- package/dist/lib/history.js.map +1 -0
- package/dist/lib/hybridSearch.d.ts +80 -0
- package/dist/lib/hybridSearch.d.ts.map +1 -0
- package/dist/lib/hybridSearch.js +296 -0
- package/dist/lib/hybridSearch.js.map +1 -0
- package/dist/lib/import.d.ts +52 -0
- package/dist/lib/import.d.ts.map +1 -0
- package/dist/lib/import.js +365 -0
- package/dist/lib/import.js.map +1 -0
- package/dist/lib/ingest.d.ts +51 -0
- package/dist/lib/ingest.d.ts.map +1 -0
- package/dist/lib/ingest.js +144 -0
- package/dist/lib/ingest.js.map +1 -0
- package/dist/lib/lensing.d.ts +35 -0
- package/dist/lib/lensing.d.ts.map +1 -0
- package/dist/lib/lensing.js +85 -0
- package/dist/lib/lensing.js.map +1 -0
- package/dist/lib/llm.d.ts +84 -0
- package/dist/lib/llm.d.ts.map +1 -0
- package/dist/lib/llm.js +386 -0
- package/dist/lib/llm.js.map +1 -0
- package/dist/lib/lock.d.ts +28 -0
- package/dist/lib/lock.d.ts.map +1 -0
- package/dist/lib/lock.js +145 -0
- package/dist/lib/lock.js.map +1 -0
- package/dist/lib/maintenance.d.ts +124 -0
- package/dist/lib/maintenance.d.ts.map +1 -0
- package/dist/lib/maintenance.js +587 -0
- package/dist/lib/maintenance.js.map +1 -0
- package/dist/lib/migrate.d.ts +19 -0
- package/dist/lib/migrate.d.ts.map +1 -0
- package/dist/lib/migrate.js +260 -0
- package/dist/lib/migrate.js.map +1 -0
- package/dist/lib/preferences.d.ts +49 -0
- package/dist/lib/preferences.d.ts.map +1 -0
- package/dist/lib/preferences.js +149 -0
- package/dist/lib/preferences.js.map +1 -0
- package/dist/lib/projectIdentity.d.ts +66 -0
- package/dist/lib/projectIdentity.d.ts.map +1 -0
- package/dist/lib/projectIdentity.js +148 -0
- package/dist/lib/projectIdentity.js.map +1 -0
- package/dist/lib/recall.d.ts +82 -0
- package/dist/lib/recall.d.ts.map +1 -0
- package/dist/lib/recall.js +289 -0
- package/dist/lib/recall.js.map +1 -0
- package/dist/lib/resolver.d.ts +116 -0
- package/dist/lib/resolver.d.ts.map +1 -0
- package/dist/lib/resolver.js +372 -0
- package/dist/lib/resolver.js.map +1 -0
- package/dist/lib/retry.d.ts +24 -0
- package/dist/lib/retry.d.ts.map +1 -0
- package/dist/lib/retry.js +60 -0
- package/dist/lib/retry.js.map +1 -0
- package/dist/lib/rulesGen.d.ts +51 -0
- package/dist/lib/rulesGen.d.ts.map +1 -0
- package/dist/lib/rulesGen.js +167 -0
- package/dist/lib/rulesGen.js.map +1 -0
- package/dist/lib/search.d.ts +51 -0
- package/dist/lib/search.d.ts.map +1 -0
- package/dist/lib/search.js +190 -0
- package/dist/lib/search.js.map +1 -0
- package/dist/lib/staticSearch.d.ts +70 -0
- package/dist/lib/staticSearch.d.ts.map +1 -0
- package/dist/lib/staticSearch.js +162 -0
- package/dist/lib/staticSearch.js.map +1 -0
- package/dist/lib/store.d.ts +79 -0
- package/dist/lib/store.d.ts.map +1 -0
- package/dist/lib/store.js +227 -0
- package/dist/lib/store.js.map +1 -0
- package/dist/lib/structuredIngest.d.ts +37 -0
- package/dist/lib/structuredIngest.d.ts.map +1 -0
- package/dist/lib/structuredIngest.js +208 -0
- package/dist/lib/structuredIngest.js.map +1 -0
- package/dist/lib/tags.d.ts +26 -0
- package/dist/lib/tags.d.ts.map +1 -0
- package/dist/lib/tags.js +109 -0
- package/dist/lib/tags.js.map +1 -0
- package/dist/lib/timeline.d.ts +34 -0
- package/dist/lib/timeline.d.ts.map +1 -0
- package/dist/lib/timeline.js +116 -0
- package/dist/lib/timeline.js.map +1 -0
- package/dist/lib/trace.d.ts +42 -0
- package/dist/lib/trace.d.ts.map +1 -0
- package/dist/lib/trace.js +338 -0
- package/dist/lib/trace.js.map +1 -0
- package/dist/lib/webIndex.d.ts +28 -0
- package/dist/lib/webIndex.d.ts.map +1 -0
- package/dist/lib/webIndex.js +208 -0
- package/dist/lib/webIndex.js.map +1 -0
- package/dist/lib/webIngest.d.ts +51 -0
- package/dist/lib/webIngest.d.ts.map +1 -0
- package/dist/lib/webIngest.js +533 -0
- package/dist/lib/webIngest.js.map +1 -0
- package/dist/lib/wikilinks.d.ts +63 -0
- package/dist/lib/wikilinks.d.ts.map +1 -0
- package/dist/lib/wikilinks.js +146 -0
- package/dist/lib/wikilinks.js.map +1 -0
- package/dist/sandbox/client.d.ts +82 -0
- package/dist/sandbox/client.d.ts.map +1 -0
- package/dist/sandbox/client.js +128 -0
- package/dist/sandbox/client.js.map +1 -0
- package/dist/sandbox/helper-template.d.ts +14 -0
- package/dist/sandbox/helper-template.d.ts.map +1 -0
- package/dist/sandbox/helper-template.js +285 -0
- package/dist/sandbox/helper-template.js.map +1 -0
- package/dist/sandbox/index.d.ts +10 -0
- package/dist/sandbox/index.d.ts.map +1 -0
- package/dist/sandbox/index.js +10 -0
- package/dist/sandbox/index.js.map +1 -0
- package/dist/sandbox/manager.d.ts +40 -0
- package/dist/sandbox/manager.d.ts.map +1 -0
- package/dist/sandbox/manager.js +220 -0
- package/dist/sandbox/manager.js.map +1 -0
- package/dist/sandbox/server.d.ts +44 -0
- package/dist/sandbox/server.d.ts.map +1 -0
- package/dist/sandbox/server.js +661 -0
- package/dist/sandbox/server.js.map +1 -0
- package/package.json +103 -0
- package/prompts/synthesize.md +21 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gnosys Rules Generation — Phase 8b.
|
|
3
|
+
*
|
|
4
|
+
* Generates agent rules content from user preferences and project conventions.
|
|
5
|
+
* Output is injected into agent rules files (CLAUDE.md, .cursor/rules/gnosys.mdc)
|
|
6
|
+
* inside GNOSYS:START / GNOSYS:END blocks. User content outside these blocks
|
|
7
|
+
* is never touched.
|
|
8
|
+
*
|
|
9
|
+
* The generated block contains:
|
|
10
|
+
* 1. Base Gnosys tool instructions (always present)
|
|
11
|
+
* 2. User preferences (from scope='user' category='preferences')
|
|
12
|
+
* 3. Project conventions (from scope='project' for current projectId)
|
|
13
|
+
*/
|
|
14
|
+
import fs from "fs/promises";
|
|
15
|
+
import path from "path";
|
|
16
|
+
import { getAllPreferences } from "./preferences.js";
|
|
17
|
+
// ─── Block markers ──────────────────────────────────────────────────────
|
|
18
|
+
const MARKER_START_HTML = "<!-- GNOSYS:START -->";
|
|
19
|
+
const MARKER_END_HTML = "<!-- GNOSYS:END -->";
|
|
20
|
+
const MARKER_START_MDC = "<!-- GNOSYS:START -->";
|
|
21
|
+
const MARKER_END_MDC = "<!-- GNOSYS:END -->";
|
|
22
|
+
// ─── Base instructions ──────────────────────────────────────────────────
|
|
23
|
+
function getBaseInstructions() {
|
|
24
|
+
return `## Gnosys Memory System
|
|
25
|
+
|
|
26
|
+
This project uses **Gnosys** for persistent memory. Run \`gnosys init\` before any other Gnosys command.
|
|
27
|
+
|
|
28
|
+
### Core workflow
|
|
29
|
+
1. **Start of task**: Run \`gnosys_discover\` with keywords to find relevant memories
|
|
30
|
+
2. **Read**: Use \`gnosys_read\` to load specific memories
|
|
31
|
+
3. **Write**: Use \`gnosys_add\` for new knowledge, \`gnosys_update\` to modify existing
|
|
32
|
+
4. **Reinforce**: Use \`gnosys_reinforce\` when a memory proves useful
|
|
33
|
+
|
|
34
|
+
### Memory triggers — write automatically when:
|
|
35
|
+
- A decision is made (library choice, architecture pattern, workflow convention)
|
|
36
|
+
- A spec or requirement is provided
|
|
37
|
+
- Significant findings emerge from implementation
|
|
38
|
+
- Something works differently than expected`;
|
|
39
|
+
}
|
|
40
|
+
// ─── Content generation ─────────────────────────────────────────────────
|
|
41
|
+
/**
|
|
42
|
+
* Generate the full GNOSYS block content from preferences and project conventions.
|
|
43
|
+
*/
|
|
44
|
+
export function generateRulesBlock(preferences, projectConventions, opts) {
|
|
45
|
+
const sections = [];
|
|
46
|
+
// 1. Base instructions (always present)
|
|
47
|
+
sections.push(getBaseInstructions());
|
|
48
|
+
// 2. User preferences
|
|
49
|
+
if (preferences.length > 0) {
|
|
50
|
+
const prefLines = preferences.map((p) => `- **${p.title}**: ${p.value}`);
|
|
51
|
+
sections.push(`### User preferences\n\n${prefLines.join("\n")}`);
|
|
52
|
+
}
|
|
53
|
+
// 3. Project conventions
|
|
54
|
+
if (projectConventions.length > 0) {
|
|
55
|
+
const convLines = projectConventions.map((m) => {
|
|
56
|
+
// Extract content body (strip "# Title\n\n" prefix)
|
|
57
|
+
const lines = m.content.split("\n");
|
|
58
|
+
const body = lines[0].startsWith("# ")
|
|
59
|
+
? lines.slice(2).join("\n").trim()
|
|
60
|
+
: m.content.trim();
|
|
61
|
+
return `- **${m.title}**: ${body.split("\n")[0]}`;
|
|
62
|
+
});
|
|
63
|
+
sections.push(`### Project conventions\n\n${convLines.join("\n")}`);
|
|
64
|
+
}
|
|
65
|
+
return sections.join("\n\n");
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Wrap content in GNOSYS markers.
|
|
69
|
+
*/
|
|
70
|
+
function wrapInMarkers(content) {
|
|
71
|
+
return `${MARKER_START_HTML}\n${content}\n${MARKER_END_HTML}`;
|
|
72
|
+
}
|
|
73
|
+
// ─── File injection ─────────────────────────────────────────────────────
|
|
74
|
+
/**
|
|
75
|
+
* Inject generated rules into a file, preserving content outside GNOSYS blocks.
|
|
76
|
+
* Creates the file if it doesn't exist.
|
|
77
|
+
*/
|
|
78
|
+
export async function injectRules(filePath, generatedBlock) {
|
|
79
|
+
const wrapped = wrapInMarkers(generatedBlock);
|
|
80
|
+
let existing = null;
|
|
81
|
+
try {
|
|
82
|
+
existing = await fs.readFile(filePath, "utf-8");
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// File doesn't exist — will create
|
|
86
|
+
}
|
|
87
|
+
if (existing === null) {
|
|
88
|
+
// Create new file with just the GNOSYS block
|
|
89
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
90
|
+
await fs.writeFile(filePath, wrapped + "\n", "utf-8");
|
|
91
|
+
return { created: true };
|
|
92
|
+
}
|
|
93
|
+
// File exists — replace existing GNOSYS block or append
|
|
94
|
+
const startIdx = existing.indexOf(MARKER_START_HTML);
|
|
95
|
+
const endIdx = existing.indexOf(MARKER_END_HTML);
|
|
96
|
+
let newContent;
|
|
97
|
+
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
98
|
+
// Replace existing block
|
|
99
|
+
const before = existing.substring(0, startIdx);
|
|
100
|
+
const after = existing.substring(endIdx + MARKER_END_HTML.length);
|
|
101
|
+
newContent = before + wrapped + after;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// No existing block — append at end
|
|
105
|
+
newContent = existing.trimEnd() + "\n\n" + wrapped + "\n";
|
|
106
|
+
}
|
|
107
|
+
await fs.writeFile(filePath, newContent, "utf-8");
|
|
108
|
+
return { created: false };
|
|
109
|
+
}
|
|
110
|
+
// ─── High-level sync ────────────────────────────────────────────────────
|
|
111
|
+
/**
|
|
112
|
+
* Sync rules for a project: reads preferences + project conventions from
|
|
113
|
+
* central DB, generates the rules block, and injects into the agent rules file.
|
|
114
|
+
*/
|
|
115
|
+
export async function syncRules(centralDb, projectDir, agentRulesTarget, projectId) {
|
|
116
|
+
if (!agentRulesTarget)
|
|
117
|
+
return null;
|
|
118
|
+
const filePath = path.resolve(projectDir, agentRulesTarget);
|
|
119
|
+
// Gather preferences (user-scoped)
|
|
120
|
+
const preferences = getAllPreferences(centralDb);
|
|
121
|
+
// Gather project conventions (decisions + conventions for this project)
|
|
122
|
+
let projectConventions = [];
|
|
123
|
+
if (projectId) {
|
|
124
|
+
const projectMems = centralDb.getMemoriesByProject(projectId);
|
|
125
|
+
projectConventions = projectMems.filter((m) => (m.category === "decisions" || m.category === "conventions") &&
|
|
126
|
+
m.status === "active");
|
|
127
|
+
}
|
|
128
|
+
// Determine format from file extension
|
|
129
|
+
let format = "generic";
|
|
130
|
+
if (agentRulesTarget.endsWith(".md"))
|
|
131
|
+
format = "claude";
|
|
132
|
+
if (agentRulesTarget.endsWith(".mdc"))
|
|
133
|
+
format = "cursor";
|
|
134
|
+
// Generate and inject
|
|
135
|
+
const block = generateRulesBlock(preferences, projectConventions, { format });
|
|
136
|
+
const { created } = await injectRules(filePath, block);
|
|
137
|
+
return {
|
|
138
|
+
content: block,
|
|
139
|
+
created,
|
|
140
|
+
filePath,
|
|
141
|
+
prefCount: preferences.length,
|
|
142
|
+
conventionCount: projectConventions.length,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Remove the GNOSYS block from a rules file (cleanup).
|
|
147
|
+
*/
|
|
148
|
+
export async function removeRulesBlock(filePath) {
|
|
149
|
+
let content;
|
|
150
|
+
try {
|
|
151
|
+
content = await fs.readFile(filePath, "utf-8");
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
const startIdx = content.indexOf(MARKER_START_HTML);
|
|
157
|
+
const endIdx = content.indexOf(MARKER_END_HTML);
|
|
158
|
+
if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
const before = content.substring(0, startIdx);
|
|
162
|
+
const after = content.substring(endIdx + MARKER_END_HTML.length);
|
|
163
|
+
const newContent = (before + after).replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
164
|
+
await fs.writeFile(filePath, newContent, "utf-8");
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=rulesGen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rulesGen.js","sourceRoot":"","sources":["../../src/lib/rulesGen.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gnosys Search — SQLite FTS5 keyword index for fast text search.
|
|
3
|
+
* FTS5-based search and discovery across all Gnosys stores.
|
|
4
|
+
*/
|
|
5
|
+
import { GnosysStore } from "./store.js";
|
|
6
|
+
export interface SearchResult {
|
|
7
|
+
relative_path: string;
|
|
8
|
+
title: string;
|
|
9
|
+
snippet: string;
|
|
10
|
+
rank: number;
|
|
11
|
+
}
|
|
12
|
+
export interface DiscoverResult {
|
|
13
|
+
relative_path: string;
|
|
14
|
+
title: string;
|
|
15
|
+
relevance: string;
|
|
16
|
+
rank: number;
|
|
17
|
+
}
|
|
18
|
+
export declare class GnosysSearch {
|
|
19
|
+
private db;
|
|
20
|
+
private storePath;
|
|
21
|
+
private available;
|
|
22
|
+
constructor(storePath: string);
|
|
23
|
+
private initSchema;
|
|
24
|
+
/**
|
|
25
|
+
* Clear the entire search index.
|
|
26
|
+
*/
|
|
27
|
+
clearIndex(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Rebuild the entire search index from a single store.
|
|
30
|
+
* Clears all existing entries first.
|
|
31
|
+
*/
|
|
32
|
+
reindex(store: GnosysStore): Promise<number>;
|
|
33
|
+
/**
|
|
34
|
+
* Add memories from a store to the index WITHOUT clearing existing entries.
|
|
35
|
+
* Used for multi-store indexing: clear once, then addStoreMemories for each store.
|
|
36
|
+
* Optional storeLabel prefix is prepended to relative_path for disambiguation.
|
|
37
|
+
*/
|
|
38
|
+
addStoreMemories(store: GnosysStore, storeLabel?: string): Promise<number>;
|
|
39
|
+
/**
|
|
40
|
+
* Search memories by keyword query.
|
|
41
|
+
*/
|
|
42
|
+
search(query: string, limit?: number): SearchResult[];
|
|
43
|
+
/**
|
|
44
|
+
* Discover memories by searching relevance keyword clouds.
|
|
45
|
+
* Returns lightweight metadata only — no file contents.
|
|
46
|
+
* This is the primary discovery mechanism replacing the static manifest.
|
|
47
|
+
*/
|
|
48
|
+
discover(query: string, limit?: number): DiscoverResult[];
|
|
49
|
+
close(): void;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/lib/search.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,OAAO,EAAE,WAAW,EAAU,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,YAAY;IAEvB,OAAO,CAAC,EAAE,CAAa;IACvB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAS;gBAEd,SAAS,EAAE,MAAM;IA4B7B,OAAO,CAAC,UAAU;IAclB;;OAEG;IACH,UAAU,IAAI,IAAI;IAKlB;;;OAGG;IACG,OAAO,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IAKlD;;;;OAIG;IACG,gBAAgB,CAAC,KAAK,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAmChF;;OAEG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,YAAY,EAAE;IAqCzD;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,cAAc,EAAE;IAqC7D,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gnosys Search — SQLite FTS5 keyword index for fast text search.
|
|
3
|
+
* FTS5-based search and discovery across all Gnosys stores.
|
|
4
|
+
*/
|
|
5
|
+
// Dynamic import — gracefully handles missing native module (dlopen failures)
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7
|
+
let Database = null;
|
|
8
|
+
try {
|
|
9
|
+
Database = (await import("better-sqlite3")).default;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
// better-sqlite3 native module not available — search degrades gracefully
|
|
13
|
+
}
|
|
14
|
+
import path from "path";
|
|
15
|
+
export class GnosysSearch {
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
+
db = null;
|
|
18
|
+
storePath;
|
|
19
|
+
available = false;
|
|
20
|
+
constructor(storePath) {
|
|
21
|
+
this.storePath = storePath;
|
|
22
|
+
if (!Database) {
|
|
23
|
+
// Native module not available — search features disabled
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// Try file-based DB first, fall back to in-memory.
|
|
27
|
+
// We must verify writes actually work — some filesystems (e.g., mounted
|
|
28
|
+
// volumes in sandboxed environments) allow file creation but block the
|
|
29
|
+
// journal/WAL delete operations that SQLite requires.
|
|
30
|
+
try {
|
|
31
|
+
const dbPath = path.join(storePath, ".config", "search.db");
|
|
32
|
+
this.db = new Database(dbPath);
|
|
33
|
+
this.initSchema();
|
|
34
|
+
// Smoke-test: insert + delete to confirm journal ops work
|
|
35
|
+
this.db.exec("CREATE TABLE IF NOT EXISTS _write_test (v INTEGER); INSERT INTO _write_test VALUES (1); DELETE FROM _write_test; DROP TABLE _write_test;");
|
|
36
|
+
this.available = true;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Fallback to in-memory (works everywhere, rebuilt on each start)
|
|
40
|
+
try {
|
|
41
|
+
this.db?.close();
|
|
42
|
+
}
|
|
43
|
+
catch { /* ignore */ }
|
|
44
|
+
this.db = new Database(":memory:");
|
|
45
|
+
this.initSchema();
|
|
46
|
+
this.available = true;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
initSchema() {
|
|
50
|
+
this.db.exec(`
|
|
51
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
|
|
52
|
+
relative_path,
|
|
53
|
+
title,
|
|
54
|
+
category,
|
|
55
|
+
tags,
|
|
56
|
+
relevance,
|
|
57
|
+
content,
|
|
58
|
+
tokenize='porter unicode61'
|
|
59
|
+
);
|
|
60
|
+
`);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Clear the entire search index.
|
|
64
|
+
*/
|
|
65
|
+
clearIndex() {
|
|
66
|
+
if (!this.db)
|
|
67
|
+
return;
|
|
68
|
+
this.db.exec("DELETE FROM memories_fts");
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Rebuild the entire search index from a single store.
|
|
72
|
+
* Clears all existing entries first.
|
|
73
|
+
*/
|
|
74
|
+
async reindex(store) {
|
|
75
|
+
this.clearIndex();
|
|
76
|
+
return this.addStoreMemories(store);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Add memories from a store to the index WITHOUT clearing existing entries.
|
|
80
|
+
* Used for multi-store indexing: clear once, then addStoreMemories for each store.
|
|
81
|
+
* Optional storeLabel prefix is prepended to relative_path for disambiguation.
|
|
82
|
+
*/
|
|
83
|
+
async addStoreMemories(store, storeLabel) {
|
|
84
|
+
if (!this.db)
|
|
85
|
+
return 0;
|
|
86
|
+
const memories = await store.getAllMemories();
|
|
87
|
+
const insert = this.db.prepare("INSERT INTO memories_fts (relative_path, title, category, tags, relevance, content) VALUES (?, ?, ?, ?, ?, ?)");
|
|
88
|
+
const tx = this.db.transaction(() => {
|
|
89
|
+
for (const m of memories) {
|
|
90
|
+
const tags = Array.isArray(m.frontmatter.tags)
|
|
91
|
+
? m.frontmatter.tags.join(" ")
|
|
92
|
+
: Object.values(m.frontmatter.tags).flat().join(" ");
|
|
93
|
+
const relevance = m.frontmatter.relevance || "";
|
|
94
|
+
const indexPath = storeLabel
|
|
95
|
+
? `${storeLabel}:${m.relativePath}`
|
|
96
|
+
: m.relativePath;
|
|
97
|
+
insert.run(indexPath, m.frontmatter.title, m.frontmatter.category, tags, relevance, m.content);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
tx();
|
|
101
|
+
return memories.length;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Search memories by keyword query.
|
|
105
|
+
*/
|
|
106
|
+
search(query, limit = 20) {
|
|
107
|
+
if (!this.db)
|
|
108
|
+
return [];
|
|
109
|
+
// FTS5 query — escape special characters
|
|
110
|
+
const safeQuery = query.replace(/['"]/g, "").trim();
|
|
111
|
+
if (!safeQuery)
|
|
112
|
+
return [];
|
|
113
|
+
const stmt = this.db.prepare(`
|
|
114
|
+
SELECT
|
|
115
|
+
relative_path,
|
|
116
|
+
title,
|
|
117
|
+
snippet(memories_fts, 5, '>>>', '<<<', '...', 40) as snippet,
|
|
118
|
+
rank
|
|
119
|
+
FROM memories_fts
|
|
120
|
+
WHERE memories_fts MATCH ?
|
|
121
|
+
ORDER BY rank
|
|
122
|
+
LIMIT ?
|
|
123
|
+
`);
|
|
124
|
+
try {
|
|
125
|
+
return stmt.all(safeQuery, limit);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// If FTS5 query fails, fall back to simple LIKE search
|
|
129
|
+
const likeStmt = this.db.prepare(`
|
|
130
|
+
SELECT
|
|
131
|
+
relative_path,
|
|
132
|
+
title,
|
|
133
|
+
substr(content, 1, 200) as snippet,
|
|
134
|
+
0 as rank
|
|
135
|
+
FROM memories_fts
|
|
136
|
+
WHERE content LIKE ? OR title LIKE ? OR tags LIKE ?
|
|
137
|
+
LIMIT ?
|
|
138
|
+
`);
|
|
139
|
+
const pattern = `%${safeQuery}%`;
|
|
140
|
+
return likeStmt.all(pattern, pattern, pattern, limit);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Discover memories by searching relevance keyword clouds.
|
|
145
|
+
* Returns lightweight metadata only — no file contents.
|
|
146
|
+
* This is the primary discovery mechanism replacing the static manifest.
|
|
147
|
+
*/
|
|
148
|
+
discover(query, limit = 20) {
|
|
149
|
+
if (!this.db)
|
|
150
|
+
return [];
|
|
151
|
+
const safeQuery = query.replace(/['"]/g, "").trim();
|
|
152
|
+
if (!safeQuery)
|
|
153
|
+
return [];
|
|
154
|
+
// Search primarily on relevance + title + tags (not content body)
|
|
155
|
+
// FTS5 column filter: {relevance title tags}
|
|
156
|
+
const stmt = this.db.prepare(`
|
|
157
|
+
SELECT
|
|
158
|
+
relative_path,
|
|
159
|
+
title,
|
|
160
|
+
relevance,
|
|
161
|
+
rank
|
|
162
|
+
FROM memories_fts
|
|
163
|
+
WHERE memories_fts MATCH ?
|
|
164
|
+
ORDER BY rank
|
|
165
|
+
LIMIT ?
|
|
166
|
+
`);
|
|
167
|
+
try {
|
|
168
|
+
// Try column-filtered search on relevance/title/tags first
|
|
169
|
+
const colQuery = `{relevance title tags} : ${safeQuery}`;
|
|
170
|
+
const results = stmt.all(colQuery, limit);
|
|
171
|
+
if (results.length > 0)
|
|
172
|
+
return results;
|
|
173
|
+
// Fall back to full-text search if column filter finds nothing
|
|
174
|
+
return stmt.all(safeQuery, limit);
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
// If FTS5 column filter syntax fails, fall back to full search
|
|
178
|
+
try {
|
|
179
|
+
return stmt.all(safeQuery, limit);
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
close() {
|
|
187
|
+
this.db?.close();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/lib/search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gnosys Web — Zero-dependency runtime search module.
|
|
3
|
+
*
|
|
4
|
+
* Exported as `gnosys/web` subpath. Loads a pre-computed gnosys-index.json
|
|
5
|
+
* and provides search functions for serverless chatbot integrations.
|
|
6
|
+
*
|
|
7
|
+
* CRITICAL: This module must have ZERO dependencies beyond Node.js built-ins.
|
|
8
|
+
* No better-sqlite3, no gray-matter, no @anthropic-ai/sdk, nothing.
|
|
9
|
+
*/
|
|
10
|
+
export interface GnosysWebIndex {
|
|
11
|
+
version: number;
|
|
12
|
+
generated: string;
|
|
13
|
+
documentCount: number;
|
|
14
|
+
documents: DocumentManifest[];
|
|
15
|
+
invertedIndex: Record<string, IndexEntry[]>;
|
|
16
|
+
}
|
|
17
|
+
export interface DocumentManifest {
|
|
18
|
+
id: string;
|
|
19
|
+
path: string;
|
|
20
|
+
title: string;
|
|
21
|
+
category: string;
|
|
22
|
+
tags: string[];
|
|
23
|
+
relevance: string;
|
|
24
|
+
contentHash: string;
|
|
25
|
+
contentLength: number;
|
|
26
|
+
created: string | null;
|
|
27
|
+
status: string;
|
|
28
|
+
}
|
|
29
|
+
export interface IndexEntry {
|
|
30
|
+
docIndex: number;
|
|
31
|
+
score: number;
|
|
32
|
+
}
|
|
33
|
+
export interface SearchOptions {
|
|
34
|
+
limit?: number;
|
|
35
|
+
minScore?: number;
|
|
36
|
+
category?: string;
|
|
37
|
+
tags?: string[];
|
|
38
|
+
boostRecent?: boolean;
|
|
39
|
+
}
|
|
40
|
+
export interface SearchResult {
|
|
41
|
+
document: DocumentManifest;
|
|
42
|
+
score: number;
|
|
43
|
+
matchedTokens: string[];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Load a pre-computed Gnosys web index from a file path or raw JSON string.
|
|
47
|
+
* Caches the result for repeated calls with the same source.
|
|
48
|
+
*/
|
|
49
|
+
export declare function loadIndex(pathOrJson: string): GnosysWebIndex;
|
|
50
|
+
/**
|
|
51
|
+
* Clear the cached index (useful for testing).
|
|
52
|
+
*/
|
|
53
|
+
export declare function clearIndexCache(): void;
|
|
54
|
+
/**
|
|
55
|
+
* Search the pre-computed index and return ranked results.
|
|
56
|
+
*/
|
|
57
|
+
export declare function search(index: GnosysWebIndex, query: string, options?: SearchOptions): SearchResult[];
|
|
58
|
+
/**
|
|
59
|
+
* Get a specific document's metadata by ID or path.
|
|
60
|
+
*/
|
|
61
|
+
export declare function getDocument(index: GnosysWebIndex, idOrPath: string): DocumentManifest | null;
|
|
62
|
+
/**
|
|
63
|
+
* List all documents, optionally filtered by category, tags, or status.
|
|
64
|
+
*/
|
|
65
|
+
export declare function listDocuments(index: GnosysWebIndex, filter?: {
|
|
66
|
+
category?: string;
|
|
67
|
+
tags?: string[];
|
|
68
|
+
status?: string;
|
|
69
|
+
}): DocumentManifest[];
|
|
70
|
+
//# sourceMappingURL=staticSearch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staticSearch.d.ts","sourceRoot":"","sources":["../../src/lib/staticSearch.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAmCD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,CAsC5D;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAGtC;AAED;;GAEG;AACH,wBAAgB,MAAM,CACpB,KAAK,EAAE,cAAc,EACrB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,aAAkB,GAC1B,YAAY,EAAE,CA6DhB;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,cAAc,EACrB,QAAQ,EAAE,MAAM,GACf,gBAAgB,GAAG,IAAI,CAMzB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,cAAc,EACrB,MAAM,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/D,gBAAgB,EAAE,CAUpB"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gnosys Web — Zero-dependency runtime search module.
|
|
3
|
+
*
|
|
4
|
+
* Exported as `gnosys/web` subpath. Loads a pre-computed gnosys-index.json
|
|
5
|
+
* and provides search functions for serverless chatbot integrations.
|
|
6
|
+
*
|
|
7
|
+
* CRITICAL: This module must have ZERO dependencies beyond Node.js built-ins.
|
|
8
|
+
* No better-sqlite3, no gray-matter, no @anthropic-ai/sdk, nothing.
|
|
9
|
+
*/
|
|
10
|
+
import { readFileSync, existsSync } from "fs";
|
|
11
|
+
// ─── Module-level cache ──────────────────────────────────────────────────
|
|
12
|
+
let cachedIndex = null;
|
|
13
|
+
let cachedSource = null;
|
|
14
|
+
// ─── Stop words (same list used by webIndex.ts at build time) ────────────
|
|
15
|
+
const STOP_WORDS = new Set([
|
|
16
|
+
"a", "an", "the", "and", "or", "but", "in", "on", "at", "to", "for",
|
|
17
|
+
"of", "with", "by", "from", "is", "it", "as", "be", "was", "are",
|
|
18
|
+
"been", "has", "had", "have", "do", "does", "did", "will", "would",
|
|
19
|
+
"could", "should", "may", "might", "shall", "can", "this", "that",
|
|
20
|
+
"these", "those", "not", "no", "nor", "so", "if", "then", "than",
|
|
21
|
+
"too", "very", "just", "about", "above", "after", "again", "all",
|
|
22
|
+
"also", "am", "any", "because", "before", "between", "both", "each",
|
|
23
|
+
"few", "he", "she", "her", "him", "his", "how", "its", "me", "more",
|
|
24
|
+
"most", "my", "new", "now", "only", "other", "our", "out", "own",
|
|
25
|
+
"re", "same", "some", "such", "up", "us", "we", "what", "when",
|
|
26
|
+
"where", "which", "who", "whom", "why", "you", "your",
|
|
27
|
+
]);
|
|
28
|
+
// ─── Tokenization ────────────────────────────────────────────────────────
|
|
29
|
+
function tokenize(text) {
|
|
30
|
+
return text
|
|
31
|
+
.toLowerCase()
|
|
32
|
+
.replace(/[^\w\s-]/g, " ")
|
|
33
|
+
.split(/\s+/)
|
|
34
|
+
.filter((t) => t.length >= 2 && !STOP_WORDS.has(t));
|
|
35
|
+
}
|
|
36
|
+
// ─── Public API ──────────────────────────────────────────────────────────
|
|
37
|
+
/**
|
|
38
|
+
* Load a pre-computed Gnosys web index from a file path or raw JSON string.
|
|
39
|
+
* Caches the result for repeated calls with the same source.
|
|
40
|
+
*/
|
|
41
|
+
export function loadIndex(pathOrJson) {
|
|
42
|
+
if (cachedIndex && cachedSource === pathOrJson) {
|
|
43
|
+
return cachedIndex;
|
|
44
|
+
}
|
|
45
|
+
let raw;
|
|
46
|
+
if (pathOrJson.trimStart().startsWith("{")) {
|
|
47
|
+
raw = pathOrJson;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
if (!existsSync(pathOrJson)) {
|
|
51
|
+
throw new Error(`Gnosys web index not found: ${pathOrJson}`);
|
|
52
|
+
}
|
|
53
|
+
raw = readFileSync(pathOrJson, "utf-8");
|
|
54
|
+
}
|
|
55
|
+
let parsed;
|
|
56
|
+
try {
|
|
57
|
+
parsed = JSON.parse(raw);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
throw new Error("Invalid JSON in Gnosys web index");
|
|
61
|
+
}
|
|
62
|
+
const index = parsed;
|
|
63
|
+
if (!index.version || typeof index.version !== "number") {
|
|
64
|
+
throw new Error("Invalid Gnosys web index: missing or invalid version field");
|
|
65
|
+
}
|
|
66
|
+
if (index.version > 1) {
|
|
67
|
+
throw new Error(`Gnosys web index version ${index.version} is not supported by this version of gnosys/web. ` +
|
|
68
|
+
`Please update the gnosys package.`);
|
|
69
|
+
}
|
|
70
|
+
cachedIndex = index;
|
|
71
|
+
cachedSource = pathOrJson;
|
|
72
|
+
return index;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Clear the cached index (useful for testing).
|
|
76
|
+
*/
|
|
77
|
+
export function clearIndexCache() {
|
|
78
|
+
cachedIndex = null;
|
|
79
|
+
cachedSource = null;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Search the pre-computed index and return ranked results.
|
|
83
|
+
*/
|
|
84
|
+
export function search(index, query, options = {}) {
|
|
85
|
+
const { limit = 6, minScore = 0.1, category, tags, boostRecent = false } = options;
|
|
86
|
+
const queryTokens = tokenize(query);
|
|
87
|
+
if (queryTokens.length === 0)
|
|
88
|
+
return [];
|
|
89
|
+
// Accumulate scores per document
|
|
90
|
+
const docScores = new Map();
|
|
91
|
+
for (const token of queryTokens) {
|
|
92
|
+
const entries = index.invertedIndex[token];
|
|
93
|
+
if (!entries)
|
|
94
|
+
continue;
|
|
95
|
+
for (const entry of entries) {
|
|
96
|
+
const existing = docScores.get(entry.docIndex);
|
|
97
|
+
if (existing) {
|
|
98
|
+
existing.score += entry.score;
|
|
99
|
+
if (!existing.matchedTokens.includes(token)) {
|
|
100
|
+
existing.matchedTokens.push(token);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
docScores.set(entry.docIndex, {
|
|
105
|
+
score: entry.score,
|
|
106
|
+
matchedTokens: [token],
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Build results with filters
|
|
112
|
+
const results = [];
|
|
113
|
+
for (const [docIndex, { score, matchedTokens }] of docScores) {
|
|
114
|
+
const doc = index.documents[docIndex];
|
|
115
|
+
if (!doc)
|
|
116
|
+
continue;
|
|
117
|
+
// Apply filters
|
|
118
|
+
if (category && doc.category !== category)
|
|
119
|
+
continue;
|
|
120
|
+
if (tags && tags.length > 0 && !tags.some((t) => doc.tags.includes(t)))
|
|
121
|
+
continue;
|
|
122
|
+
let finalScore = score;
|
|
123
|
+
// Optional recency boost
|
|
124
|
+
if (boostRecent && doc.created) {
|
|
125
|
+
const ageMs = Date.now() - new Date(doc.created).getTime();
|
|
126
|
+
const ageDays = ageMs / (1000 * 60 * 60 * 24);
|
|
127
|
+
// Boost documents less than 30 days old, max 1.5x
|
|
128
|
+
if (ageDays < 30) {
|
|
129
|
+
finalScore *= 1 + 0.5 * (1 - ageDays / 30);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (finalScore < minScore)
|
|
133
|
+
continue;
|
|
134
|
+
results.push({ document: doc, score: finalScore, matchedTokens });
|
|
135
|
+
}
|
|
136
|
+
// Sort by score descending
|
|
137
|
+
results.sort((a, b) => b.score - a.score);
|
|
138
|
+
return results.slice(0, limit);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get a specific document's metadata by ID or path.
|
|
142
|
+
*/
|
|
143
|
+
export function getDocument(index, idOrPath) {
|
|
144
|
+
return (index.documents.find((d) => d.id === idOrPath || d.path === idOrPath) ?? null);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* List all documents, optionally filtered by category, tags, or status.
|
|
148
|
+
*/
|
|
149
|
+
export function listDocuments(index, filter) {
|
|
150
|
+
if (!filter)
|
|
151
|
+
return [...index.documents];
|
|
152
|
+
return index.documents.filter((d) => {
|
|
153
|
+
if (filter.category && d.category !== filter.category)
|
|
154
|
+
return false;
|
|
155
|
+
if (filter.tags && filter.tags.length > 0 && !filter.tags.some((t) => d.tags.includes(t)))
|
|
156
|
+
return false;
|
|
157
|
+
if (filter.status && d.status !== filter.status)
|
|
158
|
+
return false;
|
|
159
|
+
return true;
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=staticSearch.js.map
|