stellar-memory 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +362 -0
  3. package/dist/api/routes/analytics.d.ts +15 -0
  4. package/dist/api/routes/analytics.js +131 -0
  5. package/dist/api/routes/analytics.js.map +1 -0
  6. package/dist/api/routes/conflicts.d.ts +12 -0
  7. package/dist/api/routes/conflicts.js +67 -0
  8. package/dist/api/routes/conflicts.js.map +1 -0
  9. package/dist/api/routes/consolidation.d.ts +11 -0
  10. package/dist/api/routes/consolidation.js +63 -0
  11. package/dist/api/routes/consolidation.js.map +1 -0
  12. package/dist/api/routes/constellation.d.ts +4 -0
  13. package/dist/api/routes/constellation.js +84 -0
  14. package/dist/api/routes/constellation.js.map +1 -0
  15. package/dist/api/routes/memories.d.ts +4 -0
  16. package/dist/api/routes/memories.js +219 -0
  17. package/dist/api/routes/memories.js.map +1 -0
  18. package/dist/api/routes/observations.d.ts +10 -0
  19. package/dist/api/routes/observations.js +42 -0
  20. package/dist/api/routes/observations.js.map +1 -0
  21. package/dist/api/routes/orbit.d.ts +4 -0
  22. package/dist/api/routes/orbit.js +71 -0
  23. package/dist/api/routes/orbit.js.map +1 -0
  24. package/dist/api/routes/projects.d.ts +15 -0
  25. package/dist/api/routes/projects.js +121 -0
  26. package/dist/api/routes/projects.js.map +1 -0
  27. package/dist/api/routes/scan.d.ts +4 -0
  28. package/dist/api/routes/scan.js +403 -0
  29. package/dist/api/routes/scan.js.map +1 -0
  30. package/dist/api/routes/sun.d.ts +4 -0
  31. package/dist/api/routes/sun.js +43 -0
  32. package/dist/api/routes/sun.js.map +1 -0
  33. package/dist/api/routes/system.d.ts +4 -0
  34. package/dist/api/routes/system.js +70 -0
  35. package/dist/api/routes/system.js.map +1 -0
  36. package/dist/api/routes/temporal.d.ts +13 -0
  37. package/dist/api/routes/temporal.js +82 -0
  38. package/dist/api/routes/temporal.js.map +1 -0
  39. package/dist/api/server.d.ts +2 -0
  40. package/dist/api/server.js +99 -0
  41. package/dist/api/server.js.map +1 -0
  42. package/dist/api/websocket.d.ts +53 -0
  43. package/dist/api/websocket.js +168 -0
  44. package/dist/api/websocket.js.map +1 -0
  45. package/dist/cli/index.d.ts +12 -0
  46. package/dist/cli/index.js +35 -0
  47. package/dist/cli/index.js.map +1 -0
  48. package/dist/cli/init.d.ts +10 -0
  49. package/dist/cli/init.js +163 -0
  50. package/dist/cli/init.js.map +1 -0
  51. package/dist/engine/analytics.d.ts +93 -0
  52. package/dist/engine/analytics.js +437 -0
  53. package/dist/engine/analytics.js.map +1 -0
  54. package/dist/engine/conflict.d.ts +54 -0
  55. package/dist/engine/conflict.js +322 -0
  56. package/dist/engine/conflict.js.map +1 -0
  57. package/dist/engine/consolidation.d.ts +83 -0
  58. package/dist/engine/consolidation.js +368 -0
  59. package/dist/engine/consolidation.js.map +1 -0
  60. package/dist/engine/constellation.d.ts +66 -0
  61. package/dist/engine/constellation.js +382 -0
  62. package/dist/engine/constellation.js.map +1 -0
  63. package/dist/engine/corona.d.ts +53 -0
  64. package/dist/engine/corona.js +181 -0
  65. package/dist/engine/corona.js.map +1 -0
  66. package/dist/engine/embedding.d.ts +44 -0
  67. package/dist/engine/embedding.js +168 -0
  68. package/dist/engine/embedding.js.map +1 -0
  69. package/dist/engine/gravity.d.ts +63 -0
  70. package/dist/engine/gravity.js +121 -0
  71. package/dist/engine/gravity.js.map +1 -0
  72. package/dist/engine/multiproject.d.ts +75 -0
  73. package/dist/engine/multiproject.js +241 -0
  74. package/dist/engine/multiproject.js.map +1 -0
  75. package/dist/engine/observation.d.ts +82 -0
  76. package/dist/engine/observation.js +357 -0
  77. package/dist/engine/observation.js.map +1 -0
  78. package/dist/engine/orbit.d.ts +91 -0
  79. package/dist/engine/orbit.js +249 -0
  80. package/dist/engine/orbit.js.map +1 -0
  81. package/dist/engine/planet.d.ts +64 -0
  82. package/dist/engine/planet.js +432 -0
  83. package/dist/engine/planet.js.map +1 -0
  84. package/dist/engine/procedural.d.ts +71 -0
  85. package/dist/engine/procedural.js +259 -0
  86. package/dist/engine/procedural.js.map +1 -0
  87. package/dist/engine/quality.d.ts +48 -0
  88. package/dist/engine/quality.js +245 -0
  89. package/dist/engine/quality.js.map +1 -0
  90. package/dist/engine/repository.d.ts +79 -0
  91. package/dist/engine/repository.js +13 -0
  92. package/dist/engine/repository.js.map +1 -0
  93. package/dist/engine/sun.d.ts +61 -0
  94. package/dist/engine/sun.js +240 -0
  95. package/dist/engine/sun.js.map +1 -0
  96. package/dist/engine/temporal.d.ts +67 -0
  97. package/dist/engine/temporal.js +283 -0
  98. package/dist/engine/temporal.js.map +1 -0
  99. package/dist/engine/types.d.ts +179 -0
  100. package/dist/engine/types.js +27 -0
  101. package/dist/engine/types.js.map +1 -0
  102. package/dist/index.d.ts +2 -0
  103. package/dist/index.js +60 -0
  104. package/dist/index.js.map +1 -0
  105. package/dist/mcp/connector-registry.d.ts +20 -0
  106. package/dist/mcp/connector-registry.js +35 -0
  107. package/dist/mcp/connector-registry.js.map +1 -0
  108. package/dist/mcp/server.d.ts +13 -0
  109. package/dist/mcp/server.js +242 -0
  110. package/dist/mcp/server.js.map +1 -0
  111. package/dist/mcp/tools/daemon-tool.d.ts +16 -0
  112. package/dist/mcp/tools/daemon-tool.js +58 -0
  113. package/dist/mcp/tools/daemon-tool.js.map +1 -0
  114. package/dist/mcp/tools/ingestion-tools.d.ts +20 -0
  115. package/dist/mcp/tools/ingestion-tools.js +34 -0
  116. package/dist/mcp/tools/ingestion-tools.js.map +1 -0
  117. package/dist/mcp/tools/memory-tools.d.ts +122 -0
  118. package/dist/mcp/tools/memory-tools.js +1037 -0
  119. package/dist/mcp/tools/memory-tools.js.map +1 -0
  120. package/dist/scanner/cloud/github.d.ts +34 -0
  121. package/dist/scanner/cloud/github.js +260 -0
  122. package/dist/scanner/cloud/github.js.map +1 -0
  123. package/dist/scanner/cloud/google-drive.d.ts +30 -0
  124. package/dist/scanner/cloud/google-drive.js +289 -0
  125. package/dist/scanner/cloud/google-drive.js.map +1 -0
  126. package/dist/scanner/cloud/notion.d.ts +33 -0
  127. package/dist/scanner/cloud/notion.js +231 -0
  128. package/dist/scanner/cloud/notion.js.map +1 -0
  129. package/dist/scanner/cloud/slack.d.ts +38 -0
  130. package/dist/scanner/cloud/slack.js +282 -0
  131. package/dist/scanner/cloud/slack.js.map +1 -0
  132. package/dist/scanner/cloud/types.d.ts +73 -0
  133. package/dist/scanner/cloud/types.js +9 -0
  134. package/dist/scanner/cloud/types.js.map +1 -0
  135. package/dist/scanner/index.d.ts +35 -0
  136. package/dist/scanner/index.js +420 -0
  137. package/dist/scanner/index.js.map +1 -0
  138. package/dist/scanner/local/filesystem.d.ts +33 -0
  139. package/dist/scanner/local/filesystem.js +203 -0
  140. package/dist/scanner/local/filesystem.js.map +1 -0
  141. package/dist/scanner/local/git.d.ts +24 -0
  142. package/dist/scanner/local/git.js +161 -0
  143. package/dist/scanner/local/git.js.map +1 -0
  144. package/dist/scanner/local/parsers/code.d.ts +3 -0
  145. package/dist/scanner/local/parsers/code.js +127 -0
  146. package/dist/scanner/local/parsers/code.js.map +1 -0
  147. package/dist/scanner/local/parsers/index.d.ts +11 -0
  148. package/dist/scanner/local/parsers/index.js +24 -0
  149. package/dist/scanner/local/parsers/index.js.map +1 -0
  150. package/dist/scanner/local/parsers/json-parser.d.ts +3 -0
  151. package/dist/scanner/local/parsers/json-parser.js +117 -0
  152. package/dist/scanner/local/parsers/json-parser.js.map +1 -0
  153. package/dist/scanner/local/parsers/markdown.d.ts +3 -0
  154. package/dist/scanner/local/parsers/markdown.js +120 -0
  155. package/dist/scanner/local/parsers/markdown.js.map +1 -0
  156. package/dist/scanner/local/parsers/text.d.ts +3 -0
  157. package/dist/scanner/local/parsers/text.js +41 -0
  158. package/dist/scanner/local/parsers/text.js.map +1 -0
  159. package/dist/scanner/metadata-scanner.d.ts +67 -0
  160. package/dist/scanner/metadata-scanner.js +356 -0
  161. package/dist/scanner/metadata-scanner.js.map +1 -0
  162. package/dist/scanner/types.d.ts +47 -0
  163. package/dist/scanner/types.js +19 -0
  164. package/dist/scanner/types.js.map +1 -0
  165. package/dist/service/daemon.d.ts +23 -0
  166. package/dist/service/daemon.js +105 -0
  167. package/dist/service/daemon.js.map +1 -0
  168. package/dist/service/scheduler.d.ts +73 -0
  169. package/dist/service/scheduler.js +281 -0
  170. package/dist/service/scheduler.js.map +1 -0
  171. package/dist/storage/database.d.ts +10 -0
  172. package/dist/storage/database.js +265 -0
  173. package/dist/storage/database.js.map +1 -0
  174. package/dist/storage/queries.d.ts +85 -0
  175. package/dist/storage/queries.js +865 -0
  176. package/dist/storage/queries.js.map +1 -0
  177. package/dist/storage/sqlite-repository.d.ts +32 -0
  178. package/dist/storage/sqlite-repository.js +68 -0
  179. package/dist/storage/sqlite-repository.js.map +1 -0
  180. package/dist/storage/vec.d.ts +62 -0
  181. package/dist/storage/vec.js +111 -0
  182. package/dist/storage/vec.js.map +1 -0
  183. package/dist/utils/config.d.ts +5 -0
  184. package/dist/utils/config.js +60 -0
  185. package/dist/utils/config.js.map +1 -0
  186. package/dist/utils/logger.d.ts +36 -0
  187. package/dist/utils/logger.js +86 -0
  188. package/dist/utils/logger.js.map +1 -0
  189. package/dist/utils/time.d.ts +21 -0
  190. package/dist/utils/time.js +42 -0
  191. package/dist/utils/time.js.map +1 -0
  192. package/dist/utils/tokenizer.d.ts +13 -0
  193. package/dist/utils/tokenizer.js +46 -0
  194. package/dist/utils/tokenizer.js.map +1 -0
  195. package/package.json +77 -0
  196. package/scripts/check-node.mjs +36 -0
  197. package/scripts/setup.mjs +157 -0
