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.
Files changed (188) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1387 -0
  3. package/dist/cli.d.ts +7 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +3753 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/index.d.ts +8 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +2267 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/lib/archive.d.ts +95 -0
  12. package/dist/lib/archive.d.ts.map +1 -0
  13. package/dist/lib/archive.js +311 -0
  14. package/dist/lib/archive.js.map +1 -0
  15. package/dist/lib/ask.d.ts +77 -0
  16. package/dist/lib/ask.d.ts.map +1 -0
  17. package/dist/lib/ask.js +316 -0
  18. package/dist/lib/ask.js.map +1 -0
  19. package/dist/lib/audit.d.ts +47 -0
  20. package/dist/lib/audit.d.ts.map +1 -0
  21. package/dist/lib/audit.js +136 -0
  22. package/dist/lib/audit.js.map +1 -0
  23. package/dist/lib/bootstrap.d.ts +56 -0
  24. package/dist/lib/bootstrap.d.ts.map +1 -0
  25. package/dist/lib/bootstrap.js +163 -0
  26. package/dist/lib/bootstrap.js.map +1 -0
  27. package/dist/lib/config.d.ts +239 -0
  28. package/dist/lib/config.d.ts.map +1 -0
  29. package/dist/lib/config.js +371 -0
  30. package/dist/lib/config.js.map +1 -0
  31. package/dist/lib/dashboard.d.ts +81 -0
  32. package/dist/lib/dashboard.d.ts.map +1 -0
  33. package/dist/lib/dashboard.js +314 -0
  34. package/dist/lib/dashboard.js.map +1 -0
  35. package/dist/lib/db.d.ts +182 -0
  36. package/dist/lib/db.d.ts.map +1 -0
  37. package/dist/lib/db.js +620 -0
  38. package/dist/lib/db.js.map +1 -0
  39. package/dist/lib/dbSearch.d.ts +65 -0
  40. package/dist/lib/dbSearch.d.ts.map +1 -0
  41. package/dist/lib/dbSearch.js +239 -0
  42. package/dist/lib/dbSearch.js.map +1 -0
  43. package/dist/lib/dbWrite.d.ts +56 -0
  44. package/dist/lib/dbWrite.d.ts.map +1 -0
  45. package/dist/lib/dbWrite.js +171 -0
  46. package/dist/lib/dbWrite.js.map +1 -0
  47. package/dist/lib/dream.d.ts +170 -0
  48. package/dist/lib/dream.d.ts.map +1 -0
  49. package/dist/lib/dream.js +706 -0
  50. package/dist/lib/dream.js.map +1 -0
  51. package/dist/lib/embeddings.d.ts +84 -0
  52. package/dist/lib/embeddings.d.ts.map +1 -0
  53. package/dist/lib/embeddings.js +226 -0
  54. package/dist/lib/embeddings.js.map +1 -0
  55. package/dist/lib/export.d.ts +92 -0
  56. package/dist/lib/export.d.ts.map +1 -0
  57. package/dist/lib/export.js +362 -0
  58. package/dist/lib/export.js.map +1 -0
  59. package/dist/lib/federated.d.ts +113 -0
  60. package/dist/lib/federated.d.ts.map +1 -0
  61. package/dist/lib/federated.js +346 -0
  62. package/dist/lib/federated.js.map +1 -0
  63. package/dist/lib/graph.d.ts +50 -0
  64. package/dist/lib/graph.d.ts.map +1 -0
  65. package/dist/lib/graph.js +118 -0
  66. package/dist/lib/graph.js.map +1 -0
  67. package/dist/lib/history.d.ts +39 -0
  68. package/dist/lib/history.d.ts.map +1 -0
  69. package/dist/lib/history.js +112 -0
  70. package/dist/lib/history.js.map +1 -0
  71. package/dist/lib/hybridSearch.d.ts +80 -0
  72. package/dist/lib/hybridSearch.d.ts.map +1 -0
  73. package/dist/lib/hybridSearch.js +296 -0
  74. package/dist/lib/hybridSearch.js.map +1 -0
  75. package/dist/lib/import.d.ts +52 -0
  76. package/dist/lib/import.d.ts.map +1 -0
  77. package/dist/lib/import.js +365 -0
  78. package/dist/lib/import.js.map +1 -0
  79. package/dist/lib/ingest.d.ts +51 -0
  80. package/dist/lib/ingest.d.ts.map +1 -0
  81. package/dist/lib/ingest.js +144 -0
  82. package/dist/lib/ingest.js.map +1 -0
  83. package/dist/lib/lensing.d.ts +35 -0
  84. package/dist/lib/lensing.d.ts.map +1 -0
  85. package/dist/lib/lensing.js +85 -0
  86. package/dist/lib/lensing.js.map +1 -0
  87. package/dist/lib/llm.d.ts +84 -0
  88. package/dist/lib/llm.d.ts.map +1 -0
  89. package/dist/lib/llm.js +386 -0
  90. package/dist/lib/llm.js.map +1 -0
  91. package/dist/lib/lock.d.ts +28 -0
  92. package/dist/lib/lock.d.ts.map +1 -0
  93. package/dist/lib/lock.js +145 -0
  94. package/dist/lib/lock.js.map +1 -0
  95. package/dist/lib/maintenance.d.ts +124 -0
  96. package/dist/lib/maintenance.d.ts.map +1 -0
  97. package/dist/lib/maintenance.js +587 -0
  98. package/dist/lib/maintenance.js.map +1 -0
  99. package/dist/lib/migrate.d.ts +19 -0
  100. package/dist/lib/migrate.d.ts.map +1 -0
  101. package/dist/lib/migrate.js +260 -0
  102. package/dist/lib/migrate.js.map +1 -0
  103. package/dist/lib/preferences.d.ts +49 -0
  104. package/dist/lib/preferences.d.ts.map +1 -0
  105. package/dist/lib/preferences.js +149 -0
  106. package/dist/lib/preferences.js.map +1 -0
  107. package/dist/lib/projectIdentity.d.ts +66 -0
  108. package/dist/lib/projectIdentity.d.ts.map +1 -0
  109. package/dist/lib/projectIdentity.js +148 -0
  110. package/dist/lib/projectIdentity.js.map +1 -0
  111. package/dist/lib/recall.d.ts +82 -0
  112. package/dist/lib/recall.d.ts.map +1 -0
  113. package/dist/lib/recall.js +289 -0
  114. package/dist/lib/recall.js.map +1 -0
  115. package/dist/lib/resolver.d.ts +116 -0
  116. package/dist/lib/resolver.d.ts.map +1 -0
  117. package/dist/lib/resolver.js +372 -0
  118. package/dist/lib/resolver.js.map +1 -0
  119. package/dist/lib/retry.d.ts +24 -0
  120. package/dist/lib/retry.d.ts.map +1 -0
  121. package/dist/lib/retry.js +60 -0
  122. package/dist/lib/retry.js.map +1 -0
  123. package/dist/lib/rulesGen.d.ts +51 -0
  124. package/dist/lib/rulesGen.d.ts.map +1 -0
  125. package/dist/lib/rulesGen.js +167 -0
  126. package/dist/lib/rulesGen.js.map +1 -0
  127. package/dist/lib/search.d.ts +51 -0
  128. package/dist/lib/search.d.ts.map +1 -0
  129. package/dist/lib/search.js +190 -0
  130. package/dist/lib/search.js.map +1 -0
  131. package/dist/lib/staticSearch.d.ts +70 -0
  132. package/dist/lib/staticSearch.d.ts.map +1 -0
  133. package/dist/lib/staticSearch.js +162 -0
  134. package/dist/lib/staticSearch.js.map +1 -0
  135. package/dist/lib/store.d.ts +79 -0
  136. package/dist/lib/store.d.ts.map +1 -0
  137. package/dist/lib/store.js +227 -0
  138. package/dist/lib/store.js.map +1 -0
  139. package/dist/lib/structuredIngest.d.ts +37 -0
  140. package/dist/lib/structuredIngest.d.ts.map +1 -0
  141. package/dist/lib/structuredIngest.js +208 -0
  142. package/dist/lib/structuredIngest.js.map +1 -0
  143. package/dist/lib/tags.d.ts +26 -0
  144. package/dist/lib/tags.d.ts.map +1 -0
  145. package/dist/lib/tags.js +109 -0
  146. package/dist/lib/tags.js.map +1 -0
  147. package/dist/lib/timeline.d.ts +34 -0
  148. package/dist/lib/timeline.d.ts.map +1 -0
  149. package/dist/lib/timeline.js +116 -0
  150. package/dist/lib/timeline.js.map +1 -0
  151. package/dist/lib/trace.d.ts +42 -0
  152. package/dist/lib/trace.d.ts.map +1 -0
  153. package/dist/lib/trace.js +338 -0
  154. package/dist/lib/trace.js.map +1 -0
  155. package/dist/lib/webIndex.d.ts +28 -0
  156. package/dist/lib/webIndex.d.ts.map +1 -0
  157. package/dist/lib/webIndex.js +208 -0
  158. package/dist/lib/webIndex.js.map +1 -0
  159. package/dist/lib/webIngest.d.ts +51 -0
  160. package/dist/lib/webIngest.d.ts.map +1 -0
  161. package/dist/lib/webIngest.js +533 -0
  162. package/dist/lib/webIngest.js.map +1 -0
  163. package/dist/lib/wikilinks.d.ts +63 -0
  164. package/dist/lib/wikilinks.d.ts.map +1 -0
  165. package/dist/lib/wikilinks.js +146 -0
  166. package/dist/lib/wikilinks.js.map +1 -0
  167. package/dist/sandbox/client.d.ts +82 -0
  168. package/dist/sandbox/client.d.ts.map +1 -0
  169. package/dist/sandbox/client.js +128 -0
  170. package/dist/sandbox/client.js.map +1 -0
  171. package/dist/sandbox/helper-template.d.ts +14 -0
  172. package/dist/sandbox/helper-template.d.ts.map +1 -0
  173. package/dist/sandbox/helper-template.js +285 -0
  174. package/dist/sandbox/helper-template.js.map +1 -0
  175. package/dist/sandbox/index.d.ts +10 -0
  176. package/dist/sandbox/index.d.ts.map +1 -0
  177. package/dist/sandbox/index.js +10 -0
  178. package/dist/sandbox/index.js.map +1 -0
  179. package/dist/sandbox/manager.d.ts +40 -0
  180. package/dist/sandbox/manager.d.ts.map +1 -0
  181. package/dist/sandbox/manager.js +220 -0
  182. package/dist/sandbox/manager.js.map +1 -0
  183. package/dist/sandbox/server.d.ts +44 -0
  184. package/dist/sandbox/server.d.ts.map +1 -0
  185. package/dist/sandbox/server.js +661 -0
  186. package/dist/sandbox/server.js.map +1 -0
  187. package/package.json +103 -0
  188. 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