/** * Referencing a file by name, and knowing what it costs before you do. * * The terminal has had `@` for a while: type it, get a subsequence match over every path in the * workspace, pick one. The browser never got it, which is the gap this closes. But copying the * terminal's version would have missed the two things that make this worth building rather than * ticking off. * * ## Ranked by what this project is doing, not alphabetically * * A fuzzy match over four thousand paths answers "which files contain these letters". It does not * answer "which file did you mean", and the difference is most of the value. KONECK already knows * things no generic picker does: * * - which files git has touched recently — what the project is in the middle of; * - which files this session has already read or written — what *you* are in the middle of; * - which files the project ledger holds findings about — where the sharp edges were. * * A file carrying all three is almost certainly the one being reached for, and it is offered first * with the reason attached, because a ranked list that will not say why it ranked something is a * list you cannot correct. * * ## The cost is shown before the attachment is made * * Every candidate carries an estimate of what including it would spend. This matters here more than * it might elsewhere: the person this was built for has run into truncated prompts and exhausted * windows repeatedly, and "src/engine.ts ~48k tokens" is the difference between an informed choice * and a surprise three turns later. It is an estimate and is labelled as one — four characters to * the token is the usual rough figure and it is rough — but a rough number in front of the decision * beats an exact one after it. * * A directory is offered as an outline rather than a concatenation, for the same reason: naming * twenty files costs a few hundred tokens and pasting them costs a hundred thousand. */ /** Why a candidate is where it is in the list. Shown, because an unexplained ranking cannot be trusted. */ export type Because = /** Changed in git recently — what the project is in the middle of. */ 'recently changed' /** Read or written by this session — what you are in the middle of. */ | 'used in this session' /** The ledger holds a finding about it — where something was learned the hard way. */ | 'has notes'; export interface Candidate { /** Repo-relative path. */ path: string; kind: 'file' | 'dir'; /** Roughly what including it would spend, or null for a directory, which is offered as an outline. */ tokens: number | null; /** Bytes on disk, when known. */ bytes: number | null; /** Every reason this ranked where it did, in the order they were applied. */ because: Because[]; } /** What the workspace knows that a filename match does not. */ export interface Signals { /** Files git has touched recently, newest first. */ recent?: readonly string[]; /** Files this session has read or written. */ session?: readonly string[]; /** Files the ledger holds findings about. */ noted?: readonly string[]; } /** * Four characters to the token. * * The usual rough figure for English and code alike. Deliberately not a real tokeniser: loading one * to put an advisory number beside forty filenames would cost more than the number is worth, and * being out by a fifth does not change the decision anybody makes with it. */ export declare const CHARS_PER_TOKEN = 4; export declare function tokensForBytes(bytes: number): number; /** * The reasons a path carries, in a fixed order. * * Fixed so the list reads the same way every time — a reason that moves around between keystrokes * makes the ranking look arbitrary even when it is not. */ export declare function reasonsFor(p: string, signals: Signals): Because[]; /** * The candidates for a query, best first. * * An empty query is not an empty answer: it offers what the project is doing — recent, in-session, * noted — which is the list somebody wants when they type `@` and pause. A generic picker shows the * alphabet at that moment, which is never what was meant. */ export declare function rankMentions(entries: readonly string[], query: string, signals?: Signals, limit?: number): Array<{ path: string; score: number; because: Because[]; }>; /** * Fills in size and cost, and says which are directories. * * Done for the shown candidates only. Sizing four thousand paths to display forty is the kind of * work that makes a picker feel slow for no reason anybody can see. */ export declare function describeCandidates(cwd: string, ranked: ReadonlyArray<{ path: string; because: Because[]; }>): Promise; /** A size a person reads, rather than a byte count. */ export declare function humanBytes(bytes: number | null): string; /** A token count a person reads. Approximate, and never dressed up as exact. */ export declare function humanTokens(tokens: number | null): string; /** * The short forms shown in a row, in place of the full reason. * * A row is a row, not a sentence: "used in this session" reads as prose beside a filename and * "in session" reads as a label. The wording is shared so the terminal and the browser cannot drift * into saying different things about the same signal. */ export declare function shortReason(because: Because): string; /** * What a picker row says after the path: why it is there, and what it will cost. * * Shared by both surfaces. `bytes` is undefined while the size is still being read, which is the * normal first state rather than an error — the rows appear at once and the numbers arrive behind * them, because a picker that waits for forty stat calls before showing anything feels broken. */ export declare function rowDetail(because: readonly Because[], bytes: number | undefined, kind?: 'file' | 'dir'): string; //# sourceMappingURL=mention.d.ts.map