import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import process from 'node:process'; import { KnownWordCacheManager, getKnownWordCacheLifecycleConfig, } from '../src/anki-integration/known-word-cache.js'; import { getMatureIntervalThresholdDays } from '../src/anki-integration/known-word-maturity.js'; import { resolveConfigDir } from '../src/config/path-resolution.js'; import { ConfigService } from '../src/config/service.js'; import { parseSubtitleCues } from '../src/core/services/subtitle-cue-parser.js'; import { createTokenizerDepsRuntime, tokenizeSubtitle } from '../src/core/services/tokenizer.js'; import { resolveCompleteTokenReading, resolveKnownWordReadingForMatch, resolveKnownWordText, } from '../src/core/services/tokenizer/annotation-stage.js'; import { MecabTokenizer } from '../src/mecab-tokenizer.js'; import type { MergedToken } from '../src/types.js'; import type { KnownWordMaturityTier } from '../src/types/subtitle.js'; import { createYomitanRuntimeStateWithSearch, destroyParserWindow, loadElectronModule, withTimeout, type YomitanRuntimeState, } from './yomitan-script-runtime.js'; interface CliOptions { input: string; configDir?: string; yomitanUserDataPath?: string; yomitanExtensionPath?: string; limit: number; audit: boolean; refresh: boolean; json: boolean; quiet: boolean; profileCopy: boolean; } type TierOrFallback = KnownWordMaturityTier | 'known-no-tier'; interface TokenReport { cueIndex: number; startTime: number; surface: string; headword: string; reading: string; tier: TierOrFallback; noteIds: number[]; } interface AuditMismatch extends TokenReport { liveTier: KnownWordMaturityTier | 'no-notes'; intervals: number[]; } const TIERS: readonly KnownWordMaturityTier[] = ['new', 'learning', 'young', 'mature']; const FALLBACK_TIER_COLORS: Record = { new: '#ee99a0', learning: '#b7bdf8', young: '#91d7e3', mature: '#a6da95', }; function parseCliArgs(argv: string[]): CliOptions { const options: CliOptions = { input: '', limit: 0, audit: false, refresh: false, json: false, quiet: false, profileCopy: false, }; const rest: string[] = []; for (let i = 0; i < argv.length; i += 1) { const arg = argv[i]!; const takeValue = (flag: string): string => { const next = argv[i + 1]; if (!next) { throw new Error(`Missing value for ${flag}`); } i += 1; return next; }; if (arg === '--help' || arg === '-h') { process.stdout.write(`${usage()}\n`); process.exit(0); } else if (arg === '--input') { options.input = takeValue(arg); } else if (arg === '--config-dir') { options.configDir = takeValue(arg); } else if (arg === '--yomitan-user-data') { options.yomitanUserDataPath = takeValue(arg); } else if (arg === '--yomitan-extension-path') { options.yomitanExtensionPath = takeValue(arg); } else if (arg === '--limit') { options.limit = Math.max(0, Number.parseInt(takeValue(arg), 10) || 0); } else if (arg === '--audit') { options.audit = true; } else if (arg === '--refresh') { options.refresh = true; } else if (arg === '--json') { options.json = true; } else if (arg === '--quiet') { options.quiet = true; } else if (arg === '--profile-copy') { options.profileCopy = true; } else if (arg === '--') { // `bun run