UNPKG

socraticode

Version:

SocratiCode — MCP server for local codebase indexing, semantic search, and code dependency graphs. All private, all local via Docker.

105 lines • 5.78 kB
/** * Read up to `maxBytes` bytes from the start of a file and decode as UTF-8. * Reads only the head (not the whole file) so scanning large extensionless * binaries/data files stays cheap. Opens non-blocking and throws for a * non-regular file (FIFO/socket/device) so a mistyped or swapped path can't * block the open; may also throw on open/read errors. */ export declare function readFileHead(absolutePath: string, maxBytes?: number): Promise<string>; /** * Detect the canonical extension of source text already held in memory, scoring * the same window {@link readFileHead} reads from disk: the first * {@link DETECT_HEAD_BYTES} **bytes** of UTF-8, not characters. * * The byte window is load-bearing, not incidental. `detectExtensionlessExtension` * counts pattern hits, so a larger window can change its answer; a character * slice of a file with multibyte text near the top would cover more content than * the disk head and could disagree about identical bytes. Re-encoding keeps the * two answers comparable — including at the boundary, where each decodes a * character split by the cut to U+FFFD. * * Content that was lossily decoded — invalid UTF-8 replaced by U+FFFD — re-encodes * to at least as many bytes as it came from, three per U+FFFD, so it scores a * window no longer than the raw file's. A decoded string cannot recover the bytes * it came from, so the in-memory and on-disk sides cannot be made to score the * same span of such a file; the disk-side resolver routes through this helper so * both settle on the narrower window instead of disagreeing. The window is * therefore about {@link DETECT_HEAD_BYTES} / 3 characters for wholly lossy * content, and a latin-1 file whose only code markers sit past that point reads as * "not code" wherever this window is scored. * * The leading character slice only bounds the allocation, keeping a whole-file * encode off this path: the first N characters always encode to at least N * bytes, so slicing to N characters cannot drop anything inside the N-byte * window. */ export declare function detectExtensionFromSource(source: string): string | null; /** * The canonical detection window for this content. * * The one place the window's end is decided, so that a head read from disk and * the same window taken from whole-file content are comparable — which they * are not if either side skips this, because a U+FFFD standing in for an * invalid byte costs three bytes of budget on the way back out and slides the * end of the window. * * Idempotent: the result is already at most `maxBytes`, so normalising it again * changes nothing, and {@link readFileHead} can return a normalised head * without altering what detection scores. */ export declare function detectionHeadWindow(source: string, maxBytes?: number): string; /** * Like {@link resolveExtensionlessExtension} but **throws** on a read/stat * failure instead of collapsing it to `null`, so a caller that must not conflate * "unreadable" with "not code" can tell them apart — e.g. the incremental * symbol-graph purge, which would otherwise drop a still-valid payload on a * transient I/O blip. Returns `null` only for a genuine non-match: detection * disabled, a {@link SPECIAL_FILES} name, a non-regular file, or content that is * not indexable code. */ export declare function resolveExtensionlessExtensionStrict(absolutePath: string): Promise<string | null>; /** What a head-read decided, together with the bytes it decided on. */ export interface ExtensionlessDetection { /** The canonical extension detected, or null for a genuine non-match. */ extension: string | null; /** * The head the answer was scored on, or null when nothing was read at all — * detection disabled, a {@link SPECIAL_FILES} name, or a non-regular file. * * A caller that records what the build consumed needs this: the answer was * derived from these bytes and from nothing else, so they are the thing to * watch. Watching the file's size instead would miss a same-length edit that * flips the answer (`# hello!!` becoming `#!/bin/sh`), and watching its whole * content would hash bytes the decision never saw. */ head: string | null; /** * Whether the head-read was attempted and threw, as opposed to never being * attempted. A caller recording what the build consumed needs the two apart: * a file it could not open may start being code the moment it can be read, * while one it never opened (detection off, a {@link SPECIAL_FILES} name, a * FIFO) is not an input at all. */ unreadable: boolean; } /** * Like {@link resolveExtensionlessExtensionStrict}, but also reports the head * the detection ran on. Throws on a read/stat failure for the same reason the * strict variant does. */ export declare function resolveExtensionlessDetectionStrict(absolutePath: string): Promise<ExtensionlessDetection>; /** * Detect the canonical extension of an extensionless file by head content, or * `null` when detection is disabled, the name is a {@link SPECIAL_FILES} entry, * the file is unreadable, or the content is not indexable code. Callers that * need graph-eligibility (grammar-bearing only) additionally check * `getAstGrepLang(result) !== null`. */ export declare function resolveExtensionlessExtension(absolutePath: string): Promise<string | null>; /** * {@link resolveExtensionlessExtension}, reporting the head the answer was * scored on so a caller can record what it consumed. A failure reads as * "nothing was read", which is what it was. */ export declare function resolveExtensionlessDetection(absolutePath: string): Promise<ExtensionlessDetection>; //# sourceMappingURL=extensionless.d.ts.map