@mnemonik/shared 7.154.3 → 7.155.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.
Files changed (46) hide show
  1. package/dist/ast/grammars.d.ts +4 -2
  2. package/dist/ast/grammars.d.ts.map +1 -1
  3. package/dist/ast/grammars.js +45 -3
  4. package/dist/ast/grammars.js.map +1 -1
  5. package/dist/codeScanner.d.ts +26 -329
  6. package/dist/codeScanner.d.ts.map +1 -1
  7. package/dist/codeScanner.js +65 -1141
  8. package/dist/codeScanner.js.map +1 -1
  9. package/dist/fileSelection.d.ts +451 -0
  10. package/dist/fileSelection.d.ts.map +1 -0
  11. package/dist/fileSelection.js +1256 -0
  12. package/dist/fileSelection.js.map +1 -0
  13. package/dist/hookTrace.d.ts +40 -0
  14. package/dist/hookTrace.d.ts.map +1 -1
  15. package/dist/hookTrace.js +126 -0
  16. package/dist/hookTrace.js.map +1 -1
  17. package/dist/index.d.ts +3 -2
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +3 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/runtimeAssets.d.ts +12 -0
  22. package/dist/runtimeAssets.d.ts.map +1 -0
  23. package/dist/runtimeAssets.js +43 -0
  24. package/dist/runtimeAssets.js.map +1 -0
  25. package/dist/sourceRedaction.d.ts +45 -1
  26. package/dist/sourceRedaction.d.ts.map +1 -1
  27. package/dist/sourceRedaction.js +62 -22
  28. package/dist/sourceRedaction.js.map +1 -1
  29. package/dist/sourceRedactionPool.d.ts +198 -0
  30. package/dist/sourceRedactionPool.d.ts.map +1 -0
  31. package/dist/sourceRedactionPool.js +756 -0
  32. package/dist/sourceRedactionPool.js.map +1 -0
  33. package/dist/vueSource.d.ts +0 -2
  34. package/dist/vueSource.d.ts.map +1 -1
  35. package/dist/vueSource.js +8 -19
  36. package/dist/vueSource.js.map +1 -1
  37. package/package.json +5 -1
  38. package/src/ast/grammars.ts +52 -7
  39. package/src/codeScanner.ts +121 -1321
  40. package/src/fileSelection.ts +1440 -0
  41. package/src/hookTrace.ts +143 -0
  42. package/src/index.ts +31 -2
  43. package/src/runtimeAssets.ts +49 -0
  44. package/src/sourceRedaction.ts +85 -20
  45. package/src/sourceRedactionPool.ts +877 -0
  46. package/src/vueSource.ts +8 -23
