@mnemonik/shared 6.5.0 → 6.5.2

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/dist/index.d.ts CHANGED
@@ -7,7 +7,6 @@
7
7
  export { MCP_INSTRUCTIONS, MCP_INSTRUCTIONS_RAW, getMcpInstructions } from './instructions.js';
8
8
  export { USAGE_GUIDE } from './usageGuide.js';
9
9
  export { CodeScanner, AUTHORITY_FILE_MATCHERS, BUILT_IN_IGNORE_DIRS, makeIgnoreMatcher, isGitBoundary, FIXTURE_PATH_RE, isFixturePath, type CodeChunk, type ScanOptions, } from './codeScanner.js';
10
- export { FileSystemReader, getFileSystemReader, type ScanFilesResult, type ChangedFilesResult, type FileData, } from './FileSystemReader.js';
11
10
  export { SECRET_PATTERNS, SECRET_REDACTION_PLACEHOLDER, scrubSecrets } from './secretPatterns.js';
12
11
  export { FETCH_TIMEOUT_MS, TELEMETRY_TIMEOUT_MS, POST_TOOL_TIMEOUT_MS, MCP_PRECHECK_TIMEOUT_MS, withHookTimeout, } from './hookTimeouts.js';
13
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EACL,WAAW,EACX,uBAAuB,EACvB,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,aAAa,EACb,KAAK,SAAS,EACd,KAAK,WAAW,GACjB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,QAAQ,GACd,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,4BAA4B,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAClG,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,GAChB,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EACL,WAAW,EACX,uBAAuB,EACvB,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,aAAa,EACb,KAAK,SAAS,EACd,KAAK,WAAW,GACjB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,4BAA4B,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAClG,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,GAChB,MAAM,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -7,7 +7,6 @@
7
7
  export { MCP_INSTRUCTIONS, MCP_INSTRUCTIONS_RAW, getMcpInstructions } from './instructions.js';
8
8
  export { USAGE_GUIDE } from './usageGuide.js';
9
9
  export { CodeScanner, AUTHORITY_FILE_MATCHERS, BUILT_IN_IGNORE_DIRS, makeIgnoreMatcher, isGitBoundary, FIXTURE_PATH_RE, isFixturePath, } from './codeScanner.js';
10
- export { FileSystemReader, getFileSystemReader, } from './FileSystemReader.js';
11
10
  export { SECRET_PATTERNS, SECRET_REDACTION_PLACEHOLDER, scrubSecrets } from './secretPatterns.js';
12
11
  export { FETCH_TIMEOUT_MS, TELEMETRY_TIMEOUT_MS, POST_TOOL_TIMEOUT_MS, MCP_PRECHECK_TIMEOUT_MS, withHookTimeout, } from './hookTimeouts.js';
13
12
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EACL,WAAW,EACX,uBAAuB,EACvB,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,aAAa,GAGd,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,gBAAgB,EAChB,mBAAmB,GAIpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,4BAA4B,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAClG,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,GAChB,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EACL,WAAW,EACX,uBAAuB,EACvB,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,aAAa,GAGd,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,4BAA4B,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAClG,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,GAChB,MAAM,mBAAmB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mnemonik/shared",
3
- "version": "6.5.0",
3
+ "version": "6.5.2",
4
4
  "description": "Shared constants and utilities for Mnemonik packages",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/index.ts CHANGED
@@ -18,13 +18,6 @@ export {
18
18
  type CodeChunk,
19
19
  type ScanOptions,
20
20
  } from './codeScanner.js';
21
- export {
22
- FileSystemReader,
23
- getFileSystemReader,
24
- type ScanFilesResult,
25
- type ChangedFilesResult,
26
- type FileData,
27
- } from './FileSystemReader.js';
28
21
  export { SECRET_PATTERNS, SECRET_REDACTION_PLACEHOLDER, scrubSecrets } from './secretPatterns.js';
