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.
- package/LICENSE +21 -0
- package/README.md +362 -0
- package/dist/api/routes/analytics.d.ts +15 -0
- package/dist/api/routes/analytics.js +131 -0
- package/dist/api/routes/analytics.js.map +1 -0
- package/dist/api/routes/conflicts.d.ts +12 -0
- package/dist/api/routes/conflicts.js +67 -0
- package/dist/api/routes/conflicts.js.map +1 -0
- package/dist/api/routes/consolidation.d.ts +11 -0
- package/dist/api/routes/consolidation.js +63 -0
- package/dist/api/routes/consolidation.js.map +1 -0
- package/dist/api/routes/constellation.d.ts +4 -0
- package/dist/api/routes/constellation.js +84 -0
- package/dist/api/routes/constellation.js.map +1 -0
- package/dist/api/routes/memories.d.ts +4 -0
- package/dist/api/routes/memories.js +219 -0
- package/dist/api/routes/memories.js.map +1 -0
- package/dist/api/routes/observations.d.ts +10 -0
- package/dist/api/routes/observations.js +42 -0
- package/dist/api/routes/observations.js.map +1 -0
- package/dist/api/routes/orbit.d.ts +4 -0
- package/dist/api/routes/orbit.js +71 -0
- package/dist/api/routes/orbit.js.map +1 -0
- package/dist/api/routes/projects.d.ts +15 -0
- package/dist/api/routes/projects.js +121 -0
- package/dist/api/routes/projects.js.map +1 -0
- package/dist/api/routes/scan.d.ts +4 -0
- package/dist/api/routes/scan.js +403 -0
- package/dist/api/routes/scan.js.map +1 -0
- package/dist/api/routes/sun.d.ts +4 -0
- package/dist/api/routes/sun.js +43 -0
- package/dist/api/routes/sun.js.map +1 -0
- package/dist/api/routes/system.d.ts +4 -0
- package/dist/api/routes/system.js +70 -0
- package/dist/api/routes/system.js.map +1 -0
- package/dist/api/routes/temporal.d.ts +13 -0
- package/dist/api/routes/temporal.js +82 -0
- package/dist/api/routes/temporal.js.map +1 -0
- package/dist/api/server.d.ts +2 -0
- package/dist/api/server.js +99 -0
- package/dist/api/server.js.map +1 -0
- package/dist/api/websocket.d.ts +53 -0
- package/dist/api/websocket.js +168 -0
- package/dist/api/websocket.js.map +1 -0
- package/dist/cli/index.d.ts +12 -0
- package/dist/cli/index.js +35 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +10 -0
- package/dist/cli/init.js +163 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/engine/analytics.d.ts +93 -0
- package/dist/engine/analytics.js +437 -0
- package/dist/engine/analytics.js.map +1 -0
- package/dist/engine/conflict.d.ts +54 -0
- package/dist/engine/conflict.js +322 -0
- package/dist/engine/conflict.js.map +1 -0
- package/dist/engine/consolidation.d.ts +83 -0
- package/dist/engine/consolidation.js +368 -0
- package/dist/engine/consolidation.js.map +1 -0
- package/dist/engine/constellation.d.ts +66 -0
- package/dist/engine/constellation.js +382 -0
- package/dist/engine/constellation.js.map +1 -0
- package/dist/engine/corona.d.ts +53 -0
- package/dist/engine/corona.js +181 -0
- package/dist/engine/corona.js.map +1 -0
- package/dist/engine/embedding.d.ts +44 -0
- package/dist/engine/embedding.js +168 -0
- package/dist/engine/embedding.js.map +1 -0
- package/dist/engine/gravity.d.ts +63 -0
- package/dist/engine/gravity.js +121 -0
- package/dist/engine/gravity.js.map +1 -0
- package/dist/engine/multiproject.d.ts +75 -0
- package/dist/engine/multiproject.js +241 -0
- package/dist/engine/multiproject.js.map +1 -0
- package/dist/engine/observation.d.ts +82 -0
- package/dist/engine/observation.js +357 -0
- package/dist/engine/observation.js.map +1 -0
- package/dist/engine/orbit.d.ts +91 -0
- package/dist/engine/orbit.js +249 -0
- package/dist/engine/orbit.js.map +1 -0
- package/dist/engine/planet.d.ts +64 -0
- package/dist/engine/planet.js +432 -0
- package/dist/engine/planet.js.map +1 -0
- package/dist/engine/procedural.d.ts +71 -0
- package/dist/engine/procedural.js +259 -0
- package/dist/engine/procedural.js.map +1 -0
- package/dist/engine/quality.d.ts +48 -0
- package/dist/engine/quality.js +245 -0
- package/dist/engine/quality.js.map +1 -0
- package/dist/engine/repository.d.ts +79 -0
- package/dist/engine/repository.js +13 -0
- package/dist/engine/repository.js.map +1 -0
- package/dist/engine/sun.d.ts +61 -0
- package/dist/engine/sun.js +240 -0
- package/dist/engine/sun.js.map +1 -0
- package/dist/engine/temporal.d.ts +67 -0
- package/dist/engine/temporal.js +283 -0
- package/dist/engine/temporal.js.map +1 -0
- package/dist/engine/types.d.ts +179 -0
- package/dist/engine/types.js +27 -0
- package/dist/engine/types.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/connector-registry.d.ts +20 -0
- package/dist/mcp/connector-registry.js +35 -0
- package/dist/mcp/connector-registry.js.map +1 -0
- package/dist/mcp/server.d.ts +13 -0
- package/dist/mcp/server.js +242 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools/daemon-tool.d.ts +16 -0
- package/dist/mcp/tools/daemon-tool.js +58 -0
- package/dist/mcp/tools/daemon-tool.js.map +1 -0
- package/dist/mcp/tools/ingestion-tools.d.ts +20 -0
- package/dist/mcp/tools/ingestion-tools.js +34 -0
- package/dist/mcp/tools/ingestion-tools.js.map +1 -0
- package/dist/mcp/tools/memory-tools.d.ts +122 -0
- package/dist/mcp/tools/memory-tools.js +1037 -0
- package/dist/mcp/tools/memory-tools.js.map +1 -0
- package/dist/scanner/cloud/github.d.ts +34 -0
- package/dist/scanner/cloud/github.js +260 -0
- package/dist/scanner/cloud/github.js.map +1 -0
- package/dist/scanner/cloud/google-drive.d.ts +30 -0
- package/dist/scanner/cloud/google-drive.js +289 -0
- package/dist/scanner/cloud/google-drive.js.map +1 -0
- package/dist/scanner/cloud/notion.d.ts +33 -0
- package/dist/scanner/cloud/notion.js +231 -0
- package/dist/scanner/cloud/notion.js.map +1 -0
- package/dist/scanner/cloud/slack.d.ts +38 -0
- package/dist/scanner/cloud/slack.js +282 -0
- package/dist/scanner/cloud/slack.js.map +1 -0
- package/dist/scanner/cloud/types.d.ts +73 -0
- package/dist/scanner/cloud/types.js +9 -0
- package/dist/scanner/cloud/types.js.map +1 -0
- package/dist/scanner/index.d.ts +35 -0
- package/dist/scanner/index.js +420 -0
- package/dist/scanner/index.js.map +1 -0
- package/dist/scanner/local/filesystem.d.ts +33 -0
- package/dist/scanner/local/filesystem.js +203 -0
- package/dist/scanner/local/filesystem.js.map +1 -0
- package/dist/scanner/local/git.d.ts +24 -0
- package/dist/scanner/local/git.js +161 -0
- package/dist/scanner/local/git.js.map +1 -0
- package/dist/scanner/local/parsers/code.d.ts +3 -0
- package/dist/scanner/local/parsers/code.js +127 -0
- package/dist/scanner/local/parsers/code.js.map +1 -0
- package/dist/scanner/local/parsers/index.d.ts +11 -0
- package/dist/scanner/local/parsers/index.js +24 -0
- package/dist/scanner/local/parsers/index.js.map +1 -0
- package/dist/scanner/local/parsers/json-parser.d.ts +3 -0
- package/dist/scanner/local/parsers/json-parser.js +117 -0
- package/dist/scanner/local/parsers/json-parser.js.map +1 -0
- package/dist/scanner/local/parsers/markdown.d.ts +3 -0
- package/dist/scanner/local/parsers/markdown.js +120 -0
- package/dist/scanner/local/parsers/markdown.js.map +1 -0
- package/dist/scanner/local/parsers/text.d.ts +3 -0
- package/dist/scanner/local/parsers/text.js +41 -0
- package/dist/scanner/local/parsers/text.js.map +1 -0
- package/dist/scanner/metadata-scanner.d.ts +67 -0
- package/dist/scanner/metadata-scanner.js +356 -0
- package/dist/scanner/metadata-scanner.js.map +1 -0
- package/dist/scanner/types.d.ts +47 -0
- package/dist/scanner/types.js +19 -0
- package/dist/scanner/types.js.map +1 -0
- package/dist/service/daemon.d.ts +23 -0
- package/dist/service/daemon.js +105 -0
- package/dist/service/daemon.js.map +1 -0
- package/dist/service/scheduler.d.ts +73 -0
- package/dist/service/scheduler.js +281 -0
- package/dist/service/scheduler.js.map +1 -0
- package/dist/storage/database.d.ts +10 -0
- package/dist/storage/database.js +265 -0
- package/dist/storage/database.js.map +1 -0
- package/dist/storage/queries.d.ts +85 -0
- package/dist/storage/queries.js +865 -0
- package/dist/storage/queries.js.map +1 -0
- package/dist/storage/sqlite-repository.d.ts +32 -0
- package/dist/storage/sqlite-repository.js +68 -0
- package/dist/storage/sqlite-repository.js.map +1 -0
- package/dist/storage/vec.d.ts +62 -0
- package/dist/storage/vec.js +111 -0
- package/dist/storage/vec.js.map +1 -0
- package/dist/utils/config.d.ts +5 -0
- package/dist/utils/config.js +60 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/logger.d.ts +36 -0
- package/dist/utils/logger.js +86 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/time.d.ts +21 -0
- package/dist/utils/time.js +42 -0
- package/dist/utils/time.js.map +1 -0
- package/dist/utils/tokenizer.d.ts +13 -0
- package/dist/utils/tokenizer.js +46 -0
- package/dist/utils/tokenizer.js.map +1 -0
- package/package.json +77 -0
- package/scripts/check-node.mjs +36 -0
- 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
|