/** * VoiceAnalyzer — manuscript-level character VOICE analysis. * * Where {@link ProseAnalyzer.analyzeCharacterVoices} answers "what does this * character sound like *in this chapter*?", VoiceAnalyzer answers two * manuscript-wide questions: * * 1. SIMILARITY — "do two characters sound too alike?" Two characters whose * voice fingerprints sit closer than {@link SIMILAR_VOICE_THRESHOLD} are * flagged as a `similarPair` (their voices are not distinct). * 2. DRIFT — "does a character's voice wander across chapters?" A character * whose per-chapter fingerprint strays from its own aggregate centroid by * more than {@link DRIFT_THRESHOLD} is flagged as `drifting`. * * The fingerprint is built from the existing prose-analyzer primitives * (`extractDialogueByCharacter` + `analyzeCharacterVoices`), so the heuristics * stay consistent with the per-chapter tooling. Pure deterministic — no LLM. */ /** Two characters closer than this normalised distance count as "too alike". */ export declare const SIMILAR_VOICE_THRESHOLD = 0.15; /** * A character whose worst per-chapter fingerprint sits farther than this from * its own aggregate centroid is flagged as having a drifting (inconsistent) * voice. */ export declare const DRIFT_THRESHOLD = 0.25; /** Minimum dialogue lines a character needs before its voice is measured. */ export declare const MIN_LINES_FOR_VOICE = 3; /** The numeric + lexical signature of a character's dialogue. */ export interface VoiceFingerprint { /** Mean words per dialogue line. */ avgSentenceLength: number; /** Mean characters per word. */ avgWordLength: number; /** Unique words / total words (lexical variety, 0–1). */ typeTokenRatio: number; /** Most distinctive repeated 2-word phrases. */ topBigrams: string[]; } /** A character's fingerprint as measured within a single chapter. */ export interface ChapterFingerprint extends VoiceFingerprint { chapter: string; lineCount: number; } /** Aggregate per-character voice across the whole manuscript. */ export interface CharacterVoice { character: string; /** Total dialogue lines across all chapters. */ lineCount: number; /** Number of chapters the character speaks in. */ chapterCount: number; /** Aggregate fingerprint over all the character's dialogue. */ fingerprint: VoiceFingerprint; /** Per-chapter fingerprints, in chapter order. */ perChapter: ChapterFingerprint[]; } /** A pair of characters whose voices are too similar. */ export interface SimilarPair { a: string; b: string; /** Normalised Euclidean distance between their aggregate fingerprints. */ distance: number; } /** A character whose voice drifts across chapters. */ export interface DriftingCharacter { character: string; /** Largest distance from any chapter fingerprint to the aggregate centroid. */ maxDistance: number; /** Chapters whose fingerprint exceeds {@link DRIFT_THRESHOLD}. */ outlierChapters: string[]; } /** Full manuscript voice report. */ export interface VoiceReport { characters: CharacterVoice[]; similarPairs: SimilarPair[]; drifting: DriftingCharacter[]; } export declare class VoiceAnalyzer { private readonly projectPath; private readonly prose; constructor(projectPath: string); /** * Analyse every `chapters/*.md` file and produce a manuscript-wide voice * report: aggregate fingerprints, too-similar pairs, and drifting voices. */ analyzeManuscript(): Promise; /** All character pairs whose aggregate fingerprints are too close. */ private findSimilarPairs; /** Characters whose per-chapter voice strays from their own centroid. */ private findDrifting; /** Strip YAML frontmatter and fenced code blocks before voice extraction. */ private prepareText; } /** * Normalised Euclidean distance between two fingerprints over the three numeric * features. Each feature delta is divided by its scale so the axes are * comparable; the result is a small unit-less number (0 = identical voices). */ export declare function fingerprintDistance(a: VoiceFingerprint, b: VoiceFingerprint): number; //# sourceMappingURL=voice-analyzer.d.ts.map