@@ -0,0 +1,451 @@
1
+ /**
2
+ * Which files the scanner indexes under a root: the walk, every ignore rule it
3
+ * applies (.gitignore, .mnemonikignore, .gitattributes, the built-in and soft
4
+ * defaults, secret files, protected paths, nested projects) and its limits.
5
+ *
6
+ * It is its own module, apart from codeScanner.ts, so a caller that only needs
7
+ * the selection (the CLI's estimate before the first scan) loads none of the
8
+ * chunker, the source redaction or the compilers behind it (26 Oct 2026: the
9
+ * public CLI bundled the Svelte and Vue compilers into its estimate). It is the
10
+ * scanner's own selection, not a copy: CodeScanner extends FileSelector.
11
+ * Exported alone as `@mnemonik/shared/file-selection`.
12
+ */
13
+ import { type Stats } from 'fs';
14
+ import { type Ignore } from 'ignore';
15
+ import { type GitattributesLayer, type GitattributesRule, type IgnoreFileVerdict, type IndexClassification, type IndexExclusionReason } from './indexClassifier.js';
16
+ /**
17
+ * Run one filesystem operation within FILE_OP_TIMEOUT_MS, and once more within
18
+ * FILE_OP_RETRY_TIMEOUT_MS if the first attempt timed out. Other errors are not
19
+ * retried: ENOENT, EACCES and the containment refusals answer the same way twice.
20
+ */
21
+ export declare function fileOp<T>(op: () => Promise<T>, label: string, budgets?: {
22
+ first: number;
23
+ retry: number;
24
+ }): Promise<T>;
25
+ /** A file's identity on disk as the stat cache compares it. */
26
+ export interface FileStatTuple {
27
+ size: number;
28
+ mtimeMs: number;
29
+ ctimeMs: number;
30
+ ino: number;
31
+ }
32
+ export declare function statTuple(stats: Stats): FileStatTuple;
33
+ /**
34
+ * Mutable per-walk state threaded through the recursive traversals.
35
+ * `complete` starts true and is latched false by any caught fs error
36
+ * (readdir failure, per-entry lstat/realpath/stat throw) - meaning the
37
+ * result may be missing entries that still exist on disk. Callers that
38
+ * derive *removals* from a walk must treat an incomplete walk as
39
+ * untrustworthy; adds/changes are unaffected.
40
+ */
41
+ export interface WalkState {
42
+ complete: boolean;
43
+ /** Entries a filesystem error or timeout left out of this walk (a blind spot). */
44
+ unreadable?: number;
45
+ /** The first few of them, with the error code or message. */
46
+ unreadableSamples?: Array<{
47
+ path: string;
48
+ reason: string;
49
+ }>;
50
+ /**
51
+ * The directories on the path from the root to the one being walked, by
52
+ * device and inode. Symlinks are never followed, so a loop needs a bind
53
+ * mount or a filesystem cycle; reaching one of its own ancestors ends it
54
+ * (what the old depth cap of 10 was for) and makes the walk incomplete.
55
+ * Only ancestors count: the same directory reached twice without a loop (a
56
+ * bind mount in two places) is walked both times, since skipping it would
57
+ * leave a walk that looks complete and the removed-files diff would delete
58
+ * that subtree.
59
+ */
60
+ ancestors?: Set<string>;
61
+ /**
62
+ * When present, every rule file the walk reads is recorded here by
63
+ * directory, so the daemon can upload the lines it actually applied.
64
+ */
65
+ rules?: Map<string, IndexRuleFiles>;
66
+ }
67
+ /** An entry the walk could not read: the walk is no longer complete, and it counts. */
68
+ export declare function markUnreadable(walk: WalkState, path: string, error: unknown): void;
69
+ /**
70
+ * Enter a directory unless it is one of its own ancestors. Returns the
71
+ * function that leaves it again (call it once its subtree is walked), or null
72
+ * for a cycle, which is not walked and makes the walk incomplete, so the
73
+ * removed-files diff never treats what it did not see as deleted.
74
+ */
75
+ export declare function enterDirectory(walk: WalkState, path: string): Promise<(() => void) | null>;
76
+ /** The rule-file lines one directory contributed to a walk. */
77
+ export interface IndexRuleFiles {
78
+ /** .gitignore + .mnemonikignore lines, unioned, in read order. */
79
+ ignore: string[];
80
+ /** .gitattributes lines, in read order. */
81
+ attributes: string[];
82
+ }
83
+ /**
84
+ * A file a walk declined to index because a soft default excluded it.
85
+ * `stage: 'content'` means the file was read and its content decided.
86
+ */
87
+ export interface IndexExcludedFile {
88
+ /** Root-relative POSIX path, spelled as a chunk's `filePath`. */
89
+ path: string;
90
+ reason: IndexExclusionReason;
91
+ rule: string;
92
+ stage: 'path' | 'content';
93
+ }
94
+ /**
95
+ * Directory basenames the scanner never indexes, across every ecosystem -
96
+ * generated output, package-manager/build caches, and vendored deps. SINGLE
97
+ * SOURCE OF TRUTH: the scanner package's discovery + watch walks import this
98
+ * (`packages/scanner/src/{discovery,watcher}.ts`) and `src/core/utils.ts`
99
+ * derives from it, so every ignore surface agrees instead of drifting (the
100
+ * pre-2026-07 state had four divergent copies). Add only UNAMBIGUOUS names -
101
+ * a project's own `.gitignore` / `.mnemonikignore` covers the ambiguous,
102
+ * project-specific cases (`vendor/`, custom dirs) hierarchically.
103
+ */
104
+ export declare const BUILT_IN_IGNORE_DIRS: readonly string[];
105
+ /**
106
+ * Non-directory built-in ignore patterns (files, globs, and the segment-
107
+ * anchored fixture rule), combined with BUILT_IN_IGNORE_DIRS to form
108
+ * DEFAULT_OPTIONS.ignorePatterns. See `shouldIgnore` for match semantics.
109
+ *
110
+ * `/tests/fixtures/*`: leading '/' = segment-anchored - matches tests/fixtures/
111
+ * at the path root or immediately after a '/', so nested packages/x/tests/
112
+ * fixtures/ are excluded but integration-tests/fixtures/ is NOT. Fixture
113
+ * snapshot dirs were being indexed as real source and outranking the genuine
114
+ * file in code_search. (Subprojects and linked worktrees are excluded
115
+ * generically by the isProjectBoundary rule below, not a path convention.)
116
+ */
117
+ export declare const BUILT_IN_IGNORE_FILE_PATTERNS: readonly string[];
118
+ /**
119
+ * True when `relPath` names a file whose contents are credentials. Applied
120
+ * by `shouldIgnore` (every walker: directory scan, explicit file lists, and
121
+ * the authority-file walk) and by `makeIgnoreMatcher` (server-side backstop
122
+ * for older or misbehaving daemons). Accepts OS-native or POSIX separators.
123
+ */
124
+ export declare function isSecretFile(relPath: string): boolean;
125
+ /**
126
+ * Every file type the scanner will chunk, by extension. Matched
127
+ * case-INSENSITIVELY (see `isIncludedExtension`), so lowercase spellings here
128
+ * also cover `.R`, `.SQL`, `.PS1` and the uppercase `.C`/`.H` of older trees.
129
+ *
130
+ * Exported because this list IS the product's language coverage: a project
131
+ * written in something absent from it indexes zero code, and `code_search`
132
+ * reports that as "no indexed matches" rather than "I do not read this
133
+ * language" - invisible to the developer and to us. Coverage is a separate
134
+ * dial from parse quality: a language with no structured extractor falls
135
+ * through to `chunkRaw`, which is what Go, Java and C already get in
136
+ * production, and crude chunks beat no chunks by an enormous margin.
137
+ *
138
+ * Data formats (`.json`, `.yaml`, `.toml`, `.lock`) are deliberately absent:
139
+ * the ones that carry meaning are already collected verbatim by
140
+ * `AUTHORITY_FILE_MATCHERS`, and blanket-indexing the extension would pull in
141
+ * lockfiles and generated output. `.tfvars` is absent for a different reason -
142
+ * `terraform.tfvars` is a conventional home for provider credentials and
143
+ * `isSecretFile` now excludes it outright.
144
+ */
145
+ export declare const DEFAULT_INCLUDE_EXTENSIONS: readonly string[];
146
+ /**
147
+ * Cursor's rule files: `.cursorrules`, and any `.mdc` under `.cursor/rules/`.
148
+ * Cursor is a launch host, so these are instruction files, the same class as
149
+ * CLAUDE.md and AGENTS.md. Neither spelling can come in through
150
+ * `DEFAULT_INCLUDE_EXTENSIONS`: `.cursorrules` has no extension (`extname`
151
+ * answers ''), and `.mdc` is not a format this scanner reads anywhere else. So
152
+ * both are admitted by path at the chunking gate (`isChunkable`) and chunked as
153
+ * Markdown. They are not `.md` paths, so their whole-file content lands in
154
+ * project_source_content.
155
+ *
156
+ * Matched at any depth, because Cursor also reads nested `.cursor/rules`
157
+ * directories in a monorepo. Case-sensitive, as Cursor spells them. Accepts
158
+ * OS-native or POSIX separators.
159
+ */
160
+ export declare function isCursorRulePath(relPath: string): boolean;
161
+ /**
162
+ * Language string for a file path or a bare extension, `'unknown'` when the
163
+ * extension is unmapped. A free function rather than a method because callers
164
+ * that never scan anything (AST grammar resolution, server-side symbol
165
+ * preference) need the same answer without constructing a scanner.
166
+ */
167
+ export declare function languageForExtension(filePathOrExt: string): string;
168
+ /** Content-only build authority, shared by scanner collection and server admission. */
169
+ export declare const XCODE_AUTHORITY_PATH: RegExp;
170
+ export declare const isXcodeAuthorityPath: (path: string) => boolean;
171
+ /** Exact CMake build entrypoint; ordinary .txt files remain outside collection. */
172
+ export declare const CMAKE_LISTS_PATH: RegExp;
173
+ export declare const isCmakeListsPath: (path: string) => boolean;
174
+ /**
175
+ * True when `relPath` is collected verbatim as authority content and must not
176
+ * also be chunked. Accepts OS-native or POSIX separators.
177
+ */
178
+ export declare function isAuthorityOnlyPath(relPath: string): boolean;
179
+ /**
180
+ * Matchers for authority manifest / config / CI files whose verbatim content
181
+ * is collected by `collectAuthorityFiles` and pushed to the server for
182
+ * doc-truth Fingerprint parsing. Operates on relative, forward-slash paths.
183
+ */
184
+ export declare const AUTHORITY_FILE_MATCHERS: Array<(relPath: string) => boolean>;
185
+ /**
186
+ * Segment-anchored match for `tests/fixtures/` - matches at the path root or
187
+ * immediately after a `/`. Indexing no longer uses it (the fixture rule is the
188
+ * soft default in indexClassifier.ts, which a project can override); it stays
189
+ * for query-time hygiene over rows indexed before that rule existed
190
+ * (`MemoryManager.searchCodePointers`).
191
+ */
192
+ export declare const FIXTURE_PATH_RE: RegExp;
193
+ export declare function isFixturePath(path: string): boolean;
194
+ /**
195
+ * Server-side ignore matcher for defense-in-depth path dropping on /scan/push,
196
+ * /scan/reconcile, and file_push. Combines the universal BUILT_IN_IGNORE_DIRS
197
+ * (segment-exact) with a project's uploaded .gitignore/.mnemonikignore patterns
198
+ * (gitignore syntax via the `ignore` package). Built once per request and reused
199
+ * across all pushed paths (root-relative, forward-slash). This does NOT replace
200
+ * the daemon's precise hierarchical walk - it backstops it so a buggy, stale, or
201
+ * malicious client cannot index a path the project ignores, making .mnemonikignore
202
+ * a hard guarantee rather than best-effort.
203
+ */
204
+ export declare function makeIgnoreMatcher(extraPatterns?: readonly string[]): (relPath: string) => boolean;
205
+ /**
206
+ * True when `dirPath` is the root of a DIFFERENT repository than the one
207
+ * being scanned: it carries its own `.git` entry. A `.git` FILE marks a
208
+ * linked git worktree (wherever the user keeps them) or a submodule
209
+ * checkout; a `.git` DIRECTORY marks a nested clone. Files under such a
210
+ * boundary belong to that repository's own project identity - scanning them
211
+ * into the parent project is wrong attribution for any codebase, so the
212
+ * walkers skip these subtrees. Convention-free: this is git's own marker,
213
+ * not a host/tool path layout. The scan ROOT is exempt by construction
214
+ * (callers only test child directories).
215
+ */
216
+ export declare function isGitBoundary(dirPath: string): Promise<boolean>;
217
+ /**
218
+ * There is no depth limit on a project's walk. A cap of 10 folders guarded the
219
+ * first scanner (February 2026) against runaway recursion through symlinked
220
+ * trees; symlinks have long been skipped by every walker (confineSymlink,
221
+ * skipSymlink), and WalkState.ancestors ends a bind-mount cycle. The cap only hid
222
+ * ordinary deep files (Java and Kotlin package trees, Next.js monorepos), and the
223
+ * watcher, which had no cap, then pushed a deep file the next walk reported removed.
224
+ */
225
+ /**
226
+ * True when `dirPath` starts a subtree that is not this project's to index.
227
+ * A file belongs to the project of the nearest `.mnemonik.json` above it, so:
228
+ * - a folder with its own identity file is a subproject, indexed as its own
229
+ * project, never also into this one. A file naming the enclosing project
230
+ * (`enclosingProjectId`, a copy) is the same project, and a
231
+ * configuration-only file (no `projectId`) names none: neither is a
232
+ * boundary. A file that cannot be read as an identity still is, and so is
233
+ * a copy in a full clone (its own `.git` DIRECTORY): a second checkout of
234
+ * the project, whose files would otherwise be indexed twice;
235
+ * - a linked worktree (a `.git` FILE whose git directory has a `commondir`,
236
+ * or is missing) is another checkout of a project, indexed from its main
237
+ * checkout.
238
+ * A nested clone or a submodule without its own `.mnemonik.json` belongs to
239
+ * this project and is indexed with it (its `.git` directory is ignored by the
240
+ * built-in rules). The scan ROOT is exempt by construction (callers only test
241
+ * child directories).
242
+ */
243
+ export declare function isProjectBoundary(dirPath: string, enclosingProjectId?: string): Promise<boolean>;
244
+ /**
245
+ * The project a scan root's own identity file names, or undefined: the
246
+ * enclosing project for isProjectBoundary.
247
+ */
248
+ export declare function rootProjectId(rootPath: string): Promise<string | undefined>;
249
+ /**
250
+ * True when `dirPath` is the top of a linked git worktree: its `.git` is a
251
+ * FILE pointing at a git directory that has a `commondir`, or at one that no
252
+ * longer exists (a pruned or moved worktree, or one made on another machine):
253
+ * nothing shows it is this project's own. A submodule's `.git` file points at
254
+ * `.git/modules/<name>`, which exists and has no `commondir`.
255
+ */
256
+ export declare function isLinkedWorktreeCheckout(dirPath: string): Promise<boolean>;
257
+ /**
258
+ * One directory's compiled ignore-file rules, keyed by its path relative to
259
+ * the scan root (`''` = root). A stack of these, root->leaf, is threaded
260
+ * through the walkers; `ignoredByStack` tests deepest-first so a nested
261
+ * `.gitignore` overrides a shallower one (git precedence).
262
+ */
263
+ export interface IgnoreLayer {
264
+ baseRel: string;
265
+ /** Compiled .gitignore + .mnemonikignore; null when this directory has neither. */
266
+ ig: Ignore | null;
267
+ /** Any `!pattern` line: a path under here may be re-included from a soft default. */
268
+ hasNegation: boolean;
269
+ /** Parsed .gitattributes; null when this directory has none. */
270
+ attributes: GitattributesRule[] | null;
271
+ }
272
+ export type IgnoreStack = IgnoreLayer[];
273
+ /** The selection settings CodeScanner's options share (see ScanOptions). */
274
+ export interface FileSelectionOptions {
275
+ ignorePatterns?: string[];
276
+ includeExtensions?: string[];
277
+ /** Additional protected paths for isolated homes/tests; defaults can only be extended. */
278
+ protectedPaths?: readonly string[];
279
+ }
280
+ /** The ignore patterns applied when a caller gives none. */
281
+ export declare const DEFAULT_IGNORE_PATTERNS: readonly string[];
282
+ /**
283
+ * The scanner's file selection. `listFilesWithStatus` is the inventory every
284
+ * scan and the CLI's estimate count; CodeScanner adds reading and chunking.
285
+ */
286
+ export declare class FileSelector {
287
+ protected readonly selection: Required<FileSelectionOptions>;
288
+ protected readonly protectedPaths: readonly string[];
289
+ /** Cumulative observations since this scanner started, including repeated walks. */
290
+ readonly skipReasons: {
291
+ symlink_skipped: number;
292
+ };
293
+ protected skipSymlink(stats: Stats): boolean;
294
+ /**
295
+ * `includeExtensions` folded to lowercase for matching. The gate used to
296
+ * compare `extname()` verbatim while `detectLanguage` lowercased, so a
297
+ * project spelling its files the canonical way - `.R` for R, `.SQL`/`.PS1`
298
+ * on Windows, `.C`/`.H` in older C trees - indexed zero of them and nothing
299
+ * said why.
300
+ */
301
+ protected readonly includeExtensionSet: ReadonlySet<string>;
302
+ constructor(options?: FileSelectionOptions);
303
+ protected isProtected(path: string): boolean;
304
+ protected isProtectedRead(path: string): Promise<boolean>;
305
+ /**
306
+ * The chunkable-file gate, shared by every walker and by the explicit
307
+ * file-list path so all three agree on what exists. `relPath` is the path
308
+ * relative to the scan root (OS-native separators accepted).
309
+ */
310
+ protected isChunkable(absOrRelPath: string, relPath: string): boolean;
311
+ /**
312
+ * Read `.gitignore` + `.mnemonikignore` + `.gitattributes` in `absDir` and
313
+ * compile them into one layer (the two ignore files are unioned -
314
+ * `.mnemonikignore` is just an additional gitignore-syntax exclusion list).
315
+ * Returns null when none of the three exists. FAIL-CLOSED: a non-ENOENT read
316
+ * error (EISDIR, EACCES, timeout) latches `walk.complete = false` so callers
317
+ * deriving *removals* from the walk treat it as untrustworthy rather than
318
+ * silently ignoring nothing.
319
+ */
320
+ protected loadIgnoreLayer(absDir: string, baseRel: string, walk: WalkState, rootPath: string): Promise<IgnoreLayer | null>;
321
+ /**
322
+ * What the .gitignore / .mnemonikignore layers in `stack` say about
323
+ * `entryRel` (relative to the scan root). Tested deepest-first: the first
324
+ * layer that explicitly ignores OR re-includes (via a `!negation`) wins,
325
+ * matching git's rule that a deeper `.gitignore` overrides a shallower one.
326
+ * Directories are tested with a trailing slash so `foo/` dir-only rules
327
+ * match the directory itself. `'unignored'` means a negation matched this
328
+ * path itself; a re-included parent directory leaves its files `'unmatched'`.
329
+ */
330
+ protected ignoreVerdict(entryRel: string, isDir: boolean, stack: IgnoreStack): IgnoreFileVerdict;
331
+ protected ignoredByStack(entryRel: string, isDir: boolean, stack: IgnoreStack): boolean;
332
+ protected static attributeLayers(stack: IgnoreStack): GitattributesLayer[];
333
+ /**
334
+ * Skip this directory? User-ignored directories are skipped as before. A
335
+ * directory a soft default excludes (vendor/, generated/, ...) is pruned too -
336
+ * nothing under it can be indexed - UNLESS a rule above it could re-include
337
+ * something inside it: a `!pattern` or a `-linguist-*` attribute. Then the
338
+ * walk descends and judges each file, so the override works. Rule files
339
+ * INSIDE a pruned directory are never read: a vendored library's own
340
+ * .gitignore negations are not the project's intent.
341
+ */
342
+ protected skipDirectory(relPath: string, stack: IgnoreStack): boolean;
343
+ /**
344
+ * The index decision for a file from its path and the rule stack: `null`
345
+ * when a user ignore rule excludes it, otherwise the classifier's answer
346
+ * (precedence in indexClassifier.ts). Content rules are the caller's next
347
+ * step for an included path without an `override`.
348
+ */
349
+ protected classifyEntry(relPath: string, stack: IgnoreStack): IndexClassification | null;
350
+ /**
351
+ * Reconstruct the root->parent ignore-layer stack for a single file, for the
352
+ * incremental `scanFiles` path (which has a flat file list, not a recursive
353
+ * walk). Layers are memoized per directory in `cache` across a batch.
354
+ */
355
+ protected buildIgnoreStackForFile(fileRel: string, rootPath: string, cache: Map<string, IgnoreLayer | null>, walk: WalkState): Promise<IgnoreStack>;
356
+ /**
357
+ * A predicate for walkers outside this class (the daemon's file watcher):
358
+ * true when the walk would descend into `absDir`, by the same rules
359
+ * `listFiles` applies - user ignore files and soft-default directories,
360
+ * with their overrides. Rule files are read once per directory for the life
361
+ * of the returned predicate; take a fresh one after a rule file changes.
362
+ * Directories outside `rootPath`, and the root itself, always pass.
363
+ */
364
+ directoryFilter(rootPath: string): (absDir: string) => Promise<boolean>;
365
+ /**
366
+ * True when any directory strictly between `rootPath` and `filePath`
367
+ * starts a subtree this project does not index (see isProjectBoundary). Paths that don't
368
+ * resolve under `rootPath` are not judged here (return false) - the
369
+ * server's path validation handles escapes.
370
+ */
371
+ protected insideNestedGitBoundary(filePath: string, rootPath: string): Promise<boolean>;
372
+ protected rootProjectIds: Map<string, {
373
+ at: number;
374
+ projectId: Promise<string | undefined>;
375
+ }>;
376
+ /**
377
+ * isProjectBoundary, with the scan root's own project as the enclosing one.
378
+ * The root's file is read at most once a minute: a walk tests many folders,
379
+ * and a project relinked since is seen on the next scan.
380
+ */
381
+ protected isBoundary(dirPath: string, rootPath: string): Promise<boolean>;
382
+ protected canonicalRoots: Map<string, string>;
383
+ protected canonicalRoot(root: string): Promise<string>;
384
+ static containsCanonical(candidate: string, root: string, paths?: {
385
+ sep: "/" | "\\";
386
+ resolve: (...paths: string[]) => string;
387
+ }, caseInsensitive?: boolean): boolean;
388
+ protected confineSymlink(fullPath: string, canonicalRoot: string): Promise<boolean>;
389
+ readConfined(fullPath: string, canonicalRoot: string, enumerated?: Stats): Promise<string>;
390
+ /**
391
+ * Enumerate scan-eligible relative file paths under `rootPath` without
392
+ * reading or chunking content. Same walk + ignorePatterns + extension
393
+ * filter as `scanDirectory`, but bounded to O(file count) directory ops
394
+ * - cheap enough to call on every periodic reconcile tick.
395
+ *
396
+ * Returns relative paths normalized against `rootPath`, matching the
397
+ * shape the server stores in `memories.metadata->>'filePath'`. Use this
398
+ * for the scanner reconciliation channel (`POST /api/v1/scan/reconcile`).
399
+ *
400
+ * Defensive: skips any path whose `relative()` result contains a `..`
401
+ * traversal segment (can happen when a symlink resolves under root but
402
+ * the readdir entry path doesn't normalize cleanly). The server's
403
+ * `safeScanPath` rejects such paths; filtering here keeps a single
404
+ * malformed entry from failing the entire reconcile push.
405
+ */
406
+ listFiles(rootPath: string): Promise<string[]>;
407
+ /**
408
+ * Completeness-aware variant of `listFiles`. `complete === false` means
409
+ * an fs error truncated part of the walk - the returned paths may be
410
+ * missing files that still exist on disk. The daemon's reconcile channel
411
+ * MUST NOT derive removedFiles from an incomplete inventory (a truncated
412
+ * walk otherwise reads as mass deletion and deprecates live memories).
413
+ */
414
+ listFilesWithStatus(rootPath: string): Promise<{
415
+ paths: string[];
416
+ complete: boolean;
417
+ /**
418
+ * Listed paths a project rule re-included (a `!pattern` or a
419
+ * `-linguist-*` attribute). Content rules do not apply to them; every
420
+ * other listed path still answers to the content rules when read.
421
+ */
422
+ overridden: Set<string>;
423
+ /** Entries a filesystem error or timeout left out. */
424
+ unreadable: number;
425
+ /** The first few unreadable entries, with the reason. */
426
+ unreadableSamples: Array<{
427
+ path: string;
428
+ reason: string;
429
+ }>;
430
+ /** Each listed path's size, times and inode, for the daemon's stat cache. */
431
+ stats: Map<string, FileStatTuple>;
432
+ }>;
433
+ /**
434
+ * The rule files the last complete `listFilesWithStatus` walk of `rootPath`
435
+ * applied, by directory ('' = root). Null before the first complete walk.
436
+ * The daemon uploads these lines so the server judges paths as it did.
437
+ */
438
+ indexRuleFiles(rootPath: string): Promise<ReadonlyMap<string, IndexRuleFiles> | null>;
439
+ protected readonly ruleFilesByRoot: Map<string, Map<string, IndexRuleFiles>>;
440
+ protected traversePaths(currentPath: string, rootPath: string, out: string[], depth: number, walk: WalkState, stack?: IgnoreStack, overridden?: Set<string>, statsOut?: Map<string, FileStatTuple>): Promise<void>;
441
+ /**
442
+ * Check if path should be ignored
443
+ * Fixed glob-to-regex conversion and substring matching.
444
+ * - Escape regex special chars before replacing * with .*
445
+ * - Replace ALL * occurrences (not just the first)
446
+ * - For non-glob patterns, match on path segments to avoid false positives
447
+ * (e.g., '.env' should not match '.environment.ts')
448
+ */
449
+ protected shouldIgnore(path: string): boolean;
450
+ }
451
+ //# sourceMappingURL=fileSelection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileSelection.d.ts","sourceRoot":"","sources":["../src/fileSelection.ts"],"names":[],"mappings":"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"}