29
22
  export {
30
23
  FETCH_TIMEOUT_MS,
@@ -1,60 +0,0 @@
1
- /**
2
- * FileSystemReader — Encapsulates all filesystem operations for scanning.
3
- *
4
- * This module is the boundary between disk I/O and the rest of the system.
5
- * In a cloud deployment, a lightweight client-side agent uses this module
6
- * locally and pushes results to the server's indexing API; the server
7
- * itself never touches the filesystem.
8
- */
9
- import { CodeChunk } from './codeScanner.js';
10
- export interface FileData {
11
- /** Relative path from project root */
12
- path: string;
13
- /** SHA-256 hash (content + size + mtime) */
14
- hash: string;
15
- }
16
- export interface ScanFilesResult {
17
- files: FileData[];
18
- chunks: CodeChunk[];
19
- }
20
- export interface ChangedFilesResult {
21
- changed: FileData[];
22
- chunks: CodeChunk[];
23
- skipped: number;
24
- }
25
- export declare class FileSystemReader {
26
- /**
27
- * Recursively list all scannable text files in a project directory.
28
- * Returns absolute paths.
29
- */
30
- readProjectFiles(dirPath: string): Promise<string[]>;
31
- /**
32
- * Compute SHA-256 hash for a file (content + size + mtime).
33
- * Returns empty string on error or if the file exceeds MAX_FILE_SIZE.
34
- */
35
- calculateFileHash(filePath: string): Promise<string>;
36
- /**
37
- * Scan a directory and return files + extracted code chunks.
38
- */
39
- scanDirectory(projectPath: string): Promise<ScanFilesResult>;
40
- /**
41
- * Determine which files have changed compared to known hashes.
42
- * Returns changed files plus their extracted code chunks.
43
- */
44
- detectChangedFiles(projectPath: string, knownHashes: Map<string, string>): Promise<ChangedFilesResult>;
45
- /**
46
- * Read file content with timeout protection.
47
- */
48
- readFileContent(filePath: string): Promise<string | null>;
49
- /**
50
- * Stat a file with timeout protection.
51
- */
52
- statFile(filePath: string): Promise<{
53
- mtime: Date;
54
- size: number;
55
- } | null>;
56
- private traverseDirectory;
57
- private isTextFile;
58
- }
59
- export declare function getFileSystemReader(): FileSystemReader;
60
- //# sourceMappingURL=FileSystemReader.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"FileSystemReader.d.ts","sourceRoot":"","sources":["../src/FileSystemReader.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,EAAe,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAqE1D,MAAM,WAAW,QAAQ;IACvB,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,gBAAgB;IAC3B;;;OAGG;IACG,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAM1D;;;OAGG;IACG,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAiC1D;;OAEG;IACG,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAoBlE;;;OAGG;IACG,kBAAkB,CACtB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,kBAAkB,CAAC;IA0C9B;;OAEG;IACG,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAY/D;;OAEG;IACG,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;YAejE,iBAAiB;IAqC/B,OAAO,CAAC,UAAU;CAKnB;AAID,wBAAgB,mBAAmB,IAAI,gBAAgB,CAGtD"}
@@ -1,241 +0,0 @@
1
- /**
2
- * FileSystemReader — Encapsulates all filesystem operations for scanning.
3
- *
4
- * This module is the boundary between disk I/O and the rest of the system.
5
- * In a cloud deployment, a lightweight client-side agent uses this module
6
- * locally and pushes results to the server's indexing API; the server
7
- * itself never touches the filesystem.
8
- */
9
- import { readdir, readFile, stat } from 'fs/promises';
10
- import { join, relative, isAbsolute } from 'path';
11
- import { createHash } from 'crypto';
12
- import { debug as logDebug, warn as logWarn, info as logInfo } from './logger.js';
13
- import { withTimeout } from './asyncUtils.js';
14
- import { CodeScanner } from './codeScanner.js';
15
- const FILE_OP_TIMEOUT_MS = 5000;
16
- const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB
17
- // NOTE: this SKIP_DIRS is NOT an index gate — FileSystemReader's walk methods
18
- // (scanDirectory/detectChangedFiles) have no live caller; only readFileContent
19
- // (single file, no ignore) is used, for JIT citation verification. The
20
- // authoritative built-in list is BUILT_IN_IGNORE_DIRS in codeScanner.ts. Left
21
- // as-is (dead) rather than rewired to avoid churn on unused code.
22
- const SKIP_DIRS = new Set([
23
- 'node_modules',
24
- '.git',
25
- 'dist',
26
- 'build',
27
- '.next',
28
- 'coverage',
29
- 'venv',
30
- '.venv',
31
- 'env',
32
- '.env',
33
- '__pycache__',
34
- '.cache',
35
- 'target',
36
- '.tox',
37
- '.mypy_cache',
38
- '.pytest_cache',
39
- ]);
40
- const TEXT_EXTENSIONS = new Set([
41
- 'ts',
42
- 'tsx',
43
- 'js',
44
- 'jsx',
45
- 'py',
46
- 'java',
47
- 'cpp',
48
- 'c',
49
- 'h',
50
- 'hpp',
51
- 'go',
52
- 'rs',
53
- 'php',
54
- 'rb',
55
- 'swift',
56
- 'kt',
57
- 'scala',
58
- 'sh',
59
- 'bash',
60
- 'sql',
61
- 'html',
62
- 'css',
63
- 'scss',
64
- 'sass',
65
- 'less',
66
- 'json',
67
- 'yaml',
68
- 'yml',
69
- 'toml',
70
- 'xml',
71
- 'md',
72
- 'txt',
73
- 'vue',
74
- 'svelte',
75
- 'astro',
76
- ]);
77
- const SKIP_PATTERNS = ['.min.js', '.min.css', '.bundle.js', '.legacy.js', '.map'];
78
- export class FileSystemReader {
79
- /**
80
- * Recursively list all scannable text files in a project directory.
81
- * Returns absolute paths.
82
- */
83
- async readProjectFiles(dirPath) {
84
- const files = [];
85
- await this.traverseDirectory(dirPath, files);
86
- return files;
87
- }
88
- /**
89
- * Compute SHA-256 hash for a file (content + size + mtime).
90
- * Returns empty string on error or if the file exceeds MAX_FILE_SIZE.
91
- */
92
- async calculateFileHash(filePath) {
93
- try {
94
- const stats = await withTimeout(stat(filePath), FILE_OP_TIMEOUT_MS, `stat timed out: ${filePath}`);
95
- if (stats.size > MAX_FILE_SIZE) {
96
- logDebug('Skipping file hash — exceeds size limit', {
97
- file: filePath,
98
- size: stats.size,
99
- });
100
- return '';
101
- }
102
- const content = await withTimeout(readFile(filePath, 'utf-8'), FILE_OP_TIMEOUT_MS, `readFile timed out: ${filePath}`);
103
- const hashInput = `${content}\n${stats.size}\n${stats.mtime.getTime()}`;
104
- return createHash('sha256').update(hashInput).digest('hex');
105
- }
106
- catch (error) {
107
- logWarn('Failed to calculate file hash', {
108
- file: filePath,
109
- error: error.message,
110
- });
111
- return '';
112
- }
113
- }
114
- /**
115
- * Scan a directory and return files + extracted code chunks.
116
- */
117
- async scanDirectory(projectPath) {
118
- const scanner = new CodeScanner();
119
- const chunks = await scanner.scanDirectory(projectPath);
120
- const files = [];
121
- const seen = new Set();
122
- for (const chunk of chunks) {
123
- if (!seen.has(chunk.filePath)) {
124
- seen.add(chunk.filePath);
125
- const absPath = isAbsolute(chunk.filePath)
126
- ? chunk.filePath
127
- : join(projectPath, chunk.filePath);
128
- const hash = await this.calculateFileHash(absPath);
129
- files.push({ path: chunk.filePath, hash });
130
- }
131
- }
132
- return { files, chunks };
133
- }
134
- /**
135
- * Determine which files have changed compared to known hashes.
136
- * Returns changed files plus their extracted code chunks.
137
- */
138
- async detectChangedFiles(projectPath, knownHashes) {
139
- const allFiles = await this.readProjectFiles(projectPath);
140
- const changed = [];
141
- let skipped = 0;
142
- for (const absPath of allFiles) {
143
- try {
144
- const hash = await this.calculateFileHash(absPath);
145
- const relPath = relative(projectPath, absPath);
146
- const known = knownHashes.get(absPath) ?? knownHashes.get(relPath);
147
- if (!known || known !== hash) {
148
- changed.push({ path: absPath, hash });
149
- }
150
- else {
151
- skipped++;
152
- }
153
- }
154
- catch (error) {
155
- logWarn('Failed to process file for incremental scan', {
156
- file: absPath,
157
- error: error.message,
158
- });
159
- }
160
- }
161
- logInfo('Incremental scan analysis', {
162
- totalFiles: allFiles.length,
163
- filesToScan: changed.length,
164
- filesSkipped: skipped,
165
- });
166
- if (changed.length === 0) {
167
- return { changed: [], chunks: [], skipped };
168
- }
169
- const scanner = new CodeScanner();
170
- const chunks = await scanner.scanFiles(changed.map((f) => f.path), projectPath);
171
- return { changed, chunks, skipped };
172
- }
173
- /**
174
- * Read file content with timeout protection.
175
- */
176
- async readFileContent(filePath) {
177
- try {
178
- return await withTimeout(readFile(filePath, 'utf-8'), FILE_OP_TIMEOUT_MS, `readFile timed out: ${filePath}`);
179
- }
180
- catch {
181
- return null;
182
- }
183
- }
184
- /**
185
- * Stat a file with timeout protection.
186
- */
187
- async statFile(filePath) {
188
- try {
189
- const s = await withTimeout(stat(filePath), FILE_OP_TIMEOUT_MS, `stat timed out: ${filePath}`);
190
- return { mtime: s.mtime, size: s.size };
191
- }
192
- catch {
193
- return null;
194
- }
195
- }
196
- // ── private helpers ─────────────────────────────────────────────────
197
- async traverseDirectory(dirPath, files) {
198
- try {
199
- const entries = await withTimeout(readdir(dirPath, { withFileTypes: true }), FILE_OP_TIMEOUT_MS, `readdir timed out: ${dirPath}`);
200
- for (const entry of entries) {
201
- const fullPath = join(dirPath, entry.name);
202
- if (entry.isDirectory()) {
203
- if (SKIP_DIRS.has(entry.name))
204
- continue;
205
- await this.traverseDirectory(fullPath, files);
206
- }
207
- else if (entry.isFile() && this.isTextFile(entry.name)) {
208
- try {
209
- const fileStat = await withTimeout(stat(fullPath), FILE_OP_TIMEOUT_MS, `stat timed out: ${fullPath}`);
210
- if (fileStat.size <= 100 * 1024) {
211
- files.push(fullPath);
212
- }
213
- }
214
- catch {
215
- // can't stat → skip
216
- }
217
- }
218
- }
219
- }
220
- catch (error) {
221
- logWarn('Failed to read directory', {
222
- path: dirPath,
223
- error: error.message,
224
- });
225
- }
226
- }
227
- isTextFile(filename) {
228
- if (SKIP_PATTERNS.some((p) => filename.endsWith(p)))
229
- return false;
230
- const ext = filename.split('.').pop()?.toLowerCase();
231
- return TEXT_EXTENSIONS.has(ext || '');
232
- }
233
- }
234
- /** Singleton for convenience */
235
- let _instance = null;
236
- export function getFileSystemReader() {
237
- if (!_instance)
238
- _instance = new FileSystemReader();
239
- return _instance;
240
- }
241
- //# sourceMappingURL=FileSystemReader.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"FileSystemReader.js","sourceRoot":"","sources":["../src/FileSystemReader.ts"],"names":[],"mappings":"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"}
@@ -1,304 +0,0 @@
1
- /**
2
- * FileSystemReader — Encapsulates all filesystem operations for scanning.
3
- *
4
- * This module is the boundary between disk I/O and the rest of the system.
5
- * In a cloud deployment, a lightweight client-side agent uses this module
6
- * locally and pushes results to the server's indexing API; the server
7
- * itself never touches the filesystem.
8
- */
9
-
10
- import { readdir, readFile, stat } from 'fs/promises';
11
- import { join, relative, isAbsolute } from 'path';
12
- import { createHash } from 'crypto';
13
- import { debug as logDebug, warn as logWarn, info as logInfo } from './logger.js';
14
- import { withTimeout } from './asyncUtils.js';
15
- import { CodeScanner, CodeChunk } from './codeScanner.js';
16
-
17
- const FILE_OP_TIMEOUT_MS = 5000;
18
- const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB
19
-
20
- // NOTE: this SKIP_DIRS is NOT an index gate — FileSystemReader's walk methods
21
- // (scanDirectory/detectChangedFiles) have no live caller; only readFileContent
22
- // (single file, no ignore) is used, for JIT citation verification. The
23
- // authoritative built-in list is BUILT_IN_IGNORE_DIRS in codeScanner.ts. Left
24
- // as-is (dead) rather than rewired to avoid churn on unused code.
25
- const SKIP_DIRS = new Set([
26
- 'node_modules',
27
- '.git',
28
- 'dist',
29
- 'build',
30
- '.next',
31
- 'coverage',
32
- 'venv',
33
- '.venv',
34
- 'env',
35
- '.env',
36
- '__pycache__',
37
- '.cache',
38
- 'target',
39
- '.tox',
40
- '.mypy_cache',
41
- '.pytest_cache',
42
- ]);
43
-
44
- const TEXT_EXTENSIONS = new Set([
45
- 'ts',
46
- 'tsx',
47
- 'js',
48
- 'jsx',
49
- 'py',
50
- 'java',
51
- 'cpp',
52
- 'c',
53
- 'h',
54
- 'hpp',
55
- 'go',
56
- 'rs',
57
- 'php',
58
- 'rb',
59
- 'swift',
60
- 'kt',
61
- 'scala',
62
- 'sh',
63
- 'bash',
64
- 'sql',
65
- 'html',
66
- 'css',
67
- 'scss',
68
- 'sass',
69
- 'less',
70
- 'json',
71
- 'yaml',
72
- 'yml',
73
- 'toml',
74
- 'xml',
75
- 'md',
76
- 'txt',
77
- 'vue',
78
- 'svelte',
79
- 'astro',
80
- ]);
81
-
82
- const SKIP_PATTERNS = ['.min.js', '.min.css', '.bundle.js', '.legacy.js', '.map'];
83
-
84
- export interface FileData {
85
- /** Relative path from project root */
86
- path: string;
87
- /** SHA-256 hash (content + size + mtime) */
88
- hash: string;
89
- }
90
-
91
- export interface ScanFilesResult {
92
- files: FileData[];
93
- chunks: CodeChunk[];
94
- }
95
-
96
- export interface ChangedFilesResult {
97
- changed: FileData[];
98
- chunks: CodeChunk[];
99
- skipped: number;
100
- }
101
-
102
- export class FileSystemReader {
103
- /**
104
- * Recursively list all scannable text files in a project directory.
105
- * Returns absolute paths.
106
- */
107
- async readProjectFiles(dirPath: string): Promise<string[]> {
108
- const files: string[] = [];
109
- await this.traverseDirectory(dirPath, files);
110
- return files;
111
- }
112
-
113
- /**
114
- * Compute SHA-256 hash for a file (content + size + mtime).
115
- * Returns empty string on error or if the file exceeds MAX_FILE_SIZE.
116
- */
117
- async calculateFileHash(filePath: string): Promise<string> {
118
- try {
119
- const stats = await withTimeout(
120
- stat(filePath),
121
- FILE_OP_TIMEOUT_MS,
122
- `stat timed out: ${filePath}`
123
- );
124
-
125
- if (stats.size > MAX_FILE_SIZE) {
126
- logDebug('Skipping file hash — exceeds size limit', {
127
- file: filePath,
128
- size: stats.size,
129
- });
130
- return '';
131
- }
132
-
133
- const content = await withTimeout(
134
- readFile(filePath, 'utf-8'),
135
- FILE_OP_TIMEOUT_MS,
136
- `readFile timed out: ${filePath}`
137
- );
138
-
139
- const hashInput = `${content}\n${stats.size}\n${stats.mtime.getTime()}`;
140
- return createHash('sha256').update(hashInput).digest('hex');
141
- } catch (error) {
142
- logWarn('Failed to calculate file hash', {
143
- file: filePath,
144
- error: (error as Error).message,
145
- });
146
- return '';
147
- }
148
- }
149
-
150
- /**
151
- * Scan a directory and return files + extracted code chunks.
152
- */
153
- async scanDirectory(projectPath: string): Promise<ScanFilesResult> {
154
- const scanner = new CodeScanner();
155
- const chunks = await scanner.scanDirectory(projectPath);
156
-
157
- const files: FileData[] = [];
158
- const seen = new Set<string>();
159
- for (const chunk of chunks) {
160
- if (!seen.has(chunk.filePath)) {
161
- seen.add(chunk.filePath);
162
- const absPath = isAbsolute(chunk.filePath)
163
- ? chunk.filePath
164
- : join(projectPath, chunk.filePath);
165
- const hash = await this.calculateFileHash(absPath);
166
- files.push({ path: chunk.filePath, hash });
167
- }
168
- }
169
-
170
- return { files, chunks };
171
- }
172
-
173
- /**
174
- * Determine which files have changed compared to known hashes.
175
- * Returns changed files plus their extracted code chunks.
176
- */
177
- async detectChangedFiles(
178
- projectPath: string,
179
- knownHashes: Map<string, string>
180
- ): Promise<ChangedFilesResult> {
181
- const allFiles = await this.readProjectFiles(projectPath);
182
- const changed: FileData[] = [];
183
- let skipped = 0;
184
-
185
- for (const absPath of allFiles) {
186
- try {
187
- const hash = await this.calculateFileHash(absPath);
188
- const relPath = relative(projectPath, absPath);
189
- const known = knownHashes.get(absPath) ?? knownHashes.get(relPath);
190
- if (!known || known !== hash) {
191
- changed.push({ path: absPath, hash });
192
- } else {
193
- skipped++;
194
- }
195
- } catch (error) {
196
- logWarn('Failed to process file for incremental scan', {
197
- file: absPath,
198
- error: (error as Error).message,
199
- });
200
- }
201
- }
202
-
203
- logInfo('Incremental scan analysis', {
204
- totalFiles: allFiles.length,
205
- filesToScan: changed.length,
206
- filesSkipped: skipped,
207
- });
208
-
209
- if (changed.length === 0) {
210
- return { changed: [], chunks: [], skipped };
211
- }
212
-
213
- const scanner = new CodeScanner();
214
- const chunks = await scanner.scanFiles(
215
- changed.map((f) => f.path),
216
- projectPath
217
- );
218
-
219
- return { changed, chunks, skipped };
220
- }
221
-
222
- /**
223
- * Read file content with timeout protection.
224
- */
225
- async readFileContent(filePath: string): Promise<string | null> {
226
- try {
227
- return await withTimeout(
228
- readFile(filePath, 'utf-8'),
229
- FILE_OP_TIMEOUT_MS,
230
- `readFile timed out: ${filePath}`
231
- );
232
- } catch {
233
- return null;
234
- }
235
- }
236
-
237
- /**
238
- * Stat a file with timeout protection.
239
- */
240
- async statFile(filePath: string): Promise<{ mtime: Date; size: number } | null> {
241
- try {
242
- const s = await withTimeout(
243
- stat(filePath),
244
- FILE_OP_TIMEOUT_MS,
245
- `stat timed out: ${filePath}`
246
- );
247
- return { mtime: s.mtime, size: s.size };
248
- } catch {
249
- return null;
250
- }
251
- }
252
-
253
- // ── private helpers ─────────────────────────────────────────────────
254
-
255
- private async traverseDirectory(dirPath: string, files: string[]): Promise<void> {
256
- try {
257
- const entries = await withTimeout(
258
- readdir(dirPath, { withFileTypes: true }),
259
- FILE_OP_TIMEOUT_MS,
260
- `readdir timed out: ${dirPath}`
261
- );
262
-
263
- for (const entry of entries) {
264
- const fullPath = join(dirPath, entry.name);
265
-
266
- if (entry.isDirectory()) {
267
- if (SKIP_DIRS.has(entry.name)) continue;
268
- await this.traverseDirectory(fullPath, files);
269
- } else if (entry.isFile() && this.isTextFile(entry.name)) {
270
- try {
271
- const fileStat = await withTimeout(
272
- stat(fullPath),
273
- FILE_OP_TIMEOUT_MS,
274
- `stat timed out: ${fullPath}`
275
- );
276
- if (fileStat.size <= 100 * 1024) {
277
- files.push(fullPath);
278
- }
279
- } catch {
280
- // can't stat → skip
281
- }
282
- }
283
- }
284
- } catch (error) {
285
- logWarn('Failed to read directory', {
286
- path: dirPath,
287
- error: (error as Error).message,
288
- });
289
- }
290
- }
291
-
292
- private isTextFile(filename: string): boolean {
293
- if (SKIP_PATTERNS.some((p) => filename.endsWith(p))) return false;
294
- const ext = filename.split('.').pop()?.toLowerCase();
295
- return TEXT_EXTENSIONS.has(ext || '');
296
- }
297
- }
298
-
299
- /** Singleton for convenience */
300
- let _instance: FileSystemReader | null = null;
301
- export function getFileSystemReader(): FileSystemReader {
302
- if (!_instance) _instance = new FileSystemReader();
303
- return _instance;
304
- }