@@ -0,0 +1,420 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { resolve } from 'node:path';
3
+ import { DEFAULT_SCAN_CONFIG } from './types.js';
4
+ import { collectFilesWithStats, readTextFile, buildSourceHash, watchDirectory } from './local/filesystem.js';
5
+ import { getParser } from './local/parsers/index.js';
6
+ import { scanGitHistory } from './local/git.js';
7
+ import { memoryExistsForSource, getMemoryBySourcePath, insertMemory, insertDataSource, updateDataSource, getAllDataSources, getDataSourceByPath, } from '../storage/queries.js';
8
+ import { IMPACT_DEFAULTS } from '../engine/types.js';
9
+ import { recencyScore, frequencyScore, importanceToDistance, } from '../engine/orbit.js';
10
+ import { getConfig } from '../utils/config.js';
11
+ // Full-scan exclude patterns (home directory scan)
12
+ export const FULL_SCAN_EXTRA_EXCLUDES = [
13
+ 'AppData', 'Application Data', '.npm', '.yarn', '.pnpm-store',
14
+ '.docker', '.gradle', '.m2', 'Temp', 'tmp', '.Trash', '.local',
15
+ 'Library', 'Pictures', 'Videos', 'Music', 'Downloads', 'Desktop',
16
+ 'OneDrive', '$Recycle.Bin', 'System Volume Information',
17
+ 'ProgramData', 'Program Files', 'Program Files (x86)', 'Windows',
18
+ ];
19
+ // ---------------------------------------------------------------------------
20
+ // StellarScanner
21
+ // ---------------------------------------------------------------------------
22
+ export class StellarScanner {
23
+ config;
24
+ stopWatchers = [];
25
+ constructor(config = {}) {
26
+ this.config = {
27
+ ...DEFAULT_SCAN_CONFIG,
28
+ ...config,
29
+ // Merge array overrides rather than replacing defaults entirely
30
+ excludePatterns: config.excludePatterns ?? DEFAULT_SCAN_CONFIG.excludePatterns,
31
+ fileExtensions: config.fileExtensions ?? DEFAULT_SCAN_CONFIG.fileExtensions,
32
+ };
33
+ }
34
+ // -------------------------------------------------------------------------
35
+ // scan() — one-shot scan of all configured paths
36
+ // -------------------------------------------------------------------------
37
+ async scan() {
38
+ const startMs = Date.now();
39
+ let scannedFiles = 0;
40
+ let createdMemories = 0;
41
+ let skippedFiles = 0;
42
+ let errorFiles = 0;
43
+ const cfg = getConfig();
44
+ const project = cfg.defaultProject;
45
+ for (const scanPath of this.config.paths) {
46
+ const absPath = resolve(scanPath);
47
+ // Register or update data source record
48
+ await this._ensureDataSource(absPath);
49
+ // Collect files
50
+ const { entries, skippedCount } = await collectFilesWithStats(absPath, this.config);
51
+ skippedFiles += skippedCount;
52
+ // Process each file
53
+ for (const entry of entries) {
54
+ scannedFiles++;
55
+ const result = await this._processFile(entry, project);
56
+ if (result === 'created')
57
+ createdMemories++;
58
+ else if (result === 'skip')
59
+ skippedFiles++;
60
+ else
61
+ errorFiles++;
62
+ }
63
+ // Update data source stats
64
+ const ds = getDataSourceByPath(absPath);
65
+ if (ds) {
66
+ updateDataSource(ds.id, {
67
+ status: 'active',
68
+ last_scanned_at: new Date().toISOString(),
69
+ file_count: entries.length,
70
+ total_size: entries.reduce((sum, e) => sum + e.size, 0),
71
+ });
72
+ }
73
+ }
74
+ // Git history scanning (if a configured path is a git repo)
75
+ for (const scanPath of this.config.paths) {
76
+ const absPath = resolve(scanPath);
77
+ createdMemories += this._insertGitMemories(absPath, project);
78
+ }
79
+ return {
80
+ scannedFiles,
81
+ createdMemories,
82
+ skippedFiles,
83
+ errorFiles,
84
+ durationMs: Date.now() - startMs,
85
+ };
86
+ }
87
+ // -------------------------------------------------------------------------
88
+ // scanPath() — scan a single directory path (used by MCP tool)
89
+ // -------------------------------------------------------------------------
90
+ async scanPath(dirPath, opts = {}) {
91
+ const absPath = resolve(dirPath);
92
+ const startMs = Date.now();
93
+ let scannedFiles = 0;
94
+ let createdMemories = 0;
95
+ let skippedFiles = 0;
96
+ let errorFiles = 0;
97
+ const cfg = getConfig();
98
+ const project = cfg.defaultProject;
99
+ await this._ensureDataSource(absPath);
100
+ const localConfig = { ...this.config };
101
+ if (opts.recursive === false) {
102
+ // Non-recursive: override with a sentinel — collectFiles checks for it
103
+ localConfig.paths = [absPath];
104
+ }
105
+ const { entries, skippedCount } = await collectFilesWithStats(absPath, localConfig);
106
+ skippedFiles += skippedCount;
107
+ for (const entry of entries) {
108
+ scannedFiles++;
109
+ const result = await this._processFile(entry, project);
110
+ if (result === 'created')
111
+ createdMemories++;
112
+ else if (result === 'skip')
113
+ skippedFiles++;
114
+ else
115
+ errorFiles++;
116
+ }
117
+ const ds = getDataSourceByPath(absPath);
118
+ if (ds) {
119
+ updateDataSource(ds.id, {
120
+ status: 'active',
121
+ last_scanned_at: new Date().toISOString(),
122
+ file_count: entries.length,
123
+ total_size: entries.reduce((sum, e) => sum + e.size, 0),
124
+ });
125
+ }
126
+ if (opts.includeGit !== false) {
127
+ createdMemories += this._insertGitMemories(absPath, project);
128
+ }
129
+ return {
130
+ scannedFiles,
131
+ createdMemories,
132
+ skippedFiles,
133
+ errorFiles,
134
+ durationMs: Date.now() - startMs,
135
+ };
136
+ }
137
+ // -------------------------------------------------------------------------
138
+ // scanWithProgress() — scan with progress callbacks + abort support
139
+ // -------------------------------------------------------------------------
140
+ async scanWithProgress(opts) {
141
+ const startMs = Date.now();
142
+ let scannedFiles = 0;
143
+ let createdMemories = 0;
144
+ let skippedFiles = 0;
145
+ let errorFiles = 0;
146
+ const cfg = getConfig();
147
+ const project = cfg.defaultProject;
148
+ const scanPaths = opts.paths && opts.paths.length > 0
149
+ ? opts.paths
150
+ : this.config.paths;
151
+ for (const scanPath of scanPaths) {
152
+ if (opts.abortSignal?.aborted)
153
+ break;
154
+ const absPath = resolve(scanPath);
155
+ opts.onProgress({ phase: 'collecting', path: absPath });
156
+ await this._ensureDataSource(absPath);
157
+ const { entries, skippedCount } = await collectFilesWithStats(absPath, this.config);
158
+ skippedFiles += skippedCount;
159
+ opts.onProgress({
160
+ phase: 'collected',
161
+ path: absPath,
162
+ totalFiles: entries.length,
163
+ });
164
+ for (const entry of entries) {
165
+ if (opts.abortSignal?.aborted)
166
+ break;
167
+ scannedFiles++;
168
+ opts.onProgress({
169
+ phase: 'processing',
170
+ path: absPath,
171
+ currentFile: entry.path,
172
+ totalFiles: entries.length,
173
+ scannedFiles,
174
+ createdMemories,
175
+ skippedFiles,
176
+ errorFiles,
177
+ });
178
+ const result = await this._processFile(entry, project);
179
+ if (result === 'created')
180
+ createdMemories++;
181
+ else if (result === 'skip')
182
+ skippedFiles++;
183
+ else
184
+ errorFiles++;
185
+ }
186
+ // Update data source stats
187
+ const ds = getDataSourceByPath(absPath);
188
+ if (ds) {
189
+ updateDataSource(ds.id, {
190
+ status: 'active',
191
+ last_scanned_at: new Date().toISOString(),
192
+ file_count: entries.length,
193
+ total_size: entries.reduce((sum, e) => sum + e.size, 0),
194
+ });
195
+ }
196
+ opts.onProgress({
197
+ phase: 'path_complete',
198
+ path: absPath,
199
+ scannedFiles,
200
+ createdMemories,
201
+ skippedFiles,
202
+ errorFiles,
203
+ });
204
+ }
205
+ // Git history scanning
206
+ if (opts.includeGit !== false && !opts.abortSignal?.aborted) {
207
+ for (const scanPath of scanPaths) {
208
+ if (opts.abortSignal?.aborted)
209
+ break;
210
+ const absPath = resolve(scanPath);
211
+ createdMemories += this._insertGitMemories(absPath, project);
212
+ }
213
+ }
214
+ return {
215
+ scannedFiles,
216
+ createdMemories,
217
+ skippedFiles,
218
+ errorFiles,
219
+ durationMs: Date.now() - startMs,
220
+ };
221
+ }
222
+ // -------------------------------------------------------------------------
223
+ // watch() — start watching all configured paths for changes
224
+ // -------------------------------------------------------------------------
225
+ watch() {
226
+ const cfg = getConfig();
227
+ const project = cfg.defaultProject;
228
+ for (const scanPath of this.config.paths) {
229
+ const absPath = resolve(scanPath);
230
+ const stop = watchDirectory(absPath, this.config, async (_event, filePath) => {
231
+ const ext = filePath.slice(filePath.lastIndexOf('.')).toLowerCase();
232
+ const parser = getParser(ext);
233
+ if (!parser)
234
+ return;
235
+ try {
236
+ const stat = await import('node:fs/promises').then((m) => m.stat(filePath));
237
+ const entry = {
238
+ path: filePath,
239
+ size: stat.size,
240
+ mtimeMs: stat.mtimeMs,
241
+ extension: ext,
242
+ };
243
+ await this._processFile(entry, project);
244
+ }
245
+ catch {
246
+ // Ignore watcher errors
247
+ }
248
+ });
249
+ this.stopWatchers.push(stop);
250
+ }
251
+ }
252
+ // -------------------------------------------------------------------------
253
+ // stop() — stop all file watchers
254
+ // -------------------------------------------------------------------------
255
+ stop() {
256
+ for (const stop of this.stopWatchers)
257
+ stop();
258
+ this.stopWatchers = [];
259
+ }
260
+ // -------------------------------------------------------------------------
261
+ // Private: process one file entry
262
+ // -------------------------------------------------------------------------
263
+ async _processFile(entry, project) {
264
+ const parser = getParser(entry.extension);
265
+ if (!parser)
266
+ return 'skip';
267
+ const sourceHash = buildSourceHash(entry.path, entry.mtimeMs);
268
+ // Dedup check: same path + same mtime hash → skip
269
+ if (memoryExistsForSource(entry.path, sourceHash))
270
+ return 'skip';
271
+ // Check if there's an older memory for this path — soft-delete it so the
272
+ // FTS index stays tidy, then create a fresh one.
273
+ const existing = getMemoryBySourcePath(entry.path);
274
+ const content = await readTextFile(entry.path);
275
+ if (content === null)
276
+ return 'skip'; // binary or unreadable
277
+ let parsed;
278
+ try {
279
+ parsed = parser.parse(entry.path, content);
280
+ }
281
+ catch {
282
+ return 'error';
283
+ }
284
+ if (existing) {
285
+ // Reuse the existing memory's position / importance rather than starting fresh.
286
+ // We update the stored memory inline to avoid polluting the orbit log.
287
+ try {
288
+ const db = await import('../storage/database.js').then((m) => m.getDatabase());
289
+ const now = new Date().toISOString();
290
+ db.prepare(`
291
+ UPDATE memories
292
+ SET content = ?, summary = ?, tags = ?, metadata = ?,
293
+ source_hash = ?, updated_at = ?
294
+ WHERE id = ?
295
+ `).run(parsed.content, parsed.summary, JSON.stringify(parsed.tags), JSON.stringify({ ...parsed.metadata }), sourceHash, now, existing.id);
296
+ return 'created'; // "created" in the sense of "produced an up-to-date memory"
297
+ }
298
+ catch {
299
+ return 'error';
300
+ }
301
+ }
302
+ // Brand-new file — create a fresh memory planet using insertMemory directly
303
+ // so we can pass scanner-specific source fields (source, source_path, source_hash).
304
+ try {
305
+ const cfg = getConfig();
306
+ const type = parsed.type;
307
+ const impact = IMPACT_DEFAULTS[type] ?? 0.35;
308
+ const now = new Date().toISOString();
309
+ const rec = recencyScore(null, now, cfg.decayHalfLifeHours);
310
+ const freq = frequencyScore(0, cfg.frequencySaturationPoint);
311
+ const importance = Math.min(1.0, cfg.weights.recency * rec +
312
+ cfg.weights.frequency * freq +
313
+ cfg.weights.impact * impact +
314
+ cfg.weights.relevance * 0);
315
+ const distance = importanceToDistance(importance);
316
+ const raw = parsed.content.trim();
317
+ const summary = parsed.summary
318
+ ? parsed.summary
319
+ : raw.slice(0, 50).trimEnd() + (raw.length > 50 ? '\u2026' : '');
320
+ insertMemory({
321
+ id: randomUUID(),
322
+ project,
323
+ content: parsed.content,
324
+ summary,
325
+ type,
326
+ tags: parsed.tags,
327
+ distance,
328
+ importance,
329
+ velocity: 0,
330
+ impact,
331
+ access_count: 0,
332
+ last_accessed_at: null,
333
+ metadata: parsed.metadata,
334
+ source: 'scanner',
335
+ source_path: entry.path,
336
+ source_hash: sourceHash,
337
+ created_at: now,
338
+ updated_at: now,
339
+ deleted_at: null,
340
+ });
341
+ return 'created';
342
+ }
343
+ catch {
344
+ return 'error';
345
+ }
346
+ }
347
+ // -------------------------------------------------------------------------
348
+ // Private: insert git commit memories using insertMemory directly
349
+ // -------------------------------------------------------------------------
350
+ _insertGitMemories(repoPath, project) {
351
+ const gitMemories = scanGitHistory(repoPath, 50);
352
+ const cfg = getConfig();
353
+ let count = 0;
354
+ for (const mem of gitMemories) {
355
+ try {
356
+ const type = mem.type;
357
+ const impact = IMPACT_DEFAULTS[type] ?? 0.5;
358
+ const now = new Date().toISOString();
359
+ const rec = recencyScore(null, now, cfg.decayHalfLifeHours);
360
+ const freq = frequencyScore(0, cfg.frequencySaturationPoint);
361
+ const importance = Math.min(1.0, cfg.weights.recency * rec +
362
+ cfg.weights.frequency * freq +
363
+ cfg.weights.impact * impact +
364
+ cfg.weights.relevance * 0);
365
+ const distance = importanceToDistance(importance);
366
+ insertMemory({
367
+ id: randomUUID(),
368
+ project,
369
+ content: mem.content,
370
+ summary: mem.summary,
371
+ type,
372
+ tags: mem.tags,
373
+ distance,
374
+ importance,
375
+ velocity: 0,
376
+ impact,
377
+ access_count: 0,
378
+ last_accessed_at: null,
379
+ metadata: mem.metadata,
380
+ source: 'git',
381
+ source_path: repoPath,
382
+ source_hash: null,
383
+ created_at: now,
384
+ updated_at: now,
385
+ deleted_at: null,
386
+ });
387
+ count++;
388
+ }
389
+ catch {
390
+ // Best-effort
391
+ }
392
+ }
393
+ return count;
394
+ }
395
+ // -------------------------------------------------------------------------
396
+ // Private: ensure a data_sources row exists for a path
397
+ // -------------------------------------------------------------------------
398
+ async _ensureDataSource(absPath) {
399
+ const existing = getDataSourceByPath(absPath);
400
+ if (existing)
401
+ return;
402
+ insertDataSource({
403
+ id: randomUUID(),
404
+ path: absPath,
405
+ type: 'local',
406
+ status: 'active',
407
+ last_scanned_at: null,
408
+ file_count: 0,
409
+ total_size: 0,
410
+ config: this.config,
411
+ });
412
+ }
413
+ }
414
+ // ---------------------------------------------------------------------------
415
+ // Module-level convenience: list all registered sources
416
+ // ---------------------------------------------------------------------------
417
+ export function listDataSources() {
418
+ return getAllDataSources();
419
+ }
420
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/scanner/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { FileEntry, ScanConfig } from '../types.js';
2
+ /**
3
+ * Returns true if any segment of `filePath` matches any exclude pattern.
4
+ * Supports simple glob wildcards (* and ?) via a regex conversion.
5
+ */
6
+ export declare function isExcluded(filePath: string, patterns: string[]): boolean;
7
+ /**
8
+ * Build a deterministic dedup hash from the file path and mtime.
9
+ * We intentionally do NOT hash file content — that would be slow for large files.
10
+ * mtime changes whenever the file is modified, which is sufficient for our dedup.
11
+ */
12
+ export declare function buildSourceHash(filePath: string, mtimeMs: number): string;
13
+ /**
14
+ * Recursively collect all files under `dirPath` that pass config filters.
15
+ * Uses `opendir` (streaming, no giant readdir arrays) so it is memory-efficient.
16
+ */
17
+ export declare function collectFiles(dirPath: string, config: ScanConfig, _depth?: number): Promise<FileEntry[]>;
18
+ /** Read a file as UTF-8 text. Returns null on error (binary, permission, etc.). */
19
+ export declare function readTextFile(filePath: string): Promise<string | null>;
20
+ export type WatchCallback = (event: 'add' | 'change' | 'rename', filePath: string) => void;
21
+ /**
22
+ * Watch `dirPath` recursively using Node's built-in `fs.watch`.
23
+ * Returns a cleanup function that stops watching when called.
24
+ */
25
+ export declare function watchDirectory(dirPath: string, config: ScanConfig, callback: WatchCallback): () => void;
26
+ /** Collect all files and also return the base name of skipped/binary files for reporting. */
27
+ export declare function collectFilesWithStats(dirPath: string, config: ScanConfig): Promise<{
28
+ entries: FileEntry[];
29
+ skippedCount: number;
30
+ }>;
31
+ /** Return true if the filename looks like a typical dotfile to skip. */
32
+ export declare function isDotfile(name: string): boolean;
33
+ //# sourceMappingURL=filesystem.d.ts.map
@@ -0,0 +1,203 @@
1
+ import { opendir, readFile, stat } from 'node:fs/promises';
2
+ import { join, extname, basename } from 'node:path';
3
+ import { createHash } from 'node:crypto';
4
+ // ---------------------------------------------------------------------------
5
+ // Exclude-pattern matching (no external deps)
6
+ // ---------------------------------------------------------------------------
7
+ /**
8
+ * Returns true if any segment of `filePath` matches any exclude pattern.
9
+ * Supports simple glob wildcards (* and ?) via a regex conversion.
10
+ */
11
+ export function isExcluded(filePath, patterns) {
12
+ // Normalise to forward slashes for consistent matching
13
+ const normalised = filePath.replace(/\\/g, '/');
14
+ const segments = normalised.split('/');
15
+ for (const pattern of patterns) {
16
+ if (!pattern)
17
+ continue;
18
+ // Convert glob wildcard to regex
19
+ const reSource = pattern
20
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&') // escape regex special chars (except * and ?)
21
+ .replace(/\*/g, '.*')
22
+ .replace(/\?/g, '.');
23
+ const re = new RegExp(`^${reSource}$`, 'i');
24
+ // Match against every path segment
25
+ if (segments.some((seg) => re.test(seg)))
26
+ return true;
27
+ // Also match against the whole normalised path
28
+ if (re.test(normalised))
29
+ return true;
30
+ }
31
+ return false;
32
+ }
33
+ // ---------------------------------------------------------------------------
34
+ // Source hash
35
+ // ---------------------------------------------------------------------------
36
+ /**
37
+ * Build a deterministic dedup hash from the file path and mtime.
38
+ * We intentionally do NOT hash file content — that would be slow for large files.
39
+ * mtime changes whenever the file is modified, which is sufficient for our dedup.
40
+ */
41
+ export function buildSourceHash(filePath, mtimeMs) {
42
+ return createHash('sha1')
43
+ .update(`${filePath}:${mtimeMs}`)
44
+ .digest('hex')
45
+ .slice(0, 16);
46
+ }
47
+ // ---------------------------------------------------------------------------
48
+ // Recursive directory walker
49
+ // ---------------------------------------------------------------------------
50
+ /**
51
+ * Recursively collect all files under `dirPath` that pass config filters.
52
+ * Uses `opendir` (streaming, no giant readdir arrays) so it is memory-efficient.
53
+ */
54
+ export async function collectFiles(dirPath, config, _depth = 0) {
55
+ const entries = [];
56
+ // Guard: never descend into excluded directories
57
+ if (isExcluded(dirPath, config.excludePatterns))
58
+ return entries;
59
+ let dir;
60
+ try {
61
+ dir = await opendir(dirPath);
62
+ }
63
+ catch {
64
+ // Permission denied or not a directory — skip silently
65
+ return entries;
66
+ }
67
+ const extSet = new Set(config.fileExtensions.map((e) => e.toLowerCase()));
68
+ for await (const dirent of dir) {
69
+ const fullPath = join(dirPath, dirent.name);
70
+ if (dirent.isDirectory()) {
71
+ if (!isExcluded(dirent.name, config.excludePatterns)) {
72
+ const sub = await collectFiles(fullPath, config, _depth + 1);
73
+ entries.push(...sub);
74
+ }
75
+ continue;
76
+ }
77
+ if (!dirent.isFile())
78
+ continue;
79
+ const ext = extname(dirent.name).toLowerCase();
80
+ if (!extSet.has(ext))
81
+ continue;
82
+ if (isExcluded(dirent.name, config.excludePatterns))
83
+ continue;
84
+ if (isExcluded(fullPath, config.excludePatterns))
85
+ continue;
86
+ // Stat to get size and mtime
87
+ let fileStat;
88
+ try {
89
+ fileStat = await stat(fullPath);
90
+ }
91
+ catch {
92
+ continue;
93
+ }
94
+ if (fileStat.size === 0 || fileStat.size > config.maxFileSize)
95
+ continue;
96
+ entries.push({
97
+ path: fullPath,
98
+ size: fileStat.size,
99
+ mtimeMs: fileStat.mtimeMs,
100
+ extension: ext,
101
+ });
102
+ }
103
+ return entries;
104
+ }
105
+ // ---------------------------------------------------------------------------
106
+ // File content reader
107
+ // ---------------------------------------------------------------------------
108
+ /** Read a file as UTF-8 text. Returns null on error (binary, permission, etc.). */
109
+ export async function readTextFile(filePath) {
110
+ try {
111
+ const buf = await readFile(filePath);
112
+ // Quick binary-detection heuristic: if the first 8 KB contains a null byte,
113
+ // treat it as binary and skip.
114
+ const sample = buf.slice(0, 8192);
115
+ for (let i = 0; i < sample.length; i++) {
116
+ if (sample[i] === 0)
117
+ return null;
118
+ }
119
+ return buf.toString('utf8');
120
+ }
121
+ catch {
122
+ return null;
123
+ }
124
+ }
125
+ // ---------------------------------------------------------------------------
126
+ // File watcher (node:fs.watch wrapper)
127
+ // ---------------------------------------------------------------------------
128
+ import { watch as fsWatch } from 'node:fs';
129
+ /**
130
+ * Watch `dirPath` recursively using Node's built-in `fs.watch`.
131
+ * Returns a cleanup function that stops watching when called.
132
+ */
133
+ export function watchDirectory(dirPath, config, callback) {
134
+ const extSet = new Set(config.fileExtensions.map((e) => e.toLowerCase()));
135
+ const watcher = fsWatch(dirPath, { recursive: true }, (event, filename) => {
136
+ if (!filename)
137
+ return;
138
+ const fullPath = join(dirPath, filename);
139
+ const ext = extname(filename).toLowerCase();
140
+ if (!extSet.has(ext))
141
+ return;
142
+ if (isExcluded(fullPath, config.excludePatterns))
143
+ return;
144
+ const mapped = event === 'rename' ? 'rename' : 'change';
145
+ callback(mapped, fullPath);
146
+ });
147
+ return () => watcher.close();
148
+ }
149
+ /** Collect all files and also return the base name of skipped/binary files for reporting. */
150
+ export async function collectFilesWithStats(dirPath, config) {
151
+ // We walk twice only to gather skip count — optimise by tracking inline
152
+ let skippedCount = 0;
153
+ async function walk(p) {
154
+ const result = [];
155
+ if (isExcluded(p, config.excludePatterns))
156
+ return result;
157
+ let dir;
158
+ try {
159
+ dir = await opendir(p);
160
+ }
161
+ catch {
162
+ return result;
163
+ }
164
+ const extSet = new Set(config.fileExtensions.map((e) => e.toLowerCase()));
165
+ for await (const dirent of dir) {
166
+ const fullPath = join(p, dirent.name);
167
+ if (dirent.isDirectory()) {
168
+ if (!isExcluded(dirent.name, config.excludePatterns)) {
169
+ result.push(...(await walk(fullPath)));
170
+ }
171
+ continue;
172
+ }
173
+ if (!dirent.isFile())
174
+ continue;
175
+ const ext = extname(dirent.name).toLowerCase();
176
+ if (!extSet.has(ext) || isExcluded(dirent.name, config.excludePatterns) || isExcluded(fullPath, config.excludePatterns)) {
177
+ skippedCount++;
178
+ continue;
179
+ }
180
+ let fileStat;
181
+ try {
182
+ fileStat = await stat(fullPath);
183
+ }
184
+ catch {
185
+ skippedCount++;
186
+ continue;
187
+ }
188
+ if (fileStat.size === 0 || fileStat.size > config.maxFileSize) {
189
+ skippedCount++;
190
+ continue;
191
+ }
192
+ result.push({ path: fullPath, size: fileStat.size, mtimeMs: fileStat.mtimeMs, extension: ext });
193
+ }
194
+ return result;
195
+ }
196
+ const entries = await walk(dirPath);
197
+ return { entries, skippedCount };
198
+ }
199
+ /** Return true if the filename looks like a typical dotfile to skip. */
200
+ export function isDotfile(name) {
201
+ return basename(name).startsWith('.');
202
+ }
203
+ //# sourceMappingURL=filesystem.js.map