llm-wiki-compiler 0.3.0 → 0.4.0

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.
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/commands/ingest.ts","../src/utils/markdown.ts","../src/utils/constants.ts","../src/utils/output.ts","../src/ingest/web.ts","../src/ingest/file.ts","../src/commands/compile.ts","../src/compiler/index.ts","../src/utils/state.ts","../src/compiler/source-state.ts","../src/compiler/hasher.ts","../src/providers/anthropic.ts","../src/providers/openai.ts","../src/providers/ollama.ts","../src/providers/minimax.ts","../src/utils/claude-settings.ts","../src/utils/provider.ts","../src/utils/llm.ts","../src/utils/lock.ts","../src/compiler/prompts.ts","../src/compiler/deps.ts","../src/compiler/orphan.ts","../src/compiler/resolver.ts","../src/compiler/indexgen.ts","../src/compiler/obsidian.ts","../src/utils/embeddings.ts","../src/compiler/candidates.ts","../src/compiler/page-renderer.ts","../src/compiler/provenance.ts","../src/commands/query.ts","../src/commands/watch.ts","../src/linter/rules.ts","../src/linter/index.ts","../src/commands/lint.ts","../src/commands/review-list.ts","../src/commands/review-show.ts","../src/commands/review-approve.ts","../src/commands/review-helpers.ts","../src/commands/review-reject.ts","../src/mcp/server.ts","../src/mcp/tools.ts","../src/mcp/provider-check.ts","../src/mcp/resources.ts"],"sourcesContent":["/**\n * CLI entry point for llmwiki — the knowledge compiler.\n *\n * Registers all commands (ingest, compile, query, watch, lint) via Commander.\n * Validates the correct API key for the selected LLM provider.\n * Designed for `npx llmwiki` or global install via `npm install -g llm-wiki-compiler`.\n */\n\nimport \"dotenv/config\";\nimport { createRequire } from \"module\";\nimport { Command } from \"commander\";\nimport ingestCommand from \"./commands/ingest.js\";\nimport compileCommand from \"./commands/compile.js\";\nimport queryCommand from \"./commands/query.js\";\nimport watchCommand from \"./commands/watch.js\";\nimport lintCommand from \"./commands/lint.js\";\nimport reviewListCommand from \"./commands/review-list.js\";\nimport reviewShowCommand from \"./commands/review-show.js\";\nimport reviewApproveCommand from \"./commands/review-approve.js\";\nimport reviewRejectCommand from \"./commands/review-reject.js\";\nimport { startMCPServer } from \"./mcp/server.js\";\nimport { DEFAULT_PROVIDER } from \"./utils/constants.js\";\nimport { resolveAnthropicAuthFromEnv } from \"./utils/claude-settings.js\";\n\nconst require = createRequire(import.meta.url);\nconst { version } = require(\"../package.json\") as { version: string };\n\nconst program = new Command();\n\nprogram\n .name(\"llmwiki\")\n .description(\"The knowledge compiler — raw sources in, interlinked wiki out\")\n .version(version);\n\nprogram\n .command(\"ingest <source>\")\n .description(\"Ingest a URL or local file into sources/\")\n .action(async (source: string) => {\n try {\n await ingestCommand(source);\n } catch (err) {\n console.error(`\\x1b[31mError:\\x1b[0m ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"compile\")\n .description(\"Compile sources/ into an interlinked wiki\")\n .option(\n \"--review\",\n \"Write generated pages as review candidates under .llmwiki/candidates/ instead of mutating wiki/. Orphan-marking for deleted sources is deferred until the next non-review compile.\",\n )\n .action(async (options: { review?: boolean }) => {\n try {\n requireProvider();\n await compileCommand({ review: options.review });\n } catch (err) {\n console.error(`\\x1b[31mError:\\x1b[0m ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\nconst reviewCommand = program\n .command(\"review\")\n .description(\"Inspect and act on pending compile review candidates\");\n\nreviewCommand\n .command(\"list\")\n .description(\"List pending review candidates\")\n .action(async () => {\n try {\n await reviewListCommand();\n } catch (err) {\n console.error(`\\x1b[31mError:\\x1b[0m ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\nreviewCommand\n .command(\"show <id>\")\n .description(\"Print a single candidate's metadata and body\")\n .action(async (id: string) => {\n try {\n await reviewShowCommand(id);\n } catch (err) {\n console.error(`\\x1b[31mError:\\x1b[0m ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\nreviewCommand\n .command(\"approve <id>\")\n .description(\"Approve a candidate and promote it into wiki/concepts/\")\n .action(async (id: string) => {\n try {\n await reviewApproveCommand(id);\n } catch (err) {\n console.error(`\\x1b[31mError:\\x1b[0m ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\nreviewCommand\n .command(\"reject <id>\")\n .description(\"Reject a candidate and archive it without touching wiki/\")\n .action(async (id: string) => {\n try {\n await reviewRejectCommand(id);\n } catch (err) {\n console.error(`\\x1b[31mError:\\x1b[0m ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"query <question>\")\n .description(\"Ask a question against the wiki\")\n .option(\"--save\", \"Save the answer as a wiki page\")\n .action(async (question: string, options: { save?: boolean }) => {\n try {\n requireProvider();\n await queryCommand(process.cwd(), question, options);\n } catch (err) {\n console.error(`\\x1b[31mError:\\x1b[0m ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"watch\")\n .description(\"Watch sources/ and auto-recompile on changes\")\n .action(async () => {\n try {\n requireProvider();\n await watchCommand();\n } catch (err) {\n console.error(`\\x1b[31mError:\\x1b[0m ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"lint\")\n .description(\"Run rule-based quality checks against the wiki\")\n .action(async () => {\n try {\n await lintCommand();\n } catch (err) {\n console.error(`\\x1b[31mError:\\x1b[0m ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"serve\")\n .description(\"Start an MCP server exposing wiki tools and resources over stdio\")\n .option(\"--root <dir>\", \"Project root directory\", process.cwd())\n .action(async (options: { root: string }) => {\n try {\n // Per-tool credential checks happen inside the MCP layer so read-only\n // tools and ingest still work without an API key.\n await startMCPServer({ root: options.root, version });\n } catch (err) {\n console.error(`\\x1b[31mError:\\x1b[0m ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\n/** API key env var required per provider. Null means no key needed. */\nconst PROVIDER_KEY_VARS: Record<string, string | null> = {\n anthropic: \"ANTHROPIC_API_KEY\",\n openai: \"OPENAI_API_KEY\",\n ollama: null,\n minimax: \"MINIMAX_API_KEY\",\n};\n\n/** Exit with a helpful message if the selected provider's API key is missing. */\nfunction requireProvider(): void {\n const provider = process.env.LLMWIKI_PROVIDER ?? DEFAULT_PROVIDER;\n\n if (provider === \"anthropic\") {\n const auth = resolveAnthropicAuthFromEnv();\n if (!auth.apiKey && !auth.authToken) {\n console.error(\n `\\x1b[31mError:\\x1b[0m Anthropic credentials are required for the \"anthropic\" provider.\\n` +\n ` Set one of: export ANTHROPIC_API_KEY=<your-key> OR export ANTHROPIC_AUTH_TOKEN=<your-token>`,\n );\n process.exit(1);\n }\n return;\n }\n\n const keyVar = PROVIDER_KEY_VARS[provider];\n\n if (keyVar === undefined) {\n console.error(\n `\\x1b[31mError:\\x1b[0m Unknown provider \"${provider}\".\\n` +\n ` Supported: ${Object.keys(PROVIDER_KEY_VARS).join(\", \")}`,\n );\n process.exit(1);\n }\n\n if (keyVar && !process.env[keyVar]) {\n console.error(\n `\\x1b[31mError:\\x1b[0m ${keyVar} environment variable is required for the \"${provider}\" provider.\\n` +\n ` Set it with: export ${keyVar}=<your-key>`,\n );\n process.exit(1);\n }\n}\n\nprogram.parse();\n","/**\n * Commander action for `llmwiki ingest <source>`.\n * Detects whether the source is a URL or local file, delegates to the\n * appropriate ingestion module, and saves the result as a markdown file\n * with YAML frontmatter in the sources/ directory.\n */\n\nimport path from \"path\";\nimport { mkdir, writeFile } from \"fs/promises\";\nimport { slugify, buildFrontmatter } from \"../utils/markdown.js\";\nimport { MAX_SOURCE_CHARS, MIN_SOURCE_CHARS, SOURCES_DIR } from \"../utils/constants.js\";\nimport * as output from \"../utils/output.js\";\nimport ingestWeb from \"../ingest/web.js\";\nimport ingestFile from \"../ingest/file.js\";\nimport type { IngestResult } from \"../utils/types.js\";\n\n/** Check whether a source string looks like a URL. */\nfunction isUrl(source: string): boolean {\n return source.startsWith(\"http://\") || source.startsWith(\"https://\");\n}\n\n/** Truncate result including whether truncation occurred and original length. */\ninterface TruncateResult {\n content: string;\n truncated: boolean;\n originalChars: number;\n}\n\n/** Truncate content if it exceeds the character limit, logging a warning. */\nexport function enforceCharLimit(content: string): TruncateResult {\n if (content.length <= MAX_SOURCE_CHARS) {\n return { content, truncated: false, originalChars: content.length };\n }\n\n output.status(\n \"!\",\n output.warn(\n `Content truncated from ${content.length.toLocaleString()} to ${MAX_SOURCE_CHARS.toLocaleString()} characters.`\n )\n );\n return {\n content: content.slice(0, MAX_SOURCE_CHARS),\n truncated: true,\n originalChars: content.length,\n };\n}\n\n/** Reject empty content and warn when content is trivially short. */\nfunction enforceMinContent(content: string): void {\n const length = content.trim().length;\n\n if (length === 0) {\n throw new Error(\n \"No readable content could be extracted from the source.\"\n );\n }\n\n if (length < MIN_SOURCE_CHARS) {\n output.status(\n \"!\",\n output.warn(\n `Content seems very short (${length} chars, minimum recommended is ${MIN_SOURCE_CHARS}).`\n )\n );\n }\n}\n\n/** Build the full markdown document with frontmatter. */\nexport function buildDocument(\n title: string,\n source: string,\n result: TruncateResult,\n): string {\n const meta: Record<string, unknown> = {\n title,\n source,\n ingestedAt: new Date().toISOString(),\n };\n if (result.truncated) {\n meta.truncated = true;\n meta.originalChars = result.originalChars;\n }\n const frontmatter = buildFrontmatter(meta);\n\n return `${frontmatter}\\n\\n${result.content}\\n`;\n}\n\n/** Write the ingested document to the sources/ directory. */\nasync function saveSource(title: string, document: string): Promise<string> {\n const filename = `${slugify(title)}.md`;\n const destPath = path.join(SOURCES_DIR, filename);\n\n await mkdir(SOURCES_DIR, { recursive: true });\n await writeFile(destPath, document, \"utf-8\");\n\n return destPath;\n}\n\n/**\n * Programmatic ingest entry point. Identical fetch + write logic to the CLI\n * command but returns a structured IngestResult instead of writing to stdout.\n * Used by the MCP server's ingest_source tool.\n *\n * @param source - A URL (http/https) or a local file path (.md or .txt).\n * @returns Saved filename, character count, truncation flag, and source URI.\n */\nexport async function ingestSource(source: string): Promise<IngestResult> {\n output.status(\"*\", output.info(`Ingesting: ${source}`));\n\n const { title, content } = isUrl(source)\n ? await ingestWeb(source)\n : await ingestFile(source);\n\n const result = enforceCharLimit(content);\n enforceMinContent(result.content);\n const document = buildDocument(title, source, result);\n const savedPath = await saveSource(title, document);\n\n return {\n filename: path.basename(savedPath),\n charCount: result.content.length,\n truncated: result.truncated,\n source,\n };\n}\n\n/**\n * Ingest a source (URL or local file) and save it to the sources/ directory.\n * @param source - A URL (http/https) or a local file path (.md or .txt).\n */\nexport default async function ingest(source: string): Promise<void> {\n const result = await ingestSource(source);\n const savedPath = path.join(SOURCES_DIR, result.filename);\n\n output.status(\n \"+\",\n output.success(`Saved ${output.bold(result.filename)} → ${output.source(savedPath)}`)\n );\n output.status(\"→\", output.dim(\"Next: llmwiki compile\"));\n}\n","/**\n * Markdown parsing and manipulation helpers.\n * Handles YAML frontmatter extraction, slugification, and atomic file writes\n * for wiki pages.\n */\n\nimport { writeFile, rename, readFile, mkdir } from \"fs/promises\";\nimport path from \"path\";\nimport yaml from \"js-yaml\";\nimport type { ContradictionRef, ProvenanceState } from \"./types.js\";\n\n/** The set of valid provenance state strings, used to reject unknown values. */\nconst VALID_PROVENANCE_STATES: ReadonlySet<ProvenanceState> = new Set([\n \"extracted\",\n \"merged\",\n \"inferred\",\n \"ambiguous\",\n]);\n\n/** Provenance metadata parsed from a page's frontmatter. */\ninterface ProvenanceMetadata {\n confidence?: number;\n provenanceState?: ProvenanceState;\n contradictedBy?: ContradictionRef[];\n inferredParagraphs?: number;\n}\n\n/** Convert a human-readable concept title to a filename slug. */\nexport function slugify(title: string): string {\n return title\n .toLowerCase()\n .replace(/['']/g, \"\")\n .replace(/[^\\w\\s-]/g, \"\")\n .replace(/\\s+/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\n/** Build YAML frontmatter string from key-value pairs. */\nexport function buildFrontmatter(fields: Record<string, unknown>): string {\n const dumped = yaml.dump(fields, { lineWidth: -1, quotingType: '\"' }).trimEnd();\n return `---\\n${dumped}\\n---`;\n}\n\n/** Parse YAML frontmatter from a markdown string. Returns { meta, body }. */\nexport function parseFrontmatter(content: string): {\n meta: Record<string, unknown>;\n body: string;\n} {\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---\\n?([\\s\\S]*)$/);\n if (!match) {\n return { meta: {}, body: content };\n }\n\n let meta: Record<string, unknown> = {};\n try {\n const parsed = yaml.load(match[1]);\n if (parsed && typeof parsed === \"object\") {\n meta = parsed as Record<string, unknown>;\n }\n } catch {\n // Malformed YAML — return empty meta so callers degrade gracefully.\n }\n return { meta, body: match[2] };\n}\n\n/** Atomically write a file (write to .tmp, then rename). */\nexport async function atomicWrite(filePath: string, content: string): Promise<void> {\n await mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = filePath + \".tmp\";\n await writeFile(tmpPath, content, \"utf-8\");\n await rename(tmpPath, filePath);\n}\n\n/**\n * Extract all source filenames from ^[filename.md] citation markers in a page body.\n * Handles single citations (^[source.md]) and multi-source (^[a.md, b.md]).\n * @param body - The markdown body text to parse.\n * @returns Array of unique source filenames.\n */\nexport function extractCitations(body: string): string[] {\n const citationPattern = /\\^\\[([^\\]]+)\\]/g;\n const filenames = new Set<string>();\n\n let match;\n while ((match = citationPattern.exec(body)) !== null) {\n const inner = match[1];\n for (const part of inner.split(\",\")) {\n const trimmed = part.trim();\n if (trimmed.length > 0) {\n filenames.add(trimmed);\n }\n }\n }\n\n return [...filenames];\n}\n\n/** Read a file, returning empty string if it doesn't exist. */\nexport async function safeReadFile(filePath: string): Promise<string> {\n try {\n return await readFile(filePath, \"utf-8\");\n } catch {\n return \"\";\n }\n}\n\n/** Parse a numeric confidence value, clamping to 0..1 and rejecting non-numbers. */\nfunction parseConfidence(raw: unknown): number | undefined {\n if (typeof raw !== \"number\" || !Number.isFinite(raw)) return undefined;\n if (raw < 0) return 0;\n if (raw > 1) return 1;\n return raw;\n}\n\n/** Parse a provenance state string, returning undefined for unknown values. */\nfunction parseProvenanceState(raw: unknown): ProvenanceState | undefined {\n if (typeof raw !== \"string\") return undefined;\n return VALID_PROVENANCE_STATES.has(raw as ProvenanceState)\n ? (raw as ProvenanceState)\n : undefined;\n}\n\n/** Coerce a single contradiction entry to a ContradictionRef, or null if invalid. */\nfunction coerceContradictionEntry(entry: unknown): ContradictionRef | null {\n if (typeof entry === \"string\" && entry.trim().length > 0) {\n return { slug: entry.trim() };\n }\n if (entry && typeof entry === \"object\" && \"slug\" in entry) {\n const obj = entry as { slug: unknown; reason?: unknown };\n if (typeof obj.slug !== \"string\" || obj.slug.trim().length === 0) return null;\n const ref: ContradictionRef = { slug: obj.slug.trim() };\n if (typeof obj.reason === \"string\") ref.reason = obj.reason;\n return ref;\n }\n return null;\n}\n\n/** Parse a contradictedBy array, accepting strings or objects with slug. */\nfunction parseContradictedBy(raw: unknown): ContradictionRef[] | undefined {\n if (!Array.isArray(raw)) return undefined;\n const refs = raw\n .map(coerceContradictionEntry)\n .filter((ref): ref is ContradictionRef => ref !== null);\n return refs.length > 0 ? refs : undefined;\n}\n\n/** Parse the inferred paragraph count, requiring a non-negative integer. */\nfunction parseInferredParagraphs(raw: unknown): number | undefined {\n if (typeof raw !== \"number\" || !Number.isInteger(raw) || raw < 0) return undefined;\n return raw;\n}\n\n/**\n * Extract provenance metadata fields from a parsed frontmatter record.\n * Defensively handles missing or malformed values so existing pages without\n * the new fields continue to parse correctly.\n * @param meta - Raw frontmatter object as returned by parseFrontmatter.\n * @returns Typed provenance metadata with only the fields that were present.\n */\nexport function parseProvenanceMetadata(\n meta: Record<string, unknown>,\n): ProvenanceMetadata {\n return {\n confidence: parseConfidence(meta.confidence),\n provenanceState: parseProvenanceState(meta.provenanceState),\n contradictedBy: parseContradictedBy(meta.contradictedBy),\n inferredParagraphs: parseInferredParagraphs(meta.inferredParagraphs),\n };\n}\n\n/**\n * Validate that a wiki page has non-empty content and valid frontmatter.\n * Returns true if the page is valid.\n */\nexport function validateWikiPage(content: string): boolean {\n if (!content || content.trim().length === 0) return false;\n\n const { meta, body } = parseFrontmatter(content);\n if (!meta.title) return false;\n if (body.trim().length === 0) return false;\n\n return true;\n}\n","/**\n * Shared constants for the llmwiki knowledge compiler.\n * Centralized config values to avoid magic numbers scattered across the codebase.\n */\n\n/** Maximum source file size in characters before truncation. */\nexport const MAX_SOURCE_CHARS = 100_000;\n\n/** Minimum source content length to ingest without a warning. */\nexport const MIN_SOURCE_CHARS = 50;\n\n/** Number of most relevant wiki pages to load for query context. */\nexport const QUERY_PAGE_LIMIT = 5;\n\n/** Maximum concurrent API calls during page generation. */\nexport const COMPILE_CONCURRENCY = 5;\n\n/** API retry configuration. */\nexport const RETRY_COUNT = 3;\nexport const RETRY_BASE_MS = 1000;\nexport const RETRY_MULTIPLIER = 4;\n\n/** Default provider when LLMWIKI_PROVIDER is not set. */\nexport const DEFAULT_PROVIDER = \"anthropic\";\n\n/** Default model per provider. */\nexport const PROVIDER_MODELS: Record<string, string> = {\n anthropic: \"claude-sonnet-4-20250514\",\n openai: \"gpt-4o\",\n ollama: \"llama3.1\",\n minimax: \"MiniMax-M2.7\",\n};\n\n/** Default Ollama API base URL. */\nexport const OLLAMA_DEFAULT_HOST = \"http://localhost:11434/v1\";\n\n/** Directory names relative to the project root. */\nexport const SOURCES_DIR = \"sources\";\nexport const CONCEPTS_DIR = \"wiki/concepts\";\nexport const QUERIES_DIR = \"wiki/queries\";\nexport const LLMWIKI_DIR = \".llmwiki\";\nexport const STATE_FILE = \".llmwiki/state.json\";\nexport const LOCK_FILE = \".llmwiki/lock\";\nexport const INDEX_FILE = \"wiki/index.md\";\nexport const MOC_FILE = \"wiki/MOC.md\";\nexport const EMBEDDINGS_FILE = \".llmwiki/embeddings.json\";\n\n/** Pending review candidates awaiting approval/rejection. */\nexport const CANDIDATES_DIR = \".llmwiki/candidates\";\n\n/** Rejected review candidates archived for audit (not deleted). */\nexport const CANDIDATES_ARCHIVE_DIR = \".llmwiki/candidates/archive\";\n\n/** Number of most similar pages to return from embedding-based pre-filter. */\nexport const EMBEDDING_TOP_K = 15;\n\n/** Provenance metadata thresholds used by lint rules. */\nexport const LOW_CONFIDENCE_THRESHOLD = 0.5;\nexport const MAX_INFERRED_PARAGRAPHS_WITHOUT_CITATIONS = 2;\n\n/** Embedding model to use per provider. */\nexport const EMBEDDING_MODELS: Record<string, string> = {\n anthropic: \"voyage-3-lite\",\n openai: \"text-embedding-3-small\",\n ollama: \"nomic-embed-text\",\n};\n","/**\n * ANSI colored terminal output helpers.\n * Provides consistent styling for compilation progress, status messages,\n * and streaming token display.\n */\n\nconst RESET = \"\\x1b[0m\";\nconst BOLD = \"\\x1b[1m\";\nconst DIM = \"\\x1b[2m\";\nconst GREEN = \"\\x1b[32m\";\nconst YELLOW = \"\\x1b[33m\";\nconst BLUE = \"\\x1b[34m\";\nconst MAGENTA = \"\\x1b[35m\";\nconst CYAN = \"\\x1b[36m\";\nconst RED = \"\\x1b[31m\";\n\nexport function bold(text: string): string {\n return `${BOLD}${text}${RESET}`;\n}\n\nexport function dim(text: string): string {\n return `${DIM}${text}${RESET}`;\n}\n\nexport function success(text: string): string {\n return `${GREEN}${text}${RESET}`;\n}\n\nexport function warn(text: string): string {\n return `${YELLOW}${text}${RESET}`;\n}\n\nexport function info(text: string): string {\n return `${BLUE}${text}${RESET}`;\n}\n\nexport function error(text: string): string {\n return `${RED}${text}${RESET}`;\n}\n\nexport function source(text: string): string {\n return `${CYAN}${text}${RESET}`;\n}\n\n/** Print a status line with an icon. */\nexport function status(icon: string, message: string): void {\n console.log(`${icon} ${message}`);\n}\n\n/** Print a section header. */\nexport function header(title: string): void {\n console.log(`\\n${BOLD}${title}${RESET}`);\n console.log(dim(\"─\".repeat(Math.min(title.length + 4, 60))));\n}\n","/**\n * Web URL ingestion module.\n * Fetches a URL, extracts readable content using Mozilla Readability,\n * and converts the result to clean markdown via Turndown.\n *\n * Throws descriptive errors on network failures or when the page\n * cannot be parsed into readable content.\n */\n\nimport { JSDOM } from \"jsdom\";\nimport { Readability } from \"@mozilla/readability\";\nimport TurndownService from \"turndown\";\n\ninterface WebIngestResult {\n title: string;\n content: string;\n}\n\n/** Fetch a URL and return its readable content as markdown. */\nasync function fetchAndParse(url: string): Promise<Response> {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch ${url}: HTTP ${response.status}`);\n }\n return response;\n}\n\n/** Extract readable content from raw HTML using Readability. */\nfunction extractReadableContent(html: string, url: string): { title: string; htmlContent: string } {\n const dom = new JSDOM(html, { url });\n const reader = new Readability(dom.window.document);\n const article = reader.parse();\n\n if (!article || !article.content) {\n throw new Error(`Could not extract readable content from ${url}`);\n }\n\n return {\n title: article.title || \"Untitled\",\n htmlContent: article.content,\n };\n}\n\n/** Convert HTML to clean markdown using Turndown. */\nfunction convertToMarkdown(html: string): string {\n const turndown = new TurndownService({ headingStyle: \"atx\" });\n return turndown.turndown(html);\n}\n\n/**\n * Ingest a web URL and return its content as markdown.\n * @param url - The URL to fetch and convert.\n * @returns An object with the extracted title and markdown content.\n * @throws On network failure or unparseable content.\n */\nexport default async function ingestWeb(url: string): Promise<WebIngestResult> {\n const response = await fetchAndParse(url);\n const html = await response.text();\n const { title, htmlContent } = extractReadableContent(html, url);\n const content = convertToMarkdown(htmlContent);\n\n return { title, content };\n}\n","/**\n * Local file ingestion module.\n * Reads .md and .txt files from the local filesystem and returns their\n * content as markdown. Markdown files are returned as-is; plain text files\n * are wrapped in a markdown code block. All other extensions are rejected.\n */\n\nimport { readFile } from \"fs/promises\";\nimport path from \"path\";\n\nconst SUPPORTED_EXTENSIONS = new Set([\".md\", \".txt\"]);\n\ninterface FileIngestResult {\n title: string;\n content: string;\n}\n\n/** Derive a human-readable title from a filename (without extension). */\nfunction titleFromFilename(filePath: string): string {\n const basename = path.basename(filePath, path.extname(filePath));\n return basename.replace(/[-_]+/g, \" \").trim();\n}\n\n/** Wrap plain text content in a markdown fenced block. */\nfunction wrapPlainText(text: string): string {\n return `\\`\\`\\`\\n${text}\\n\\`\\`\\``;\n}\n\n/**\n * Ingest a local file and return its content as markdown.\n * @param filePath - Absolute or relative path to a .md or .txt file.\n * @returns An object with a title derived from the filename and the markdown content.\n * @throws On unsupported file type or read failure.\n */\nexport default async function ingestFile(filePath: string): Promise<FileIngestResult> {\n const ext = path.extname(filePath).toLowerCase();\n\n if (!SUPPORTED_EXTENSIONS.has(ext)) {\n throw new Error(\n `Unsupported file type \"${ext}\". Only .md and .txt files are supported.`\n );\n }\n\n const raw = await readFile(filePath, \"utf-8\");\n const title = titleFromFilename(filePath);\n const content = ext === \".md\" ? raw : wrapPlainText(raw);\n\n return { title, content };\n}\n","/**\n * Commander action for `llmwiki compile`.\n * Checks that sources exist, then delegates to the compilation orchestrator\n * to process all new and changed source files into wiki pages.\n */\n\nimport { existsSync } from \"fs\";\nimport { compile } from \"../compiler/index.js\";\nimport * as output from \"../utils/output.js\";\nimport { SOURCES_DIR } from \"../utils/constants.js\";\nimport type { CompileOptions } from \"../utils/types.js\";\n\n/**\n * Run the compile command from the current working directory.\n * Exits early if no sources directory exists yet.\n * @param options - Optional behaviour overrides forwarded from the CLI flag set.\n */\nexport default async function compileCommand(options: CompileOptions = {}): Promise<void> {\n if (!existsSync(SOURCES_DIR)) {\n output.status(\n \"!\",\n output.warn('No sources found. Run `llmwiki ingest <url>` first.'),\n );\n return;\n }\n\n await compile(process.cwd(), options);\n}\n","/**\n * Compilation orchestrator for the llmwiki knowledge compiler.\n *\n * Coordinates the full pipeline: lock acquisition, change detection,\n * concept extraction via LLM, wiki page generation with streaming output,\n * orphan marking for deleted sources, interlink resolution, and index\n * generation. Supports incremental compilation — only new or changed\n * sources are processed through the LLM pipeline.\n */\n\nimport { readFile } from \"fs/promises\";\nimport path from \"path\";\nimport { readState, updateSourceState } from \"../utils/state.js\";\nimport {\n buildExtractionSourceStates,\n pickStatesForSources,\n} from \"./source-state.js\";\nimport {\n atomicWrite,\n safeReadFile,\n validateWikiPage,\n slugify,\n} from \"../utils/markdown.js\";\nimport { callClaude } from \"../utils/llm.js\";\nimport { acquireLock, releaseLock } from \"../utils/lock.js\";\nimport {\n CONCEPT_EXTRACTION_TOOL,\n buildExtractionPrompt,\n parseConcepts,\n} from \"./prompts.js\";\nimport { detectChanges, hashFile } from \"./hasher.js\";\nimport {\n findAffectedSources,\n findFrozenSlugs,\n findLateAffectedSources,\n freezeFailedExtractions,\n persistFrozenSlugs,\n type ExtractionResult,\n} from \"./deps.js\";\nimport { markOrphaned, orphanUnownedFrozenPages } from \"./orphan.js\";\nimport { resolveLinks } from \"./resolver.js\";\nimport { generateIndex } from \"./indexgen.js\";\nimport { generateMOC } from \"./obsidian.js\";\nimport { updateEmbeddings } from \"../utils/embeddings.js\";\nimport { writeCandidate } from \"./candidates.js\";\nimport { renderMergedPageContent } from \"./page-renderer.js\";\nimport * as output from \"../utils/output.js\";\nimport {\n COMPILE_CONCURRENCY,\n CONCEPTS_DIR,\n INDEX_FILE,\n SOURCES_DIR,\n} from \"../utils/constants.js\";\nimport pLimit from \"p-limit\";\nimport type {\n CompileOptions,\n CompileResult,\n ExtractedConcept,\n ReviewCandidate,\n SourceChange,\n SourceState,\n WikiState,\n} from \"../utils/types.js\";\n\n/** Per-source state snapshots keyed by source filename. */\ntype SourceStateMap = Record<string, SourceState>;\n\n/** Empty CompileResult used when no pipeline work runs (e.g. lock contention). */\nfunction emptyCompileResult(): CompileResult {\n return { compiled: 0, skipped: 0, deleted: 0, concepts: [], pages: [], errors: [] };\n}\n\n/**\n * Run the full compilation pipeline with lock protection.\n * Acquires .llmwiki/lock, detects changes, compiles new/changed sources,\n * marks orphaned pages, resolves interlinks, and rebuilds the index.\n * @param root - Project root directory.\n * @param options - Optional pipeline overrides (e.g. --review mode).\n */\nexport async function compile(root: string, options: CompileOptions = {}): Promise<void> {\n await compileAndReport(root, options);\n}\n\n/**\n * Run the full compilation pipeline and return a structured result.\n * Same behaviour as compile() but exposes counts, slugs, and errors so\n * non-CLI consumers (the MCP server, programmatic callers) can report\n * meaningful data without scraping terminal output.\n * @param root - Project root directory.\n * @param options - Optional pipeline overrides (e.g. --review mode).\n * @returns Structured result describing what was compiled.\n */\nexport async function compileAndReport(\n root: string,\n options: CompileOptions = {},\n): Promise<CompileResult> {\n output.header(\"llmwiki compile\");\n\n const locked = await acquireLock(root);\n if (!locked) {\n output.status(\"!\", output.error(\"Could not acquire lock. Try again later.\"));\n return {\n ...emptyCompileResult(),\n errors: [\"Could not acquire .llmwiki/lock — another compile is in progress.\"],\n };\n }\n\n try {\n return await runCompilePipeline(root, options);\n } finally {\n await releaseLock(root);\n }\n}\n\n/** Buckets of source changes used by the compile pipeline. */\ninterface ChangeBuckets {\n toCompile: SourceChange[];\n deleted: SourceChange[];\n unchanged: SourceChange[];\n}\n\n/** Sort source changes into the buckets the pipeline acts on. */\nfunction bucketChanges(changes: SourceChange[]): ChangeBuckets {\n return {\n toCompile: changes.filter((c) => c.status === \"new\" || c.status === \"changed\"),\n deleted: changes.filter((c) => c.status === \"deleted\"),\n unchanged: changes.filter((c) => c.status === \"unchanged\"),\n };\n}\n\n/** Result of phase 2: page writes plus any errors collected along the way. */\ninterface PageGenerationResult {\n pages: MergedConcept[];\n errors: string[];\n /** Candidate ids written when running in --review mode. Empty otherwise. */\n candidates: string[];\n}\n\n/** Phase 2: generate pages for merged concepts in parallel, capturing errors. */\nasync function generatePagesPhase(\n root: string,\n extractions: ExtractionResult[],\n frozenSlugs: Set<string>,\n options: CompileOptions,\n): Promise<PageGenerationResult> {\n const merged = mergeExtractions(extractions, frozenSlugs);\n // Build the per-source state snapshot once so each candidate can carry the\n // exact data needed to mark its sources compiled on approval.\n const sourceStates = options.review\n ? await buildExtractionSourceStates(root, extractions)\n : {};\n const limit = pLimit(COMPILE_CONCURRENCY);\n const errors: string[] = [];\n const candidates: string[] = [];\n const pages = await Promise.all(\n merged.map((entry) => limit(async () => {\n const result = await generateMergedPage(root, entry, options, sourceStates);\n if (result.error) errors.push(result.error);\n if (result.candidateId) candidates.push(result.candidateId);\n return entry;\n })),\n );\n return { pages, errors, candidates };\n}\n\n/** Persist source state for every extraction that produced concepts. */\nasync function persistExtractionStates(\n root: string,\n extractions: ExtractionResult[],\n): Promise<void> {\n for (const result of extractions) {\n if (result.concepts.length === 0) continue;\n await persistSourceState(root, result.sourcePath, result.sourceFile, result.concepts);\n }\n}\n\n/** Build the structured CompileResult and emit the CLI completion banner. */\nfunction summarizeCompile(\n buckets: ChangeBuckets,\n generation: PageGenerationResult,\n extractions: ExtractionResult[],\n options: CompileOptions,\n): CompileResult {\n output.header(\"Compilation complete\");\n output.status(\"✓\", output.success(\n `${buckets.toCompile.length} compiled, ${buckets.unchanged.length} skipped, ${buckets.deleted.length} deleted`,\n ));\n if (options.review && generation.candidates.length > 0) {\n output.status(\"?\", output.info(\n `${generation.candidates.length} candidate(s) awaiting review — run \\`llmwiki review list\\``,\n ));\n } else if (buckets.toCompile.length > 0) {\n output.status(\"→\", output.dim('Next: llmwiki query \"your question here\"'));\n }\n\n const errors = [...generation.errors];\n for (const result of extractions) {\n if (result.concepts.length === 0) {\n errors.push(`No concepts extracted from ${result.sourceFile}`);\n }\n }\n\n const baseResult: CompileResult = {\n compiled: buckets.toCompile.length,\n skipped: buckets.unchanged.length,\n deleted: buckets.deleted.length,\n concepts: generation.pages.map((entry) => entry.concept.concept),\n pages: generation.pages.map((entry) => entry.slug),\n errors,\n };\n if (options.review) {\n baseResult.candidates = generation.candidates;\n }\n return baseResult;\n}\n\n/** Inner pipeline, runs under lock protection. Returns structured CompileResult. */\nasync function runCompilePipeline(\n root: string,\n options: CompileOptions,\n): Promise<CompileResult> {\n const state = await readState(root);\n const changes = await detectChanges(root, state);\n augmentWithAffectedSources(changes, findAffectedSources(state, changes));\n\n const buckets = bucketChanges(changes);\n if (buckets.toCompile.length === 0 && buckets.deleted.length === 0) {\n output.status(\"✓\", output.success(\"Nothing to compile — all sources up to date.\"));\n return { ...emptyCompileResult(), skipped: buckets.unchanged.length };\n }\n\n printChangesSummary(changes);\n // In review mode the pipeline contract is \"write candidates instead of\n // mutating wiki/\". Deletion bookkeeping (orphan marking + frozen-slug\n // persistence) writes directly into wiki/ and updates state.json, so we\n // defer it to the next non-review compile pass. Source-state persistence\n // for compiled sources is also review-deferred — those entries land at\n // approve time so unapproved candidates remain re-detectable on subsequent\n // compiles.\n if (!options.review) {\n await markDeletedAsOrphaned(root, buckets.deleted, state);\n }\n\n const frozenSlugs = findFrozenSlugs(state, changes);\n reportFrozenSlugs(frozenSlugs);\n\n const extractions = await runExtractionPhases(root, buckets.toCompile, state, changes);\n if (!options.review) {\n await freezeFailedExtractions(root, extractions, frozenSlugs);\n }\n\n const generation = await generatePagesPhase(root, extractions, frozenSlugs, options);\n\n if (!options.review) {\n await persistExtractionStates(root, extractions);\n if (frozenSlugs.size > 0) {\n await orphanUnownedFrozenPages(root, frozenSlugs);\n }\n await persistFrozenSlugs(root, frozenSlugs, extractions);\n await finalizeWiki(root, generation.pages);\n }\n return summarizeCompile(buckets, generation, extractions, options);\n}\n\n/** Append affected-source changes (logging each addition) to the change list. */\nfunction augmentWithAffectedSources(changes: SourceChange[], affected: string[]): void {\n for (const file of affected) {\n output.status(\"~\", output.info(`${file} [affected by shared concept]`));\n changes.push({ file, status: \"changed\" });\n }\n}\n\n/** Mark wiki pages owned solely by deleted sources as orphaned. */\nasync function markDeletedAsOrphaned(\n root: string,\n deleted: SourceChange[],\n state: WikiState,\n): Promise<void> {\n for (const del of deleted) {\n await markOrphaned(root, del.file, state);\n }\n}\n\n/** Log frozen slugs (shared concepts whose deletion-pinned content must persist). */\nfunction reportFrozenSlugs(frozenSlugs: Set<string>): void {\n for (const slug of frozenSlugs) {\n output.status(\"i\", output.dim(`Frozen: ${slug} (shared with deleted source)`));\n }\n}\n\n/**\n * Phase 1: extract concepts for the directly-changed batch, then expand to\n * any unchanged sources whose concepts overlap with newly extracted slugs.\n */\nasync function runExtractionPhases(\n root: string,\n toCompile: SourceChange[],\n state: WikiState,\n allChanges: SourceChange[],\n): Promise<ExtractionResult[]> {\n const extractions: ExtractionResult[] = [];\n for (const change of toCompile) {\n extractions.push(await extractForSource(root, change.file));\n }\n\n const lateAffected = findLateAffectedSources(extractions, state, allChanges);\n for (const file of lateAffected) {\n output.status(\"~\", output.info(`${file} [shares concept with new source]`));\n extractions.push(await extractForSource(root, file));\n }\n\n return extractions;\n}\n\n/** Resolve interlinks, regenerate index/MOC, refresh embeddings post-write. */\nasync function finalizeWiki(root: string, pages: MergedConcept[]): Promise<void> {\n const allChangedSlugs = pages.map((entry) => entry.slug);\n const allNewSlugs = pages.filter((entry) => entry.concept.is_new).map((entry) => entry.slug);\n\n if (allChangedSlugs.length > 0) {\n output.status(\"🔗\", output.info(\"Resolving interlinks...\"));\n await resolveLinks(root, allChangedSlugs, allNewSlugs);\n }\n\n await generateIndex(root);\n await generateMOC(root);\n await safelyUpdateEmbeddings(root, allChangedSlugs);\n}\n\n/** Print a summary of detected source file changes. */\nfunction printChangesSummary(changes: SourceChange[]): void {\n const iconMap: Record<string, string> = {\n new: \"+\", changed: \"~\", unchanged: \".\", deleted: \"-\",\n };\n const fmtMap: Record<string, (s: string) => string> = {\n new: output.success, changed: output.warn, unchanged: output.dim, deleted: output.error,\n };\n\n for (const c of changes) {\n const icon = iconMap[c.status] ?? \"?\";\n const fmt = fmtMap[c.status] ?? output.dim;\n output.status(icon, fmt(`${c.file} [${c.status}]`));\n }\n}\n\n/**\n * Phase 1: Extract concepts from a source without generating pages.\n * Returns extraction data for the generation phase.\n */\nasync function extractForSource(\n root: string,\n sourceFile: string,\n): Promise<ExtractionResult> {\n output.status(\"*\", output.info(`Extracting: ${sourceFile}`));\n\n const sourcePath = path.join(root, SOURCES_DIR, sourceFile);\n const sourceContent = await readFile(sourcePath, \"utf-8\");\n const existingIndex = await safeReadFile(path.join(root, INDEX_FILE));\n const concepts = await extractConcepts(sourceContent, existingIndex);\n\n if (concepts.length > 0) {\n const names = concepts.map((c) => c.concept).join(\", \");\n output.status(\"*\", output.dim(` Found ${concepts.length} concepts: ${names}`));\n }\n return { sourceFile, sourcePath, sourceContent, concepts };\n}\n\n/** A concept with all contributing sources merged for generation. */\ninterface MergedConcept {\n slug: string;\n concept: ExtractedConcept;\n sourceFiles: string[];\n combinedContent: string;\n}\n\n/**\n * Reconcile metadata from a later-extracted concept into an existing merged entry.\n * Called when multiple sources contribute the same slug — produces the most\n * pessimistic aggregate view of confidence, provenance, and contradictions.\n *\n * Rules:\n * - confidence: min (most pessimistic value wins)\n * - provenanceState: always 'merged' once two sources are involved\n * - contradictedBy: union by slug (deduplicating on slug identity)\n * - inferredParagraphs: max (any source claiming inference wins)\n */\nexport function reconcileConceptMetadata(\n existing: ExtractedConcept,\n incoming: ExtractedConcept,\n): ExtractedConcept {\n const reconciled = { ...existing };\n\n // Minimum confidence — the weaker source's score governs the whole page.\n if (typeof incoming.confidence === \"number\") {\n reconciled.confidence = typeof existing.confidence === \"number\"\n ? Math.min(existing.confidence, incoming.confidence)\n : incoming.confidence;\n }\n\n // Merged state is the canonical answer when multiple sources contribute.\n reconciled.provenanceState = \"merged\";\n\n // Union contradictedBy entries, deduplicating by slug.\n const refs = [...(existing.contradictedBy ?? [])];\n const seenSlugs = new Set(refs.map((r) => r.slug));\n for (const ref of incoming.contradictedBy ?? []) {\n if (!seenSlugs.has(ref.slug)) {\n refs.push(ref);\n seenSlugs.add(ref.slug);\n }\n }\n reconciled.contradictedBy = refs.length > 0 ? refs : undefined;\n\n // Max inferredParagraphs — any source flagging inference raises the count.\n if (typeof incoming.inferredParagraphs === \"number\") {\n reconciled.inferredParagraphs = typeof existing.inferredParagraphs === \"number\"\n ? Math.max(existing.inferredParagraphs, incoming.inferredParagraphs)\n : incoming.inferredParagraphs;\n }\n\n return reconciled;\n}\n\n/**\n * Merge extractions so each concept slug maps to ALL contributing sources.\n * When sources A and B both extract concept X, the LLM receives combined\n * content from both sources, producing a single page that reflects all\n * contributing material rather than just the last source processed.\n * Metadata is reconciled across all contributing concepts via\n * reconcileConceptMetadata so contradictions from later sources are not lost.\n */\nfunction mergeExtractions(\n extractions: ExtractionResult[],\n frozenSlugs: Set<string>,\n): MergedConcept[] {\n const bySlug = new Map<string, MergedConcept>();\n\n for (const result of extractions) {\n if (result.concepts.length === 0) continue;\n\n for (const concept of result.concepts) {\n const slug = slugify(concept.concept);\n if (frozenSlugs.has(slug)) continue;\n\n const existing = bySlug.get(slug);\n if (existing) {\n existing.concept = reconcileConceptMetadata(existing.concept, concept);\n existing.sourceFiles.push(result.sourceFile);\n existing.combinedContent += `\\n\\n--- SOURCE: ${result.sourceFile} ---\\n\\n${result.sourceContent}`;\n } else {\n bySlug.set(slug, {\n slug,\n concept,\n sourceFiles: [result.sourceFile],\n combinedContent: `--- SOURCE: ${result.sourceFile} ---\\n\\n${result.sourceContent}`,\n });\n }\n }\n }\n\n return Array.from(bySlug.values());\n}\n\n/** Outcome of generating a single merged concept page. */\ninterface MergedPageOutcome {\n error?: string;\n candidateId?: string;\n}\n\n/**\n * Generate a wiki page from merged source content.\n * For shared concepts, the LLM sees content from all contributing sources\n * and frontmatter records every source file. When `options.review` is set,\n * the rendered page is persisted as a review candidate instead of being\n * written into `wiki/`.\n */\nasync function generateMergedPage(\n root: string,\n entry: MergedConcept,\n options: CompileOptions,\n sourceStates: SourceStateMap,\n): Promise<MergedPageOutcome> {\n const fullPage = await renderMergedPageContent(root, entry);\n\n if (options.review) {\n return await persistReviewCandidate(root, entry, fullPage, sourceStates);\n }\n\n const pagePath = path.join(root, CONCEPTS_DIR, `${entry.slug}.md`);\n const error = await writePageIfValid(pagePath, fullPage, entry.concept.concept);\n return { error: error ?? undefined };\n}\n\n/** Persist a candidate JSON record for later review and report it on stdout. */\nasync function persistReviewCandidate(\n root: string,\n entry: MergedConcept,\n fullPage: string,\n sourceStates: SourceStateMap,\n): Promise<MergedPageOutcome> {\n const candidate: ReviewCandidate = await writeCandidate(root, {\n title: entry.concept.concept,\n slug: entry.slug,\n summary: entry.concept.summary,\n sources: entry.sourceFiles,\n body: fullPage,\n sourceStates: pickStatesForSources(sourceStates, entry.sourceFiles),\n });\n output.status(\"?\", output.info(`Candidate ready: ${candidate.id} (${entry.slug})`));\n return { candidateId: candidate.id };\n}\n\n/**\n * Call Claude to extract concepts from a source document.\n * @param sourceContent - Full source document text.\n * @param existingIndex - Current wiki index for deduplication.\n * @returns Parsed array of extracted concepts.\n */\nasync function extractConcepts(\n sourceContent: string,\n existingIndex: string,\n): Promise<ExtractedConcept[]> {\n const system = buildExtractionPrompt(sourceContent, existingIndex);\n const rawOutput = await callClaude({\n system,\n messages: [{ role: \"user\", content: \"Extract the key concepts from this source.\" }],\n tools: [CONCEPT_EXTRACTION_TOOL],\n });\n\n return parseConcepts(rawOutput);\n}\n\n\n/**\n * Validate and atomically write a wiki page, logging the result.\n * @param pagePath - Absolute path to write the page.\n * @param content - Full page content including frontmatter.\n * @param conceptTitle - Title for logging purposes.\n */\nasync function writePageIfValid(\n pagePath: string,\n content: string,\n conceptTitle: string,\n): Promise<string | null> {\n if (!validateWikiPage(content)) {\n output.status(\"!\", output.warn(`Invalid page for \"${conceptTitle}\" — skipped.`));\n return `Invalid page for \"${conceptTitle}\" — failed validation`;\n }\n\n await atomicWrite(pagePath, content);\n return null;\n}\n\n/**\n * Refresh the embeddings store without failing compilation.\n * Semantic search is a non-critical enhancement — missing API keys or\n * transient provider errors should produce a warning, not a broken build.\n */\nasync function safelyUpdateEmbeddings(root: string, changedSlugs: string[]): Promise<void> {\n try {\n await updateEmbeddings(root, changedSlugs);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n output.status(\"!\", output.warn(`Skipped embeddings update: ${message}`));\n }\n}\n\n/**\n * Update the persisted state for a compiled source file.\n * @param root - Project root directory.\n * @param sourcePath - Absolute path to the source file.\n * @param sourceFile - Filename within sources/.\n * @param concepts - Concepts extracted from this source.\n */\nasync function persistSourceState(\n root: string,\n sourcePath: string,\n sourceFile: string,\n concepts: ReturnType<typeof parseConcepts>,\n): Promise<void> {\n const hash = await hashFile(sourcePath);\n const entry: SourceState = {\n hash,\n concepts: concepts.map((c) => slugify(c.concept)),\n compiledAt: new Date().toISOString(),\n };\n\n await updateSourceState(root, sourceFile, entry);\n}\n","/**\n * Manages .llmwiki/state.json — the persistent compilation state that tracks\n * source file hashes and their compiled concepts. Enables incremental\n * compilation by detecting which sources have changed since last compile.\n *\n * Uses atomic writes (write to .tmp, then rename) to prevent corruption\n * from interrupted compiles.\n */\n\nimport { readFile, writeFile, rename, mkdir, copyFile } from \"fs/promises\";\nimport { existsSync } from \"fs\";\nimport path from \"path\";\nimport { LLMWIKI_DIR, STATE_FILE } from \"./constants.js\";\nimport type { WikiState, SourceState } from \"./types.js\";\n\nfunction emptyState(): WikiState {\n return { version: 1, indexHash: \"\", sources: {} };\n}\n\n/** Read .llmwiki/state.json, recovering from corruption gracefully. */\nexport async function readState(root: string): Promise<WikiState> {\n const filePath = path.join(root, STATE_FILE);\n\n if (!existsSync(filePath)) {\n return emptyState();\n }\n\n try {\n const raw = await readFile(filePath, \"utf-8\");\n return JSON.parse(raw) as WikiState;\n } catch {\n const bakPath = filePath + \".bak\";\n console.warn(`⚠ Corrupt state.json — backed up to ${bakPath}, starting fresh.`);\n await copyFile(filePath, bakPath);\n return emptyState();\n }\n}\n\n/** Atomically write state.json (write .tmp then rename). */\nexport async function writeState(root: string, state: WikiState): Promise<void> {\n const dir = path.join(root, LLMWIKI_DIR);\n await mkdir(dir, { recursive: true });\n\n const filePath = path.join(root, STATE_FILE);\n const tmpPath = filePath + \".tmp\";\n\n await writeFile(tmpPath, JSON.stringify(state, null, 2), \"utf-8\");\n await rename(tmpPath, filePath);\n}\n\n/**\n * Update a single source's entry in state after successful compilation.\n * Per-source granularity means interrupted compiles only reprocess incomplete sources.\n */\nexport async function updateSourceState(\n root: string,\n sourceFile: string,\n entry: SourceState,\n): Promise<void> {\n const state = await readState(root);\n state.sources[sourceFile] = entry;\n await writeState(root, state);\n}\n\n/** Remove a source entry from state (for deleted sources). */\nexport async function removeSourceState(\n root: string,\n sourceFile: string,\n): Promise<void> {\n const state = await readState(root);\n delete state.sources[sourceFile];\n await writeState(root, state);\n}\n","/**\n * Source-state snapshot helpers shared between the live compile path and the\n * review-candidate path.\n *\n * The compile pipeline normally persists a `SourceState` entry for every\n * extracted source so subsequent compiles can skip unchanged inputs. When\n * compile runs in `--review` mode, page writes are deferred — but the same\n * per-source state still needs to land on approval, otherwise approved\n * sources stay marked as \"new/changed\" forever and reproduce duplicate\n * candidates on every compile.\n *\n * This module produces a `Record<sourceFile, SourceState>` snapshot from the\n * extraction results so it can ride along inside each `ReviewCandidate` and\n * be flushed to `.llmwiki/state.json` at approval time.\n */\n\nimport path from \"path\";\nimport { hashFile } from \"./hasher.js\";\nimport { slugify } from \"../utils/markdown.js\";\nimport { SOURCES_DIR } from \"../utils/constants.js\";\nimport type { ExtractionResult } from \"./deps.js\";\nimport type { SourceState } from \"../utils/types.js\";\n\n/**\n * Compute a per-source state snapshot keyed by source filename.\n *\n * Hashes every contributing source once so each candidate carries the\n * incremental-state payload required to mark its sources compiled on\n * approval. Sources with no extracted concepts are skipped — we only mark\n * sources compiled when extraction succeeded, mirroring the live path's\n * behaviour.\n *\n * @param root - Project root directory.\n * @param extractions - Extraction results from the compile pipeline.\n * @returns Map of source filename → SourceState ready for state.json.\n */\nexport async function buildExtractionSourceStates(\n root: string,\n extractions: ExtractionResult[],\n): Promise<Record<string, SourceState>> {\n const snapshot: Record<string, SourceState> = {};\n const compiledAt = new Date().toISOString();\n\n for (const result of extractions) {\n if (result.concepts.length === 0) continue;\n snapshot[result.sourceFile] = await buildEntry(root, result, compiledAt);\n }\n\n return snapshot;\n}\n\n/** Build a single SourceState entry for one extraction result. */\nasync function buildEntry(\n root: string,\n result: ExtractionResult,\n compiledAt: string,\n): Promise<SourceState> {\n const filePath = path.join(root, SOURCES_DIR, result.sourceFile);\n const hash = await hashFile(filePath);\n return {\n hash,\n concepts: result.concepts.map((concept) => slugify(concept.concept)),\n compiledAt,\n };\n}\n\n/**\n * Filter a global source-state snapshot down to entries relevant to a\n * specific candidate. A candidate carries only the source-state entries\n * for sources that actually contributed to it, so on approval we can\n * persist a minimal, accurate slice into state.json.\n *\n * @param allStates - Global per-source snapshot from buildExtractionSourceStates.\n * @param sourceFiles - Source filenames that contributed to the candidate.\n */\nexport function pickStatesForSources(\n allStates: Record<string, SourceState>,\n sourceFiles: string[],\n): Record<string, SourceState> {\n const picked: Record<string, SourceState> = {};\n for (const file of sourceFiles) {\n const entry = allStates[file];\n if (entry) picked[file] = entry;\n }\n return picked;\n}\n","/**\n * Source file hashing for change detection.\n * Computes SHA-256 hashes of source files and compares them against\n * previously stored state to determine which files need recompilation.\n * This enables incremental compilation — only changed or new sources\n * are sent through the LLM pipeline.\n */\n\nimport { createHash } from \"node:crypto\";\nimport { readFile, readdir } from \"fs/promises\";\nimport path from \"path\";\nimport { SOURCES_DIR } from \"../utils/constants.js\";\nimport type { WikiState, SourceChange } from \"../utils/types.js\";\n\n/**\n * Read a file and compute its SHA-256 hash.\n * @param filePath - Absolute path to the file to hash.\n * @returns Hex-encoded SHA-256 digest of the file contents.\n */\nexport async function hashFile(filePath: string): Promise<string> {\n const content = await readFile(filePath, \"utf-8\");\n return createHash(\"sha256\").update(content).digest(\"hex\");\n}\n\n/**\n * Scan the sources/ directory and compare file hashes against previous state\n * to identify new, changed, unchanged, and deleted source files.\n * @param root - Project root directory containing the sources/ folder.\n * @param prevState - The previously persisted WikiState to compare against.\n * @returns Array of SourceChange entries describing each file's status.\n */\nexport async function detectChanges(\n root: string,\n prevState: WikiState,\n): Promise<SourceChange[]> {\n const sourcesPath = path.join(root, SOURCES_DIR);\n const currentFiles = await listSourceFiles(sourcesPath);\n const changes: SourceChange[] = [];\n\n for (const file of currentFiles) {\n const status = await classifyFile(root, file, prevState);\n changes.push({ file, status });\n }\n\n const deletedChanges = findDeletedFiles(currentFiles, prevState);\n changes.push(...deletedChanges);\n\n return changes;\n}\n\n/**\n * List all markdown files in the sources directory.\n * @param sourcesPath - Absolute path to the sources/ directory.\n * @returns Array of filenames (not full paths).\n */\nasync function listSourceFiles(sourcesPath: string): Promise<string[]> {\n try {\n const entries = await readdir(sourcesPath);\n return entries.filter((f) => f.endsWith(\".md\"));\n } catch {\n return [];\n }\n}\n\n/**\n * Classify a single source file as new, changed, or unchanged.\n * @param root - Project root directory.\n * @param file - Filename within sources/.\n * @param prevState - Previous compilation state.\n * @returns The change status for this file.\n */\nasync function classifyFile(\n root: string,\n file: string,\n prevState: WikiState,\n): Promise<SourceChange[\"status\"]> {\n const filePath = path.join(root, SOURCES_DIR, file);\n const hash = await hashFile(filePath);\n const prev = prevState.sources[file];\n\n if (!prev) return \"new\";\n if (prev.hash !== hash) return \"changed\";\n return \"unchanged\";\n}\n\n/**\n * Find source files present in previous state but missing from disk.\n * @param currentFiles - Files currently on disk.\n * @param prevState - Previous compilation state.\n * @returns Array of SourceChange entries for deleted files.\n */\nfunction findDeletedFiles(\n currentFiles: string[],\n prevState: WikiState,\n): SourceChange[] {\n const currentSet = new Set(currentFiles);\n return Object.keys(prevState.sources)\n .filter((file) => !currentSet.has(file))\n .map((file) => ({ file, status: \"deleted\" as const }));\n}\n","/**\n * Anthropic LLM provider implementation.\n *\n * Wraps the @anthropic-ai/sdk to implement the LLMProvider interface.\n * Handles complete, streaming, and tool-use calls against Claude models.\n */\n\nimport Anthropic, { type ClientOptions } from \"@anthropic-ai/sdk\";\nimport type { LLMProvider, LLMMessage, LLMTool } from \"../utils/provider.js\";\nimport { EMBEDDING_MODELS } from \"../utils/constants.js\";\n\nconst VOYAGE_EMBEDDINGS_URL = \"https://api.voyageai.com/v1/embeddings\";\n\n/**\n * Builds the client options for the Anthropic SDK.\n *\n * Handles optional baseURL and filters out empty values so the SDK\n * can fall back to its internal defaults when not specified.\n */\ninterface AnthropicProviderOptions {\n apiKey?: string;\n authToken?: string;\n baseURL?: string;\n}\n\nexport function buildAnthropicClientOptions(\n options: AnthropicProviderOptions = {},\n): ClientOptions {\n const trimmedBaseURL = options.baseURL?.trim();\n const trimmedApiKey = options.apiKey?.trim();\n const trimmedAuthToken = options.authToken?.trim();\n\n const result: ClientOptions = {};\n\n if (trimmedApiKey) {\n result.apiKey = trimmedApiKey;\n }\n if (trimmedAuthToken) {\n result.authToken = trimmedAuthToken;\n }\n\n if (!trimmedBaseURL) {\n return result;\n }\n\n const normalizedBaseURL =\n trimmedBaseURL.endsWith(\"/\") && trimmedBaseURL.length > 1\n ? trimmedBaseURL.slice(0, -1)\n : trimmedBaseURL;\n\n result.baseURL = normalizedBaseURL;\n return result;\n}\n\n\n/** Anthropic-backed LLM provider using the official SDK. */\nexport class AnthropicProvider implements LLMProvider {\n private readonly client: Anthropic;\n private readonly model: string;\n\n constructor(model: string, options: AnthropicProviderOptions = {}) {\n this.model = model;\n this.client = new Anthropic(buildAnthropicClientOptions(options));\n }\n\n /** Send a single non-streaming completion request. */\n async complete(system: string, messages: LLMMessage[], maxTokens: number): Promise<string> {\n const response = await this.client.messages.create({\n model: this.model,\n max_tokens: maxTokens,\n system,\n messages,\n });\n\n const textBlock = response.content.find((block) => block.type === \"text\");\n return textBlock?.type === \"text\" ? textBlock.text : \"\";\n }\n\n /** Stream a completion, invoking onToken for each text chunk. */\n async stream(\n system: string,\n messages: LLMMessage[],\n maxTokens: number,\n onToken?: (text: string) => void,\n ): Promise<string> {\n const stream = this.client.messages.stream({\n model: this.model,\n max_tokens: maxTokens,\n system,\n messages,\n });\n\n let fullText = \"\";\n for await (const event of stream) {\n if (event.type === \"content_block_delta\" && event.delta.type === \"text_delta\") {\n fullText += event.delta.text;\n onToken?.(event.delta.text);\n }\n }\n\n return fullText;\n }\n\n /** Call Claude with tool definitions and return the parsed tool input as JSON. */\n async toolCall(\n system: string,\n messages: LLMMessage[],\n tools: LLMTool[],\n maxTokens: number,\n ): Promise<string> {\n const anthropicTools: Anthropic.Tool[] = tools.map((t) => ({\n name: t.name,\n description: t.description,\n input_schema: t.input_schema as Anthropic.Tool.InputSchema,\n }));\n\n const response = await this.client.messages.create({\n model: this.model,\n max_tokens: maxTokens,\n system,\n messages,\n tools: anthropicTools,\n });\n\n const toolBlock = response.content.find((block) => block.type === \"tool_use\");\n if (toolBlock?.type === \"tool_use\") {\n return JSON.stringify(toolBlock.input);\n }\n\n const textBlock = response.content.find((block) => block.type === \"text\");\n return textBlock?.type === \"text\" ? textBlock.text : \"\";\n }\n\n /**\n * Produce a single embedding vector via the Voyage API.\n *\n * Anthropic does not ship a first-party embeddings endpoint, so we delegate\n * to Voyage (their recommended partner). Requires VOYAGE_API_KEY.\n */\n async embed(text: string): Promise<number[]> {\n const apiKey = process.env.VOYAGE_API_KEY?.trim();\n if (!apiKey) {\n throw new Error(\n \"VOYAGE_API_KEY is not set. Anthropic embeddings use Voyage — set VOYAGE_API_KEY to enable semantic search.\",\n );\n }\n\n const response = await fetch(VOYAGE_EMBEDDINGS_URL, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ input: text, model: EMBEDDING_MODELS.anthropic }),\n });\n\n if (!response.ok) {\n const detail = await response.text();\n throw new Error(`Voyage embeddings request failed (${response.status}): ${detail}`);\n }\n\n const json = (await response.json()) as { data?: Array<{ embedding?: number[] }> };\n const vector = json.data?.[0]?.embedding;\n if (!Array.isArray(vector)) {\n throw new Error(\"Voyage embeddings response did not include a vector.\");\n }\n return vector;\n }\n}\n","/**\n * OpenAI LLM provider implementation.\n *\n * Wraps the openai npm package to implement the LLMProvider interface.\n * Translates Anthropic-style tool schemas (input_schema) to OpenAI format (parameters).\n */\n\nimport OpenAI from \"openai\";\nimport type { LLMProvider, LLMMessage, LLMTool } from \"../utils/provider.js\";\nimport { EMBEDDING_MODELS } from \"../utils/constants.js\";\n\n/** Construction options for an OpenAI-compatible provider. */\ninterface OpenAIProviderOptions {\n baseURL?: string;\n apiKey?: string;\n embeddingsBaseURL?: string;\n embeddingModel?: string;\n}\n\n/** Translate an Anthropic-style LLMTool to an OpenAI ChatCompletionTool. */\nexport function translateToolToOpenAI(\n tool: LLMTool,\n): OpenAI.ChatCompletionTool {\n return {\n type: \"function\",\n function: {\n name: tool.name,\n description: tool.description,\n parameters: tool.input_schema,\n },\n };\n}\n\n/** OpenAI-backed LLM provider. */\nexport class OpenAIProvider implements LLMProvider {\n protected readonly client: OpenAI;\n protected readonly embeddingsClient: OpenAI;\n protected readonly model: string;\n protected readonly configuredEmbeddingModel?: string;\n\n constructor(model: string, options: OpenAIProviderOptions = {}) {\n this.model = model;\n this.configuredEmbeddingModel = options.embeddingModel;\n // The OpenAI SDK validates OPENAI_API_KEY at construction time.\n // Pass the key explicitly so the provider controls when validation happens.\n const resolvedKey = options.apiKey ?? process.env.OPENAI_API_KEY ?? \"\";\n this.client = new OpenAI({\n apiKey: resolvedKey,\n baseURL: options.baseURL ?? null,\n });\n this.embeddingsClient = options.embeddingsBaseURL\n ? new OpenAI({ apiKey: resolvedKey, baseURL: options.embeddingsBaseURL })\n : this.client;\n }\n\n /** Send a single non-streaming completion request. */\n async complete(system: string, messages: LLMMessage[], maxTokens: number): Promise<string> {\n const response = await this.client.chat.completions.create({\n model: this.model,\n max_tokens: maxTokens,\n messages: [{ role: \"system\", content: system }, ...messages],\n });\n\n return response.choices[0]?.message?.content ?? \"\";\n }\n\n /** Stream a completion, invoking onToken for each text chunk. */\n async stream(\n system: string,\n messages: LLMMessage[],\n maxTokens: number,\n onToken?: (text: string) => void,\n ): Promise<string> {\n const stream = await this.client.chat.completions.create({\n model: this.model,\n max_tokens: maxTokens,\n messages: [{ role: \"system\", content: system }, ...messages],\n stream: true,\n });\n\n let fullText = \"\";\n for await (const chunk of stream) {\n const delta = chunk.choices[0]?.delta?.content;\n if (delta) {\n fullText += delta;\n onToken?.(delta);\n }\n }\n\n return fullText;\n }\n\n /** Call the model with tool definitions and return the parsed tool input as JSON. */\n async toolCall(\n system: string,\n messages: LLMMessage[],\n tools: LLMTool[],\n maxTokens: number,\n ): Promise<string> {\n const openaiTools = tools.map(translateToolToOpenAI);\n\n const response = await this.client.chat.completions.create({\n model: this.model,\n max_tokens: maxTokens,\n messages: [{ role: \"system\", content: system }, ...messages],\n tools: openaiTools,\n });\n\n const toolCalls = response.choices[0]?.message?.tool_calls;\n if (toolCalls && toolCalls.length > 0) {\n return toolCalls[0].function.arguments;\n }\n\n return response.choices[0]?.message?.content ?? \"\";\n }\n\n /**\n * Produce a single embedding vector via the OpenAI embeddings API.\n * Subclasses (e.g. Ollama) override embeddingModel() to pick a different model.\n */\n async embed(text: string): Promise<number[]> {\n const response = await this.embeddingsClient.embeddings.create({\n model: this.embeddingModel(),\n input: text,\n });\n\n const vector = response.data[0]?.embedding;\n if (!Array.isArray(vector)) {\n throw new Error(\"OpenAI embeddings response did not include a vector.\");\n }\n return vector;\n }\n\n /** Default embedding model for this provider. Subclasses may override. */\n protected embeddingModel(): string {\n return this.configuredEmbeddingModel ?? EMBEDDING_MODELS.openai;\n }\n}\n","/**\n * Ollama LLM provider implementation.\n *\n * Extends OpenAIProvider since Ollama exposes an OpenAI-compatible API.\n * Overrides only the constructor to set baseURL and disable API key auth.\n */\n\nimport { OpenAIProvider } from \"./openai.js\";\nimport { EMBEDDING_MODELS } from \"../utils/constants.js\";\n\n/** Construction options for an Ollama-compatible provider. */\ninterface OllamaProviderOptions {\n baseURL: string;\n embeddingsBaseURL?: string;\n embeddingModel?: string;\n}\n\n/** Ollama-backed LLM provider using the OpenAI-compatible endpoint. */\nexport class OllamaProvider extends OpenAIProvider {\n constructor(model: string, options: OllamaProviderOptions) {\n super(model, {\n baseURL: options.baseURL,\n apiKey: \"ollama\",\n embeddingsBaseURL: options.embeddingsBaseURL,\n embeddingModel: options.embeddingModel,\n });\n }\n\n /** Ollama ships a dedicated embedding model (nomic-embed-text). */\n protected override embeddingModel(): string {\n return this.configuredEmbeddingModel ?? EMBEDDING_MODELS.ollama;\n }\n}\n","/**\n * MiniMax LLM provider implementation.\n *\n * Extends OpenAIProvider since MiniMax exposes an OpenAI-compatible API.\n * Overrides only the constructor to set MiniMax's base URL and API key.\n */\n\nimport { OpenAIProvider } from \"./openai.js\";\n\n/** MiniMax API base URL. */\nconst MINIMAX_BASE_URL = \"https://api.minimax.io/v1\";\n\n/** MiniMax-backed LLM provider using the OpenAI-compatible endpoint. */\nexport class MiniMaxProvider extends OpenAIProvider {\n constructor(model: string, apiKey: string) {\n super(model, { baseURL: MINIMAX_BASE_URL, apiKey });\n }\n}\n","/**\n * Claude settings fallback helpers.\n *\n * Provides a narrow, read-only integration with `~/.claude/settings.json`.\n * We only read the `env` object and only extract Anthropic-related values that\n * llmwiki can safely consume. Explicit process env values remain higher priority.\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\n\nconst CLAUDE_SETTINGS_PATH_ENV = \"LLMWIKI_CLAUDE_SETTINGS_PATH\";\n\ninterface ClaudeSettingsEnv {\n ANTHROPIC_API_KEY?: string;\n ANTHROPIC_AUTH_TOKEN?: string;\n ANTHROPIC_BASE_URL?: string;\n ANTHROPIC_MODEL?: string;\n}\n\ninterface AnthropicAuthConfig {\n apiKey?: string;\n authToken?: string;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction normalize(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction resolveClaudeSettingsPath(env: NodeJS.ProcessEnv): string {\n return env[CLAUDE_SETTINGS_PATH_ENV] ?? path.join(homedir(), \".claude\", \"settings.json\");\n}\n\nfunction readClaudeSettingsFile(settingsPath: string): string | undefined {\n try {\n return readFileSync(settingsPath, \"utf8\");\n } catch (err) {\n if (isRecord(err) && err.code === \"ENOENT\") {\n return undefined;\n }\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(`Failed to read Claude settings at \"${settingsPath}\": ${message}`);\n }\n}\n\nexport function readClaudeSettingsEnv(env: NodeJS.ProcessEnv = process.env): ClaudeSettingsEnv | undefined {\n const settingsPath = resolveClaudeSettingsPath(env);\n const raw = readClaudeSettingsFile(settingsPath);\n if (!raw) return undefined;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(`Failed to parse Claude settings at \"${settingsPath}\": ${message}`);\n }\n\n if (!isRecord(parsed) || !isRecord(parsed.env)) {\n return undefined;\n }\n\n const values: ClaudeSettingsEnv = {\n ANTHROPIC_API_KEY: normalize(parsed.env.ANTHROPIC_API_KEY),\n ANTHROPIC_AUTH_TOKEN: normalize(parsed.env.ANTHROPIC_AUTH_TOKEN),\n ANTHROPIC_BASE_URL: normalize(parsed.env.ANTHROPIC_BASE_URL),\n ANTHROPIC_MODEL: normalize(parsed.env.ANTHROPIC_MODEL),\n };\n\n if (!values.ANTHROPIC_API_KEY && !values.ANTHROPIC_AUTH_TOKEN && !values.ANTHROPIC_BASE_URL && !values.ANTHROPIC_MODEL) {\n return undefined;\n }\n return values;\n}\n\nfunction tryReadClaudeSettingsEnv(env: NodeJS.ProcessEnv): ClaudeSettingsEnv | undefined {\n try {\n return readClaudeSettingsEnv(env);\n } catch {\n return undefined;\n }\n}\n\nfunction validateAnthropicBaseURL(value: string): string {\n const normalized = value.trim();\n try {\n const parsed = new URL(normalized);\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n throw new Error(\"Must use http:// or https:// protocol.\");\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Must be a valid http(s) URL.\";\n throw new Error(`Invalid ANTHROPIC_BASE_URL: \"${normalized}\". ${message}`);\n }\n return normalized;\n}\n\nexport function resolveAnthropicAuthFromEnv(env: NodeJS.ProcessEnv = process.env): AnthropicAuthConfig {\n const explicitApiKey = normalize(env.ANTHROPIC_API_KEY);\n if (explicitApiKey) return { apiKey: explicitApiKey };\n\n const explicitAuthToken = normalize(env.ANTHROPIC_AUTH_TOKEN);\n if (explicitAuthToken) return { authToken: explicitAuthToken };\n\n const fallback = readClaudeSettingsEnv(env);\n if (fallback?.ANTHROPIC_API_KEY) return { apiKey: fallback.ANTHROPIC_API_KEY };\n if (fallback?.ANTHROPIC_AUTH_TOKEN) return { authToken: fallback.ANTHROPIC_AUTH_TOKEN };\n return {};\n}\n\nexport function resolveAnthropicModelFromEnv(env: NodeJS.ProcessEnv = process.env): string | undefined {\n const explicitModel = env.LLMWIKI_MODEL;\n if (explicitModel !== undefined) return explicitModel;\n return tryReadClaudeSettingsEnv(env)?.ANTHROPIC_MODEL;\n}\n\nexport function resolveAnthropicBaseURLFromEnv(env: NodeJS.ProcessEnv = process.env): string | undefined {\n const explicitBaseURL = normalize(env.ANTHROPIC_BASE_URL);\n if (explicitBaseURL) return validateAnthropicBaseURL(explicitBaseURL);\n\n const fallbackBaseURL = tryReadClaudeSettingsEnv(env)?.ANTHROPIC_BASE_URL;\n if (!fallbackBaseURL) return undefined;\n return validateAnthropicBaseURL(fallbackBaseURL);\n}\n","/**\n * LLM provider abstraction layer.\n *\n * Defines the LLMProvider interface and a factory function that reads\n * LLMWIKI_PROVIDER and LLMWIKI_MODEL env vars to instantiate the\n * appropriate backend (Anthropic, OpenAI, Ollama, or MiniMax).\n */\n\nimport { DEFAULT_PROVIDER, PROVIDER_MODELS, OLLAMA_DEFAULT_HOST } from \"./constants.js\";\nimport { AnthropicProvider } from \"../providers/anthropic.js\";\nimport { OpenAIProvider } from \"../providers/openai.js\";\nimport { OllamaProvider } from \"../providers/ollama.js\";\nimport { MiniMaxProvider } from \"../providers/minimax.js\";\nimport {\n resolveAnthropicAuthFromEnv,\n resolveAnthropicBaseURLFromEnv,\n resolveAnthropicModelFromEnv,\n} from \"./claude-settings.js\";\n\n/** A single message in an LLM conversation. */\nexport interface LLMMessage {\n role: \"user\" | \"assistant\";\n content: string;\n}\n\n/** A tool definition in Anthropic-style format (used as the canonical shape). */\nexport interface LLMTool {\n name: string;\n description: string;\n input_schema: Record<string, unknown>;\n}\n\n/** Provider-agnostic interface for LLM backends. */\nexport interface LLMProvider {\n complete(system: string, messages: LLMMessage[], maxTokens: number): Promise<string>;\n stream(\n system: string,\n messages: LLMMessage[],\n maxTokens: number,\n onToken?: (text: string) => void,\n ): Promise<string>;\n toolCall(\n system: string,\n messages: LLMMessage[],\n tools: LLMTool[],\n maxTokens: number,\n ): Promise<string>;\n /** Return a single embedding vector for the given text. */\n embed(text: string): Promise<number[]>;\n}\n\nconst SUPPORTED_PROVIDERS: ReadonlySet<string> = new Set([\"anthropic\", \"openai\", \"ollama\", \"minimax\"]);\n\n/**\n * Factory that returns the appropriate LLMProvider based on env vars.\n * Reads LLMWIKI_PROVIDER (default \"anthropic\") and LLMWIKI_MODEL\n * (defaults per provider from PROVIDER_MODELS).\n *\n * Direct process.env access is acceptable here as this is a system boundary.\n */\nexport function getProvider(): LLMProvider {\n const providerName = getProviderName();\n\n switch (providerName) {\n case \"anthropic\":\n return getAnthropicProvider();\n case \"openai\":\n return new OpenAIProvider(getModelForProvider(\"openai\"), {\n baseURL: readOptionalEnv(\"OPENAI_BASE_URL\"),\n embeddingsBaseURL: readOptionalEnv(\"OPENAI_EMBEDDINGS_BASE_URL\"),\n embeddingModel: readOptionalEnv(\"LLMWIKI_EMBEDDING_MODEL\"),\n });\n case \"ollama\":\n return new OllamaProvider(getModelForProvider(\"ollama\"), {\n baseURL: readOptionalEnv(\"OLLAMA_HOST\") ?? OLLAMA_DEFAULT_HOST,\n embeddingsBaseURL: readOptionalEnv(\"OLLAMA_EMBEDDINGS_HOST\"),\n embeddingModel: readOptionalEnv(\"LLMWIKI_EMBEDDING_MODEL\"),\n });\n case \"minimax\":\n return getMiniMaxProvider();\n default:\n throw new Error(`Unhandled provider: ${providerName}`);\n }\n}\n\nfunction readOptionalEnv(name: string): string | undefined {\n const value = process.env[name]?.trim();\n return value ? value : undefined;\n}\n\nfunction getModelForProvider(providerName: \"openai\" | \"ollama\" | \"minimax\"): string {\n return process.env.LLMWIKI_MODEL ?? PROVIDER_MODELS[providerName];\n}\n\nfunction getMiniMaxProvider(): MiniMaxProvider {\n const apiKey = process.env.MINIMAX_API_KEY;\n if (!apiKey) {\n throw new Error(\n \"MiniMax provider requires MINIMAX_API_KEY environment variable.\\n\" +\n ' Set it with: export MINIMAX_API_KEY=your_key',\n );\n }\n return new MiniMaxProvider(getModelForProvider(\"minimax\"), apiKey);\n}\n\nfunction getAnthropicProvider(): AnthropicProvider {\n const model = resolveAnthropicModelFromEnv() ?? PROVIDER_MODELS.anthropic;\n const baseURL = resolveAnthropicBaseURLFromEnv();\n const auth = resolveAnthropicAuthFromEnv();\n\n return new AnthropicProvider(model, {\n baseURL,\n ...auth,\n });\n}\n\nfunction getProviderName(): string {\n const providerName = process.env.LLMWIKI_PROVIDER ?? DEFAULT_PROVIDER;\n if (!SUPPORTED_PROVIDERS.has(providerName)) {\n throw new Error(\n `Unknown provider \"${providerName}\". Supported: ${[...SUPPORTED_PROVIDERS].join(\", \")}`,\n );\n }\n return providerName;\n}\n\n/** Expose the resolved provider name for callers that need model lookup. */\nexport function getActiveProviderName(): string {\n return getProviderName();\n}\n","/**\n * Shared LLM helper with provider abstraction.\n *\n * Provides callClaude() for backward compatibility — delegates to the\n * active LLMProvider while preserving retry logic with exponential backoff.\n * The provider is selected via LLMWIKI_PROVIDER env var (see provider.ts).\n */\n\nimport { RETRY_COUNT, RETRY_BASE_MS, RETRY_MULTIPLIER } from \"./constants.js\";\nimport { getProvider } from \"./provider.js\";\nimport type { LLMMessage, LLMTool } from \"./provider.js\";\n\n/** Sleep for a given number of milliseconds. */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\ninterface CallClaudeOptions {\n system: string;\n messages: LLMMessage[];\n tools?: LLMTool[];\n maxTokens?: number;\n stream?: boolean;\n onToken?: (text: string) => void;\n}\n\n/**\n * Call the active LLM provider with retry logic.\n * Supports streaming, tool-use, and basic completion modes.\n * Preserves the original callClaude interface for backward compatibility.\n */\nexport async function callClaude(options: CallClaudeOptions): Promise<string> {\n const { system, messages, tools, maxTokens = 4096, stream = false, onToken } = options;\n const provider = getProvider();\n\n for (let attempt = 0; attempt <= RETRY_COUNT; attempt++) {\n try {\n if (stream) {\n return await provider.stream(system, messages, maxTokens, onToken);\n }\n\n if (tools && tools.length > 0) {\n return await provider.toolCall(system, messages, tools, maxTokens);\n }\n\n return await provider.complete(system, messages, maxTokens);\n } catch (error) {\n if (attempt === RETRY_COUNT) throw error;\n\n const delayMs = RETRY_BASE_MS * Math.pow(RETRY_MULTIPLIER, attempt);\n const errMsg = error instanceof Error ? error.message : String(error);\n console.warn(`⚠ API call failed (attempt ${attempt + 1}/${RETRY_COUNT + 1}): ${errMsg}`);\n console.warn(` Retrying in ${delayMs / 1000}s...`);\n await sleep(delayMs);\n }\n }\n\n throw new Error(\"Unreachable\");\n}\n","/**\n * PID-based lock file for preventing concurrent compilation.\n *\n * Fresh acquisition uses O_CREAT | O_EXCL (the 'wx' flag) for atomic lock\n * creation — the kernel guarantees only one process can create the file.\n *\n * Stale lock reclamation uses a two-lock protocol:\n * 1. Acquire a reclamation lock (.llmwiki/lock.reclaim) via 'wx' to serialize\n * all processes attempting to reclaim the same stale main lock.\n * 2. Re-verify the main lock is still stale (another reclaimer may have\n * already fixed it).\n * 3. unlink + tryCreateLock('wx') on the main lock — safe because we hold\n * exclusive reclamation access.\n * 4. Release the reclamation lock in a finally block.\n *\n * The reclamation lock itself can become stale if a process crashes during\n * the brief reclamation window. When that happens, acquireReclaimLock only\n * cleans up the stale file — it does NOT retry acquisition in the same call.\n * This eliminates the unlink-then-create race that would allow two processes\n * to both hold the reclaim lock. The outer retry loop in acquireLock handles\n * convergence: first pass cleans up the stale reclaim lock, second pass\n * acquires it cleanly via 'wx'.\n */\n\nimport { open, readFile, unlink, mkdir } from \"fs/promises\";\nimport path from \"path\";\nimport { LLMWIKI_DIR, LOCK_FILE } from \"./constants.js\";\nimport * as output from \"./output.js\";\n\nconst RECLAIM_SUFFIX = \".reclaim\";\nconst MAX_ACQUIRE_ATTEMPTS = 2;\n\n/** Check whether a process with the given PID is still running. */\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Acquire the compilation lock. Returns true if acquired, false if busy.\n *\n * Retries up to MAX_ACQUIRE_ATTEMPTS times to handle the case where the\n * first attempt cleans up a stale reclamation lock but cannot acquire it\n * in the same call (to avoid the double-winner race).\n */\nexport async function acquireLock(root: string): Promise<boolean> {\n const lockPath = path.join(root, LOCK_FILE);\n await mkdir(path.join(root, LLMWIKI_DIR), { recursive: true });\n\n for (let attempt = 0; attempt < MAX_ACQUIRE_ATTEMPTS; attempt++) {\n // Try atomic create — fails if file already exists\n const created = await tryCreateLock(lockPath);\n if (created) return true;\n\n // Lock exists. Check if the holding process is dead.\n const stale = await isLockStale(lockPath);\n if (!stale) {\n output.status(\"!\", output.warn(\"Another compilation is running.\"));\n return false;\n }\n\n // Stale lock — serialize reclamation via a second lock.\n const reclaimed = await reclaimStaleLock(root, lockPath);\n if (reclaimed) return true;\n\n // Reclamation failed (e.g. cleaned up stale reclaim lock). Retry.\n }\n\n output.status(\"!\", output.warn(\"Could not acquire lock after retrying.\"));\n return false;\n}\n\n/**\n * Reclaim a stale main lock using a serialized two-lock protocol.\n *\n * Acquires .llmwiki/lock.reclaim (via 'wx') so that only one process performs\n * the unlink + recreate sequence at a time. Re-verifies staleness under\n * the reclamation lock in case another process already fixed it.\n * @param root - Project root directory.\n * @param lockPath - Absolute path to the main lock file.\n */\nasync function reclaimStaleLock(root: string, lockPath: string): Promise<boolean> {\n const reclaimPath = lockPath + RECLAIM_SUFFIX;\n\n const gotReclaimLock = await acquireReclaimLock(reclaimPath);\n if (!gotReclaimLock) return false;\n\n try {\n // Re-verify under exclusive reclamation access.\n // Another reclaimer may have already fixed the main lock.\n if (!(await isLockStale(lockPath))) {\n return false;\n }\n\n // Still stale. Safe to reclaim — we're the only reclaimer.\n try { await unlink(lockPath); } catch { /* already gone */ }\n\n const acquired = await tryCreateLock(lockPath);\n if (acquired) {\n output.status(\"i\", output.dim(\"Reclaimed stale lock from dead process.\"));\n }\n return acquired;\n } finally {\n try { await unlink(reclaimPath); } catch { /* cleanup best-effort */ }\n }\n}\n\n/**\n * Acquire the reclamation lock. Uses 'wx' for atomic creation.\n *\n * If the reclaim lock is stale (holder crashed during reclamation), this\n * function ONLY cleans up the stale file and returns false. It does NOT\n * retry acquisition in the same call. This is the key safety property:\n * unlink and create never happen in the same call, so two processes that\n * both see a stale reclaim lock will both clean up (harmless — second\n * unlink gets ENOENT) and both return false. Neither holds the reclaim\n * lock, so neither proceeds to touch the main lock. The outer retry loop\n * in acquireLock converges on the next attempt via a clean 'wx'.\n * @param reclaimPath - Absolute path to the reclamation lock file.\n */\nasync function acquireReclaimLock(reclaimPath: string): Promise<boolean> {\n if (await tryCreateLock(reclaimPath)) return true;\n\n // Reclaim lock exists. If its holder is alive, back off.\n if (!(await isLockStale(reclaimPath))) return false;\n\n // Stale reclaim lock — clean it up but do NOT retry in this call.\n // Retrying here would reintroduce the unlink+create race.\n try { await unlink(reclaimPath); } catch { /* already gone */ }\n return false;\n}\n\n/**\n * Atomically create the lock file with our PID.\n * Returns true if we created it, false if it already exists.\n */\nasync function tryCreateLock(lockPath: string): Promise<boolean> {\n try {\n const fd = await open(lockPath, \"wx\");\n await fd.writeFile(String(process.pid), \"utf-8\");\n await fd.close();\n return true;\n } catch (err: unknown) {\n if (err instanceof Error && \"code\" in err && (err as NodeJS.ErrnoException).code === \"EEXIST\") {\n return false;\n }\n throw err;\n }\n}\n\n/** Check if an existing lock is stale (holding process is dead). */\nasync function isLockStale(lockPath: string): Promise<boolean> {\n try {\n const content = await readFile(lockPath, \"utf-8\");\n const pid = parseInt(content.trim(), 10);\n if (isNaN(pid)) return true;\n return !isProcessAlive(pid);\n } catch {\n return true;\n }\n}\n\n/** Release the compilation lock. Safe to call even if lock doesn't exist. */\nexport async function releaseLock(root: string): Promise<void> {\n const lockPath = path.join(root, LOCK_FILE);\n try {\n await unlink(lockPath);\n } catch {\n // Lock already removed or never existed\n }\n}\n","/**\n * LLM prompt templates and tool schemas for the compilation pipeline.\n * Contains the Anthropic tool definition for concept extraction,\n * prompt builders for both extraction and page generation phases,\n * and a parser for the structured tool output.\n */\n\nimport type {\n ContradictionRef,\n ExtractedConcept,\n ProvenanceState,\n} from \"../utils/types.js\";\n\n/** Allowed provenance state strings emitted by the LLM tool schema. */\nconst PROVENANCE_STATE_VALUES: ProvenanceState[] = [\n \"extracted\",\n \"merged\",\n \"inferred\",\n \"ambiguous\",\n];\n\n/**\n * Anthropic Tool definition for extracting knowledge concepts from a source.\n * Used with callClaude's tool_use mode to get structured concept data.\n */\nexport const CONCEPT_EXTRACTION_TOOL = {\n name: \"extract_concepts\",\n description: \"Extract knowledge concepts from a source document\",\n input_schema: {\n type: \"object\" as const,\n properties: {\n concepts: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n concept: {\n type: \"string\",\n description: \"Human-readable concept title\",\n },\n summary: {\n type: \"string\",\n description: \"One-line description\",\n },\n is_new: {\n type: \"boolean\",\n description: \"True if this is a new concept not in existing wiki\",\n },\n tags: {\n type: \"array\",\n items: { type: \"string\" },\n description:\n \"2-4 categorical tags for organizing this concept (e.g., 'machine-learning', 'optimization')\",\n },\n confidence: {\n type: \"number\",\n description:\n \"Confidence in this concept on a 0..1 scale (1 = directly stated, 0 = highly speculative).\",\n },\n provenance_state: {\n type: \"string\",\n enum: PROVENANCE_STATE_VALUES,\n description:\n \"How this concept was produced: 'extracted' (direct from source), 'merged' (synthesised across sources), 'inferred' (model deduction), or 'ambiguous' (sources disagree).\",\n },\n contradicted_by: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n slug: { type: \"string\", description: \"Slug of the contradicting concept.\" },\n reason: { type: \"string\", description: \"Brief reason for the contradiction.\" },\n },\n required: [\"slug\"],\n },\n description: \"Slugs of other concepts whose evidence contradicts this one.\",\n },\n inferred_paragraphs: {\n type: \"integer\",\n description:\n \"Estimated number of paragraphs in the page that will be inferred rather than directly cited.\",\n },\n },\n required: [\"concept\", \"summary\", \"is_new\"],\n },\n },\n },\n required: [\"concepts\"],\n },\n};\n\n/**\n * Build the system prompt for the concept extraction phase.\n * Instructs the LLM to analyze a source document and identify distinct concepts.\n * @param sourceContent - The full text of the source document.\n * @param existingIndex - The current wiki index.md contents (may be empty).\n * @returns System prompt string for the extraction call.\n */\nexport function buildExtractionPrompt(\n sourceContent: string,\n existingIndex: string,\n): string {\n const indexSection = existingIndex\n ? `\\n\\nHere is the existing wiki index — avoid duplicating concepts already covered:\\n\\n${existingIndex}`\n : \"\\n\\nNo existing wiki pages yet.\";\n\n return [\n \"You are a knowledge extraction engine. Analyze the following source document\",\n \"and identify 3-8 distinct, meaningful concepts worth documenting as wiki pages.\",\n \"Each concept should be a standalone topic that someone might look up.\",\n \"Focus on key ideas, techniques, patterns, or entities — not trivial details.\",\n \"Use the extract_concepts tool to return your findings.\",\n \"\",\n \"For every concept, emit provenance metadata so downstream tools can reason\",\n \"about reliability:\",\n \" - confidence: 0..1 — how certain you are the source supports this concept.\",\n \" - provenance_state: 'extracted' if directly stated, 'merged' if synthesised\",\n \" from multiple parts of the source, 'inferred' if reasoned from context,\",\n \" or 'ambiguous' if the source is contradictory or unclear.\",\n \" - contradicted_by: slugs of other concepts (in this batch or the index)\",\n \" whose evidence conflicts with this one.\",\n \" - inferred_paragraphs: estimated number of paragraphs in the resulting\",\n \" page that will be inferred rather than directly citable.\",\n indexSection,\n \"\\n\\n--- SOURCE DOCUMENT ---\\n\\n\",\n sourceContent,\n ].join(\"\\n\");\n}\n\n/**\n * Build the system prompt for wiki page generation.\n * Instructs the LLM to write a complete wiki page for a single concept.\n * @param concept - The concept title to write about.\n * @param sourceContent - The source material to draw from.\n * @param existingPage - The current page content if updating (empty for new pages).\n * @param relatedPages - Concatenated content of related wiki pages for context.\n * @returns System prompt string for the page generation call.\n */\nexport function buildPagePrompt(\n concept: string,\n sourceContent: string,\n existingPage: string,\n relatedPages: string,\n): string {\n const existingSection = existingPage\n ? `\\n\\nExisting page to update:\\n\\n${existingPage}`\n : \"\";\n\n const relatedSection = relatedPages\n ? `\\n\\nRelated wiki pages for cross-referencing:\\n\\n${relatedPages}`\n : \"\";\n\n return [\n `You are a wiki author. Write a clear, well-structured markdown page about \"${concept}\".`,\n \"Draw facts only from the provided source material.\",\n \"Include a ## Sources section at the end listing the source document.\",\n \"Suggest [[wikilinks]] to related concepts where appropriate.\",\n \"Write in a neutral, informative tone. Be concise but thorough.\",\n \"\",\n \"Source attribution: at the end of each prose paragraph, append a citation\",\n \"marker showing which source file(s) the paragraph drew from.\",\n \"Format: ^[filename.md] for single-source, ^[source-a.md, source-b.md] for multi-source.\",\n \"Place citations only at the end of prose paragraphs — not on headings, list items, or code blocks.\",\n \"Source filenames are visible as `--- SOURCE: filename.md ---` headers in the content below.\",\n \"\",\n \"If a paragraph is your inference rather than a direct extraction, leave it\",\n \"uncited — downstream lint rules will count uncited paragraphs as 'inferred'\",\n \"to compute the page's provenance metadata.\",\n existingSection,\n relatedSection,\n \"\\n\\n--- SOURCE MATERIAL ---\\n\\n\",\n sourceContent,\n ].join(\"\\n\");\n}\n\n/** Raw concept shape as it arrives from the tool JSON. */\ninterface RawConcept {\n concept: unknown;\n summary: unknown;\n is_new: unknown;\n tags?: unknown;\n confidence?: unknown;\n provenance_state?: unknown;\n contradicted_by?: unknown;\n inferred_paragraphs?: unknown;\n}\n\n/** True if the raw concept has the required string/boolean fields. */\nfunction isValidRawConcept(c: RawConcept): boolean {\n return (\n typeof c.concept === \"string\" &&\n typeof c.summary === \"string\" &&\n typeof c.is_new === \"boolean\" &&\n (c.tags === undefined || Array.isArray(c.tags))\n );\n}\n\n/** Coerce raw contradiction entries from the tool into typed refs. */\nfunction coerceContradictedBy(raw: unknown): ContradictionRef[] | undefined {\n if (!Array.isArray(raw)) return undefined;\n const refs: ContradictionRef[] = [];\n for (const entry of raw) {\n if (!entry || typeof entry !== \"object\") continue;\n const obj = entry as { slug?: unknown; reason?: unknown };\n if (typeof obj.slug !== \"string\" || obj.slug.trim().length === 0) continue;\n const ref: ContradictionRef = { slug: obj.slug.trim() };\n if (typeof obj.reason === \"string\") ref.reason = obj.reason;\n refs.push(ref);\n }\n return refs.length > 0 ? refs : undefined;\n}\n\n/** Map a validated raw concept into an ExtractedConcept. */\nfunction mapRawConcept(c: RawConcept): ExtractedConcept {\n const provenance = typeof c.provenance_state === \"string\" &&\n PROVENANCE_STATE_VALUES.includes(c.provenance_state as ProvenanceState)\n ? (c.provenance_state as ProvenanceState)\n : undefined;\n return {\n concept: c.concept as string,\n summary: c.summary as string,\n is_new: c.is_new as boolean,\n tags: Array.isArray(c.tags) ? (c.tags as string[]) : undefined,\n confidence: typeof c.confidence === \"number\" ? c.confidence : undefined,\n provenanceState: provenance,\n contradictedBy: coerceContradictedBy(c.contradicted_by),\n inferredParagraphs: typeof c.inferred_paragraphs === \"number\" &&\n Number.isInteger(c.inferred_paragraphs) && c.inferred_paragraphs >= 0\n ? c.inferred_paragraphs\n : undefined,\n };\n}\n\n/**\n * Parse the JSON tool output from concept extraction into typed objects.\n * @param toolOutput - Raw JSON string returned from the extract_concepts tool.\n * @returns Array of ExtractedConcept objects.\n */\nexport function parseConcepts(toolOutput: string): ExtractedConcept[] {\n try {\n const parsed = JSON.parse(toolOutput);\n const concepts: RawConcept[] = parsed.concepts ?? [];\n return concepts.filter(isValidRawConcept).map(mapRawConcept);\n } catch {\n return [];\n }\n}\n","/**\n * Semantic dependency tracking for cross-source concept sharing.\n *\n * When multiple source files contribute to the same concept, a change in one\n * source should trigger recompilation of that concept using content from ALL\n * contributing sources. This module builds a reverse index from concepts back\n * to their source files, then identifies which unchanged sources are affected\n * by changes to other sources that share concepts with them.\n *\n * Without this, if sources A and B both produce concept X and source A changes,\n * concept X would be regenerated using only source A's content — losing source\n * B's contribution entirely.\n */\n\nimport { readState, updateSourceState, writeState } from \"../utils/state.js\";\nimport { slugify } from \"../utils/markdown.js\";\nimport * as output from \"../utils/output.js\";\nimport type { WikiState, SourceChange, ExtractedConcept } from \"../utils/types.js\";\n\nexport interface ExtractionResult {\n sourceFile: string;\n sourcePath: string;\n sourceContent: string;\n concepts: ExtractedConcept[];\n}\n\n/**\n * Build a reverse map from concept slugs to the source files that produced them.\n * @param sources - The sources record from WikiState.\n * @returns Map where keys are concept slugs and values are arrays of source filenames.\n */\nfunction buildConceptToSourcesMap(\n sources: WikiState[\"sources\"],\n): Map<string, string[]> {\n const conceptMap = new Map<string, string[]>();\n\n for (const [sourceFile, entry] of Object.entries(sources)) {\n for (const slug of entry.concepts) {\n const existing = conceptMap.get(slug);\n if (existing) {\n existing.push(sourceFile);\n } else {\n conceptMap.set(slug, [sourceFile]);\n }\n }\n }\n\n return conceptMap;\n}\n\n/** Extract filenames from changes matching a given status. */\nfunction filesByStatus(\n changes: SourceChange[],\n ...statuses: SourceChange[\"status\"][]\n): Set<string> {\n const statusSet = new Set(statuses);\n return new Set(\n changes.filter((c) => statusSet.has(c.status)).map((c) => c.file),\n );\n}\n\n/**\n * Collect co-contributors for a source's concepts, skipping files in the\n * exclusion sets. Mutates `out` by adding newly discovered contributors.\n */\nfunction collectSharedContributors(\n sourceFile: string,\n state: WikiState,\n conceptMap: Map<string, string[]>,\n excludeSets: Set<string>[],\n out: Set<string>,\n): void {\n const sourceEntry = state.sources[sourceFile];\n if (!sourceEntry) return;\n\n for (const slug of sourceEntry.concepts) {\n const contributors = conceptMap.get(slug);\n if (!contributors || contributors.length < 2) continue;\n\n for (const contributor of contributors) {\n const isExcluded = excludeSets.some((s) => s.has(contributor));\n if (!isExcluded) out.add(contributor);\n }\n }\n}\n\n/**\n * Identify unchanged sources that need recompilation because they share\n * concepts with directly changed sources. This enables correct cross-source\n * concept regeneration — ensuring shared concepts are rebuilt with content\n * from ALL contributing sources.\n *\n * Deleted sources are intentionally excluded: recompiling a concept-mate of\n * a deleted source would regenerate the page from fewer sources, losing\n * content. Shared concepts from deleted sources are preserved as-is by\n * markOrphaned (which skips shared concepts).\n *\n * @param state - The current persisted WikiState.\n * @param directChanges - Changes detected by hash comparison.\n * @returns Filenames of indirectly affected sources not already in the changed list.\n */\nexport function findAffectedSources(\n state: WikiState,\n directChanges: SourceChange[],\n): string[] {\n const changedFiles = filesByStatus(directChanges, \"new\", \"changed\");\n const deletedFiles = filesByStatus(directChanges, \"deleted\");\n const conceptMap = buildConceptToSourcesMap(state.sources);\n const affected = new Set<string>();\n\n for (const changedFile of changedFiles) {\n collectSharedContributors(\n changedFile, state, conceptMap,\n [changedFiles, deletedFiles, affected],\n affected,\n );\n }\n\n return Array.from(affected);\n}\n\n/**\n * Find concept slugs that must NOT be regenerated during this compile batch.\n * A slug is \"frozen\" when it was shared between a deleted source and at least\n * one surviving source. Regenerating it would overwrite the existing page\n * (which has combined content from all prior contributors) with content from\n * only the surviving sources, silently losing the deleted source's contribution.\n * @param state - Current persisted state.\n * @param changes - All detected source changes in this batch.\n * @returns Set of concept slugs that compileSource should skip.\n */\nexport function findFrozenSlugs(\n state: WikiState,\n changes: SourceChange[],\n): Set<string> {\n // Start with persisted frozen slugs from prior batches.\n const frozen = new Set<string>(state.frozenSlugs ?? []);\n\n // Add new frozen slugs from deletions in this batch.\n const deletedFiles = changes\n .filter((c) => c.status === \"deleted\")\n .map((c) => c.file);\n\n const conceptMap = buildConceptToSourcesMap(state.sources);\n\n for (const file of deletedFiles) {\n const entry = state.sources[file];\n if (!entry) continue;\n\n for (const slug of entry.concepts) {\n const contributors = conceptMap.get(slug);\n if (contributors && contributors.length > 1) {\n frozen.add(slug);\n }\n }\n }\n\n return frozen;\n}\n\n/**\n * Unfreeze slugs that were successfully regenerated by all their current\n * contributors, then persist the remaining frozen set to state.\n * A slug is safe to unfreeze when every source that claims it in state\n * was compiled in this batch and successfully extracted it.\n */\nexport async function persistFrozenSlugs(\n root: string,\n frozenSlugs: Set<string>,\n successfulExtractions: ExtractionResult[],\n): Promise<void> {\n const currentState = await readState(root);\n const conceptMap = buildConceptToSourcesMap(currentState.sources);\n\n // Concepts successfully extracted in this batch, keyed by slug.\n const extractedBy = new Set<string>();\n for (const result of successfulExtractions) {\n if (result.concepts.length === 0) continue;\n for (const c of result.concepts) {\n extractedBy.add(slugify(c.concept));\n }\n }\n const compiledFiles = new Set(\n successfulExtractions\n .filter((r) => r.concepts.length > 0)\n .map((r) => r.sourceFile),\n );\n\n const remaining = new Set<string>();\n for (const slug of frozenSlugs) {\n const owners = conceptMap.get(slug) ?? [];\n // Unfreeze only if ALL current owners were compiled and extracted it.\n const allOwnersCompiled = owners.length > 0\n && owners.every((f) => compiledFiles.has(f))\n && extractedBy.has(slug);\n\n if (!allOwnersCompiled) remaining.add(slug);\n }\n\n const stateToSave = { ...currentState, frozenSlugs: Array.from(remaining) };\n await writeState(root, stateToSave);\n}\n\n/**\n * Collect concept slugs from extractions that were not in the source's\n * previous concept list — these are \"newly gained\" concepts that\n * findAffectedSources could not have matched pre-extraction.\n */\nfunction collectFreshSlugs(\n extractions: ExtractionResult[],\n state: WikiState,\n): Set<string> {\n const freshSlugs = new Set<string>();\n\n for (const result of extractions) {\n const oldConcepts = new Set(state.sources[result.sourceFile]?.concepts ?? []);\n for (const c of result.concepts) {\n const slug = slugify(c.concept);\n if (!oldConcepts.has(slug)) freshSlugs.add(slug);\n }\n }\n\n return freshSlugs;\n}\n\n/**\n * Find unchanged sources that own any of the given slugs, excluding files\n * present in the provided exclusion sets.\n */\nfunction findSlugOwners(\n slugs: Set<string>,\n conceptMap: Map<string, string[]>,\n excludeSets: Set<string>[],\n): string[] {\n const affected = new Set<string>();\n\n for (const slug of slugs) {\n const owners = conceptMap.get(slug);\n if (!owners) continue;\n for (const owner of owners) {\n const isExcluded = excludeSets.some((s) => s.has(owner));\n if (!isExcluded) affected.add(owner);\n }\n }\n\n return Array.from(affected);\n}\n\n/**\n * Post-extraction check for compiled sources whose freshly extracted concepts\n * overlap with unchanged sources not already in the batch. Covers two cases\n * that findAffectedSources (pre-extraction) cannot detect:\n * 1. New sources have no state entry, so their concepts are unknown.\n * 2. Changed sources may gain concepts they didn't previously have.\n * @param extractions - Results from Phase 1 extraction.\n * @param state - Current persisted state.\n * @param allChanges - Full changes array including deleted/unchanged entries.\n * @returns Filenames of unchanged sources that share concepts with compiled sources.\n */\nexport function findLateAffectedSources(\n extractions: ExtractionResult[],\n state: WikiState,\n allChanges: SourceChange[],\n): string[] {\n const compilingFiles = filesByStatus(allChanges, \"new\", \"changed\");\n const deletedFiles = filesByStatus(allChanges, \"deleted\");\n const conceptMap = buildConceptToSourcesMap(state.sources);\n const freshSlugs = collectFreshSlugs(extractions, state);\n\n return findSlugOwners(freshSlugs, conceptMap, [compilingFiles, deletedFiles]);\n}\n\n/**\n * Find concept slugs from a source that are also produced by other sources.\n * Used by markOrphaned to skip orphaning shared concepts when a source is\n * deleted — preserving combined content from prior compilations.\n * @param sourceFile - The source being checked.\n * @param state - Current persisted state.\n * @returns Set of slugs that have at least one other contributing source.\n */\nexport function findSharedConcepts(\n sourceFile: string,\n state: WikiState,\n): Set<string> {\n const shared = new Set<string>();\n const sourceEntry = state.sources[sourceFile];\n if (!sourceEntry) return shared;\n\n const conceptMap = buildConceptToSourcesMap(state.sources);\n\n for (const slug of sourceEntry.concepts) {\n const contributors = conceptMap.get(slug);\n if (contributors && contributors.length > 1) {\n shared.add(slug);\n }\n }\n\n return shared;\n}\n\n/**\n * Freeze concepts from failed extractions and persist their state with a\n * blank hash so they retry on the next compile. Preserves old concept lists\n * to keep dependency tracking intact.\n */\nexport async function freezeFailedExtractions(\n root: string,\n results: ExtractionResult[],\n frozenSlugs: Set<string>,\n): Promise<void> {\n for (const result of results) {\n if (result.concepts.length > 0) continue;\n\n output.status(\"!\", output.warn(`${result.sourceFile}: no concepts — will retry.`));\n const currentState = await readState(root);\n const oldConcepts = currentState.sources[result.sourceFile]?.concepts ?? [];\n for (const slug of oldConcepts) frozenSlugs.add(slug);\n\n await updateSourceState(root, result.sourceFile, {\n hash: \"\",\n concepts: oldConcepts,\n compiledAt: new Date().toISOString(),\n });\n }\n}\n","/**\n * Orphan management for deleted source files.\n *\n * When a source is deleted, its exclusively-owned concept pages are marked\n * orphaned (orphaned: true in frontmatter). Shared concepts are preserved\n * to avoid losing combined content from prior compilations.\n *\n * After compilation, frozen slugs (shared concepts that lost a contributor)\n * are checked against the updated state. Any that lost ALL owners are\n * orphaned as a cleanup pass.\n */\n\nimport path from \"path\";\nimport { readState, removeSourceState } from \"../utils/state.js\";\nimport {\n atomicWrite,\n safeReadFile,\n parseFrontmatter,\n} from \"../utils/markdown.js\";\nimport { findSharedConcepts } from \"./deps.js\";\nimport * as output from \"../utils/output.js\";\nimport { CONCEPTS_DIR } from \"../utils/constants.js\";\n\n/**\n * Mark wiki pages as orphaned when their source is deleted.\n * Only orphans concepts exclusively owned by the deleted source.\n * Shared concepts (contributed to by other live sources) are preserved\n * as-is to avoid losing combined content from prior compilations.\n */\nexport async function markOrphaned(\n root: string,\n sourceFile: string,\n state: Awaited<ReturnType<typeof readState>>,\n): Promise<void> {\n const sourceEntry = state.sources[sourceFile];\n if (!sourceEntry) return;\n\n const sharedSlugs = findSharedConcepts(sourceFile, state);\n\n for (const slug of sourceEntry.concepts) {\n if (sharedSlugs.has(slug)) {\n output.status(\"i\", output.dim(`Kept: ${slug}.md (shared with other sources)`));\n continue;\n }\n\n await orphanPage(root, slug, \"source deleted\");\n }\n\n await removeSourceState(root, sourceFile);\n}\n\n/**\n * Check frozen slugs against the updated state after compilation.\n * If no source still claims a frozen slug, orphan its page so it doesn't\n * linger as an untracked stale file.\n */\nexport async function orphanUnownedFrozenPages(\n root: string,\n frozenSlugs: Set<string>,\n): Promise<void> {\n const currentState = await readState(root);\n const ownedSlugs = new Set<string>();\n for (const entry of Object.values(currentState.sources)) {\n for (const slug of entry.concepts) ownedSlugs.add(slug);\n }\n\n for (const slug of frozenSlugs) {\n if (ownedSlugs.has(slug)) continue;\n await orphanPage(root, slug, \"no remaining sources\");\n }\n}\n\n/**\n * Mark a single concept page as orphaned if it exists and isn't already marked.\n * @param root - Project root directory.\n * @param slug - Concept slug to orphan.\n * @param reason - Human-readable reason for the log message.\n */\nasync function orphanPage(root: string, slug: string, reason: string): Promise<void> {\n const pagePath = path.join(root, CONCEPTS_DIR, `${slug}.md`);\n const content = await safeReadFile(pagePath);\n if (!content) return;\n\n const { meta } = parseFrontmatter(content);\n if (meta.orphaned === true) return;\n\n const updated = content.replace(\"---\\n\", \"---\\norphaned: true\\n\");\n await atomicWrite(pagePath, updated);\n output.status(\"⚠\", output.warn(`Orphaned: ${slug}.md (${reason})`));\n}\n","/**\n * Interlink resolution for wiki pages.\n *\n * Rule-based (not LLM-based) pass that scans wiki pages for concept title\n * mentions and wraps them in [[wikilinks]]. Obsidian-compatible format using\n * display titles, not slugs.\n *\n * Complexity: O(changed * total) per incremental compile.\n * Full recompile degrades to O(total^2).\n */\n\nimport { readdir, readFile } from \"fs/promises\";\nimport path from \"path\";\nimport { existsSync } from \"fs\";\nimport { atomicWrite, parseFrontmatter } from \"../utils/markdown.js\";\nimport { CONCEPTS_DIR } from \"../utils/constants.js\";\nimport * as output from \"../utils/output.js\";\n\ninterface PageInfo {\n slug: string;\n title: string;\n filePath: string;\n}\n\n/** Build an index of all wiki page titles from the concepts directory. */\nasync function buildTitleIndex(root: string): Promise<PageInfo[]> {\n const conceptsDir = path.join(root, CONCEPTS_DIR);\n if (!existsSync(conceptsDir)) return [];\n\n const files = await readdir(conceptsDir);\n const pages: PageInfo[] = [];\n\n for (const file of files) {\n if (!file.endsWith(\".md\")) continue;\n\n const filePath = path.join(conceptsDir, file);\n const content = await readFile(filePath, \"utf-8\");\n const { meta } = parseFrontmatter(content);\n\n if (meta.title && typeof meta.title === \"string\" && !meta.orphaned) {\n pages.push({\n slug: file.replace(/\\.md$/, \"\"),\n title: meta.title,\n filePath,\n });\n }\n }\n\n return pages;\n}\n\n/** Check if a position is inside an existing [[wikilink]]. */\nfunction isInsideWikilink(text: string, position: number): boolean {\n const before = text.lastIndexOf(\"[[\", position);\n const after = text.indexOf(\"]]\", position);\n if (before === -1 || after === -1) return false;\n\n const closeBefore = text.indexOf(\"]]\", before);\n return closeBefore >= position;\n}\n\n/** Check if a position is inside a ^[...] citation marker. */\nfunction isInsideCitation(text: string, position: number): boolean {\n const before = text.lastIndexOf(\"^[\", position);\n const after = text.indexOf(\"]\", position);\n if (before === -1 || after === -1) return false;\n\n const closeBefore = text.indexOf(\"]\", before);\n return closeBefore >= position;\n}\n\n/** Check if a match is at a word boundary. */\nfunction isWordBoundary(text: string, start: number, end: number): boolean {\n const before = start === 0 || /[\\s,.:;!?()\\[\\]{}/\"']/.test(text[start - 1]);\n const after = end >= text.length || /[\\s,.:;!?()\\[\\]{}/\"']/.test(text[end]);\n return before && after;\n}\n\n/** Find all regex matches for a title in the text, returned as position spans. */\nfunction findTitleMatches(text: string, title: string): { start: number; end: number }[] {\n const escaped = title.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const regex = new RegExp(escaped, \"gi\");\n const matches: { start: number; end: number }[] = [];\n let match;\n\n while ((match = regex.exec(text)) !== null) {\n matches.push({ start: match.index, end: match.index + match[0].length });\n }\n\n return matches;\n}\n\n/** Determine whether a match position is eligible for wikilink insertion. */\nfunction isLinkablePosition(text: string, start: number, end: number): boolean {\n if (isInsideWikilink(text, start)) return false;\n if (isInsideCitation(text, start)) return false;\n return isWordBoundary(text, start, end);\n}\n\n/**\n * Add [[wikilinks]] to a page's body for any title mentions.\n * Skips already-linked text and non-word-boundary matches.\n */\nfunction addWikilinks(body: string, titles: PageInfo[], selfTitle: string): string {\n let result = body;\n const selfLower = selfTitle.toLowerCase();\n\n for (const page of titles) {\n if (page.title.toLowerCase() === selfLower) continue;\n\n const matches = findTitleMatches(result, page.title);\n\n // Process matches in reverse to preserve positions\n for (const m of matches.reverse()) {\n if (!isLinkablePosition(result, m.start, m.end)) continue;\n result = result.slice(0, m.start) + `[[${page.title}]]` + result.slice(m.end);\n }\n }\n\n return result;\n}\n\n/**\n * Run interlink resolution on changed and affected pages.\n *\n * Two passes:\n * 1. Outbound: changed pages get [[wikilinks]] for any title they mention.\n * 2. Inbound: ALL pages get scanned for mentions of newly created titles.\n * This ensures existing pages link to new concepts without a full recompile.\n *\n * Complexity: O(changed * total) for outbound, O(newTitles * total) for inbound.\n */\nexport async function resolveLinks(\n root: string,\n changedSlugs: string[],\n newSlugs: string[],\n): Promise<number> {\n const titleIndex = await buildTitleIndex(root);\n if (titleIndex.length === 0) return 0;\n\n let linkCount = 0;\n\n // Pass 1: outbound links on changed pages\n linkCount += await resolveOutboundLinks(titleIndex, changedSlugs);\n\n // Pass 2: inbound links on all pages for new titles\n linkCount += await resolveInboundLinks(titleIndex, newSlugs);\n\n if (linkCount > 0) {\n output.status(\"🔗\", output.dim(`Resolved links in ${linkCount} page(s)`));\n }\n\n return linkCount;\n}\n\n/** Add outbound [[wikilinks]] to changed pages for any title they mention. */\nasync function resolveOutboundLinks(\n titleIndex: PageInfo[],\n changedSlugs: string[],\n): Promise<number> {\n let count = 0;\n\n for (const page of titleIndex) {\n if (!changedSlugs.includes(page.slug)) continue;\n const didLink = await linkPage(page, titleIndex);\n if (didLink) count++;\n }\n\n return count;\n}\n\n/** Scan ALL pages for mentions of newly created concept titles. */\nasync function resolveInboundLinks(\n titleIndex: PageInfo[],\n newSlugs: string[],\n): Promise<number> {\n if (newSlugs.length === 0) return 0;\n\n const newTitles = titleIndex.filter((p) => newSlugs.includes(p.slug));\n if (newTitles.length === 0) return 0;\n\n let count = 0;\n\n for (const page of titleIndex) {\n // Skip pages that were already processed in outbound pass\n if (newSlugs.includes(page.slug)) continue;\n\n const content = await readFile(page.filePath, \"utf-8\");\n const { body } = parseFrontmatter(content);\n const linked = addWikilinks(body, newTitles, page.title);\n\n if (linked !== body) {\n const newContent = content.replace(body, linked);\n await atomicWrite(page.filePath, newContent);\n count++;\n }\n }\n\n return count;\n}\n\n/** Add wikilinks to a single page, writing atomically if changed. */\nasync function linkPage(page: PageInfo, titleIndex: PageInfo[]): Promise<boolean> {\n const content = await readFile(page.filePath, \"utf-8\");\n const { body } = parseFrontmatter(content);\n const linked = addWikilinks(body, titleIndex, page.title);\n\n if (linked === body) return false;\n\n const newContent = content.replace(body, linked);\n await atomicWrite(page.filePath, newContent);\n return true;\n}\n","/**\n * Wiki index generator.\n *\n * Scans all concept pages in wiki/concepts/, extracts frontmatter metadata,\n * and produces wiki/index.md with a sorted list of all concepts and their\n * summaries. Used after each compilation pass.\n */\n\nimport { readdir } from \"fs/promises\";\nimport path from \"path\";\nimport { atomicWrite, safeReadFile, parseFrontmatter } from \"../utils/markdown.js\";\nimport { CONCEPTS_DIR, QUERIES_DIR, INDEX_FILE } from \"../utils/constants.js\";\nimport * as output from \"../utils/output.js\";\nimport type { PageSummary } from \"../utils/types.js\";\n\n/**\n * Generate the wiki/index.md listing all concept pages with summaries.\n * @param root - Project root directory.\n */\nexport async function generateIndex(root: string): Promise<void> {\n output.status(\"*\", output.info(\"Generating index...\"));\n\n const conceptsPath = path.join(root, CONCEPTS_DIR);\n const queriesPath = path.join(root, QUERIES_DIR);\n const concepts = await collectPageSummaries(conceptsPath);\n const queries = await collectPageSummaries(queriesPath);\n\n concepts.sort((a, b) => a.title.localeCompare(b.title));\n queries.sort((a, b) => a.title.localeCompare(b.title));\n\n const indexContent = buildIndexContent(concepts, queries);\n const indexPath = path.join(root, INDEX_FILE);\n await atomicWrite(indexPath, indexContent);\n\n const total = concepts.length + queries.length;\n output.status(\"+\", output.success(`Index updated with ${total} pages.`));\n}\n\n/** A scanned page paired with its parsed frontmatter. */\ninterface ScannedPage {\n slug: string;\n meta: Record<string, unknown>;\n}\n\n/**\n * Scan a wiki directory and return every .md page paired with its parsed\n * frontmatter. Read-only utility shared by index generation and the MCP\n * server's status tool.\n * @param dirPath - Absolute path to a wiki page directory.\n * @returns Array of {slug, meta} entries — empty when the directory is missing.\n */\nexport async function scanWikiPages(dirPath: string): Promise<ScannedPage[]> {\n let files: string[];\n try {\n files = await readdir(dirPath);\n } catch {\n return [];\n }\n\n const scanned: ScannedPage[] = [];\n for (const file of files.filter((f) => f.endsWith(\".md\"))) {\n const content = await safeReadFile(path.join(dirPath, file));\n const { meta } = parseFrontmatter(content);\n scanned.push({ slug: file.replace(/\\.md$/, \"\"), meta });\n }\n return scanned;\n}\n\n/**\n * Project a wiki directory into PageSummary entries (excludes orphaned and\n * untitled pages). Built on top of scanWikiPages so the MCP server can share\n * the underlying scan logic without re-reading the directory.\n * @param conceptsPath - Absolute path to wiki/concepts/.\n * @returns Array of page summary objects.\n */\nexport async function collectPageSummaries(\n conceptsPath: string,\n): Promise<PageSummary[]> {\n const scanned = await scanWikiPages(conceptsPath);\n return scanned\n .filter(({ meta }) => meta.title && typeof meta.title === \"string\" && !meta.orphaned)\n .map(({ slug, meta }) => ({\n title: meta.title as string,\n slug,\n summary: typeof meta.summary === \"string\" ? meta.summary : \"\",\n }));\n}\n\n/** Strip [[wikilink]] brackets from text, leaving the inner text intact. */\nfunction stripWikilinks(text: string): string {\n return text.replace(/\\[\\[([^\\]]+)\\]\\]/g, \"$1\");\n}\n\n/**\n * Build the index.md markdown content from page summaries.\n * @param pages - Sorted array of page summaries.\n * @returns Full index.md content string.\n */\nfunction buildIndexContent(concepts: PageSummary[], queries: PageSummary[]): string {\n const lines = [\"# Knowledge Wiki\", \"\", \"## Concepts\", \"\"];\n\n for (const page of concepts) {\n lines.push(`- **[[${page.title}]]** — ${stripWikilinks(page.summary)}`);\n }\n\n if (queries.length > 0) {\n lines.push(\"\", \"## Saved Queries\", \"\");\n for (const page of queries) {\n lines.push(`- **[[${page.title}]]** — ${stripWikilinks(page.summary)}`);\n }\n }\n\n const total = concepts.length + queries.length;\n lines.push(\"\");\n lines.push(`_${total} pages | Generated ${new Date().toISOString()}_`);\n lines.push(\"\");\n\n return lines.join(\"\\n\");\n}\n","/**\n * Obsidian integration helpers for the llmwiki knowledge compiler.\n *\n * Provides two capabilities:\n * 1. Enriching wiki page frontmatter with tags and aliases for better\n * Obsidian graph navigation and search.\n * 2. Generating a Map of Content (MOC) page that groups concept pages\n * by tag for easy browsing.\n */\n\nimport { readdir } from \"fs/promises\";\nimport path from \"path\";\nimport { slugify, atomicWrite, safeReadFile, parseFrontmatter } from \"../utils/markdown.js\";\nimport { CONCEPTS_DIR, MOC_FILE } from \"../utils/constants.js\";\n\n/** Minimum word count to generate an abbreviation alias. */\nconst ABBREVIATION_MIN_WORDS = 3;\n\n/** Conjunctions that trigger a word-swap alias. */\nconst SWAP_CONJUNCTIONS = [\" and \", \" or \"];\n\n/**\n * Enrich a frontmatter object with Obsidian-specific tags and aliases.\n * Mutates the frontmatter object in place.\n * @param frontmatter - The frontmatter object to enrich.\n * @param conceptTitle - The human-readable concept title.\n * @param tags - Tags from extraction (may be empty).\n */\nexport function addObsidianMeta(\n frontmatter: Record<string, unknown>,\n conceptTitle: string,\n tags: string[],\n): void {\n frontmatter.tags = tags;\n frontmatter.aliases = generateAliases(conceptTitle);\n}\n\n/**\n * Generate deterministic aliases from a concept title.\n * Produces up to three alias variants:\n * - Slug form (e.g., \"gradient-descent\")\n * - Word-swap around conjunctions (e.g., \"Optimization and Gradient Descent\")\n * - Abbreviation from first letters for 3+ word titles (e.g., \"RAG\")\n * @param title - The concept title to derive aliases from.\n * @returns Array of aliases that differ from the original title.\n */\nfunction generateAliases(title: string): string[] {\n const aliases: string[] = [];\n const slug = slugify(title);\n\n if (slug !== title) {\n aliases.push(slug);\n }\n\n const swapAlias = generateSwapAlias(title);\n if (swapAlias) {\n aliases.push(swapAlias);\n }\n\n const abbreviation = generateAbbreviation(title);\n if (abbreviation) {\n aliases.push(abbreviation);\n }\n\n return aliases;\n}\n\n/**\n * Generate a word-swap alias by reversing parts around a conjunction.\n * E.g., \"Gradient Descent and Optimization\" becomes \"Optimization and Gradient Descent\".\n * @param title - The concept title.\n * @returns The swapped alias, or null if no conjunction found.\n */\nfunction generateSwapAlias(title: string): string | null {\n for (const conjunction of SWAP_CONJUNCTIONS) {\n const index = title.toLowerCase().indexOf(conjunction);\n if (index === -1) continue;\n\n const before = title.slice(0, index);\n const after = title.slice(index + conjunction.length);\n const originalConjunction = title.slice(index, index + conjunction.length);\n return `${after}${originalConjunction}${before}`;\n }\n return null;\n}\n\n/**\n * Generate an abbreviation from first letters of each word for titles with 3+ words.\n * E.g., \"Retrieval Augmented Generation\" becomes \"RAG\".\n * @param title - The concept title.\n * @returns The abbreviation, or null if title has fewer than 3 words.\n */\nfunction generateAbbreviation(title: string): string | null {\n const words = title.split(/\\s+/);\n if (words.length < ABBREVIATION_MIN_WORDS) return null;\n\n const abbreviation = words.map((w) => w[0].toUpperCase()).join(\"\");\n if (abbreviation === title) return null;\n\n return abbreviation;\n}\n\n/**\n * Generate a Map of Content (MOC) page grouping concept pages by tag.\n * Reads all concept pages, extracts their tags from frontmatter, and writes\n * a structured MOC.md with sections per tag and an Uncategorized section.\n * @param root - Project root directory.\n */\nexport async function generateMOC(root: string): Promise<void> {\n const conceptsPath = path.join(root, CONCEPTS_DIR);\n const pages = await loadConceptPages(conceptsPath);\n\n const tagGroups = groupPagesByTag(pages);\n const content = buildMOCContent(tagGroups);\n\n await atomicWrite(path.join(root, MOC_FILE), content);\n}\n\n/** Minimal page info needed for MOC generation. */\ninterface PageInfo {\n title: string;\n tags: string[];\n}\n\n/**\n * Load all concept pages and extract their title and tags.\n * @param conceptsPath - Absolute path to the concepts directory.\n * @returns Array of page info objects.\n */\nasync function loadConceptPages(conceptsPath: string): Promise<PageInfo[]> {\n let files: string[];\n try {\n files = await readdir(conceptsPath);\n } catch {\n return [];\n }\n\n const pages: PageInfo[] = [];\n for (const file of files) {\n if (!file.endsWith(\".md\")) continue;\n\n const content = await safeReadFile(path.join(conceptsPath, file));\n if (!content) continue;\n\n const { meta } = parseFrontmatter(content);\n if (meta.orphaned) continue;\n\n const title = typeof meta.title === \"string\" ? meta.title : file.replace(/\\.md$/, \"\");\n const tags = Array.isArray(meta.tags) ? (meta.tags as string[]) : [];\n pages.push({ title, tags });\n }\n\n return pages;\n}\n\n/**\n * Group pages by their tags into a map. Pages with no tags go under \"Uncategorized\".\n * @param pages - Array of page info objects.\n * @returns Map of tag name to array of page titles.\n */\nfunction groupPagesByTag(pages: PageInfo[]): Map<string, string[]> {\n const groups = new Map<string, string[]>();\n\n for (const page of pages) {\n if (page.tags.length === 0) {\n appendToGroup(groups, \"Uncategorized\", page.title);\n continue;\n }\n\n for (const tag of page.tags) {\n appendToGroup(groups, tag, page.title);\n }\n }\n\n return groups;\n}\n\n/** Append a title to a group, creating the group if needed. */\nfunction appendToGroup(groups: Map<string, string[]>, key: string, title: string): void {\n const existing = groups.get(key);\n if (existing) {\n existing.push(title);\n } else {\n groups.set(key, [title]);\n }\n}\n\n/**\n * Build the MOC markdown content from grouped pages.\n * @param tagGroups - Map of tag name to array of page titles.\n * @returns Complete MOC markdown string.\n */\nfunction buildMOCContent(tagGroups: Map<string, string[]>): string {\n const lines: string[] = [\"# Map of Content\", \"\"];\n\n const sortedTags = [...tagGroups.keys()].sort((a, b) => {\n // \"Uncategorized\" always goes last\n if (a === \"Uncategorized\") return 1;\n if (b === \"Uncategorized\") return -1;\n return a.localeCompare(b);\n });\n\n for (const tag of sortedTags) {\n const titles = tagGroups.get(tag) ?? [];\n lines.push(`## ${tag}`, \"\");\n for (const title of titles.sort()) {\n lines.push(`- [[${title}]]`);\n }\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\");\n}\n","/**\n * Embedding-based semantic search utilities.\n *\n * Maintains a persistent store of page embeddings in .llmwiki/embeddings.json\n * and provides cosine-similarity retrieval so the query command can narrow\n * hundreds of pages down to a small top-K before calling the selection LLM.\n *\n * The store is additive: successful embedding calls update entries; failures\n * degrade gracefully (caller falls back to full-index selection).\n */\n\nimport { readFile, readdir } from \"fs/promises\";\nimport { existsSync } from \"fs\";\nimport path from \"path\";\nimport { getProvider, getActiveProviderName } from \"./provider.js\";\nimport { atomicWrite, safeReadFile, parseFrontmatter } from \"./markdown.js\";\nimport {\n CONCEPTS_DIR,\n QUERIES_DIR,\n EMBEDDINGS_FILE,\n EMBEDDING_TOP_K,\n EMBEDDING_MODELS,\n} from \"./constants.js\";\nimport * as output from \"./output.js\";\n\n/** A single embedded page record. */\nexport interface EmbeddingEntry {\n slug: string;\n title: string;\n summary: string;\n vector: number[];\n updatedAt: string;\n}\n\n/** Root shape of .llmwiki/embeddings.json. */\nexport interface EmbeddingStore {\n version: 1;\n model: string;\n dimensions: number;\n entries: EmbeddingEntry[];\n}\n\n/** A retrievable page record on disk (concepts/ or queries/). */\ninterface PageRecord {\n slug: string;\n title: string;\n summary: string;\n}\n\n/**\n * Cosine similarity between two equal-length vectors.\n * Returns 0 when either vector has zero magnitude (safer than NaN for ranking).\n */\nexport function cosineSimilarity(a: number[], b: number[]): number {\n if (a.length !== b.length || a.length === 0) return 0;\n\n let dot = 0;\n let magA = 0;\n let magB = 0;\n for (let i = 0; i < a.length; i++) {\n dot += a[i] * b[i];\n magA += a[i] * a[i];\n magB += b[i] * b[i];\n }\n\n if (magA === 0 || magB === 0) return 0;\n return dot / (Math.sqrt(magA) * Math.sqrt(magB));\n}\n\n/** Return the top-K entries most similar to the query vector, sorted descending. */\nexport function findTopK(\n queryVec: number[],\n store: EmbeddingStore,\n k: number,\n): EmbeddingEntry[] {\n const scored = store.entries.map((entry) => ({\n entry,\n score: cosineSimilarity(queryVec, entry.vector),\n }));\n scored.sort((left, right) => right.score - left.score);\n return scored.slice(0, k).map((item) => item.entry);\n}\n\n/** Read .llmwiki/embeddings.json, returning null if it does not exist. */\nexport async function readEmbeddingStore(root: string): Promise<EmbeddingStore | null> {\n const filePath = path.join(root, EMBEDDINGS_FILE);\n if (!existsSync(filePath)) return null;\n const raw = await readFile(filePath, \"utf-8\");\n return JSON.parse(raw) as EmbeddingStore;\n}\n\n/** Atomically persist the embedding store. */\nexport async function writeEmbeddingStore(root: string, store: EmbeddingStore): Promise<void> {\n const filePath = path.join(root, EMBEDDINGS_FILE);\n await atomicWrite(filePath, JSON.stringify(store, null, 2));\n}\n\n/**\n * Embed the question, look up top-K matches, and return lightweight page records.\n * Returns [] when no store exists so callers can transparently fall back.\n */\nexport async function findRelevantPages(\n root: string,\n question: string,\n): Promise<Array<{ slug: string; title: string; summary: string }>> {\n const store = await readEmbeddingStore(root);\n if (!store || store.entries.length === 0) return [];\n const activeModel = resolveEmbeddingModel();\n if (store.model !== activeModel) {\n warnStaleEmbeddingStore(store.model, activeModel);\n return [];\n }\n\n const queryVec = await getProvider().embed(question);\n return findTopK(queryVec, store, EMBEDDING_TOP_K).map((entry) => ({\n slug: entry.slug,\n title: entry.title,\n summary: entry.summary,\n }));\n}\n\n/** Scan concepts/ and queries/ directories, returning retrievable pages. */\nasync function collectPageRecords(root: string): Promise<PageRecord[]> {\n const records: PageRecord[] = [];\n for (const dir of [CONCEPTS_DIR, QUERIES_DIR]) {\n const absDir = path.join(root, dir);\n let files: string[];\n try {\n files = await readdir(absDir);\n } catch {\n continue;\n }\n for (const file of files.filter((f) => f.endsWith(\".md\"))) {\n const content = await safeReadFile(path.join(absDir, file));\n const { meta } = parseFrontmatter(content);\n if (meta.orphaned || typeof meta.title !== \"string\") continue;\n records.push({\n slug: file.replace(/\\.md$/, \"\"),\n title: meta.title,\n summary: typeof meta.summary === \"string\" ? meta.summary : \"\",\n });\n }\n }\n return records;\n}\n\n/** Build the text that represents a page in the embedding space. */\nfunction buildEmbeddingText(record: PageRecord): string {\n return record.summary\n ? `${record.title}\\n\\n${record.summary}`\n : record.title;\n}\n\n/**\n * Embed every page in `records` whose slug appears in `slugsToEmbed`,\n * returning the new entries. Failures bubble up to the caller.\n */\nasync function embedPages(\n records: PageRecord[],\n slugsToEmbed: Set<string>,\n): Promise<EmbeddingEntry[]> {\n const provider = getProvider();\n const now = new Date().toISOString();\n const fresh: EmbeddingEntry[] = [];\n\n for (const record of records) {\n if (!slugsToEmbed.has(record.slug)) continue;\n const vector = await provider.embed(buildEmbeddingText(record));\n fresh.push({\n slug: record.slug,\n title: record.title,\n summary: record.summary,\n vector,\n updatedAt: now,\n });\n }\n return fresh;\n}\n\n/** Tracks which (stored, active) model pairs have already been warned about. */\nconst warnedStaleModels = new Set<string>();\n\n/** Warn once per (stored, active) model pair so queries stay quiet on repeat runs. */\nfunction warnStaleEmbeddingStore(storedModel: string, activeModel: string): void {\n const key = `${storedModel}→${activeModel}`;\n if (warnedStaleModels.has(key)) return;\n warnedStaleModels.add(key);\n output.status(\n \"!\",\n output.warn(\n `Embedding store was built with \"${storedModel}\" but active embedding model is \"${activeModel}\". ` +\n `Falling back to full-index selection. Run 'llmwiki compile' to rebuild embeddings.`,\n ),\n );\n}\n\n/** Test-only hook: clear the warned-pair cache so each test sees a fresh warning. */\nexport function resetStaleEmbeddingWarnings(): void {\n warnedStaleModels.clear();\n}\n\n/** Choose the active embedding model name, defaulting to anthropic's voyage model. */\nexport function resolveEmbeddingModel(): string {\n const providerName = getActiveProviderName();\n const configuredModel = process.env.LLMWIKI_EMBEDDING_MODEL?.trim();\n if (configuredModel && (providerName === \"openai\" || providerName === \"ollama\")) {\n return configuredModel;\n }\n return EMBEDDING_MODELS[providerName] ?? EMBEDDING_MODELS.anthropic;\n}\n\n/** Merge fresh embeddings into an existing store, dropping slugs not in liveSlugs. */\nfunction mergeEntries(\n existing: EmbeddingEntry[],\n fresh: EmbeddingEntry[],\n liveSlugs: Set<string>,\n): EmbeddingEntry[] {\n const bySlug = new Map<string, EmbeddingEntry>();\n for (const entry of existing) {\n if (liveSlugs.has(entry.slug)) bySlug.set(entry.slug, entry);\n }\n for (const entry of fresh) {\n bySlug.set(entry.slug, entry);\n }\n return Array.from(bySlug.values());\n}\n\n/**\n * Re-embed the given changed slugs and prune any entries whose pages no longer\n * exist on disk. Changed slugs not present as live pages are silently skipped.\n */\nexport async function updateEmbeddings(root: string, changedSlugs: string[]): Promise<void> {\n const records = await collectPageRecords(root);\n const liveSlugs = new Set(records.map((r) => r.slug));\n const embeddingModel = resolveEmbeddingModel();\n const existingStore = await readEmbeddingStore(root);\n const modelChanged = Boolean(existingStore && existingStore.model !== embeddingModel);\n const toEmbed = new Set(changedSlugs.filter((slug) => liveSlugs.has(slug)));\n const previousEntries = modelChanged ? [] : existingStore?.entries ?? [];\n\n // Cold start: embed every page so the store is immediately useful.\n if (!existingStore || modelChanged) {\n for (const record of records) toEmbed.add(record.slug);\n }\n\n if (!modelChanged && toEmbed.size === 0 && previousEntries.every((e) => liveSlugs.has(e.slug))) {\n return;\n }\n\n const freshEntries = await embedPages(records, toEmbed);\n const mergedEntries = mergeEntries(previousEntries, freshEntries, liveSlugs);\n\n const dimensions = mergedEntries[0]?.vector.length ?? 0;\n const store: EmbeddingStore = {\n version: 1,\n model: embeddingModel,\n dimensions,\n entries: mergedEntries,\n };\n await writeEmbeddingStore(root, store);\n output.status(\"*\", output.dim(`Embeddings updated (${mergedEntries.length} pages).`));\n}\n","/**\n * Review candidate persistence for the llmwiki compile pipeline.\n *\n * When `llmwiki compile --review` runs, generated wiki pages are routed\n * here as JSON candidate records under `.llmwiki/candidates/` instead of\n * being written directly to `wiki/`. Reviewers then approve or reject the\n * proposals via the `llmwiki review` subcommands.\n *\n * Candidates are deliberately kept as standalone JSON so they survive across\n * compile runs and can be inspected manually without the CLI. Each record\n * stores the full page body so approval is a pure copy — the LLM is never\n * called again at approval time.\n */\n\nimport { readdir, rename, unlink, writeFile, mkdir } from \"fs/promises\";\nimport { existsSync } from \"fs\";\nimport path from \"path\";\nimport { randomBytes } from \"crypto\";\nimport { atomicWrite, safeReadFile } from \"../utils/markdown.js\";\nimport * as output from \"../utils/output.js\";\nimport {\n CANDIDATES_DIR,\n CANDIDATES_ARCHIVE_DIR,\n} from \"../utils/constants.js\";\nimport type { ReviewCandidate, SourceState } from \"../utils/types.js\";\n\n/** Length (bytes) of the random suffix appended to candidate ids. */\nconst ID_SUFFIX_BYTES = 4;\n\n/** Filesystem extension used for candidate JSON files. */\nconst CANDIDATE_EXT = \".json\";\n\n/** Input shape for creating a new candidate (id + timestamp generated here). */\ninterface CandidateDraft {\n title: string;\n slug: string;\n summary: string;\n sources: string[];\n body: string;\n /**\n * Per-source state entries to persist into `.llmwiki/state.json` when this\n * candidate is approved. Keyed by source filename. Optional so callers that\n * never need incremental tracking (legacy / tests) can omit it.\n */\n sourceStates?: Record<string, SourceState>;\n}\n\n/** Build a deterministic-but-unique id from a slug and a short random suffix. */\nfunction buildCandidateId(slug: string): string {\n const suffix = randomBytes(ID_SUFFIX_BYTES).toString(\"hex\");\n return `${slug}-${suffix}`;\n}\n\n/** Absolute path to a candidate's JSON file. */\nfunction candidatePath(root: string, id: string): string {\n return path.join(root, CANDIDATES_DIR, `${id}${CANDIDATE_EXT}`);\n}\n\n/** Absolute path to the archived JSON file for a rejected candidate. */\nfunction archivePath(root: string, id: string): string {\n return path.join(root, CANDIDATES_ARCHIVE_DIR, `${id}${CANDIDATE_EXT}`);\n}\n\n/**\n * Persist a new candidate record and return it. The id is generated from the\n * slug plus a short random suffix so multiple compile runs can co-exist.\n * @param root - Project root directory.\n * @param draft - The candidate fields to persist.\n * @returns The full ReviewCandidate (with id + generatedAt populated).\n */\nexport async function writeCandidate(\n root: string,\n draft: CandidateDraft,\n): Promise<ReviewCandidate> {\n const candidate: ReviewCandidate = {\n id: buildCandidateId(draft.slug),\n title: draft.title,\n slug: draft.slug,\n summary: draft.summary,\n sources: draft.sources,\n body: draft.body,\n generatedAt: new Date().toISOString(),\n ...(draft.sourceStates ? { sourceStates: draft.sourceStates } : {}),\n };\n\n await atomicWrite(candidatePath(root, candidate.id), JSON.stringify(candidate, null, 2));\n return candidate;\n}\n\n/**\n * Emit a CLI error, set exit code 1, and return null. Used by candidate load\n * helpers to avoid duplicating the error-path boilerplate.\n * @param message - Error message to display.\n */\nfunction failWithError(message: string): null {\n output.status(\"!\", output.error(message));\n process.exitCode = 1;\n return null;\n}\n\n/**\n * Load a candidate by id and, if missing, emit the standard \"not found\" CLI\n * error and set process.exitCode = 1. Returns null when the candidate is\n * missing so callers can early-return without re-implementing the same\n * error block in every review subcommand.\n * @param root - Project root directory.\n * @param id - Candidate id to look up.\n */\nexport async function loadCandidateOrFail(\n root: string,\n id: string,\n): Promise<ReviewCandidate | null> {\n const candidate = await readCandidate(root, id);\n if (!candidate) return failWithError(`Candidate not found: ${id}`);\n return candidate;\n}\n\n/**\n * Re-read a candidate under the lock and abort if it has disappeared.\n *\n * This is the authoritative TOCTOU guard: a concurrent approve or reject may\n * have removed the candidate after the pre-lock fast-fail but before the lock\n * was acquired. Returning `null` signals the caller to abort without writing\n * any output artefact.\n * @param root - Project root directory.\n * @param id - Candidate id to load.\n * @returns The candidate if still present, or `null` after setting exit code 1.\n */\nexport async function loadCandidateUnderLockOrFail(\n root: string,\n id: string,\n): Promise<ReviewCandidate | null> {\n const candidate = await readCandidate(root, id);\n if (!candidate) {\n return failWithError(`Candidate ${id} was removed by another process during review.`);\n }\n return candidate;\n}\n\n/** Parse a single candidate JSON file. Returns null when the file is missing or malformed. */\nexport async function readCandidate(\n root: string,\n id: string,\n): Promise<ReviewCandidate | null> {\n const raw = await safeReadFile(candidatePath(root, id));\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as ReviewCandidate;\n if (!isValidCandidate(parsed)) return null;\n return parsed;\n } catch {\n return null;\n }\n}\n\n/** Defensive type-guard so corrupted candidate files don't blow up the CLI. */\nfunction isValidCandidate(value: unknown): value is ReviewCandidate {\n if (!value || typeof value !== \"object\") return false;\n const candidate = value as Record<string, unknown>;\n return (\n typeof candidate.id === \"string\" &&\n typeof candidate.title === \"string\" &&\n typeof candidate.slug === \"string\" &&\n typeof candidate.body === \"string\" &&\n Array.isArray(candidate.sources)\n );\n}\n\n/**\n * List every candidate currently pending review, sorted by generation time.\n * Skips files that aren't candidate JSON (e.g. the archive subdirectory).\n * @param root - Project root directory.\n * @returns All pending review candidates.\n */\nexport async function listCandidates(root: string): Promise<ReviewCandidate[]> {\n const dir = path.join(root, CANDIDATES_DIR);\n if (!existsSync(dir)) return [];\n\n const entries = await readdir(dir, { withFileTypes: true });\n const candidates: ReviewCandidate[] = [];\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith(CANDIDATE_EXT)) continue;\n const id = entry.name.slice(0, -CANDIDATE_EXT.length);\n const candidate = await readCandidate(root, id);\n if (candidate) candidates.push(candidate);\n }\n\n candidates.sort((a, b) => a.generatedAt.localeCompare(b.generatedAt));\n return candidates;\n}\n\n/**\n * Count pending candidates using the same validity filter as listCandidates,\n * so consumers (e.g. `wiki_status.pendingCandidates`) never report counts\n * that disagree with what `review list` actually shows. Malformed JSON files\n * are skipped here exactly as they are by listCandidates.\n */\nexport async function countCandidates(root: string): Promise<number> {\n const candidates = await listCandidates(root);\n return candidates.length;\n}\n\n/** Remove a pending candidate from disk. Returns false when nothing existed to remove. */\nexport async function deleteCandidate(root: string, id: string): Promise<boolean> {\n const filePath = candidatePath(root, id);\n if (!existsSync(filePath)) return false;\n await unlink(filePath);\n return true;\n}\n\n/**\n * Move a candidate from the pending area into the archive subdirectory so\n * rejected proposals stay auditable without touching `wiki/`.\n * @param root - Project root directory.\n * @param id - Candidate id to archive.\n * @returns True when the candidate was found and archived.\n */\nexport async function archiveCandidate(root: string, id: string): Promise<boolean> {\n const sourcePath = candidatePath(root, id);\n if (!existsSync(sourcePath)) return false;\n\n const target = archivePath(root, id);\n await mkdir(path.dirname(target), { recursive: true });\n // Copy via writeFile + unlink to support cross-filesystem rename failures.\n try {\n await rename(sourcePath, target);\n } catch {\n const raw = await safeReadFile(sourcePath);\n await writeFile(target, raw, \"utf-8\");\n await unlink(sourcePath);\n }\n return true;\n}\n","/**\n * Wiki page rendering for the llmwiki compile pipeline.\n *\n * Encapsulates the single-page generation step: gather related pages, call\n * the LLM, build frontmatter, and produce the final markdown blob. Splitting\n * this away from the orchestrator (`compiler/index.ts`) keeps the orchestrator\n * focused on phase sequencing and lets the review-candidate code path reuse\n * the exact same renderer used for direct writes.\n */\n\nimport { readdir } from \"fs/promises\";\nimport path from \"path\";\nimport {\n buildFrontmatter,\n parseFrontmatter,\n safeReadFile,\n} from \"../utils/markdown.js\";\nimport { callClaude } from \"../utils/llm.js\";\nimport { buildPagePrompt } from \"./prompts.js\";\nimport { addObsidianMeta } from \"./obsidian.js\";\nimport { addProvenanceMeta, reportContradictionWarnings } from \"./provenance.js\";\nimport { CONCEPTS_DIR } from \"../utils/constants.js\";\nimport type { ExtractedConcept } from \"../utils/types.js\";\n\n/** Maximum number of existing concept pages to include as cross-reference context. */\nconst RELATED_PAGE_CONTEXT_LIMIT = 5;\n\n/** A merged-concept input from the orchestrator (multiple sources merged into one). */\ninterface RenderableConcept {\n slug: string;\n concept: ExtractedConcept;\n sourceFiles: string[];\n combinedContent: string;\n}\n\n/**\n * Render a wiki page (frontmatter + body) for a merged concept by calling\n * the LLM with cross-referencing context from existing concept pages.\n * @param root - Project root directory.\n * @param entry - The merged concept to render.\n * @returns Full markdown content (frontmatter + body, trailing newline).\n */\nexport async function renderMergedPageContent(\n root: string,\n entry: RenderableConcept,\n): Promise<string> {\n const pagePath = path.join(root, CONCEPTS_DIR, `${entry.slug}.md`);\n const existingPage = await safeReadFile(pagePath);\n const relatedPages = await loadRelatedPages(root, entry.slug);\n\n const system = buildPagePrompt(\n entry.concept.concept,\n entry.combinedContent,\n existingPage,\n relatedPages,\n );\n\n const pageBody = await callClaude({\n system,\n messages: [\n { role: \"user\", content: `Write the wiki page for \"${entry.concept.concept}\".` },\n ],\n });\n\n const frontmatter = buildMergedFrontmatter(entry, existingPage);\n reportContradictionWarnings(entry.concept.concept, entry.concept);\n return `${frontmatter}\\n\\n${pageBody}\\n`;\n}\n\n/** Construct the frontmatter block for a merged concept, preserving createdAt. */\nfunction buildMergedFrontmatter(entry: RenderableConcept, existingPage: string): string {\n const now = new Date().toISOString();\n const existing = existingPage ? parseFrontmatter(existingPage) : null;\n const createdAt = (existing?.meta.createdAt && typeof existing.meta.createdAt === \"string\")\n ? existing.meta.createdAt\n : now;\n const frontmatterFields: Record<string, unknown> = {\n title: entry.concept.concept,\n summary: entry.concept.summary,\n sources: entry.sourceFiles,\n createdAt,\n updatedAt: now,\n };\n addObsidianMeta(frontmatterFields, entry.concept.concept, entry.concept.tags ?? []);\n addProvenanceMeta(frontmatterFields, entry.concept);\n return buildFrontmatter(frontmatterFields);\n}\n\n/**\n * Load related wiki pages to provide cross-referencing context.\n * Returns concatenated content of up to RELATED_PAGE_CONTEXT_LIMIT pages.\n * @param root - Project root directory.\n * @param excludeSlug - Slug of the current page to exclude.\n * @returns Concatenated related page contents (empty when concepts dir is missing).\n */\nasync function loadRelatedPages(root: string, excludeSlug: string): Promise<string> {\n const conceptsPath = path.join(root, CONCEPTS_DIR);\n let files: string[];\n\n try {\n files = await readdir(conceptsPath);\n } catch {\n return \"\";\n }\n\n const related = files\n .filter((f) => f.endsWith(\".md\") && f !== `${excludeSlug}.md`)\n .slice(0, RELATED_PAGE_CONTEXT_LIMIT);\n\n const contents: string[] = [];\n for (const f of related) {\n const content = await safeReadFile(path.join(conceptsPath, f));\n if (!content) continue;\n const { meta } = parseFrontmatter(content);\n if (meta.orphaned) continue;\n contents.push(content);\n }\n\n return contents.join(\"\\n\\n---\\n\\n\");\n}\n","/**\n * Helpers for surfacing provenance metadata during compilation.\n *\n * Keeps the compile orchestrator small by isolating the logic that copies\n * confidence/contradiction signals from extracted concepts onto wiki page\n * frontmatter and emits compile-time warnings when contradictions are\n * reported.\n */\n\nimport * as output from \"../utils/output.js\";\nimport type { ExtractedConcept } from \"../utils/types.js\";\n\n/**\n * Copy provenance metadata fields from an extracted concept onto the\n * frontmatter record, omitting fields the LLM did not provide so existing\n * pages without these fields stay clean.\n * @param fields - Mutable frontmatter record being assembled for a page.\n * @param concept - Source concept whose provenance metadata to apply.\n */\nexport function addProvenanceMeta(\n fields: Record<string, unknown>,\n concept: ExtractedConcept,\n): void {\n if (typeof concept.confidence === \"number\") {\n fields.confidence = concept.confidence;\n }\n if (concept.provenanceState) {\n fields.provenanceState = concept.provenanceState;\n }\n if (concept.contradictedBy && concept.contradictedBy.length > 0) {\n fields.contradictedBy = concept.contradictedBy;\n }\n if (typeof concept.inferredParagraphs === \"number\") {\n fields.inferredParagraphs = concept.inferredParagraphs;\n }\n}\n\n/**\n * Print a compile-time warning when a concept reports contradictions with\n * other pages. Returns silently when there is nothing to report.\n * @param conceptTitle - Human-readable title of the concept being compiled.\n * @param concept - The extracted concept whose contradictions to surface.\n */\nexport function reportContradictionWarnings(\n conceptTitle: string,\n concept: ExtractedConcept,\n): void {\n const refs = concept.contradictedBy;\n if (!refs || refs.length === 0) return;\n const slugs = refs.map((r) => r.slug).join(\", \");\n output.status(\n \"!\",\n output.warn(`Contradiction reported on \"${conceptTitle}\" — conflicts with: ${slugs}`),\n );\n}\n","/**\n * Commander action for `llmwiki query <question>`.\n * Two-step LLM-powered wiki query that first selects relevant pages from the\n * wiki index, then streams an answer grounded in those pages. Optionally saves\n * the response as a new page in wiki/queries/.\n *\n * Step 1 - Page Selection: Reads wiki/index.md and asks Claude (via tool_use)\n * to pick the most relevant concept pages for the question.\n *\n * Step 2 - Answer Generation: Loads the selected pages in full and streams\n * a cited answer to the terminal.\n */\n\nimport { existsSync } from \"fs\";\nimport path from \"path\";\nimport { callClaude } from \"../utils/llm.js\";\nimport type { LLMTool } from \"../utils/provider.js\";\nimport { atomicWrite, safeReadFile, slugify, buildFrontmatter, parseFrontmatter } from \"../utils/markdown.js\";\nimport { generateIndex } from \"../compiler/indexgen.js\";\nimport * as output from \"../utils/output.js\";\nimport { QUERY_PAGE_LIMIT, INDEX_FILE, CONCEPTS_DIR, QUERIES_DIR } from \"../utils/constants.js\";\nimport { findRelevantPages, updateEmbeddings } from \"../utils/embeddings.js\";\nimport type { QueryResult } from \"../utils/types.js\";\n\n/** Directories to search when loading selected pages, in priority order. */\nconst PAGE_DIRS = [CONCEPTS_DIR, QUERIES_DIR];\n\n/** Tool schema for page selection (provider-agnostic). */\nconst PAGE_SELECTION_TOOL: LLMTool = {\n name: \"select_pages\",\n description: \"Select the most relevant wiki pages to answer a question\",\n input_schema: {\n type: \"object\" as const,\n properties: {\n pages: {\n type: \"array\",\n items: {\n type: \"string\",\n description: \"Slug of a relevant wiki page (e.g. 'llm-knowledge-bases')\",\n },\n maxItems: QUERY_PAGE_LIMIT,\n },\n reasoning: {\n type: \"string\",\n description: \"Brief explanation of why these pages were selected\",\n },\n },\n required: [\"pages\", \"reasoning\"],\n },\n};\n\ninterface PageSelectionResult {\n pages: string[];\n reasoning: string;\n}\n\n/**\n * Select the most relevant wiki pages for a question using Claude tool_use.\n * @param question - The user's natural language question.\n * @param indexContent - The full text of wiki/index.md.\n * @returns Parsed page slugs and reasoning from Claude.\n */\nexport async function selectPages(\n question: string,\n indexContent: string,\n): Promise<PageSelectionResult> {\n const systemPrompt =\n \"You are a knowledge base assistant. Given a question and a wiki index, select the most relevant pages.\";\n\n const userMessage = `Question: ${question}\\n\\nWiki Index:\\n${indexContent}`;\n\n const rawResult = await callClaude({\n system: systemPrompt,\n messages: [{ role: \"user\", content: userMessage }],\n tools: [PAGE_SELECTION_TOOL],\n });\n\n try {\n const parsed = JSON.parse(rawResult);\n return {\n pages: Array.isArray(parsed.pages) ? parsed.pages.filter((p: unknown) => typeof p === \"string\") : [],\n reasoning: typeof parsed.reasoning === \"string\" ? parsed.reasoning : \"No reasoning provided\",\n };\n } catch {\n return { pages: [], reasoning: \"Failed to parse page selection response\" };\n }\n}\n\n/** Render a list of candidate pages in the same bullet format selectPages() consumes. */\nfunction buildFilteredIndex(\n candidates: Array<{ slug: string; title: string; summary: string }>,\n): string {\n return candidates\n .map((entry) => `- **${entry.slug}**: ${entry.title} — ${entry.summary}`)\n .join(\"\\n\");\n}\n\ninterface SelectedPages {\n pages: string[];\n rawPages: string[];\n reasoning: string;\n}\n\n/**\n * Pick relevant pages using embedding pre-filter when available.\n * Falls back to sending the full wiki index when no embeddings store exists\n * or when the embedding call fails.\n */\nasync function selectRelevantPages(root: string, question: string): Promise<SelectedPages> {\n const candidates = await tryFindRelevantPages(root, question);\n\n if (candidates.length > 0) {\n const filteredIndex = buildFilteredIndex(candidates);\n const { pages: rawPages, reasoning } = await selectPages(question, filteredIndex);\n // Tool output holds slugs directly in the semantic path — no slugify needed.\n return { pages: rawPages, rawPages, reasoning };\n }\n\n const indexContent = await safeReadFile(path.join(root, INDEX_FILE));\n const { pages: rawPages, reasoning } = await selectPages(question, indexContent);\n return { pages: rawPages.map((p) => slugify(p)), rawPages, reasoning };\n}\n\n/** Embedding-based candidate lookup that never throws. */\nasync function tryFindRelevantPages(\n root: string,\n question: string,\n): Promise<Array<{ slug: string; title: string; summary: string }>> {\n try {\n return await findRelevantPages(root, question);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n output.status(\"!\", output.dim(`Semantic pre-filter unavailable (${message}); using full index.`));\n return [];\n }\n}\n\n/**\n * Load the full content of each selected wiki page.\n * Skips pages that don't exist and warns the user.\n * @param root - Absolute path to the project root directory.\n * @param slugs - Array of page slugs to load from wiki/concepts/.\n * @returns Combined page contents with slug headers for context.\n */\nexport async function loadSelectedPages(root: string, slugs: string[]): Promise<string> {\n const sections: string[] = [];\n\n for (const slug of slugs) {\n let content = \"\";\n for (const dir of PAGE_DIRS) {\n const candidate = await safeReadFile(path.join(root, dir, `${slug}.md`));\n if (!candidate) continue;\n const { meta } = parseFrontmatter(candidate);\n if (meta.orphaned) continue;\n content = candidate;\n break;\n }\n\n if (!content) {\n output.status(\"?\", output.warn(`Page not found: ${slug}.md — skipping`));\n continue;\n }\n\n sections.push(`--- Page: ${slug} ---\\n${content}`);\n }\n\n return sections.join(\"\\n\\n\");\n}\n\n/** Shared system prompt for the answer-generation step. */\nconst ANSWER_SYSTEM_PROMPT =\n \"You are a knowledge assistant. Answer the question using ONLY the wiki content provided. \" +\n \"Cite specific pages using [[Page Title]] wikilinks. \" +\n \"If the wiki doesn't contain enough information, say so.\";\n\n/**\n * Call the LLM with the loaded wiki pages as grounding context.\n * Streams to the provided onToken callback when one is supplied,\n * otherwise returns the full answer without streaming.\n */\nasync function callAnswerLLM(\n question: string,\n pagesContent: string,\n onToken?: (text: string) => void,\n): Promise<string> {\n const userMessage = `Question: ${question}\\n\\nRelevant wiki pages:\\n${pagesContent}`;\n return callClaude({\n system: ANSWER_SYSTEM_PROMPT,\n messages: [{ role: \"user\", content: userMessage }],\n stream: Boolean(onToken),\n onToken,\n });\n}\n\n/**\n * Generate a one-line summary from the answer for use in the wiki index.\n * Takes the first sentence (up to 120 chars) so the page-selection LLM\n * has retrieval signal beyond just the title.\n * @param answer - The full answer text.\n * @returns A short summary string.\n */\nexport function summarizeAnswer(answer: string): string {\n const firstLine = answer.trim().split(/\\n/)[0] ?? \"\";\n const firstSentence = firstLine.split(/(?<=[.!?])\\s/)[0] ?? firstLine;\n return firstSentence.slice(0, 120);\n}\n\n/**\n * Save a query answer as a wiki page in the queries/ directory,\n * then regenerate the wiki index so the answer is immediately retrievable.\n * @param root - Absolute path to the project root directory.\n * @param question - The original question used as the page title.\n * @param answer - The generated answer body.\n */\nasync function saveQueryPage(root: string, question: string, answer: string): Promise<string> {\n const slug = slugify(question);\n const filePath = path.join(root, QUERIES_DIR, `${slug}.md`);\n\n const frontmatter = buildFrontmatter({\n title: question,\n summary: summarizeAnswer(answer),\n type: \"query\",\n createdAt: new Date().toISOString(),\n });\n\n const document = `${frontmatter}\\n\\n${answer}\\n`;\n await atomicWrite(filePath, document);\n\n output.status(\n \"+\",\n output.success(`Saved query → ${output.source(filePath)}`),\n );\n\n // Regenerate the index so the saved query is immediately discoverable\n // by the next query's page-selection step.\n await generateIndex(root);\n\n // Index the new query so semantic search retrieves it on the next question.\n // Non-critical: embedding failures (e.g. missing VOYAGE_API_KEY) don't block save.\n try {\n await updateEmbeddings(root, [slug]);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n output.status(\"!\", output.warn(`Skipped embeddings update: ${message}`));\n }\n\n return slug;\n}\n\n/** Options for generateAnswer — programmatic-friendly. */\ninterface GenerateAnswerOptions {\n /** Persist the answer as a wiki query page when set. */\n save?: boolean;\n /** Per-token callback for streaming. Omit for non-streaming usage. */\n onToken?: (text: string) => void;\n /** Callback fired once page selection completes — lets CLIs print reasoning before streaming. */\n onPageSelection?: (pages: string[], reasoning: string) => void;\n}\n\n/**\n * Run the two-step page-selection + answer-generation pipeline and return\n * a structured QueryResult. This is the programmatic entry point used by\n * the MCP server and any non-CLI consumer.\n *\n * @param root - Absolute path to the project root directory.\n * @param question - The natural language question to answer.\n * @param options - Streaming + save behaviour controls.\n * @returns Answer text, selected slugs, reasoning, and saved slug if applicable.\n */\nexport async function generateAnswer(\n root: string,\n question: string,\n options: GenerateAnswerOptions = {},\n): Promise<QueryResult> {\n if (!existsSync(path.join(root, INDEX_FILE))) {\n throw new Error(\"Wiki index not found. Run `llmwiki compile` first.\");\n }\n\n const { pages, reasoning } = await selectRelevantPages(root, question);\n options.onPageSelection?.(pages, reasoning);\n\n const pagesContent = await loadSelectedPages(root, pages);\n\n if (!pagesContent) {\n return { answer: \"\", selectedPages: pages, reasoning };\n }\n\n const answer = await callAnswerLLM(question, pagesContent, options.onToken);\n\n let saved: string | undefined;\n if (options.save) {\n saved = await saveQueryPage(root, question, answer);\n }\n\n return { answer, selectedPages: pages, reasoning, saved };\n}\n\n/**\n * Run a two-step LLM-powered query against the knowledge wiki.\n * @param root - Absolute path to the project root directory.\n * @param question - The natural language question to answer.\n * @param options - Command options (e.g. --save to persist the answer).\n */\nexport default async function queryCommand(\n root: string,\n question: string,\n options: { save?: boolean },\n): Promise<void> {\n if (!existsSync(path.join(root, INDEX_FILE))) {\n output.status(\"!\", output.error(\"Wiki index not found. Run `llmwiki compile` first.\"));\n return;\n }\n\n output.header(\"Selecting relevant pages\");\n\n const result = await generateAnswer(root, question, {\n save: options.save,\n onToken: (text) => process.stdout.write(text),\n onPageSelection: (pages, reasoning) => {\n output.status(\"i\", output.dim(`Reasoning: ${reasoning}`));\n output.status(\"*\", output.info(`Selected ${pages.length} page(s): ${pages.join(\", \")}`));\n output.header(\"Generating answer\");\n },\n });\n\n // Newline after streamed answer so subsequent terminal output formats cleanly.\n process.stdout.write(\"\\n\");\n\n if (!result.answer) {\n output.status(\"!\", output.error(\"No matching pages found. Try refining your question.\"));\n return;\n }\n\n if (result.saved) {\n output.status(\"→\", output.dim(\"Saved. Future queries will use this answer as context.\"));\n } else {\n output.status(\"→\", output.dim(\"Tip: use --save to add this answer to your wiki\"));\n }\n}\n","/**\n * Commander action for `llmwiki watch`.\n *\n * Monitors sources/ for file changes via chokidar and triggers incremental\n * recompilation automatically. Uses a debounce to batch rapid changes into\n * a single compile pass. Respects the .llmwiki/lock file — queues changes\n * if a compile is already running.\n */\n\nimport { watch as chokidarWatch } from \"chokidar\";\nimport { existsSync } from \"fs\";\nimport path from \"path\";\nimport { compile } from \"../compiler/index.js\";\nimport { SOURCES_DIR } from \"../utils/constants.js\";\nimport * as output from \"../utils/output.js\";\n\nconst DEBOUNCE_MS = 500;\n\n/**\n * Start watching sources/ for changes and auto-recompile.\n * Runs until the process is killed (Ctrl+C).\n */\nexport default async function watchCommand(): Promise<void> {\n const sourcesPath = path.resolve(SOURCES_DIR);\n\n if (!existsSync(sourcesPath)) {\n output.status(\n \"!\",\n output.warn('No sources/ directory found. Run `llmwiki ingest <url>` first.'),\n );\n return;\n }\n\n output.header(\"llmwiki watch\");\n output.status(\"👁\", output.info(`Watching ${sourcesPath} for changes...`));\n output.status(\"i\", output.dim(\"Press Ctrl+C to stop.\\n\"));\n\n let compiling = false;\n let pendingRecompile = false;\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n const triggerCompile = async () => {\n if (compiling) {\n pendingRecompile = true;\n return;\n }\n\n compiling = true;\n try {\n await compile(process.cwd());\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n output.status(\"!\", output.error(`Compile failed: ${msg}`));\n }\n\n compiling = false;\n\n // If changes arrived during compilation, recompile\n if (pendingRecompile) {\n pendingRecompile = false;\n await triggerCompile();\n }\n };\n\n const scheduleCompile = (eventPath: string, event: string) => {\n output.status(\n \"~\",\n output.dim(`${event}: ${path.basename(eventPath)}`),\n );\n\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(triggerCompile, DEBOUNCE_MS);\n };\n\n const watcher = chokidarWatch(sourcesPath, {\n ignoreInitial: true,\n awaitWriteFinish: { stabilityThreshold: 200 },\n });\n\n watcher\n .on(\"add\", (p) => scheduleCompile(p, \"added\"))\n .on(\"change\", (p) => scheduleCompile(p, \"changed\"))\n .on(\"unlink\", (p) => scheduleCompile(p, \"deleted\"));\n\n // Keep process alive\n await new Promise<void>(() => {});\n}\n","/**\n * Lint rules for wiki quality checks.\n *\n * Each rule is a function that takes a project root path and returns\n * an array of LintResult diagnostics. Rules perform pure static analysis\n * with no LLM calls — they inspect frontmatter, wikilinks, citations,\n * and file structure to find potential issues.\n */\n\nimport { readdir, readFile } from \"fs/promises\";\nimport { existsSync } from \"fs\";\nimport path from \"path\";\nimport { parseFrontmatter, parseProvenanceMetadata, slugify } from \"../utils/markdown.js\";\nimport {\n CONCEPTS_DIR,\n LOW_CONFIDENCE_THRESHOLD,\n MAX_INFERRED_PARAGRAPHS_WITHOUT_CITATIONS,\n QUERIES_DIR,\n SOURCES_DIR,\n} from \"../utils/constants.js\";\nimport type { LintResult } from \"./types.js\";\n\n/** Minimum body length (in characters) for a page to be considered non-empty. */\nconst MIN_BODY_LENGTH = 50;\n\n/** Pattern matching [[Wikilink Title]] references in markdown content. */\nconst WIKILINK_PATTERN = /\\[\\[([^\\]]+)\\]\\]/g;\n\n/** Pattern matching ^[filename.md] citation markers in markdown content. */\nconst CITATION_PATTERN = /\\^\\[([^\\]]+)\\]/g;\n\n/** Match result with its line number and captured group. */\ninterface LineMatch {\n captured: string;\n line: number;\n}\n\n/**\n * Scan all lines of a page's content and return regex matches with line numbers.\n * Shared by rules that need to locate patterns within page bodies.\n */\nfunction findMatchesInContent(content: string, pattern: RegExp): LineMatch[] {\n const results: LineMatch[] = [];\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const matches = lines[i].matchAll(pattern);\n for (const match of matches) {\n results.push({ captured: match[1], line: i + 1 });\n }\n }\n return results;\n}\n\n/**\n * Read all .md files from a directory, returning their paths and parsed content.\n * Returns an empty array if the directory does not exist.\n */\nasync function readMarkdownFiles(\n dirPath: string,\n): Promise<Array<{ filePath: string; content: string }>> {\n if (!existsSync(dirPath)) return [];\n\n const entries = await readdir(dirPath);\n const mdFiles = entries.filter((f) => f.endsWith(\".md\"));\n\n const results = await Promise.all(\n mdFiles.map(async (fileName) => {\n const filePath = path.join(dirPath, fileName);\n const content = await readFile(filePath, \"utf-8\");\n return { filePath, content };\n }),\n );\n\n return results;\n}\n\n/**\n * Collect all wiki pages from both concepts/ and queries/ directories.\n */\nasync function collectAllPages(\n root: string,\n): Promise<Array<{ filePath: string; content: string }>> {\n const conceptPages = await readMarkdownFiles(path.join(root, CONCEPTS_DIR));\n const queryPages = await readMarkdownFiles(path.join(root, QUERIES_DIR));\n return [...conceptPages, ...queryPages];\n}\n\n/**\n * Build a set of slugs for all existing wiki pages.\n * Used to verify that wikilink targets actually exist.\n */\nfunction buildPageSlugSet(\n pages: Array<{ filePath: string }>,\n): Set<string> {\n const slugs = new Set<string>();\n for (const page of pages) {\n const baseName = path.basename(page.filePath, \".md\");\n slugs.add(baseName.toLowerCase());\n }\n return slugs;\n}\n\n/** Find [[Title]] wikilinks that don't match any existing wiki page. */\nexport async function checkBrokenWikilinks(root: string): Promise<LintResult[]> {\n const pages = await collectAllPages(root);\n const existingSlugs = buildPageSlugSet(pages);\n const results: LintResult[] = [];\n\n for (const page of pages) {\n for (const { captured, line } of findMatchesInContent(page.content, WIKILINK_PATTERN)) {\n const linkSlug = slugify(captured);\n if (!existingSlugs.has(linkSlug)) {\n results.push({\n rule: \"broken-wikilink\",\n severity: \"error\",\n file: page.filePath,\n message: `Broken wikilink [[${captured}]] — no matching page found`,\n line,\n });\n }\n }\n }\n\n return results;\n}\n\n/** Find pages with `orphaned: true` in their frontmatter. */\nexport async function checkOrphanedPages(root: string): Promise<LintResult[]> {\n const pages = await collectAllPages(root);\n const results: LintResult[] = [];\n\n for (const page of pages) {\n const { meta } = parseFrontmatter(page.content);\n if (meta.orphaned === true) {\n results.push({\n rule: \"orphaned-page\",\n severity: \"warning\",\n file: page.filePath,\n message: `Page is marked as orphaned`,\n });\n }\n }\n\n return results;\n}\n\n/** Find pages with empty or missing `summary` in frontmatter. */\nexport async function checkMissingSummaries(root: string): Promise<LintResult[]> {\n const pages = await collectAllPages(root);\n const results: LintResult[] = [];\n\n for (const page of pages) {\n const { meta } = parseFrontmatter(page.content);\n const summary = meta.summary;\n const isMissing = !summary || (typeof summary === \"string\" && summary.trim() === \"\");\n\n if (isMissing) {\n results.push({\n rule: \"missing-summary\",\n severity: \"warning\",\n file: page.filePath,\n message: `Page has no summary in frontmatter`,\n });\n }\n }\n\n return results;\n}\n\n/** Find multiple pages whose titles match case-insensitively. */\nexport async function checkDuplicateConcepts(root: string): Promise<LintResult[]> {\n const pages = await collectAllPages(root);\n const titleMap = new Map<string, string[]>();\n\n for (const page of pages) {\n const { meta } = parseFrontmatter(page.content);\n const title = typeof meta.title === \"string\" ? meta.title : \"\";\n if (!title) continue;\n\n const normalizedTitle = title.toLowerCase().trim();\n const existing = titleMap.get(normalizedTitle) ?? [];\n existing.push(page.filePath);\n titleMap.set(normalizedTitle, existing);\n }\n\n const results: LintResult[] = [];\n for (const [title, files] of titleMap) {\n if (files.length <= 1) continue;\n for (const file of files) {\n results.push({\n rule: \"duplicate-concept\",\n severity: \"error\",\n file,\n message: `Duplicate title \"${title}\" — also in ${files.filter((f) => f !== file).join(\", \")}`,\n });\n }\n }\n\n return results;\n}\n\n/** Find pages with frontmatter but very short or empty body content. */\nexport async function checkEmptyPages(root: string): Promise<LintResult[]> {\n const pages = await collectAllPages(root);\n const results: LintResult[] = [];\n\n for (const page of pages) {\n const { meta, body } = parseFrontmatter(page.content);\n const hasTitle = typeof meta.title === \"string\" && meta.title.trim() !== \"\";\n const isBodyEmpty = body.trim().length < MIN_BODY_LENGTH;\n\n if (hasTitle && isBodyEmpty) {\n results.push({\n rule: \"empty-page\",\n severity: \"warning\",\n file: page.filePath,\n message: `Page body is empty or too short (< ${MIN_BODY_LENGTH} chars)`,\n });\n }\n }\n\n return results;\n}\n\n/**\n * Flag pages whose frontmatter declares confidence below the threshold.\n * Pages without a confidence field are silently skipped to preserve\n * backward-compatibility with pre-existing wikis.\n */\nexport async function checkLowConfidencePages(root: string): Promise<LintResult[]> {\n const pages = await collectAllPages(root);\n const results: LintResult[] = [];\n\n for (const page of pages) {\n const { meta } = parseFrontmatter(page.content);\n const { confidence } = parseProvenanceMetadata(meta);\n if (confidence === undefined || confidence >= LOW_CONFIDENCE_THRESHOLD) continue;\n results.push({\n rule: \"low-confidence\",\n severity: \"warning\",\n file: page.filePath,\n message: `Page confidence ${confidence.toFixed(2)} is below ${LOW_CONFIDENCE_THRESHOLD}`,\n });\n }\n\n return results;\n}\n\n/** Flag pages whose frontmatter records contradictions with other pages. */\nexport async function checkContradictedPages(root: string): Promise<LintResult[]> {\n const pages = await collectAllPages(root);\n const results: LintResult[] = [];\n\n for (const page of pages) {\n const { meta } = parseFrontmatter(page.content);\n const { contradictedBy } = parseProvenanceMetadata(meta);\n if (!contradictedBy || contradictedBy.length === 0) continue;\n const slugs = contradictedBy.map((r) => r.slug).join(\", \");\n results.push({\n rule: \"contradicted-page\",\n severity: \"warning\",\n file: page.filePath,\n message: `Page contradicts: ${slugs}`,\n });\n }\n\n return results;\n}\n\n/**\n * Flag pages with too many inferred paragraphs unsupported by direct citations.\n * Uses the metadata-reported count when present and falls back to counting\n * uncited prose paragraphs in the body.\n */\nexport async function checkInferredWithoutCitations(root: string): Promise<LintResult[]> {\n const pages = await collectAllPages(root);\n const results: LintResult[] = [];\n\n for (const page of pages) {\n const { meta, body } = parseFrontmatter(page.content);\n const provenance = parseProvenanceMetadata(meta);\n const inferred = provenance.inferredParagraphs ?? countUncitedProseParagraphs(body);\n if (inferred <= MAX_INFERRED_PARAGRAPHS_WITHOUT_CITATIONS) continue;\n results.push({\n rule: \"excess-inferred-paragraphs\",\n severity: \"warning\",\n file: page.filePath,\n message: `Page has ${inferred} inferred paragraphs without citations (max ${MAX_INFERRED_PARAGRAPHS_WITHOUT_CITATIONS})`,\n });\n }\n\n return results;\n}\n\n/** Match a paragraph that looks like prose (not a heading, list, or code block). */\nconst PROSE_PARAGRAPH_LEAD = /^[A-Za-z]/;\n\n/** Count prose paragraphs in a body that lack a ^[citation] marker. */\nfunction countUncitedProseParagraphs(body: string): number {\n const paragraphs = body.split(/\\n\\s*\\n/);\n let count = 0;\n for (const block of paragraphs) {\n const trimmed = block.trim();\n if (trimmed.length === 0) continue;\n if (!PROSE_PARAGRAPH_LEAD.test(trimmed)) continue;\n if (CITATION_PATTERN.test(trimmed)) {\n CITATION_PATTERN.lastIndex = 0;\n continue;\n }\n CITATION_PATTERN.lastIndex = 0;\n count += 1;\n }\n return count;\n}\n\n/**\n * Expand a captured citation string into individual source filenames.\n * A multi-source citation like \"a.md, b.md\" is split on commas so each\n * filename can be checked independently. Single-source citations are\n * returned as a one-element array unchanged.\n */\nfunction splitCitationFilenames(captured: string): string[] {\n return captured.split(\",\").map((s) => s.trim()).filter((s) => s.length > 0);\n}\n\n/** Find ^[filename.md] citations referencing source files that don't exist.\n * Handles both single-source ^[file.md] and multi-source ^[a.md, b.md] forms.\n * Each filename in a multi-source citation is checked independently — only the\n * missing ones are reported, not the entire captured text as one filename.\n */\nexport async function checkBrokenCitations(root: string): Promise<LintResult[]> {\n const pages = await collectAllPages(root);\n const sourcesDir = path.join(root, SOURCES_DIR);\n const results: LintResult[] = [];\n\n for (const page of pages) {\n for (const { captured, line } of findMatchesInContent(page.content, CITATION_PATTERN)) {\n for (const filename of splitCitationFilenames(captured)) {\n const citedPath = path.join(sourcesDir, filename);\n if (!existsSync(citedPath)) {\n results.push({\n rule: \"broken-citation\",\n severity: \"error\",\n file: page.filePath,\n message: `Broken citation ^[${filename}] — source file not found`,\n line,\n });\n }\n }\n }\n }\n\n return results;\n}\n","/**\n * Wiki linter orchestrator.\n *\n * Imports all lint rules, runs them concurrently, and aggregates\n * results into a summary with error/warning/info counts.\n * This is the main entry point for programmatic lint access.\n */\n\nimport type { LintResult, LintRule, LintSummary } from \"./types.js\";\nimport {\n checkBrokenWikilinks,\n checkOrphanedPages,\n checkMissingSummaries,\n checkDuplicateConcepts,\n checkEmptyPages,\n checkBrokenCitations,\n checkLowConfidencePages,\n checkContradictedPages,\n checkInferredWithoutCitations,\n} from \"./rules.js\";\n\n/** All lint rules to execute during a lint pass. */\nconst ALL_RULES: LintRule[] = [\n checkBrokenWikilinks,\n checkOrphanedPages,\n checkMissingSummaries,\n checkDuplicateConcepts,\n checkEmptyPages,\n checkBrokenCitations,\n checkLowConfidencePages,\n checkContradictedPages,\n checkInferredWithoutCitations,\n];\n\n/**\n * Count occurrences of a specific severity level in the results.\n */\nfunction countBySeverity(\n results: LintResult[],\n severity: LintResult[\"severity\"],\n): number {\n return results.filter((r) => r.severity === severity).length;\n}\n\n/**\n * Run all lint rules concurrently against the wiki at the given root.\n * @param root - Absolute path to the project root directory.\n * @returns A summary containing all diagnostics and severity counts.\n */\nexport async function lint(root: string): Promise<LintSummary> {\n const ruleResults = await Promise.all(\n ALL_RULES.map((rule) => rule(root)),\n );\n\n const results = ruleResults.flat();\n\n return {\n errors: countBySeverity(results, \"error\"),\n warnings: countBySeverity(results, \"warning\"),\n info: countBySeverity(results, \"info\"),\n results,\n };\n}\n","/**\n * Commander action for `llmwiki lint`.\n *\n * Runs rule-based quality checks against the wiki without any LLM calls.\n * Prints colored diagnostics grouped by severity and exits with code 1\n * if any errors are found.\n */\n\nimport { lint } from \"../linter/index.js\";\nimport * as output from \"../utils/output.js\";\nimport type { LintResult } from \"../linter/types.js\";\n\n/** Map severity levels to output formatting functions. */\nconst SEVERITY_FORMATTERS: Record<LintResult[\"severity\"], (text: string) => string> = {\n error: output.error,\n warning: output.warn,\n info: output.info,\n};\n\n/** Map severity levels to display icons. */\nconst SEVERITY_ICONS: Record<LintResult[\"severity\"], string> = {\n error: \"x\",\n warning: \"!\",\n info: \"i\",\n};\n\n/** Print a single lint result with colored output. */\nfunction printResult(result: LintResult): void {\n const formatter = SEVERITY_FORMATTERS[result.severity];\n const icon = SEVERITY_ICONS[result.severity];\n const location = result.line ? `${result.file}:${result.line}` : result.file;\n output.status(icon, `${formatter(result.severity)} ${output.dim(location)} ${result.message}`);\n}\n\n/**\n * Run the lint command: execute all rules and print results.\n * Exits with code 1 if any errors are found.\n */\nexport default async function lintCommand(): Promise<void> {\n output.header(\"Linting wiki\");\n\n const summary = await lint(process.cwd());\n\n for (const result of summary.results) {\n printResult(result);\n }\n\n console.log();\n const summaryLine = [\n output.error(`${summary.errors} error(s)`),\n output.warn(`${summary.warnings} warning(s)`),\n output.info(`${summary.info} info`),\n ].join(\", \");\n output.status(\"*\", summaryLine);\n\n if (summary.errors > 0) {\n process.exit(1);\n }\n}\n","/**\n * Commander action for `llmwiki review list`.\n *\n * Prints every pending review candidate (id, slug, sources, generated time)\n * so reviewers can pick one to inspect with `llmwiki review show <id>`.\n */\n\nimport { listCandidates } from \"../compiler/candidates.js\";\nimport * as output from \"../utils/output.js\";\n\n/** List every pending candidate from .llmwiki/candidates/. */\nexport default async function reviewListCommand(): Promise<void> {\n output.header(\"Pending review candidates\");\n\n const candidates = await listCandidates(process.cwd());\n if (candidates.length === 0) {\n output.status(\"✓\", output.success(\"No pending candidates.\"));\n return;\n }\n\n for (const candidate of candidates) {\n const sources = candidate.sources.join(\", \");\n const meta = output.dim(`${candidate.generatedAt} | sources: ${sources}`);\n output.status(\"?\", `${output.info(candidate.id)} → ${candidate.slug} ${meta}`);\n }\n\n output.status(\n \"→\",\n output.dim(`Use \\`llmwiki review show <id>\\` to inspect a candidate.`),\n );\n}\n","/**\n * Commander action for `llmwiki review show <id>`.\n *\n * Prints a single candidate's metadata header followed by its full body so\n * reviewers can read the proposed page before approving or rejecting.\n */\n\nimport { loadCandidateOrFail } from \"../compiler/candidates.js\";\nimport * as output from \"../utils/output.js\";\n\n/** Print a single candidate's full content to stdout. */\nexport default async function reviewShowCommand(id: string): Promise<void> {\n const candidate = await loadCandidateOrFail(process.cwd(), id);\n if (!candidate) return;\n\n output.header(`Candidate ${candidate.id}`);\n output.status(\"i\", output.dim(`title: ${candidate.title}`));\n output.status(\"i\", output.dim(`slug: ${candidate.slug}`));\n output.status(\"i\", output.dim(`summary: ${candidate.summary}`));\n output.status(\"i\", output.dim(`sources: ${candidate.sources.join(\", \")}`));\n output.status(\"i\", output.dim(`generated: ${candidate.generatedAt}`));\n\n console.log();\n console.log(candidate.body);\n}\n","/**\n * Commander action for `llmwiki review approve <id>`.\n *\n * Promotes a pending candidate into the live wiki: writes the page body to\n * wiki/concepts/<slug>.md, refreshes the index/MOC, updates embeddings, and\n * removes the candidate file. Approval never re-invokes the LLM — the body\n * stored in the candidate is written verbatim.\n *\n * All mutations are performed under `.llmwiki/lock` to prevent races with a\n * concurrent compile or sibling approve/reject. The candidate is re-read under\n * the lock (TOCTOU guard) — if it disappears between the fast-fail check and\n * lock acquisition (e.g. a concurrent reject ran first), the approval aborts\n * cleanly rather than writing a page from a stale in-memory snapshot.\n */\n\nimport path from \"path\";\nimport {\n atomicWrite,\n validateWikiPage,\n} from \"../utils/markdown.js\";\nimport {\n deleteCandidate,\n listCandidates,\n} from \"../compiler/candidates.js\";\nimport { generateIndex } from \"../compiler/indexgen.js\";\nimport { generateMOC } from \"../compiler/obsidian.js\";\nimport { resolveLinks } from \"../compiler/resolver.js\";\nimport { updateEmbeddings } from \"../utils/embeddings.js\";\nimport { updateSourceState } from \"../utils/state.js\";\nimport { CONCEPTS_DIR } from \"../utils/constants.js\";\nimport * as output from \"../utils/output.js\";\nimport type { ReviewCandidate } from \"../utils/types.js\";\nimport { runReviewUnderLock, readCandidateUnderLock } from \"./review-helpers.js\";\n\n/** Approve a pending candidate by promoting its body into wiki/concepts/. */\nexport default async function reviewApproveCommand(id: string): Promise<void> {\n await runReviewUnderLock(id, approveUnderLock);\n}\n\n/**\n * Perform all wiki mutations for an approval while holding the lock.\n *\n * Re-reads the candidate under the lock so that a concurrent reject that ran\n * between the pre-lock fast-fail and lock acquisition is detected. Aborts with\n * exit code 1 if the candidate has disappeared or fails page validation.\n */\nasync function approveUnderLock(root: string, id: string): Promise<void> {\n const candidate = await readCandidateUnderLock(root, id);\n if (!candidate) return;\n\n if (!validateWikiPage(candidate.body)) {\n output.status(\"!\", output.error(`Candidate ${id} failed page validation; not approved.`));\n process.exitCode = 1;\n return;\n }\n\n const pagePath = path.join(root, CONCEPTS_DIR, `${candidate.slug}.md`);\n await atomicWrite(pagePath, candidate.body);\n output.status(\"+\", output.success(`Approved → ${output.source(pagePath)}`));\n\n await persistCandidateSourceStates(root, candidate);\n await refreshWikiAfterApproval(root, candidate.slug);\n await deleteCandidate(root, id);\n output.status(\"✓\", output.dim(`Candidate ${id} cleared.`));\n}\n\n/**\n * Flush the source-state snapshot stored on the candidate into\n * `.llmwiki/state.json` so the contributing source files are marked\n * compiled. Without this, approved candidates would re-appear on the next\n * `compile` run because the source still looks \"new\" or \"changed\" to the\n * change detector.\n *\n * When a single source produced multiple candidates (e.g. an extraction\n * yielded several concepts), persisting state on the first approval would\n * mark the source as fully compiled and silently strand the remaining\n * pending candidates — the next `compile --review` would skip the source\n * entirely. To avoid that, we only persist a source's state when no OTHER\n * pending candidate still references that source filename.\n */\nasync function persistCandidateSourceStates(\n root: string,\n candidate: ReviewCandidate,\n): Promise<void> {\n const states = candidate.sourceStates;\n if (!states) return;\n const otherSources = await collectOtherCandidateSources(root, candidate.id);\n for (const [sourceFile, entry] of Object.entries(states)) {\n if (otherSources.has(sourceFile)) continue;\n await updateSourceState(root, sourceFile, entry);\n }\n}\n\n/**\n * Build the set of source filenames referenced by every pending candidate\n * other than the one currently being approved. Used to defer source-state\n * persistence until the LAST candidate from a given source is reviewed.\n */\nasync function collectOtherCandidateSources(\n root: string,\n approvingId: string,\n): Promise<Set<string>> {\n const pending = await listCandidates(root);\n const sources = new Set<string>();\n for (const candidate of pending) {\n if (candidate.id === approvingId) continue;\n for (const source of candidate.sources) sources.add(source);\n }\n return sources;\n}\n\n/** Refresh interlinks, index, MOC, and embeddings after writing a candidate. */\nasync function refreshWikiAfterApproval(root: string, slug: string): Promise<void> {\n await resolveLinks(root, [slug], [slug]);\n await generateIndex(root);\n await generateMOC(root);\n await safelyUpdateEmbeddings(root, [slug]);\n}\n\n/**\n * Refresh the embeddings store without failing approval.\n * Mirrors the compiler's tolerance: missing API keys / transient provider\n * failures should warn, not abort the approval flow.\n */\nasync function safelyUpdateEmbeddings(root: string, slugs: string[]): Promise<void> {\n try {\n await updateEmbeddings(root, slugs);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n output.status(\"!\", output.warn(`Skipped embeddings update: ${message}`));\n }\n}\n","/**\n * Shared helpers for review subcommands (approve and reject).\n *\n * Both commands follow the same pattern:\n * 1. Fast-fail: read the candidate before locking (cheap early exit for bad ids).\n * 2. Acquire lock: serialize against concurrent compile / approve / reject.\n * 3. Under-lock re-read: authoritative TOCTOU guard — abort if the candidate\n * was removed between steps 1 and 2 (e.g. a concurrent reject ran first).\n * 4. Run the mutation.\n * 5. Release lock.\n *\n * Extracting this pattern avoids duplicating the acquire/release boilerplate\n * in both approve and reject.\n */\n\nimport {\n loadCandidateOrFail,\n loadCandidateUnderLockOrFail,\n} from \"../compiler/candidates.js\";\nimport { acquireLock, releaseLock } from \"../utils/lock.js\";\nimport * as output from \"../utils/output.js\";\n\n/** Re-export for use by the under-lock mutation functions in approve/reject. */\nexport { loadCandidateUnderLockOrFail as readCandidateUnderLock };\n\n/**\n * Run a review mutation under the `.llmwiki/lock`.\n *\n * Performs the pre-lock fast-fail, acquires the lock, then delegates to the\n * provided `underLock` callback. The lock is released in a `finally` block.\n *\n * @param id - Candidate id to review.\n * @param underLock - Async mutation to run while holding the lock.\n */\nexport async function runReviewUnderLock(\n id: string,\n underLock: (root: string, id: string) => Promise<void>,\n): Promise<void> {\n const root = process.cwd();\n\n // Fast-fail: surface a clear error for obviously missing ids.\n // The authoritative check happens under the lock via loadCandidateUnderLockOrFail.\n const preCheck = await loadCandidateOrFail(root, id);\n if (!preCheck) return;\n\n const locked = await acquireLock(root);\n if (!locked) {\n output.status(\"!\", output.error(\"Could not acquire lock. Try again later.\"));\n process.exitCode = 1;\n return;\n }\n\n try {\n await underLock(root, id);\n } finally {\n await releaseLock(root);\n }\n}\n","/**\n * Commander action for `llmwiki review reject <id>`.\n *\n * Removes a candidate from the pending area without touching `wiki/`.\n * Rejected candidates are moved into .llmwiki/candidates/archive/ so they\n * remain auditable but never appear in `llmwiki review list` again.\n *\n * The archive mutation is performed under `.llmwiki/lock` to serialize\n * concurrent approve/reject and approve-vs-compile operations, matching\n * the lock discipline used by compile and approve.\n *\n * The candidate is re-read under the lock (TOCTOU guard) — if it disappears\n * between the pre-lock fast-fail and lock acquisition, the rejection aborts\n * cleanly rather than silently succeeding on a stale handle.\n */\n\nimport { archiveCandidate } from \"../compiler/candidates.js\";\nimport * as output from \"../utils/output.js\";\nimport { runReviewUnderLock, readCandidateUnderLock } from \"./review-helpers.js\";\n\n/** Reject a pending candidate by archiving its JSON record. */\nexport default async function reviewRejectCommand(id: string): Promise<void> {\n await runReviewUnderLock(id, rejectUnderLock);\n}\n\n/**\n * Perform the archive mutation while holding the lock.\n *\n * Re-reads the candidate under the lock so that a concurrent approve that ran\n * between the pre-lock fast-fail and lock acquisition is detected. Aborts with\n * exit code 1 if the candidate has disappeared.\n */\nasync function rejectUnderLock(root: string, id: string): Promise<void> {\n const candidate = await readCandidateUnderLock(root, id);\n if (!candidate) return;\n\n await archiveCandidate(root, id);\n output.status(\n \"-\",\n output.warn(`Rejected candidate ${id} (${candidate.slug}) — archived, wiki unchanged.`),\n );\n}\n","/**\n * MCP (Model Context Protocol) server entry point for llmwiki.\n *\n * Exposes llmwiki's automated pipelines (ingest, compile, query, search,\n * lint, read, status) as MCP tools so AI agents can drive the compiler\n * without scraping CLI output. Read-only wiki views are exposed as\n * MCP resources for direct context injection.\n *\n * Transport: stdio. The server reads JSON-RPC messages on stdin and\n * writes responses on stdout, which is the standard surface area for\n * Claude Desktop, Cursor, and other MCP-aware clients.\n */\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { registerWikiTools } from \"./tools.js\";\nimport { registerWikiResources } from \"./resources.js\";\n\ninterface ServerOptions {\n /** Project root directory the server operates on. */\n root: string;\n /** Server version surfaced to MCP clients in the initialize handshake. */\n version: string;\n}\n\n/**\n * Start the MCP server bound to stdio transport.\n * Resolves once the transport closes (typically when the parent process exits).\n *\n * @param options - Root directory and server version (the CLI passes its own\n * version so the server doesn't need to read package.json).\n */\nexport async function startMCPServer(options: ServerOptions): Promise<void> {\n const { root, version } = options;\n const server = new McpServer({ name: \"llmwiki\", version }, {\n instructions:\n \"llmwiki is a knowledge compiler. Use ingest_source to add raw sources, \" +\n \"compile_wiki to run the LLM pipeline, query_wiki for grounded answers, \" +\n \"and search_pages to retrieve relevant pages. read_page, lint_wiki, and \" +\n \"wiki_status work without an API key.\",\n });\n\n registerWikiTools(server, root);\n registerWikiResources(server, root);\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n","/**\n * MCP tool registrations for llmwiki.\n *\n * Each tool wraps an existing pipeline function (ingest, compile, query,\n * search, read, lint, status) and converts its structured result into\n * an MCP CallToolResult. Tools that need an LLM provider validate the\n * provider lazily — the server itself starts without credentials so\n * read-only tools always work.\n */\n\nimport path from \"path\";\nimport { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { ingestSource } from \"../commands/ingest.js\";\nimport { compileAndReport } from \"../compiler/index.js\";\nimport { generateAnswer, selectPages } from \"../commands/query.js\";\nimport { lint } from \"../linter/index.js\";\nimport { collectPageSummaries, scanWikiPages } from \"../compiler/indexgen.js\";\nimport { detectChanges } from \"../compiler/hasher.js\";\nimport { countCandidates } from \"../compiler/candidates.js\";\nimport { readState } from \"../utils/state.js\";\nimport { safeReadFile, parseFrontmatter } from \"../utils/markdown.js\";\nimport { findRelevantPages } from \"../utils/embeddings.js\";\nimport {\n CONCEPTS_DIR,\n INDEX_FILE,\n QUERIES_DIR,\n} from \"../utils/constants.js\";\nimport { ensureProviderAvailable } from \"./provider-check.js\";\n\n/** Directories searched (in priority order) when resolving a page slug. */\nconst PAGE_DIRS = [CONCEPTS_DIR, QUERIES_DIR];\n\n/** Shape returned by search_pages for each matching page. */\ninterface PageRecord {\n slug: string;\n title: string;\n summary: string;\n body: string;\n}\n\n/**\n * Wrap an arbitrary JSON value as the standard MCP CallToolResult.\n * MCP requires content blocks even for structured payloads, so we mirror\n * the JSON in a text block for clients that don't read structuredContent.\n */\nfunction jsonResult(payload: unknown): {\n content: Array<{ type: \"text\"; text: string }>;\n structuredContent: { result: unknown };\n} {\n return {\n content: [{ type: \"text\" as const, text: JSON.stringify(payload, null, 2) }],\n structuredContent: { result: payload },\n };\n}\n\n/** Register all 7 wiki tools on the given MCP server instance. */\nexport function registerWikiTools(server: McpServer, root: string): void {\n registerIngestTool(server, root);\n registerCompileTool(server, root);\n registerQueryTool(server, root);\n registerSearchTool(server, root);\n registerReadTool(server, root);\n registerLintTool(server, root);\n registerStatusTool(server, root);\n}\n\nfunction registerIngestTool(server: McpServer, root: string): void {\n server.registerTool(\n \"ingest_source\",\n {\n title: \"Ingest Source\",\n description:\n \"Fetch a URL or copy a local file into sources/. Returns the saved filename, \" +\n \"character count, and whether content was truncated to fit the size limit.\",\n inputSchema: {\n source: z\n .string()\n .describe(\"URL (http/https) or absolute path to a .md/.txt file\"),\n },\n },\n async ({ source }) => {\n const previousCwd = process.cwd();\n try {\n process.chdir(root);\n const result = await ingestSource(source);\n return jsonResult(result);\n } finally {\n process.chdir(previousCwd);\n }\n },\n );\n}\n\nfunction registerCompileTool(server: McpServer, root: string): void {\n server.registerTool(\n \"compile_wiki\",\n {\n title: \"Compile Wiki\",\n description:\n \"Run the incremental compile pipeline: extract concepts from new/changed \" +\n \"sources, generate wiki pages, resolve interlinks, and rebuild the index. \" +\n \"Requires an LLM provider with credentials.\",\n inputSchema: {},\n },\n async () => {\n ensureProviderAvailable();\n const result = await compileAndReport(root);\n return jsonResult(result);\n },\n );\n}\n\nfunction registerQueryTool(server: McpServer, root: string): void {\n server.registerTool(\n \"query_wiki\",\n {\n title: \"Query Wiki\",\n description:\n \"Ask a natural-language question. Selects relevant pages with the LLM, \" +\n \"loads them, and returns a grounded answer with citations. Set save=true \" +\n \"to persist the answer as a wiki page. Requires an LLM provider.\",\n inputSchema: {\n question: z.string().describe(\"The natural-language question to answer.\"),\n save: z\n .boolean()\n .optional()\n .describe(\"Persist the answer as a wiki/queries/ page when true.\"),\n },\n },\n async ({ question, save }) => {\n ensureProviderAvailable();\n const result = await generateAnswer(root, question, { save });\n return jsonResult(result);\n },\n );\n}\n\nfunction registerSearchTool(server: McpServer, root: string): void {\n server.registerTool(\n \"search_pages\",\n {\n title: \"Search Pages\",\n description:\n \"Select pages relevant to a question and return their full content. \" +\n \"Uses semantic embeddings when available, falling back to LLM-based \" +\n \"selection over the wiki index. Requires an LLM provider.\",\n inputSchema: {\n question: z.string().describe(\"The query used to rank pages.\"),\n },\n },\n async ({ question }) => {\n ensureProviderAvailable();\n const slugs = await pickSearchSlugs(root, question);\n const records = await loadPageRecords(root, slugs);\n return jsonResult({ pages: records });\n },\n );\n}\n\n/** Resolve search candidates: prefer semantic search, fall back to LLM selection. */\nasync function pickSearchSlugs(root: string, question: string): Promise<string[]> {\n try {\n const candidates = await findRelevantPages(root, question);\n if (candidates.length > 0) return candidates.map((c) => c.slug);\n } catch {\n // Embeddings unavailable — fall through to index-based selection.\n }\n\n const indexContent = await safeReadFile(path.join(root, INDEX_FILE));\n const { pages } = await selectPages(question, indexContent);\n return pages;\n}\n\nfunction registerReadTool(server: McpServer, root: string): void {\n server.registerTool(\n \"read_page\",\n {\n title: \"Read Page\",\n description:\n \"Read a single wiki page by slug. Searches concepts/ first, then queries/. \" +\n \"Returns the parsed frontmatter and body. No LLM call required.\",\n inputSchema: {\n slug: z.string().describe(\"Page slug, without .md extension.\"),\n },\n },\n async ({ slug }) => {\n const page = await readPage(root, slug);\n if (!page) {\n throw new Error(`Page not found: ${slug}`);\n }\n return jsonResult(page);\n },\n );\n}\n\nfunction registerLintTool(server: McpServer, root: string): void {\n server.registerTool(\n \"lint_wiki\",\n {\n title: \"Lint Wiki\",\n description:\n \"Run rule-based quality checks (broken wikilinks, orphans, duplicates, \" +\n \"empty pages, broken citations). Returns structured diagnostics. No LLM call.\",\n inputSchema: {},\n },\n async () => {\n const summary = await lint(root);\n return jsonResult(summary);\n },\n );\n}\n\nfunction registerStatusTool(server: McpServer, root: string): void {\n server.registerTool(\n \"wiki_status\",\n {\n title: \"Wiki Status\",\n description:\n \"Summarize the wiki: page count, source count, last compile time, \" +\n \"orphaned pages, and pending source changes. Read-only — never \" +\n \"modifies the workspace.\",\n inputSchema: {},\n },\n async () => jsonResult(await collectStatus(root)),\n );\n}\n\n/** Read-only status snapshot used by the wiki_status tool. */\nasync function collectStatus(root: string): Promise<WikiStatus> {\n const concepts = await collectPageSummaries(path.join(root, CONCEPTS_DIR));\n const queries = await collectPageSummaries(path.join(root, QUERIES_DIR));\n const state = await readState(root);\n const changes = await detectChanges(root, state);\n const orphans = await findOrphanedSlugs(root);\n const pendingCandidates = await countCandidates(root);\n const compileTimes = Object.values(state.sources).map((s) => s.compiledAt);\n const lastCompile = compileTimes.length > 0\n ? compileTimes.sort().slice(-1)[0]\n : null;\n\n return {\n pages: { concepts: concepts.length, queries: queries.length, total: concepts.length + queries.length },\n sources: Object.keys(state.sources).length,\n lastCompiledAt: lastCompile,\n orphanedPages: orphans,\n pendingCandidates,\n pendingChanges: changes\n .filter((c) => c.status !== \"unchanged\")\n .map((c) => ({ file: c.file, status: c.status })),\n };\n}\n\ninterface WikiStatus {\n pages: { concepts: number; queries: number; total: number };\n sources: number;\n lastCompiledAt: string | null;\n orphanedPages: string[];\n /** Number of compile candidates awaiting human review. */\n pendingCandidates: number;\n pendingChanges: Array<{ file: string; status: string }>;\n}\n\n/** Find concept slugs whose pages are flagged as orphaned. */\nasync function findOrphanedSlugs(root: string): Promise<string[]> {\n const scanned = await scanWikiPages(path.join(root, CONCEPTS_DIR));\n return scanned.filter(({ meta }) => meta.orphaned).map(({ slug }) => slug);\n}\n\n/** Load full content for a list of slugs, skipping missing/orphaned pages. */\nasync function loadPageRecords(root: string, slugs: string[]): Promise<PageRecord[]> {\n const records: PageRecord[] = [];\n for (const slug of slugs) {\n const page = await readPage(root, slug);\n if (page) records.push(page);\n }\n return records;\n}\n\n/**\n * Locate a page by slug across the priority-ordered page directories,\n * skipping orphaned entries to match the query pipeline's behaviour.\n */\nexport async function readPage(root: string, slug: string): Promise<PageRecord | null> {\n for (const dir of PAGE_DIRS) {\n const content = await safeReadFile(path.join(root, dir, `${slug}.md`));\n if (!content) continue;\n\n const { meta, body } = parseFrontmatter(content);\n if (meta.orphaned) continue;\n\n return {\n slug,\n title: typeof meta.title === \"string\" ? meta.title : slug,\n summary: typeof meta.summary === \"string\" ? meta.summary : \"\",\n body: body.trim(),\n };\n }\n return null;\n}\n\n","/**\n * Per-tool provider validation for the MCP server.\n *\n * The MCP server starts without any API key check so read-only tools\n * (read_page, lint_wiki, wiki_status) and the ingest tool always work.\n * Tools that need an LLM call (compile, query, search) invoke this guard\n * to surface a clean error if credentials are missing.\n */\n\nimport { DEFAULT_PROVIDER } from \"../utils/constants.js\";\nimport { resolveAnthropicAuthFromEnv } from \"../utils/claude-settings.js\";\n\n/** Map of provider name to the env var that satisfies it. Null = no key needed. */\nconst PROVIDER_KEY_VARS: Record<string, string | null> = {\n anthropic: \"ANTHROPIC_API_KEY\",\n openai: \"OPENAI_API_KEY\",\n ollama: null,\n minimax: \"MINIMAX_API_KEY\",\n};\n\n/**\n * Throw if the active LLM provider is missing credentials.\n * Anthropic accepts either ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN\n * (resolved through the Claude Code settings fallback chain).\n */\nexport function ensureProviderAvailable(): void {\n const provider = process.env.LLMWIKI_PROVIDER ?? DEFAULT_PROVIDER;\n\n if (provider === \"anthropic\") {\n const auth = resolveAnthropicAuthFromEnv();\n if (!auth.apiKey && !auth.authToken) {\n throw new Error(\n 'Anthropic credentials are required for the \"anthropic\" provider. ' +\n \"Set ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN.\",\n );\n }\n return;\n }\n\n const keyVar = PROVIDER_KEY_VARS[provider];\n if (keyVar === undefined) {\n throw new Error(\n `Unknown provider \"${provider}\". Supported: ${Object.keys(PROVIDER_KEY_VARS).join(\", \")}`,\n );\n }\n\n if (keyVar && !process.env[keyVar]) {\n throw new Error(\n `${keyVar} environment variable is required for the \"${provider}\" provider.`,\n );\n }\n}\n","/**\n * MCP resource registrations for llmwiki.\n *\n * Resources expose read-only views of the wiki under the llmwiki:// URI\n * scheme. Hosts can attach these as context without invoking a tool —\n * useful for letting agents browse the wiki passively.\n */\n\nimport path from \"path\";\nimport { readdir } from \"fs/promises\";\nimport { McpServer, ResourceTemplate } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport {\n CONCEPTS_DIR,\n INDEX_FILE,\n QUERIES_DIR,\n SOURCES_DIR,\n STATE_FILE,\n} from \"../utils/constants.js\";\nimport { safeReadFile, parseFrontmatter } from \"../utils/markdown.js\";\nimport { readState } from \"../utils/state.js\";\n\n/** Standard JSON content block for an MCP resource read result. */\nfunction jsonContent(uri: URL, payload: unknown): {\n uri: string;\n mimeType: string;\n text: string;\n} {\n return {\n uri: uri.href,\n mimeType: \"application/json\",\n text: JSON.stringify(payload, null, 2),\n };\n}\n\n/** Standard markdown content block for an MCP resource read result. */\nfunction markdownContent(uri: URL, text: string): {\n uri: string;\n mimeType: string;\n text: string;\n} {\n return {\n uri: uri.href,\n mimeType: \"text/markdown\",\n text,\n };\n}\n\n/** Register all 5 read-only wiki resources on the given MCP server. */\nexport function registerWikiResources(server: McpServer, root: string): void {\n registerIndexResource(server, root);\n registerSourcesResource(server, root);\n registerStateResource(server, root);\n registerConceptResource(server, root);\n registerQueryResource(server, root);\n}\n\nfunction registerIndexResource(server: McpServer, root: string): void {\n server.registerResource(\n \"wiki-index\",\n \"llmwiki://index\",\n {\n title: \"Wiki Index\",\n description: \"Full content of wiki/index.md (auto-generated table of contents).\",\n mimeType: \"text/markdown\",\n },\n async (uri) => {\n const content = await safeReadFile(path.join(root, INDEX_FILE));\n return { contents: [markdownContent(uri, content)] };\n },\n );\n}\n\nfunction registerSourcesResource(server: McpServer, root: string): void {\n server.registerResource(\n \"wiki-sources\",\n \"llmwiki://sources\",\n {\n title: \"Wiki Sources\",\n description: \"List of ingested source files with frontmatter metadata.\",\n mimeType: \"application/json\",\n },\n async (uri) => ({\n contents: [jsonContent(uri, await listSources(root))],\n }),\n );\n}\n\nfunction registerStateResource(server: McpServer, root: string): void {\n server.registerResource(\n \"wiki-state\",\n \"llmwiki://state\",\n {\n title: \"Compilation State\",\n description: \"Per-source hashes, concepts, and last compile times from .llmwiki/state.json.\",\n mimeType: \"application/json\",\n },\n async (uri) => {\n const state = await readState(root);\n return { contents: [jsonContent(uri, state)] };\n },\n );\n}\n\nfunction registerConceptResource(server: McpServer, root: string): void {\n server.registerResource(\n \"wiki-concept\",\n new ResourceTemplate(\"llmwiki://concept/{slug}\", {\n list: async () => listPagesUnder(root, CONCEPTS_DIR, \"concept\"),\n }),\n {\n title: \"Wiki Concept\",\n description: \"A single concept page from wiki/concepts/ — frontmatter plus body.\",\n mimeType: \"application/json\",\n },\n async (uri, { slug }) => ({\n contents: [jsonContent(uri, await loadPageWithMeta(root, CONCEPTS_DIR, String(slug)))],\n }),\n );\n}\n\nfunction registerQueryResource(server: McpServer, root: string): void {\n server.registerResource(\n \"wiki-query\",\n new ResourceTemplate(\"llmwiki://query/{slug}\", {\n list: async () => listPagesUnder(root, QUERIES_DIR, \"query\"),\n }),\n {\n title: \"Wiki Query\",\n description: \"A single saved query page from wiki/queries/ — frontmatter plus body.\",\n mimeType: \"application/json\",\n },\n async (uri, { slug }) => ({\n contents: [jsonContent(uri, await loadPageWithMeta(root, QUERIES_DIR, String(slug)))],\n }),\n );\n}\n\n/** Source listing: filename, frontmatter (truncation, source URL, etc.). */\nasync function listSources(root: string): Promise<Array<Record<string, unknown>>> {\n const sourcesPath = path.join(root, SOURCES_DIR);\n let files: string[];\n try {\n files = await readdir(sourcesPath);\n } catch {\n return [];\n }\n\n const records: Array<Record<string, unknown>> = [];\n for (const file of files.filter((f) => f.endsWith(\".md\"))) {\n const content = await safeReadFile(path.join(sourcesPath, file));\n const { meta } = parseFrontmatter(content);\n records.push({ filename: file, ...meta });\n }\n return records;\n}\n\n/** Read a single page and return a structured payload (slug, meta, body). */\nasync function loadPageWithMeta(\n root: string,\n dir: string,\n slug: string,\n): Promise<{ slug: string; meta: Record<string, unknown>; body: string }> {\n const filePath = path.join(root, dir, `${slug}.md`);\n const content = await safeReadFile(filePath);\n if (!content) {\n throw new Error(`Page not found: ${dir}/${slug}.md`);\n }\n\n const { meta, body } = parseFrontmatter(content);\n return { slug, meta, body: body.trim() };\n}\n\n/** Build a resource list payload by enumerating .md files in a wiki directory. */\nasync function listPagesUnder(\n root: string,\n dir: string,\n scheme: \"concept\" | \"query\",\n): Promise<{ resources: Array<{ uri: string; name: string }> }> {\n const pagesPath = path.join(root, dir);\n let files: string[];\n try {\n files = await readdir(pagesPath);\n } catch {\n return { resources: [] };\n }\n\n const resources = files\n .filter((f) => f.endsWith(\".md\"))\n .map((f) => {\n const slug = f.replace(/\\.md$/, \"\");\n return { uri: `llmwiki://${scheme}/${slug}`, name: slug };\n });\n\n return { resources };\n}\n"],"mappings":"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","names":["path","mkdir","writeFile","readFile","path","source","path","mkdir","writeFile","existsSync","readFile","path","readFile","writeFile","rename","mkdir","path","path","readFile","mkdir","writeFile","rename","path","readFile","path","readFile","path","status","path","path","error","readFile","mkdir","path","path","mkdir","readFile","path","path","readdir","readFile","path","existsSync","path","existsSync","readdir","readFile","readdir","path","path","readdir","readdir","path","path","readdir","readFile","readdir","existsSync","path","path","existsSync","readFile","readdir","readdir","rename","unlink","writeFile","mkdir","existsSync","path","path","existsSync","readdir","unlink","mkdir","rename","writeFile","readdir","path","path","readdir","path","readFile","error","existsSync","existsSync","path","rawPages","reasoning","path","existsSync","existsSync","path","path","existsSync","readdir","readFile","existsSync","path","existsSync","readdir","path","readFile","path","path","source","safelyUpdateEmbeddings","McpServer","path","PAGE_DIRS","source","path","path","readdir","path","readdir","version","McpServer","require","source","PROVIDER_KEY_VARS"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/commands/ingest.ts","../src/utils/markdown.ts","../src/utils/constants.ts","../src/utils/output.ts","../src/ingest/web.ts","../src/ingest/file.ts","../src/commands/compile.ts","../src/compiler/index.ts","../src/utils/state.ts","../src/compiler/source-state.ts","../src/compiler/hasher.ts","../src/providers/anthropic.ts","../src/providers/openai.ts","../src/providers/ollama.ts","../src/providers/minimax.ts","../src/utils/claude-settings.ts","../src/utils/provider.ts","../src/utils/llm.ts","../src/utils/lock.ts","../src/compiler/prompts.ts","../src/schema/types.ts","../src/schema/defaults.ts","../src/schema/loader.ts","../src/schema/helpers.ts","../src/compiler/deps.ts","../src/compiler/orphan.ts","../src/compiler/resolver.ts","../src/compiler/indexgen.ts","../src/compiler/obsidian.ts","../src/utils/embeddings.ts","../src/compiler/candidates.ts","../src/linter/rules.ts","../src/compiler/page-renderer.ts","../src/compiler/provenance.ts","../src/commands/query.ts","../src/commands/watch.ts","../src/linter/index.ts","../src/commands/lint.ts","../src/commands/schema.ts","../src/commands/review-list.ts","../src/commands/review-show.ts","../src/commands/review-approve.ts","../src/commands/review-helpers.ts","../src/commands/review-reject.ts","../src/mcp/server.ts","../src/mcp/tools.ts","../src/mcp/provider-check.ts","../src/mcp/resources.ts"],"sourcesContent":["/**\n * CLI entry point for llmwiki — the knowledge compiler.\n *\n * Registers all commands (ingest, compile, query, watch, lint) via Commander.\n * Validates the correct API key for the selected LLM provider.\n * Designed for `npx llmwiki` or global install via `npm install -g llm-wiki-compiler`.\n */\n\nimport \"dotenv/config\";\nimport { createRequire } from \"module\";\nimport { Command } from \"commander\";\nimport ingestCommand from \"./commands/ingest.js\";\nimport compileCommand from \"./commands/compile.js\";\nimport queryCommand from \"./commands/query.js\";\nimport watchCommand from \"./commands/watch.js\";\nimport lintCommand from \"./commands/lint.js\";\nimport { schemaInitCommand, schemaShowCommand } from \"./commands/schema.js\";\nimport reviewListCommand from \"./commands/review-list.js\";\nimport reviewShowCommand from \"./commands/review-show.js\";\nimport reviewApproveCommand from \"./commands/review-approve.js\";\nimport reviewRejectCommand from \"./commands/review-reject.js\";\nimport { startMCPServer } from \"./mcp/server.js\";\nimport { DEFAULT_PROVIDER } from \"./utils/constants.js\";\nimport { resolveAnthropicAuthFromEnv } from \"./utils/claude-settings.js\";\n\nconst require = createRequire(import.meta.url);\nconst { version } = require(\"../package.json\") as { version: string };\n\nconst program = new Command();\n\nprogram\n .name(\"llmwiki\")\n .description(\"The knowledge compiler — raw sources in, interlinked wiki out\")\n .version(version);\n\nprogram\n .command(\"ingest <source>\")\n .description(\"Ingest a URL or local file into sources/\")\n .action(async (source: string) => {\n try {\n await ingestCommand(source);\n } catch (err) {\n console.error(`\\x1b[31mError:\\x1b[0m ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"compile\")\n .description(\"Compile sources/ into an interlinked wiki\")\n .option(\n \"--review\",\n \"Write generated pages as review candidates under .llmwiki/candidates/ instead of mutating wiki/. Orphan-marking for deleted sources is deferred until the next non-review compile.\",\n )\n .action(async (options: { review?: boolean }) => {\n try {\n requireProvider();\n await compileCommand({ review: options.review });\n } catch (err) {\n console.error(`\\x1b[31mError:\\x1b[0m ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\nconst reviewCommand = program\n .command(\"review\")\n .description(\"Inspect and act on pending compile review candidates\");\n\nreviewCommand\n .command(\"list\")\n .description(\"List pending review candidates\")\n .action(async () => {\n try {\n await reviewListCommand();\n } catch (err) {\n console.error(`\\x1b[31mError:\\x1b[0m ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\nreviewCommand\n .command(\"show <id>\")\n .description(\"Print a single candidate's metadata and body\")\n .action(async (id: string) => {\n try {\n await reviewShowCommand(id);\n } catch (err) {\n console.error(`\\x1b[31mError:\\x1b[0m ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\nreviewCommand\n .command(\"approve <id>\")\n .description(\"Approve a candidate and promote it into wiki/concepts/\")\n .action(async (id: string) => {\n try {\n await reviewApproveCommand(id);\n } catch (err) {\n console.error(`\\x1b[31mError:\\x1b[0m ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\nreviewCommand\n .command(\"reject <id>\")\n .description(\"Reject a candidate and archive it without touching wiki/\")\n .action(async (id: string) => {\n try {\n await reviewRejectCommand(id);\n } catch (err) {\n console.error(`\\x1b[31mError:\\x1b[0m ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"query <question>\")\n .description(\"Ask a question against the wiki\")\n .option(\"--save\", \"Save the answer as a wiki page\")\n .action(async (question: string, options: { save?: boolean }) => {\n try {\n requireProvider();\n await queryCommand(process.cwd(), question, options);\n } catch (err) {\n console.error(`\\x1b[31mError:\\x1b[0m ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"watch\")\n .description(\"Watch sources/ and auto-recompile on changes\")\n .action(async () => {\n try {\n requireProvider();\n await watchCommand();\n } catch (err) {\n console.error(`\\x1b[31mError:\\x1b[0m ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"lint\")\n .description(\"Run rule-based quality checks against the wiki\")\n .action(async () => {\n try {\n await lintCommand();\n } catch (err) {\n console.error(`\\x1b[31mError:\\x1b[0m ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\nconst schemaCmd = program\n .command(\"schema\")\n .description(\"Inspect or initialize the project's wiki schema config\");\n\nschemaCmd\n .command(\"init\")\n .description(\"Write a starter schema file to .llmwiki/schema.json\")\n .action(async () => {\n try {\n await schemaInitCommand();\n } catch (err) {\n console.error(`\\x1b[31mError:\\x1b[0m ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\nschemaCmd\n .command(\"show\")\n .description(\"Print the resolved schema for this project\")\n .action(async () => {\n try {\n await schemaShowCommand();\n } catch (err) {\n console.error(`\\x1b[31mError:\\x1b[0m ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"serve\")\n .description(\"Start an MCP server exposing wiki tools and resources over stdio\")\n .option(\"--root <dir>\", \"Project root directory\", process.cwd())\n .action(async (options: { root: string }) => {\n try {\n // Per-tool credential checks happen inside the MCP layer so read-only\n // tools and ingest still work without an API key.\n await startMCPServer({ root: options.root, version });\n } catch (err) {\n console.error(`\\x1b[31mError:\\x1b[0m ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\n/** API key env var required per provider. Null means no key needed. */\nconst PROVIDER_KEY_VARS: Record<string, string | null> = {\n anthropic: \"ANTHROPIC_API_KEY\",\n openai: \"OPENAI_API_KEY\",\n ollama: null,\n minimax: \"MINIMAX_API_KEY\",\n};\n\n/** Exit with a helpful message if the selected provider's API key is missing. */\nfunction requireProvider(): void {\n const provider = process.env.LLMWIKI_PROVIDER ?? DEFAULT_PROVIDER;\n\n if (provider === \"anthropic\") {\n const auth = resolveAnthropicAuthFromEnv();\n if (!auth.apiKey && !auth.authToken) {\n console.error(\n `\\x1b[31mError:\\x1b[0m Anthropic credentials are required for the \"anthropic\" provider.\\n` +\n ` Set one of: export ANTHROPIC_API_KEY=<your-key> OR export ANTHROPIC_AUTH_TOKEN=<your-token>`,\n );\n process.exit(1);\n }\n return;\n }\n\n const keyVar = PROVIDER_KEY_VARS[provider];\n\n if (keyVar === undefined) {\n console.error(\n `\\x1b[31mError:\\x1b[0m Unknown provider \"${provider}\".\\n` +\n ` Supported: ${Object.keys(PROVIDER_KEY_VARS).join(\", \")}`,\n );\n process.exit(1);\n }\n\n if (keyVar && !process.env[keyVar]) {\n console.error(\n `\\x1b[31mError:\\x1b[0m ${keyVar} environment variable is required for the \"${provider}\" provider.\\n` +\n ` Set it with: export ${keyVar}=<your-key>`,\n );\n process.exit(1);\n }\n}\n\nprogram.parse();\n","/**\n * Commander action for `llmwiki ingest <source>`.\n * Detects whether the source is a URL or local file, delegates to the\n * appropriate ingestion module, and saves the result as a markdown file\n * with YAML frontmatter in the sources/ directory.\n */\n\nimport path from \"path\";\nimport { mkdir, writeFile } from \"fs/promises\";\nimport { slugify, buildFrontmatter } from \"../utils/markdown.js\";\nimport { MAX_SOURCE_CHARS, MIN_SOURCE_CHARS, SOURCES_DIR } from \"../utils/constants.js\";\nimport * as output from \"../utils/output.js\";\nimport ingestWeb from \"../ingest/web.js\";\nimport ingestFile from \"../ingest/file.js\";\nimport type { IngestResult } from \"../utils/types.js\";\n\n/** Check whether a source string looks like a URL. */\nfunction isUrl(source: string): boolean {\n return source.startsWith(\"http://\") || source.startsWith(\"https://\");\n}\n\n/** Truncate result including whether truncation occurred and original length. */\ninterface TruncateResult {\n content: string;\n truncated: boolean;\n originalChars: number;\n}\n\n/** Truncate content if it exceeds the character limit, logging a warning. */\nexport function enforceCharLimit(content: string): TruncateResult {\n if (content.length <= MAX_SOURCE_CHARS) {\n return { content, truncated: false, originalChars: content.length };\n }\n\n output.status(\n \"!\",\n output.warn(\n `Content truncated from ${content.length.toLocaleString()} to ${MAX_SOURCE_CHARS.toLocaleString()} characters.`\n )\n );\n return {\n content: content.slice(0, MAX_SOURCE_CHARS),\n truncated: true,\n originalChars: content.length,\n };\n}\n\n/** Reject empty content and warn when content is trivially short. */\nfunction enforceMinContent(content: string): void {\n const length = content.trim().length;\n\n if (length === 0) {\n throw new Error(\n \"No readable content could be extracted from the source.\"\n );\n }\n\n if (length < MIN_SOURCE_CHARS) {\n output.status(\n \"!\",\n output.warn(\n `Content seems very short (${length} chars, minimum recommended is ${MIN_SOURCE_CHARS}).`\n )\n );\n }\n}\n\n/** Build the full markdown document with frontmatter. */\nexport function buildDocument(\n title: string,\n source: string,\n result: TruncateResult,\n): string {\n const meta: Record<string, unknown> = {\n title,\n source,\n ingestedAt: new Date().toISOString(),\n };\n if (result.truncated) {\n meta.truncated = true;\n meta.originalChars = result.originalChars;\n }\n const frontmatter = buildFrontmatter(meta);\n\n return `${frontmatter}\\n\\n${result.content}\\n`;\n}\n\n/** Write the ingested document to the sources/ directory. */\nasync function saveSource(title: string, document: string): Promise<string> {\n const filename = `${slugify(title)}.md`;\n const destPath = path.join(SOURCES_DIR, filename);\n\n await mkdir(SOURCES_DIR, { recursive: true });\n await writeFile(destPath, document, \"utf-8\");\n\n return destPath;\n}\n\n/**\n * Programmatic ingest entry point. Identical fetch + write logic to the CLI\n * command but returns a structured IngestResult instead of writing to stdout.\n * Used by the MCP server's ingest_source tool.\n *\n * @param source - A URL (http/https) or a local file path (.md or .txt).\n * @returns Saved filename, character count, truncation flag, and source URI.\n */\nexport async function ingestSource(source: string): Promise<IngestResult> {\n output.status(\"*\", output.info(`Ingesting: ${source}`));\n\n const { title, content } = isUrl(source)\n ? await ingestWeb(source)\n : await ingestFile(source);\n\n const result = enforceCharLimit(content);\n enforceMinContent(result.content);\n const document = buildDocument(title, source, result);\n const savedPath = await saveSource(title, document);\n\n return {\n filename: path.basename(savedPath),\n charCount: result.content.length,\n truncated: result.truncated,\n source,\n };\n}\n\n/**\n * Ingest a source (URL or local file) and save it to the sources/ directory.\n * @param source - A URL (http/https) or a local file path (.md or .txt).\n */\nexport default async function ingest(source: string): Promise<void> {\n const result = await ingestSource(source);\n const savedPath = path.join(SOURCES_DIR, result.filename);\n\n output.status(\n \"+\",\n output.success(`Saved ${output.bold(result.filename)} → ${output.source(savedPath)}`)\n );\n output.status(\"→\", output.dim(\"Next: llmwiki compile\"));\n}\n","/**\n * Markdown parsing and manipulation helpers.\n * Handles YAML frontmatter extraction, slugification, and atomic file writes\n * for wiki pages.\n */\n\nimport { writeFile, rename, readFile, mkdir } from \"fs/promises\";\nimport path from \"path\";\nimport yaml from \"js-yaml\";\nimport type {\n ClaimCitation,\n ContradictionRef,\n ProvenanceState,\n SourceSpan,\n} from \"./types.js\";\n\n/** Regex matching `^[...]` citation markers (paragraph or claim-level). */\nconst CITATION_MARKER_PATTERN = /\\^\\[([^\\]]+)\\]/g;\n\n/** Regex matching the optional `:start-end` or `#Lstart-Lend` span suffix on a citation entry. */\nconst SPAN_SUFFIX_PATTERN = /^(?<file>[^:#]+)(?:(?::(?<colonStart>\\d+)(?:-(?<colonEnd>\\d+))?)|(?:#L(?<hashStart>\\d+)(?:-L(?<hashEnd>\\d+))?))?$/;\n\n/** The minimum valid line number in a source span (lines are 1-indexed). */\nconst MIN_LINE_NUMBER = 1;\n\n/** The set of valid provenance state strings, used to reject unknown values. */\nconst VALID_PROVENANCE_STATES: ReadonlySet<ProvenanceState> = new Set([\n \"extracted\",\n \"merged\",\n \"inferred\",\n \"ambiguous\",\n]);\n\n/** Provenance metadata parsed from a page's frontmatter. */\ninterface ProvenanceMetadata {\n confidence?: number;\n provenanceState?: ProvenanceState;\n contradictedBy?: ContradictionRef[];\n inferredParagraphs?: number;\n}\n\n/** Convert a human-readable concept title to a filename slug. */\nexport function slugify(title: string): string {\n return title\n .toLowerCase()\n .replace(/['']/g, \"\")\n .replace(/[^\\w\\s-]/g, \"\")\n .replace(/\\s+/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\n/** Build YAML frontmatter string from key-value pairs. */\nexport function buildFrontmatter(fields: Record<string, unknown>): string {\n const dumped = yaml.dump(fields, { lineWidth: -1, quotingType: '\"' }).trimEnd();\n return `---\\n${dumped}\\n---`;\n}\n\n/** Parse YAML frontmatter from a markdown string. Returns { meta, body }. */\nexport function parseFrontmatter(content: string): {\n meta: Record<string, unknown>;\n body: string;\n} {\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---\\n?([\\s\\S]*)$/);\n if (!match) {\n return { meta: {}, body: content };\n }\n\n let meta: Record<string, unknown> = {};\n try {\n const parsed = yaml.load(match[1]);\n if (parsed && typeof parsed === \"object\") {\n meta = parsed as Record<string, unknown>;\n }\n } catch {\n // Malformed YAML — return empty meta so callers degrade gracefully.\n }\n return { meta, body: match[2] };\n}\n\n/** Atomically write a file (write to .tmp, then rename). */\nexport async function atomicWrite(filePath: string, content: string): Promise<void> {\n await mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = filePath + \".tmp\";\n await writeFile(tmpPath, content, \"utf-8\");\n await rename(tmpPath, filePath);\n}\n\n/**\n * Extract all source filenames from ^[filename.md] citation markers in a page body.\n * Handles paragraph form (`^[source.md]`), multi-source (`^[a.md, b.md]`), and the\n * claim-level extension that pins a line range (`^[source.md:42-58]` or\n * `^[source.md#L42-L58]`). Only the filename component is returned — span data is\n * discarded so existing callers continue to receive a flat filename list.\n * @param body - The markdown body text to parse.\n * @returns Array of unique source filenames.\n */\nexport function extractCitations(body: string): string[] {\n const filenames = new Set<string>();\n for (const citation of extractClaimCitations(body)) {\n for (const span of citation.spans) {\n if (span.file.length > 0) filenames.add(span.file);\n }\n }\n return [...filenames];\n}\n\n/**\n * Extract claim-level citations from a markdown body. Each `^[...]` marker\n * becomes one `ClaimCitation`; comma-separated entries inside a single marker\n * become multiple spans on that citation. Entries that fail to parse against\n * the span grammar are returned as bare-file spans so callers can still tell\n * the marker was present (the linter inspects `raw` to flag malformed forms).\n * @param body - The markdown body text to parse.\n * @returns Array of ClaimCitation objects in document order.\n */\nexport function extractClaimCitations(body: string): ClaimCitation[] {\n const citations: ClaimCitation[] = [];\n let match: RegExpExecArray | null;\n CITATION_MARKER_PATTERN.lastIndex = 0;\n while ((match = CITATION_MARKER_PATTERN.exec(body)) !== null) {\n const raw = match[1];\n const spans = parseCitationEntries(raw);\n if (spans.length > 0) citations.push({ raw, spans });\n }\n return citations;\n}\n\n/** Parse the inside of `^[...]` into one or more SourceSpan entries. */\nfunction parseCitationEntries(inner: string): SourceSpan[] {\n const spans: SourceSpan[] = [];\n for (const part of inner.split(\",\")) {\n const trimmed = part.trim();\n if (trimmed.length === 0) continue;\n const span = parseSpanEntry(trimmed);\n // Skip entries with invalid line ranges — the linter flags them separately.\n if (span !== undefined) spans.push(span);\n }\n return spans;\n}\n\n/**\n * Parse a single citation entry (`file.md` / `file.md:1-3` / `file.md#L1-L3`).\n * Returns undefined when the parsed line range is semantically invalid (line\n * numbers must be >= 1 and end must be >= start).\n */\nfunction parseSpanEntry(entry: string): SourceSpan | undefined {\n const match = SPAN_SUFFIX_PATTERN.exec(entry);\n if (!match || !match.groups) {\n return { file: entry };\n }\n const { file, colonStart, colonEnd, hashStart, hashEnd } = match.groups;\n const start = colonStart ?? hashStart;\n const end = colonEnd ?? hashEnd;\n if (start === undefined) return { file };\n const startLine = Number(start);\n const endLine = end === undefined ? startLine : Number(end);\n if (!isValidLineRange(startLine, endLine)) return undefined;\n return { file, lines: { start: startLine, end: endLine } };\n}\n\n/** Returns true when both lines are >= 1 and end is not before start. */\nfunction isValidLineRange(start: number, end: number): boolean {\n return start >= MIN_LINE_NUMBER && end >= start;\n}\n\n/**\n * Detect whether a citation entry is malformed: bracket text that contains\n * `:` or `#` characters but does not match the documented span grammar, or\n * contains a semantically invalid line range (line 0 or end before start).\n * Used by the linter to flag broken claim-level provenance markers.\n */\nexport function isMalformedCitationEntry(entry: string): boolean {\n const trimmed = entry.trim();\n if (trimmed.length === 0) return true;\n if (!trimmed.includes(\":\") && !trimmed.includes(\"#\")) return false;\n const match = SPAN_SUFFIX_PATTERN.exec(trimmed);\n if (!match || !match.groups) return true;\n const { colonStart, colonEnd, hashStart, hashEnd } = match.groups;\n const start = colonStart ?? hashStart;\n const end = colonEnd ?? hashEnd;\n if (start === undefined) return false;\n const startLine = Number(start);\n const endLine = end === undefined ? startLine : Number(end);\n return !isValidLineRange(startLine, endLine);\n}\n\n/**\n * Inspect provenance for a page body, grouping every parsed span by source file.\n * Useful for tooling that wants to render a \"this page draws from\" panel without\n * worrying about how the markers were formatted in source. Each filename maps to\n * a deduplicated list of `{start, end}` line ranges (paragraph-only citations\n * appear as the empty array, signalling \"no specific span\").\n */\nexport function inspectProvenance(body: string): Map<string, Array<{ start: number; end: number }>> {\n const grouped = new Map<string, Array<{ start: number; end: number }>>();\n for (const citation of extractClaimCitations(body)) {\n for (const span of citation.spans) {\n const ranges = grouped.get(span.file) ?? [];\n if (span.lines && !rangeAlreadyTracked(ranges, span.lines)) {\n ranges.push(span.lines);\n }\n grouped.set(span.file, ranges);\n }\n }\n return grouped;\n}\n\n/** Has this start/end pair already been recorded for a file? */\nfunction rangeAlreadyTracked(\n ranges: Array<{ start: number; end: number }>,\n candidate: { start: number; end: number },\n): boolean {\n return ranges.some((r) => r.start === candidate.start && r.end === candidate.end);\n}\n\n/** Read a file, returning empty string if it doesn't exist. */\nexport async function safeReadFile(filePath: string): Promise<string> {\n try {\n return await readFile(filePath, \"utf-8\");\n } catch {\n return \"\";\n }\n}\n\n/** Parse a numeric confidence value, clamping to 0..1 and rejecting non-numbers. */\nfunction parseConfidence(raw: unknown): number | undefined {\n if (typeof raw !== \"number\" || !Number.isFinite(raw)) return undefined;\n if (raw < 0) return 0;\n if (raw > 1) return 1;\n return raw;\n}\n\n/** Parse a provenance state string, returning undefined for unknown values. */\nfunction parseProvenanceState(raw: unknown): ProvenanceState | undefined {\n if (typeof raw !== \"string\") return undefined;\n return VALID_PROVENANCE_STATES.has(raw as ProvenanceState)\n ? (raw as ProvenanceState)\n : undefined;\n}\n\n/** Coerce a single contradiction entry to a ContradictionRef, or null if invalid. */\nfunction coerceContradictionEntry(entry: unknown): ContradictionRef | null {\n if (typeof entry === \"string\" && entry.trim().length > 0) {\n return { slug: entry.trim() };\n }\n if (entry && typeof entry === \"object\" && \"slug\" in entry) {\n const obj = entry as { slug: unknown; reason?: unknown };\n if (typeof obj.slug !== \"string\" || obj.slug.trim().length === 0) return null;\n const ref: ContradictionRef = { slug: obj.slug.trim() };\n if (typeof obj.reason === \"string\") ref.reason = obj.reason;\n return ref;\n }\n return null;\n}\n\n/** Parse a contradictedBy array, accepting strings or objects with slug. */\nfunction parseContradictedBy(raw: unknown): ContradictionRef[] | undefined {\n if (!Array.isArray(raw)) return undefined;\n const refs = raw\n .map(coerceContradictionEntry)\n .filter((ref): ref is ContradictionRef => ref !== null);\n return refs.length > 0 ? refs : undefined;\n}\n\n/** Parse the inferred paragraph count, requiring a non-negative integer. */\nfunction parseInferredParagraphs(raw: unknown): number | undefined {\n if (typeof raw !== \"number\" || !Number.isInteger(raw) || raw < 0) return undefined;\n return raw;\n}\n\n/**\n * Extract provenance metadata fields from a parsed frontmatter record.\n * Defensively handles missing or malformed values so existing pages without\n * the new fields continue to parse correctly.\n * @param meta - Raw frontmatter object as returned by parseFrontmatter.\n * @returns Typed provenance metadata with only the fields that were present.\n */\nexport function parseProvenanceMetadata(\n meta: Record<string, unknown>,\n): ProvenanceMetadata {\n return {\n confidence: parseConfidence(meta.confidence),\n provenanceState: parseProvenanceState(meta.provenanceState),\n contradictedBy: parseContradictedBy(meta.contradictedBy),\n inferredParagraphs: parseInferredParagraphs(meta.inferredParagraphs),\n };\n}\n\n/**\n * Validate that a wiki page has non-empty content and valid frontmatter.\n * Returns true if the page is valid.\n */\nexport function validateWikiPage(content: string): boolean {\n if (!content || content.trim().length === 0) return false;\n\n const { meta, body } = parseFrontmatter(content);\n if (!meta.title) return false;\n if (body.trim().length === 0) return false;\n\n return true;\n}\n","/**\n * Shared constants for the llmwiki knowledge compiler.\n * Centralized config values to avoid magic numbers scattered across the codebase.\n */\n\n/** Maximum source file size in characters before truncation. */\nexport const MAX_SOURCE_CHARS = 100_000;\n\n/** Minimum source content length to ingest without a warning. */\nexport const MIN_SOURCE_CHARS = 50;\n\n/** Number of most relevant wiki pages to load for query context. */\nexport const QUERY_PAGE_LIMIT = 5;\n\n/** Maximum concurrent API calls during page generation. */\nexport const COMPILE_CONCURRENCY = 5;\n\n/** API retry configuration. */\nexport const RETRY_COUNT = 3;\nexport const RETRY_BASE_MS = 1000;\nexport const RETRY_MULTIPLIER = 4;\n\n/** Default provider when LLMWIKI_PROVIDER is not set. */\nexport const DEFAULT_PROVIDER = \"anthropic\";\n\n/** Default model per provider. */\nexport const PROVIDER_MODELS: Record<string, string> = {\n anthropic: \"claude-sonnet-4-20250514\",\n openai: \"gpt-4o\",\n ollama: \"llama3.1\",\n minimax: \"MiniMax-M2.7\",\n};\n\n/** Default Ollama API base URL. */\nexport const OLLAMA_DEFAULT_HOST = \"http://localhost:11434/v1\";\n\n/**\n * Default request timeout for cloud OpenAI-compatible providers (10 minutes).\n * Matches the OpenAI SDK's own default; called out here so it's explicit.\n */\nexport const OPENAI_DEFAULT_TIMEOUT_MS = 10 * 60 * 1000;\n\n/**\n * Default request timeout for Ollama (30 minutes). Local models on modest\n * hardware can take well over the cloud-provider default for a single\n * compile-time completion. Configurable via LLMWIKI_REQUEST_TIMEOUT_MS or\n * OLLAMA_TIMEOUT_MS env vars.\n */\nexport const OLLAMA_DEFAULT_TIMEOUT_MS = 30 * 60 * 1000;\n\n/** Directory names relative to the project root. */\nexport const SOURCES_DIR = \"sources\";\nexport const CONCEPTS_DIR = \"wiki/concepts\";\nexport const QUERIES_DIR = \"wiki/queries\";\nexport const LLMWIKI_DIR = \".llmwiki\";\nexport const STATE_FILE = \".llmwiki/state.json\";\nexport const LOCK_FILE = \".llmwiki/lock\";\nexport const INDEX_FILE = \"wiki/index.md\";\nexport const MOC_FILE = \"wiki/MOC.md\";\nexport const EMBEDDINGS_FILE = \".llmwiki/embeddings.json\";\n\n/** Pending review candidates awaiting approval/rejection. */\nexport const CANDIDATES_DIR = \".llmwiki/candidates\";\n\n/** Rejected review candidates archived for audit (not deleted). */\nexport const CANDIDATES_ARCHIVE_DIR = \".llmwiki/candidates/archive\";\n\n/** Number of most similar pages to return from embedding-based pre-filter. */\nexport const EMBEDDING_TOP_K = 15;\n\n/** Provenance metadata thresholds used by lint rules. */\nexport const LOW_CONFIDENCE_THRESHOLD = 0.5;\nexport const MAX_INFERRED_PARAGRAPHS_WITHOUT_CITATIONS = 2;\n\n/** Embedding model to use per provider. */\nexport const EMBEDDING_MODELS: Record<string, string> = {\n anthropic: \"voyage-3-lite\",\n openai: \"text-embedding-3-small\",\n ollama: \"nomic-embed-text\",\n};\n","/**\n * ANSI colored terminal output helpers.\n * Provides consistent styling for compilation progress, status messages,\n * and streaming token display.\n */\n\nconst RESET = \"\\x1b[0m\";\nconst BOLD = \"\\x1b[1m\";\nconst DIM = \"\\x1b[2m\";\nconst GREEN = \"\\x1b[32m\";\nconst YELLOW = \"\\x1b[33m\";\nconst BLUE = \"\\x1b[34m\";\nconst MAGENTA = \"\\x1b[35m\";\nconst CYAN = \"\\x1b[36m\";\nconst RED = \"\\x1b[31m\";\n\nexport function bold(text: string): string {\n return `${BOLD}${text}${RESET}`;\n}\n\nexport function dim(text: string): string {\n return `${DIM}${text}${RESET}`;\n}\n\nexport function success(text: string): string {\n return `${GREEN}${text}${RESET}`;\n}\n\nexport function warn(text: string): string {\n return `${YELLOW}${text}${RESET}`;\n}\n\nexport function info(text: string): string {\n return `${BLUE}${text}${RESET}`;\n}\n\nexport function error(text: string): string {\n return `${RED}${text}${RESET}`;\n}\n\nexport function source(text: string): string {\n return `${CYAN}${text}${RESET}`;\n}\n\n/** Print a status line with an icon. */\nexport function status(icon: string, message: string): void {\n console.log(`${icon} ${message}`);\n}\n\n/** Print a section header. */\nexport function header(title: string): void {\n console.log(`\\n${BOLD}${title}${RESET}`);\n console.log(dim(\"─\".repeat(Math.min(title.length + 4, 60))));\n}\n","/**\n * Web URL ingestion module.\n * Fetches a URL, extracts readable content using Mozilla Readability,\n * and converts the result to clean markdown via Turndown.\n *\n * Throws descriptive errors on network failures or when the page\n * cannot be parsed into readable content.\n */\n\nimport { JSDOM } from \"jsdom\";\nimport { Readability } from \"@mozilla/readability\";\nimport TurndownService from \"turndown\";\n\ninterface WebIngestResult {\n title: string;\n content: string;\n}\n\n/** Fetch a URL and return its readable content as markdown. */\nasync function fetchAndParse(url: string): Promise<Response> {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch ${url}: HTTP ${response.status}`);\n }\n return response;\n}\n\n/** Extract readable content from raw HTML using Readability. */\nfunction extractReadableContent(html: string, url: string): { title: string; htmlContent: string } {\n const dom = new JSDOM(html, { url });\n const reader = new Readability(dom.window.document);\n const article = reader.parse();\n\n if (!article || !article.content) {\n throw new Error(`Could not extract readable content from ${url}`);\n }\n\n return {\n title: article.title || \"Untitled\",\n htmlContent: article.content,\n };\n}\n\n/** Convert HTML to clean markdown using Turndown. */\nfunction convertToMarkdown(html: string): string {\n const turndown = new TurndownService({ headingStyle: \"atx\" });\n return turndown.turndown(html);\n}\n\n/**\n * Ingest a web URL and return its content as markdown.\n * @param url - The URL to fetch and convert.\n * @returns An object with the extracted title and markdown content.\n * @throws On network failure or unparseable content.\n */\nexport default async function ingestWeb(url: string): Promise<WebIngestResult> {\n const response = await fetchAndParse(url);\n const html = await response.text();\n const { title, htmlContent } = extractReadableContent(html, url);\n const content = convertToMarkdown(htmlContent);\n\n return { title, content };\n}\n","/**\n * Local file ingestion module.\n * Reads .md and .txt files from the local filesystem and returns their\n * content as markdown. Markdown files are returned as-is; plain text files\n * are wrapped in a markdown code block. All other extensions are rejected.\n */\n\nimport { readFile } from \"fs/promises\";\nimport path from \"path\";\n\nconst SUPPORTED_EXTENSIONS = new Set([\".md\", \".txt\"]);\n\ninterface FileIngestResult {\n title: string;\n content: string;\n}\n\n/** Derive a human-readable title from a filename (without extension). */\nfunction titleFromFilename(filePath: string): string {\n const basename = path.basename(filePath, path.extname(filePath));\n return basename.replace(/[-_]+/g, \" \").trim();\n}\n\n/** Wrap plain text content in a markdown fenced block. */\nfunction wrapPlainText(text: string): string {\n return `\\`\\`\\`\\n${text}\\n\\`\\`\\``;\n}\n\n/**\n * Ingest a local file and return its content as markdown.\n * @param filePath - Absolute or relative path to a .md or .txt file.\n * @returns An object with a title derived from the filename and the markdown content.\n * @throws On unsupported file type or read failure.\n */\nexport default async function ingestFile(filePath: string): Promise<FileIngestResult> {\n const ext = path.extname(filePath).toLowerCase();\n\n if (!SUPPORTED_EXTENSIONS.has(ext)) {\n throw new Error(\n `Unsupported file type \"${ext}\". Only .md and .txt files are supported.`\n );\n }\n\n const raw = await readFile(filePath, \"utf-8\");\n const title = titleFromFilename(filePath);\n const content = ext === \".md\" ? raw : wrapPlainText(raw);\n\n return { title, content };\n}\n","/**\n * Commander action for `llmwiki compile`.\n * Checks that sources exist, then delegates to the compilation orchestrator\n * to process all new and changed source files into wiki pages.\n */\n\nimport { existsSync } from \"fs\";\nimport { compile } from \"../compiler/index.js\";\nimport * as output from \"../utils/output.js\";\nimport { SOURCES_DIR } from \"../utils/constants.js\";\nimport type { CompileOptions } from \"../utils/types.js\";\n\n/**\n * Run the compile command from the current working directory.\n * Exits early if no sources directory exists yet.\n * @param options - Optional behaviour overrides forwarded from the CLI flag set.\n */\nexport default async function compileCommand(options: CompileOptions = {}): Promise<void> {\n if (!existsSync(SOURCES_DIR)) {\n output.status(\n \"!\",\n output.warn('No sources found. Run `llmwiki ingest <url>` first.'),\n );\n return;\n }\n\n await compile(process.cwd(), options);\n}\n","/**\n * Compilation orchestrator for the llmwiki knowledge compiler.\n *\n * Coordinates the full pipeline: lock acquisition, change detection,\n * concept extraction via LLM, wiki page generation with streaming output,\n * orphan marking for deleted sources, interlink resolution, and index\n * generation. Supports incremental compilation — only new or changed\n * sources are processed through the LLM pipeline.\n */\n\nimport { readFile } from \"fs/promises\";\nimport path from \"path\";\nimport { readState, updateSourceState } from \"../utils/state.js\";\nimport {\n buildExtractionSourceStates,\n pickStatesForSources,\n} from \"./source-state.js\";\nimport {\n atomicWrite,\n buildFrontmatter,\n parseFrontmatter,\n safeReadFile,\n validateWikiPage,\n slugify,\n} from \"../utils/markdown.js\";\nimport { callClaude } from \"../utils/llm.js\";\nimport { acquireLock, releaseLock } from \"../utils/lock.js\";\nimport {\n CONCEPT_EXTRACTION_TOOL,\n buildExtractionPrompt,\n buildSeedPagePrompt,\n parseConcepts,\n} from \"./prompts.js\";\nimport { loadSchema, type SchemaConfig, type SeedPage } from \"../schema/index.js\";\nimport { detectChanges, hashFile } from \"./hasher.js\";\nimport {\n findAffectedSources,\n findFrozenSlugs,\n findLateAffectedSources,\n freezeFailedExtractions,\n persistFrozenSlugs,\n type ExtractionResult,\n} from \"./deps.js\";\nimport { markOrphaned, orphanUnownedFrozenPages } from \"./orphan.js\";\nimport { resolveLinks } from \"./resolver.js\";\nimport { generateIndex } from \"./indexgen.js\";\nimport { addObsidianMeta, generateMOC } from \"./obsidian.js\";\nimport { updateEmbeddings } from \"../utils/embeddings.js\";\nimport { writeCandidate } from \"./candidates.js\";\nimport { checkPageCrossLinks } from \"../linter/rules.js\";\nimport { renderMergedPageContent } from \"./page-renderer.js\";\nimport * as output from \"../utils/output.js\";\nimport {\n COMPILE_CONCURRENCY,\n CONCEPTS_DIR,\n INDEX_FILE,\n SOURCES_DIR,\n} from \"../utils/constants.js\";\nimport pLimit from \"p-limit\";\nimport type {\n CompileOptions,\n CompileResult,\n ExtractedConcept,\n ReviewCandidate,\n SourceChange,\n SourceState,\n WikiFrontmatter,\n WikiState,\n} from \"../utils/types.js\";\n\n/** Per-source state snapshots keyed by source filename. */\ntype SourceStateMap = Record<string, SourceState>;\n\n/** Empty CompileResult used when no pipeline work runs (e.g. lock contention). */\nfunction emptyCompileResult(): CompileResult {\n return { compiled: 0, skipped: 0, deleted: 0, concepts: [], pages: [], errors: [] };\n}\n\n/**\n * Run the full compilation pipeline with lock protection.\n * Acquires .llmwiki/lock, detects changes, compiles new/changed sources,\n * marks orphaned pages, resolves interlinks, and rebuilds the index.\n * @param root - Project root directory.\n * @param options - Optional pipeline overrides (e.g. --review mode).\n */\nexport async function compile(root: string, options: CompileOptions = {}): Promise<void> {\n await compileAndReport(root, options);\n}\n\n/**\n * Run the full compilation pipeline and return a structured result.\n * Same behaviour as compile() but exposes counts, slugs, and errors so\n * non-CLI consumers (the MCP server, programmatic callers) can report\n * meaningful data without scraping terminal output.\n * @param root - Project root directory.\n * @param options - Optional pipeline overrides (e.g. --review mode).\n * @returns Structured result describing what was compiled.\n */\nexport async function compileAndReport(\n root: string,\n options: CompileOptions = {},\n): Promise<CompileResult> {\n output.header(\"llmwiki compile\");\n\n const locked = await acquireLock(root);\n if (!locked) {\n output.status(\"!\", output.error(\"Could not acquire lock. Try again later.\"));\n return {\n ...emptyCompileResult(),\n errors: [\"Could not acquire .llmwiki/lock — another compile is in progress.\"],\n };\n }\n\n try {\n return await runCompilePipeline(root, options);\n } finally {\n await releaseLock(root);\n }\n}\n\n/** Buckets of source changes used by the compile pipeline. */\ninterface ChangeBuckets {\n toCompile: SourceChange[];\n deleted: SourceChange[];\n unchanged: SourceChange[];\n}\n\n/** Sort source changes into the buckets the pipeline acts on. */\nfunction bucketChanges(changes: SourceChange[]): ChangeBuckets {\n return {\n toCompile: changes.filter((c) => c.status === \"new\" || c.status === \"changed\"),\n deleted: changes.filter((c) => c.status === \"deleted\"),\n unchanged: changes.filter((c) => c.status === \"unchanged\"),\n };\n}\n\n/** Result of phase 2: page writes plus any errors collected along the way. */\ninterface PageGenerationResult {\n pages: MergedConcept[];\n errors: string[];\n /** Candidate ids written when running in --review mode. Empty otherwise. */\n candidates: string[];\n}\n\n/** Phase 2: generate pages for merged concepts in parallel, capturing errors. */\nasync function generatePagesPhase(\n root: string,\n extractions: ExtractionResult[],\n frozenSlugs: Set<string>,\n schema: SchemaConfig,\n options: CompileOptions,\n): Promise<PageGenerationResult> {\n const merged = mergeExtractions(extractions, frozenSlugs);\n // Build the per-source state snapshot once so each candidate can carry the\n // exact data needed to mark its sources compiled on approval.\n const sourceStates = options.review\n ? await buildExtractionSourceStates(root, extractions)\n : {};\n const limit = pLimit(COMPILE_CONCURRENCY);\n const errors: string[] = [];\n const candidates: string[] = [];\n const pages = await Promise.all(\n merged.map((entry) => limit(async () => {\n const result = await generateMergedPage(root, entry, schema, options, sourceStates);\n if (result.error) errors.push(result.error);\n if (result.candidateId) candidates.push(result.candidateId);\n return entry;\n })),\n );\n return { pages, errors, candidates };\n}\n\n/** Persist source state for every extraction that produced concepts. */\nasync function persistExtractionStates(\n root: string,\n extractions: ExtractionResult[],\n): Promise<void> {\n for (const result of extractions) {\n if (result.concepts.length === 0) continue;\n await persistSourceState(root, result.sourcePath, result.sourceFile, result.concepts);\n }\n}\n\n/** Build the structured CompileResult and emit the CLI completion banner. */\nfunction summarizeCompile(\n buckets: ChangeBuckets,\n generation: PageGenerationResult,\n extractions: ExtractionResult[],\n options: CompileOptions,\n): CompileResult {\n output.header(\"Compilation complete\");\n output.status(\"✓\", output.success(\n `${buckets.toCompile.length} compiled, ${buckets.unchanged.length} skipped, ${buckets.deleted.length} deleted`,\n ));\n if (options.review && generation.candidates.length > 0) {\n output.status(\"?\", output.info(\n `${generation.candidates.length} candidate(s) awaiting review — run \\`llmwiki review list\\``,\n ));\n } else if (buckets.toCompile.length > 0) {\n output.status(\"→\", output.dim('Next: llmwiki query \"your question here\"'));\n }\n\n const errors = [...generation.errors];\n for (const result of extractions) {\n if (result.concepts.length === 0) {\n errors.push(`No concepts extracted from ${result.sourceFile}`);\n }\n }\n\n const baseResult: CompileResult = {\n compiled: buckets.toCompile.length,\n skipped: buckets.unchanged.length,\n deleted: buckets.deleted.length,\n concepts: generation.pages.map((entry) => entry.concept.concept),\n pages: generation.pages.map((entry) => entry.slug),\n errors,\n };\n if (options.review) {\n baseResult.candidates = generation.candidates;\n }\n return baseResult;\n}\n\n/** Inner pipeline, runs under lock protection. Returns structured CompileResult. */\nasync function runCompilePipeline(\n root: string,\n options: CompileOptions,\n): Promise<CompileResult> {\n const schema = await loadSchema(root);\n reportSchemaStatus(schema);\n const state = await readState(root);\n const changes = await detectChanges(root, state);\n augmentWithAffectedSources(changes, findAffectedSources(state, changes));\n\n const buckets = bucketChanges(changes);\n if (buckets.toCompile.length === 0 && buckets.deleted.length === 0) {\n output.status(\"✓\", output.success(\"Nothing to compile — all sources up to date.\"));\n // Seed pages are cheap deterministic writes — always run them even when\n // no source files changed, so adding a seed page to schema.json takes\n // effect on the next compile without needing a source file edit.\n if (!options.review) {\n const emptyGeneration: PageGenerationResult = { pages: [], errors: [], candidates: [] };\n await generateSeedPages(root, schema, emptyGeneration);\n // Rebuild index/MOC so the newly-written seed pages become discoverable,\n // and propagate any seed-page validation errors into the returned result.\n await finalizeWiki(root, emptyGeneration.pages);\n return {\n ...emptyCompileResult(),\n skipped: buckets.unchanged.length,\n errors: emptyGeneration.errors,\n };\n }\n return { ...emptyCompileResult(), skipped: buckets.unchanged.length };\n }\n\n printChangesSummary(changes);\n // In review mode the pipeline contract is \"write candidates instead of\n // mutating wiki/\". Deletion bookkeeping (orphan marking + frozen-slug\n // persistence) writes directly into wiki/ and updates state.json, so we\n // defer it to the next non-review compile pass. Source-state persistence\n // for compiled sources is also review-deferred — those entries land at\n // approve time so unapproved candidates remain re-detectable on subsequent\n // compiles.\n if (!options.review) {\n await markDeletedAsOrphaned(root, buckets.deleted, state);\n }\n\n const frozenSlugs = findFrozenSlugs(state, changes);\n reportFrozenSlugs(frozenSlugs);\n\n const extractions = await runExtractionPhases(root, buckets.toCompile, state, changes);\n if (!options.review) {\n await freezeFailedExtractions(root, extractions, frozenSlugs);\n }\n\n const generation = await generatePagesPhase(root, extractions, frozenSlugs, schema, options);\n\n if (!options.review) {\n await persistExtractionStates(root, extractions);\n if (frozenSlugs.size > 0) {\n await orphanUnownedFrozenPages(root, frozenSlugs);\n }\n await persistFrozenSlugs(root, frozenSlugs, extractions);\n // Seed pages write directly into wiki/, so skip them in review mode\n // to honour the \"no wiki/ mutation\" contract of that mode.\n await generateSeedPages(root, schema, generation);\n await finalizeWiki(root, generation.pages);\n }\n return summarizeCompile(buckets, generation, extractions, options);\n}\n\n/** Log where the schema was loaded from so the user can confirm it was picked up. */\nfunction reportSchemaStatus(schema: SchemaConfig): void {\n if (schema.loadedFrom) {\n output.status(\"i\", output.dim(`Schema: ${schema.loadedFrom}`));\n }\n}\n\n/** Append affected-source changes (logging each addition) to the change list. */\nfunction augmentWithAffectedSources(changes: SourceChange[], affected: string[]): void {\n for (const file of affected) {\n output.status(\"~\", output.info(`${file} [affected by shared concept]`));\n changes.push({ file, status: \"changed\" });\n }\n}\n\n/** Mark wiki pages owned solely by deleted sources as orphaned. */\nasync function markDeletedAsOrphaned(\n root: string,\n deleted: SourceChange[],\n state: WikiState,\n): Promise<void> {\n for (const del of deleted) {\n await markOrphaned(root, del.file, state);\n }\n}\n\n/** Log frozen slugs (shared concepts whose deletion-pinned content must persist). */\nfunction reportFrozenSlugs(frozenSlugs: Set<string>): void {\n for (const slug of frozenSlugs) {\n output.status(\"i\", output.dim(`Frozen: ${slug} (shared with deleted source)`));\n }\n}\n\n/**\n * Phase 1: extract concepts for the directly-changed batch, then expand to\n * any unchanged sources whose concepts overlap with newly extracted slugs.\n */\nasync function runExtractionPhases(\n root: string,\n toCompile: SourceChange[],\n state: WikiState,\n allChanges: SourceChange[],\n): Promise<ExtractionResult[]> {\n const extractions: ExtractionResult[] = [];\n for (const change of toCompile) {\n extractions.push(await extractForSource(root, change.file));\n }\n\n const lateAffected = findLateAffectedSources(extractions, state, allChanges);\n for (const file of lateAffected) {\n output.status(\"~\", output.info(`${file} [shares concept with new source]`));\n extractions.push(await extractForSource(root, file));\n }\n\n return extractions;\n}\n\n/** Resolve interlinks, regenerate index/MOC, refresh embeddings post-write. */\nasync function finalizeWiki(root: string, pages: MergedConcept[]): Promise<void> {\n const allChangedSlugs = pages.map((entry) => entry.slug);\n const allNewSlugs = pages.filter((entry) => entry.concept.is_new).map((entry) => entry.slug);\n\n if (allChangedSlugs.length > 0) {\n output.status(\"🔗\", output.info(\"Resolving interlinks...\"));\n await resolveLinks(root, allChangedSlugs, allNewSlugs);\n }\n\n await generateIndex(root);\n await generateMOC(root);\n await safelyUpdateEmbeddings(root, allChangedSlugs);\n}\n\n/** Print a summary of detected source file changes. */\nfunction printChangesSummary(changes: SourceChange[]): void {\n const iconMap: Record<string, string> = {\n new: \"+\", changed: \"~\", unchanged: \".\", deleted: \"-\",\n };\n const fmtMap: Record<string, (s: string) => string> = {\n new: output.success, changed: output.warn, unchanged: output.dim, deleted: output.error,\n };\n\n for (const c of changes) {\n const icon = iconMap[c.status] ?? \"?\";\n const fmt = fmtMap[c.status] ?? output.dim;\n output.status(icon, fmt(`${c.file} [${c.status}]`));\n }\n}\n\n/**\n * Phase 1: Extract concepts from a source without generating pages.\n * Returns extraction data for the generation phase.\n */\nasync function extractForSource(\n root: string,\n sourceFile: string,\n): Promise<ExtractionResult> {\n output.status(\"*\", output.info(`Extracting: ${sourceFile}`));\n\n const sourcePath = path.join(root, SOURCES_DIR, sourceFile);\n const sourceContent = await readFile(sourcePath, \"utf-8\");\n const existingIndex = await safeReadFile(path.join(root, INDEX_FILE));\n const concepts = await extractConcepts(sourceContent, existingIndex);\n\n if (concepts.length > 0) {\n const names = concepts.map((c) => c.concept).join(\", \");\n output.status(\"*\", output.dim(` Found ${concepts.length} concepts: ${names}`));\n }\n return { sourceFile, sourcePath, sourceContent, concepts };\n}\n\n/** A concept with all contributing sources merged for generation. */\ninterface MergedConcept {\n slug: string;\n concept: ExtractedConcept;\n sourceFiles: string[];\n combinedContent: string;\n}\n\n/**\n * Reconcile metadata from a later-extracted concept into an existing merged entry.\n * Called when multiple sources contribute the same slug — produces the most\n * pessimistic aggregate view of confidence, provenance, and contradictions.\n *\n * Rules:\n * - confidence: min (most pessimistic value wins)\n * - provenanceState: always 'merged' once two sources are involved\n * - contradictedBy: union by slug (deduplicating on slug identity)\n * - inferredParagraphs: max (any source claiming inference wins)\n */\nexport function reconcileConceptMetadata(\n existing: ExtractedConcept,\n incoming: ExtractedConcept,\n): ExtractedConcept {\n const reconciled = { ...existing };\n\n // Minimum confidence — the weaker source's score governs the whole page.\n if (typeof incoming.confidence === \"number\") {\n reconciled.confidence = typeof existing.confidence === \"number\"\n ? Math.min(existing.confidence, incoming.confidence)\n : incoming.confidence;\n }\n\n // Merged state is the canonical answer when multiple sources contribute.\n reconciled.provenanceState = \"merged\";\n\n // Union contradictedBy entries, deduplicating by slug.\n const refs = [...(existing.contradictedBy ?? [])];\n const seenSlugs = new Set(refs.map((r) => r.slug));\n for (const ref of incoming.contradictedBy ?? []) {\n if (!seenSlugs.has(ref.slug)) {\n refs.push(ref);\n seenSlugs.add(ref.slug);\n }\n }\n reconciled.contradictedBy = refs.length > 0 ? refs : undefined;\n\n // Max inferredParagraphs — any source flagging inference raises the count.\n if (typeof incoming.inferredParagraphs === \"number\") {\n reconciled.inferredParagraphs = typeof existing.inferredParagraphs === \"number\"\n ? Math.max(existing.inferredParagraphs, incoming.inferredParagraphs)\n : incoming.inferredParagraphs;\n }\n\n return reconciled;\n}\n\n/**\n * Merge extractions so each concept slug maps to ALL contributing sources.\n * When sources A and B both extract concept X, the LLM receives combined\n * content from both sources, producing a single page that reflects all\n * contributing material rather than just the last source processed.\n * Metadata is reconciled across all contributing concepts via\n * reconcileConceptMetadata so contradictions from later sources are not lost.\n */\nfunction mergeExtractions(\n extractions: ExtractionResult[],\n frozenSlugs: Set<string>,\n): MergedConcept[] {\n const bySlug = new Map<string, MergedConcept>();\n\n for (const result of extractions) {\n if (result.concepts.length === 0) continue;\n\n for (const concept of result.concepts) {\n const slug = slugify(concept.concept);\n if (frozenSlugs.has(slug)) continue;\n\n const existing = bySlug.get(slug);\n if (existing) {\n existing.concept = reconcileConceptMetadata(existing.concept, concept);\n existing.sourceFiles.push(result.sourceFile);\n existing.combinedContent += `\\n\\n--- SOURCE: ${result.sourceFile} ---\\n\\n${result.sourceContent}`;\n } else {\n bySlug.set(slug, {\n slug,\n concept,\n sourceFiles: [result.sourceFile],\n combinedContent: `--- SOURCE: ${result.sourceFile} ---\\n\\n${result.sourceContent}`,\n });\n }\n }\n }\n\n return Array.from(bySlug.values());\n}\n\n/** Outcome of generating a single merged concept page. */\ninterface MergedPageOutcome {\n error?: string;\n candidateId?: string;\n}\n\n/**\n * Generate a wiki page from merged source content.\n * For shared concepts, the LLM sees content from all contributing sources\n * and frontmatter records every source file. When `options.review` is set,\n * the rendered page is persisted as a review candidate instead of being\n * written into `wiki/`.\n */\nasync function generateMergedPage(\n root: string,\n entry: MergedConcept,\n schema: SchemaConfig,\n options: CompileOptions,\n sourceStates: SourceStateMap,\n): Promise<MergedPageOutcome> {\n const fullPage = await renderMergedPageContent(root, entry, schema);\n\n if (options.review) {\n return await persistReviewCandidate(root, entry, fullPage, sourceStates, schema);\n }\n\n const pagePath = path.join(root, CONCEPTS_DIR, `${entry.slug}.md`);\n const error = await writePageIfValid(pagePath, fullPage, entry.concept.concept);\n return { error: error ?? undefined };\n}\n\n/** Persist a candidate JSON record for later review and report it on stdout. */\nasync function persistReviewCandidate(\n root: string,\n entry: MergedConcept,\n fullPage: string,\n sourceStates: SourceStateMap,\n schema: SchemaConfig,\n): Promise<MergedPageOutcome> {\n // Run schema-aware lint against the candidate body so violations are visible\n // in `review show` before a reviewer approves the page. The virtual file path\n // uses the slug so diagnostics are identifiable without a real disk path.\n const virtualPath = `wiki/concepts/${entry.slug}.md`;\n const violations = checkPageCrossLinks(fullPage, virtualPath, schema);\n\n const candidate: ReviewCandidate = await writeCandidate(root, {\n title: entry.concept.concept,\n slug: entry.slug,\n summary: entry.concept.summary,\n sources: entry.sourceFiles,\n body: fullPage,\n sourceStates: pickStatesForSources(sourceStates, entry.sourceFiles),\n schemaViolations: violations.length > 0 ? violations : undefined,\n });\n output.status(\"?\", output.info(`Candidate ready: ${candidate.id} (${entry.slug})`));\n return { candidateId: candidate.id };\n}\n\n/**\n * Materialise schema-declared seed pages (overview, comparison, entity).\n * Each seed page is written under wiki/concepts/ next to concept pages so\n * existing tooling (index, MOC, lint, embeddings) treats them uniformly.\n * Slugs from generated pages this run are added so seed pages can be linked\n * deterministically without waiting for a second compile pass.\n * @param root - Project root directory.\n * @param schema - Resolved schema config.\n * @param generation - Result of the concept-page generation phase.\n */\nasync function generateSeedPages(\n root: string,\n schema: SchemaConfig,\n generation: PageGenerationResult,\n): Promise<void> {\n if (schema.seedPages.length === 0) return;\n for (const seed of schema.seedPages) {\n const error = await generateSingleSeedPage(root, schema, seed);\n if (error) generation.errors.push(error);\n }\n}\n\n/** Build, prompt, and persist a single seed page. */\nasync function generateSingleSeedPage(\n root: string,\n schema: SchemaConfig,\n seed: SeedPage,\n): Promise<string | null> {\n const slug = slugify(seed.title);\n const pagePath = path.join(root, CONCEPTS_DIR, `${slug}.md`);\n const relatedContent = await loadSeedRelatedPages(root, seed.relatedSlugs ?? []);\n const rule = schema.kinds[seed.kind];\n const system = buildSeedPagePrompt(seed, rule, relatedContent);\n const pageBody = await callClaude({\n system,\n messages: [{ role: \"user\", content: `Write the ${seed.kind} page titled \"${seed.title}\".` }],\n });\n\n const now = new Date().toISOString();\n const existing = await safeReadFile(pagePath);\n const existingMeta = existing ? parseFrontmatter(existing).meta : null;\n const createdAt = typeof existingMeta?.createdAt === \"string\" ? existingMeta.createdAt : now;\n const typedFields: WikiFrontmatter = {\n title: seed.title,\n summary: seed.summary,\n sources: [],\n kind: seed.kind,\n createdAt,\n updatedAt: now,\n };\n const frontmatterFields: Record<string, unknown> = { ...typedFields };\n addObsidianMeta(frontmatterFields, seed.title, []);\n const frontmatter = buildFrontmatter(frontmatterFields);\n return await writePageIfValid(pagePath, `${frontmatter}\\n\\n${pageBody}\\n`, seed.title);\n}\n\n/** Load the bodies of the related concept pages a seed page should weave together. */\nasync function loadSeedRelatedPages(root: string, slugs: string[]): Promise<string> {\n if (slugs.length === 0) return \"\";\n const contents: string[] = [];\n for (const slug of slugs) {\n const pagePath = path.join(root, CONCEPTS_DIR, `${slug}.md`);\n const content = await safeReadFile(pagePath);\n if (content) contents.push(content);\n }\n return contents.join(\"\\n\\n---\\n\\n\");\n}\n\n/**\n * Call Claude to extract concepts from a source document.\n * @param sourceContent - Full source document text.\n * @param existingIndex - Current wiki index for deduplication.\n * @returns Parsed array of extracted concepts.\n */\nasync function extractConcepts(\n sourceContent: string,\n existingIndex: string,\n): Promise<ExtractedConcept[]> {\n const system = buildExtractionPrompt(sourceContent, existingIndex);\n const rawOutput = await callClaude({\n system,\n messages: [{ role: \"user\", content: \"Extract the key concepts from this source.\" }],\n tools: [CONCEPT_EXTRACTION_TOOL],\n });\n\n return parseConcepts(rawOutput);\n}\n\n/**\n * Validate and atomically write a wiki page, logging the result.\n * @param pagePath - Absolute path to write the page.\n * @param content - Full page content including frontmatter.\n * @param conceptTitle - Title for logging purposes.\n */\nasync function writePageIfValid(\n pagePath: string,\n content: string,\n conceptTitle: string,\n): Promise<string | null> {\n if (!validateWikiPage(content)) {\n output.status(\"!\", output.warn(`Invalid page for \"${conceptTitle}\" — skipped.`));\n return `Invalid page for \"${conceptTitle}\" — failed validation`;\n }\n\n await atomicWrite(pagePath, content);\n return null;\n}\n\n/**\n * Refresh the embeddings store without failing compilation.\n * Semantic search is a non-critical enhancement — missing API keys or\n * transient provider errors should produce a warning, not a broken build.\n */\nasync function safelyUpdateEmbeddings(root: string, changedSlugs: string[]): Promise<void> {\n try {\n await updateEmbeddings(root, changedSlugs);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n output.status(\"!\", output.warn(`Skipped embeddings update: ${message}`));\n }\n}\n\n/**\n * Update the persisted state for a compiled source file.\n * @param root - Project root directory.\n * @param sourcePath - Absolute path to the source file.\n * @param sourceFile - Filename within sources/.\n * @param concepts - Concepts extracted from this source.\n */\nasync function persistSourceState(\n root: string,\n sourcePath: string,\n sourceFile: string,\n concepts: ReturnType<typeof parseConcepts>,\n): Promise<void> {\n const hash = await hashFile(sourcePath);\n const entry: SourceState = {\n hash,\n concepts: concepts.map((c) => slugify(c.concept)),\n compiledAt: new Date().toISOString(),\n };\n\n await updateSourceState(root, sourceFile, entry);\n}\n","/**\n * Manages .llmwiki/state.json — the persistent compilation state that tracks\n * source file hashes and their compiled concepts. Enables incremental\n * compilation by detecting which sources have changed since last compile.\n *\n * Uses atomic writes (write to .tmp, then rename) to prevent corruption\n * from interrupted compiles.\n */\n\nimport { readFile, writeFile, rename, mkdir, copyFile } from \"fs/promises\";\nimport { existsSync } from \"fs\";\nimport path from \"path\";\nimport { LLMWIKI_DIR, STATE_FILE } from \"./constants.js\";\nimport type { WikiState, SourceState } from \"./types.js\";\n\nfunction emptyState(): WikiState {\n return { version: 1, indexHash: \"\", sources: {} };\n}\n\n/** Read .llmwiki/state.json, recovering from corruption gracefully. */\nexport async function readState(root: string): Promise<WikiState> {\n const filePath = path.join(root, STATE_FILE);\n\n if (!existsSync(filePath)) {\n return emptyState();\n }\n\n try {\n const raw = await readFile(filePath, \"utf-8\");\n return JSON.parse(raw) as WikiState;\n } catch {\n const bakPath = filePath + \".bak\";\n console.warn(`⚠ Corrupt state.json — backed up to ${bakPath}, starting fresh.`);\n await copyFile(filePath, bakPath);\n return emptyState();\n }\n}\n\n/** Atomically write state.json (write .tmp then rename). */\nexport async function writeState(root: string, state: WikiState): Promise<void> {\n const dir = path.join(root, LLMWIKI_DIR);\n await mkdir(dir, { recursive: true });\n\n const filePath = path.join(root, STATE_FILE);\n const tmpPath = filePath + \".tmp\";\n\n await writeFile(tmpPath, JSON.stringify(state, null, 2), \"utf-8\");\n await rename(tmpPath, filePath);\n}\n\n/**\n * Update a single source's entry in state after successful compilation.\n * Per-source granularity means interrupted compiles only reprocess incomplete sources.\n */\nexport async function updateSourceState(\n root: string,\n sourceFile: string,\n entry: SourceState,\n): Promise<void> {\n const state = await readState(root);\n state.sources[sourceFile] = entry;\n await writeState(root, state);\n}\n\n/** Remove a source entry from state (for deleted sources). */\nexport async function removeSourceState(\n root: string,\n sourceFile: string,\n): Promise<void> {\n const state = await readState(root);\n delete state.sources[sourceFile];\n await writeState(root, state);\n}\n","/**\n * Source-state snapshot helpers shared between the live compile path and the\n * review-candidate path.\n *\n * The compile pipeline normally persists a `SourceState` entry for every\n * extracted source so subsequent compiles can skip unchanged inputs. When\n * compile runs in `--review` mode, page writes are deferred — but the same\n * per-source state still needs to land on approval, otherwise approved\n * sources stay marked as \"new/changed\" forever and reproduce duplicate\n * candidates on every compile.\n *\n * This module produces a `Record<sourceFile, SourceState>` snapshot from the\n * extraction results so it can ride along inside each `ReviewCandidate` and\n * be flushed to `.llmwiki/state.json` at approval time.\n */\n\nimport path from \"path\";\nimport { hashFile } from \"./hasher.js\";\nimport { slugify } from \"../utils/markdown.js\";\nimport { SOURCES_DIR } from \"../utils/constants.js\";\nimport type { ExtractionResult } from \"./deps.js\";\nimport type { SourceState } from \"../utils/types.js\";\n\n/**\n * Compute a per-source state snapshot keyed by source filename.\n *\n * Hashes every contributing source once so each candidate carries the\n * incremental-state payload required to mark its sources compiled on\n * approval. Sources with no extracted concepts are skipped — we only mark\n * sources compiled when extraction succeeded, mirroring the live path's\n * behaviour.\n *\n * @param root - Project root directory.\n * @param extractions - Extraction results from the compile pipeline.\n * @returns Map of source filename → SourceState ready for state.json.\n */\nexport async function buildExtractionSourceStates(\n root: string,\n extractions: ExtractionResult[],\n): Promise<Record<string, SourceState>> {\n const snapshot: Record<string, SourceState> = {};\n const compiledAt = new Date().toISOString();\n\n for (const result of extractions) {\n if (result.concepts.length === 0) continue;\n snapshot[result.sourceFile] = await buildEntry(root, result, compiledAt);\n }\n\n return snapshot;\n}\n\n/** Build a single SourceState entry for one extraction result. */\nasync function buildEntry(\n root: string,\n result: ExtractionResult,\n compiledAt: string,\n): Promise<SourceState> {\n const filePath = path.join(root, SOURCES_DIR, result.sourceFile);\n const hash = await hashFile(filePath);\n return {\n hash,\n concepts: result.concepts.map((concept) => slugify(concept.concept)),\n compiledAt,\n };\n}\n\n/**\n * Filter a global source-state snapshot down to entries relevant to a\n * specific candidate. A candidate carries only the source-state entries\n * for sources that actually contributed to it, so on approval we can\n * persist a minimal, accurate slice into state.json.\n *\n * @param allStates - Global per-source snapshot from buildExtractionSourceStates.\n * @param sourceFiles - Source filenames that contributed to the candidate.\n */\nexport function pickStatesForSources(\n allStates: Record<string, SourceState>,\n sourceFiles: string[],\n): Record<string, SourceState> {\n const picked: Record<string, SourceState> = {};\n for (const file of sourceFiles) {\n const entry = allStates[file];\n if (entry) picked[file] = entry;\n }\n return picked;\n}\n","/**\n * Source file hashing for change detection.\n * Computes SHA-256 hashes of source files and compares them against\n * previously stored state to determine which files need recompilation.\n * This enables incremental compilation — only changed or new sources\n * are sent through the LLM pipeline.\n */\n\nimport { createHash } from \"node:crypto\";\nimport { readFile, readdir } from \"fs/promises\";\nimport path from \"path\";\nimport { SOURCES_DIR } from \"../utils/constants.js\";\nimport type { WikiState, SourceChange } from \"../utils/types.js\";\n\n/**\n * Read a file and compute its SHA-256 hash.\n * @param filePath - Absolute path to the file to hash.\n * @returns Hex-encoded SHA-256 digest of the file contents.\n */\nexport async function hashFile(filePath: string): Promise<string> {\n const content = await readFile(filePath, \"utf-8\");\n return createHash(\"sha256\").update(content).digest(\"hex\");\n}\n\n/**\n * Scan the sources/ directory and compare file hashes against previous state\n * to identify new, changed, unchanged, and deleted source files.\n * @param root - Project root directory containing the sources/ folder.\n * @param prevState - The previously persisted WikiState to compare against.\n * @returns Array of SourceChange entries describing each file's status.\n */\nexport async function detectChanges(\n root: string,\n prevState: WikiState,\n): Promise<SourceChange[]> {\n const sourcesPath = path.join(root, SOURCES_DIR);\n const currentFiles = await listSourceFiles(sourcesPath);\n const changes: SourceChange[] = [];\n\n for (const file of currentFiles) {\n const status = await classifyFile(root, file, prevState);\n changes.push({ file, status });\n }\n\n const deletedChanges = findDeletedFiles(currentFiles, prevState);\n changes.push(...deletedChanges);\n\n return changes;\n}\n\n/**\n * List all markdown files in the sources directory.\n * @param sourcesPath - Absolute path to the sources/ directory.\n * @returns Array of filenames (not full paths).\n */\nasync function listSourceFiles(sourcesPath: string): Promise<string[]> {\n try {\n const entries = await readdir(sourcesPath);\n return entries.filter((f) => f.endsWith(\".md\"));\n } catch {\n return [];\n }\n}\n\n/**\n * Classify a single source file as new, changed, or unchanged.\n * @param root - Project root directory.\n * @param file - Filename within sources/.\n * @param prevState - Previous compilation state.\n * @returns The change status for this file.\n */\nasync function classifyFile(\n root: string,\n file: string,\n prevState: WikiState,\n): Promise<SourceChange[\"status\"]> {\n const filePath = path.join(root, SOURCES_DIR, file);\n const hash = await hashFile(filePath);\n const prev = prevState.sources[file];\n\n if (!prev) return \"new\";\n if (prev.hash !== hash) return \"changed\";\n return \"unchanged\";\n}\n\n/**\n * Find source files present in previous state but missing from disk.\n * @param currentFiles - Files currently on disk.\n * @param prevState - Previous compilation state.\n * @returns Array of SourceChange entries for deleted files.\n */\nfunction findDeletedFiles(\n currentFiles: string[],\n prevState: WikiState,\n): SourceChange[] {\n const currentSet = new Set(currentFiles);\n return Object.keys(prevState.sources)\n .filter((file) => !currentSet.has(file))\n .map((file) => ({ file, status: \"deleted\" as const }));\n}\n","/**\n * Anthropic LLM provider implementation.\n *\n * Wraps the @anthropic-ai/sdk to implement the LLMProvider interface.\n * Handles complete, streaming, and tool-use calls against Claude models.\n */\n\nimport Anthropic, { type ClientOptions } from \"@anthropic-ai/sdk\";\nimport type { LLMProvider, LLMMessage, LLMTool } from \"../utils/provider.js\";\nimport { EMBEDDING_MODELS } from \"../utils/constants.js\";\n\nconst VOYAGE_EMBEDDINGS_URL = \"https://api.voyageai.com/v1/embeddings\";\n\n/**\n * Builds the client options for the Anthropic SDK.\n *\n * Handles optional baseURL and filters out empty values so the SDK\n * can fall back to its internal defaults when not specified.\n */\ninterface AnthropicProviderOptions {\n apiKey?: string;\n authToken?: string;\n baseURL?: string;\n}\n\nexport function buildAnthropicClientOptions(\n options: AnthropicProviderOptions = {},\n): ClientOptions {\n const trimmedBaseURL = options.baseURL?.trim();\n const trimmedApiKey = options.apiKey?.trim();\n const trimmedAuthToken = options.authToken?.trim();\n\n const result: ClientOptions = {};\n\n if (trimmedApiKey) {\n result.apiKey = trimmedApiKey;\n }\n if (trimmedAuthToken) {\n result.authToken = trimmedAuthToken;\n }\n\n if (!trimmedBaseURL) {\n return result;\n }\n\n const normalizedBaseURL =\n trimmedBaseURL.endsWith(\"/\") && trimmedBaseURL.length > 1\n ? trimmedBaseURL.slice(0, -1)\n : trimmedBaseURL;\n\n result.baseURL = normalizedBaseURL;\n return result;\n}\n\n\n/** Anthropic-backed LLM provider using the official SDK. */\nexport class AnthropicProvider implements LLMProvider {\n private readonly client: Anthropic;\n private readonly model: string;\n\n constructor(model: string, options: AnthropicProviderOptions = {}) {\n this.model = model;\n this.client = new Anthropic(buildAnthropicClientOptions(options));\n }\n\n /** Send a single non-streaming completion request. */\n async complete(system: string, messages: LLMMessage[], maxTokens: number): Promise<string> {\n const response = await this.client.messages.create({\n model: this.model,\n max_tokens: maxTokens,\n system,\n messages,\n });\n\n const textBlock = response.content.find((block) => block.type === \"text\");\n return textBlock?.type === \"text\" ? textBlock.text : \"\";\n }\n\n /** Stream a completion, invoking onToken for each text chunk. */\n async stream(\n system: string,\n messages: LLMMessage[],\n maxTokens: number,\n onToken?: (text: string) => void,\n ): Promise<string> {\n const stream = this.client.messages.stream({\n model: this.model,\n max_tokens: maxTokens,\n system,\n messages,\n });\n\n let fullText = \"\";\n for await (const event of stream) {\n if (event.type === \"content_block_delta\" && event.delta.type === \"text_delta\") {\n fullText += event.delta.text;\n onToken?.(event.delta.text);\n }\n }\n\n return fullText;\n }\n\n /** Call Claude with tool definitions and return the parsed tool input as JSON. */\n async toolCall(\n system: string,\n messages: LLMMessage[],\n tools: LLMTool[],\n maxTokens: number,\n ): Promise<string> {\n const anthropicTools: Anthropic.Tool[] = tools.map((t) => ({\n name: t.name,\n description: t.description,\n input_schema: t.input_schema as Anthropic.Tool.InputSchema,\n }));\n\n const response = await this.client.messages.create({\n model: this.model,\n max_tokens: maxTokens,\n system,\n messages,\n tools: anthropicTools,\n });\n\n const toolBlock = response.content.find((block) => block.type === \"tool_use\");\n if (toolBlock?.type === \"tool_use\") {\n return JSON.stringify(toolBlock.input);\n }\n\n const textBlock = response.content.find((block) => block.type === \"text\");\n return textBlock?.type === \"text\" ? textBlock.text : \"\";\n }\n\n /**\n * Produce a single embedding vector via the Voyage API.\n *\n * Anthropic does not ship a first-party embeddings endpoint, so we delegate\n * to Voyage (their recommended partner). Requires VOYAGE_API_KEY.\n */\n async embed(text: string): Promise<number[]> {\n const apiKey = process.env.VOYAGE_API_KEY?.trim();\n if (!apiKey) {\n throw new Error(\n \"VOYAGE_API_KEY is not set. Anthropic embeddings use Voyage — set VOYAGE_API_KEY to enable semantic search.\",\n );\n }\n\n const response = await fetch(VOYAGE_EMBEDDINGS_URL, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({ input: text, model: EMBEDDING_MODELS.anthropic }),\n });\n\n if (!response.ok) {\n const detail = await response.text();\n throw new Error(`Voyage embeddings request failed (${response.status}): ${detail}`);\n }\n\n const json = (await response.json()) as { data?: Array<{ embedding?: number[] }> };\n const vector = json.data?.[0]?.embedding;\n if (!Array.isArray(vector)) {\n throw new Error(\"Voyage embeddings response did not include a vector.\");\n }\n return vector;\n }\n}\n","/**\n * OpenAI LLM provider implementation.\n *\n * Wraps the openai npm package to implement the LLMProvider interface.\n * Translates Anthropic-style tool schemas (input_schema) to OpenAI format (parameters).\n */\n\nimport OpenAI from \"openai\";\nimport type { LLMProvider, LLMMessage, LLMTool } from \"../utils/provider.js\";\nimport { EMBEDDING_MODELS, OPENAI_DEFAULT_TIMEOUT_MS } from \"../utils/constants.js\";\n\n/** Construction options for an OpenAI-compatible provider. */\ninterface OpenAIProviderOptions {\n baseURL?: string;\n apiKey?: string;\n embeddingsBaseURL?: string;\n embeddingModel?: string;\n /**\n * Per-request timeout in milliseconds. Defaults to 10 minutes for cloud\n * OpenAI (matches the SDK default). Long compile-time completions on\n * slower local models can exceed this — see {@link OllamaProvider} which\n * raises the default and reads LLMWIKI_REQUEST_TIMEOUT_MS / OLLAMA_TIMEOUT_MS.\n */\n timeoutMs?: number;\n}\n\n/**\n * Read an integer-millisecond timeout from an env var. Returns undefined when\n * the env var is unset, empty, non-numeric, zero, or negative — so the caller\n * silently falls back to the next source in its resolution chain (env-var\n * typos like `OLLAMA_TIMEOUT_MS=30m` are not surfaced to the user).\n */\nexport function readTimeoutEnv(name: string): number | undefined {\n const raw = process.env[name]?.trim();\n if (!raw) return undefined;\n const parsed = Number(raw);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;\n}\n\n/** Resolve the OpenAI client timeout from LLMWIKI_REQUEST_TIMEOUT_MS, if set. */\nfunction resolveOpenAITimeoutMs(): number | undefined {\n return readTimeoutEnv(\"LLMWIKI_REQUEST_TIMEOUT_MS\");\n}\n\n/** Translate an Anthropic-style LLMTool to an OpenAI ChatCompletionTool. */\nexport function translateToolToOpenAI(\n tool: LLMTool,\n): OpenAI.ChatCompletionTool {\n return {\n type: \"function\",\n function: {\n name: tool.name,\n description: tool.description,\n parameters: tool.input_schema,\n },\n };\n}\n\n/** OpenAI-backed LLM provider. */\nexport class OpenAIProvider implements LLMProvider {\n protected readonly client: OpenAI;\n protected readonly embeddingsClient: OpenAI;\n protected readonly model: string;\n protected readonly configuredEmbeddingModel?: string;\n\n constructor(model: string, options: OpenAIProviderOptions = {}) {\n this.model = model;\n this.configuredEmbeddingModel = options.embeddingModel;\n // The OpenAI SDK validates OPENAI_API_KEY at construction time.\n // Pass the key explicitly so the provider controls when validation happens.\n const resolvedKey = options.apiKey ?? process.env.OPENAI_API_KEY ?? \"\";\n const timeout = options.timeoutMs ?? resolveOpenAITimeoutMs() ?? OPENAI_DEFAULT_TIMEOUT_MS;\n this.client = new OpenAI({\n apiKey: resolvedKey,\n baseURL: options.baseURL ?? null,\n timeout,\n });\n this.embeddingsClient = options.embeddingsBaseURL\n ? new OpenAI({ apiKey: resolvedKey, baseURL: options.embeddingsBaseURL, timeout })\n : this.client;\n }\n\n /** Send a single non-streaming completion request. */\n async complete(system: string, messages: LLMMessage[], maxTokens: number): Promise<string> {\n const response = await this.client.chat.completions.create({\n model: this.model,\n max_tokens: maxTokens,\n messages: [{ role: \"system\", content: system }, ...messages],\n });\n\n return response.choices[0]?.message?.content ?? \"\";\n }\n\n /** Stream a completion, invoking onToken for each text chunk. */\n async stream(\n system: string,\n messages: LLMMessage[],\n maxTokens: number,\n onToken?: (text: string) => void,\n ): Promise<string> {\n const stream = await this.client.chat.completions.create({\n model: this.model,\n max_tokens: maxTokens,\n messages: [{ role: \"system\", content: system }, ...messages],\n stream: true,\n });\n\n let fullText = \"\";\n for await (const chunk of stream) {\n const delta = chunk.choices[0]?.delta?.content;\n if (delta) {\n fullText += delta;\n onToken?.(delta);\n }\n }\n\n return fullText;\n }\n\n /** Call the model with tool definitions and return the parsed tool input as JSON. */\n async toolCall(\n system: string,\n messages: LLMMessage[],\n tools: LLMTool[],\n maxTokens: number,\n ): Promise<string> {\n const openaiTools = tools.map(translateToolToOpenAI);\n\n const response = await this.client.chat.completions.create({\n model: this.model,\n max_tokens: maxTokens,\n messages: [{ role: \"system\", content: system }, ...messages],\n tools: openaiTools,\n });\n\n const toolCalls = response.choices[0]?.message?.tool_calls;\n if (toolCalls && toolCalls.length > 0) {\n return toolCalls[0].function.arguments;\n }\n\n return response.choices[0]?.message?.content ?? \"\";\n }\n\n /**\n * Produce a single embedding vector via the OpenAI embeddings API.\n * Subclasses (e.g. Ollama) override embeddingModel() to pick a different model.\n */\n async embed(text: string): Promise<number[]> {\n const response = await this.embeddingsClient.embeddings.create({\n model: this.embeddingModel(),\n input: text,\n });\n\n const vector = response.data[0]?.embedding;\n if (!Array.isArray(vector)) {\n throw new Error(\"OpenAI embeddings response did not include a vector.\");\n }\n return vector;\n }\n\n /** Default embedding model for this provider. Subclasses may override. */\n protected embeddingModel(): string {\n return this.configuredEmbeddingModel ?? EMBEDDING_MODELS.openai;\n }\n}\n","/**\n * Ollama LLM provider implementation.\n *\n * Extends OpenAIProvider since Ollama exposes an OpenAI-compatible API.\n * Overrides only the constructor to set baseURL and disable API key auth.\n */\n\nimport { OpenAIProvider, readTimeoutEnv } from \"./openai.js\";\nimport { EMBEDDING_MODELS, OLLAMA_DEFAULT_TIMEOUT_MS } from \"../utils/constants.js\";\n\n/** Construction options for an Ollama-compatible provider. */\ninterface OllamaProviderOptions {\n baseURL: string;\n embeddingsBaseURL?: string;\n embeddingModel?: string;\n /**\n * Per-request timeout in milliseconds. Defaults to 30 minutes for Ollama\n * because local models on modest hardware can take much longer than the\n * cloud-OpenAI default of 10. Override with OLLAMA_TIMEOUT_MS or the\n * provider-agnostic LLMWIKI_REQUEST_TIMEOUT_MS env var.\n */\n timeoutMs?: number;\n}\n\n/** Resolve the Ollama timeout: explicit option → OLLAMA_TIMEOUT_MS → LLMWIKI_REQUEST_TIMEOUT_MS → default. */\nfunction resolveOllamaTimeoutMs(explicit?: number): number {\n return (\n explicit ??\n readTimeoutEnv(\"OLLAMA_TIMEOUT_MS\") ??\n readTimeoutEnv(\"LLMWIKI_REQUEST_TIMEOUT_MS\") ??\n OLLAMA_DEFAULT_TIMEOUT_MS\n );\n}\n\n/** Ollama-backed LLM provider using the OpenAI-compatible endpoint. */\nexport class OllamaProvider extends OpenAIProvider {\n constructor(model: string, options: OllamaProviderOptions) {\n super(model, {\n baseURL: options.baseURL,\n apiKey: \"ollama\",\n embeddingsBaseURL: options.embeddingsBaseURL,\n embeddingModel: options.embeddingModel,\n timeoutMs: resolveOllamaTimeoutMs(options.timeoutMs),\n });\n }\n\n /** Ollama ships a dedicated embedding model (nomic-embed-text). */\n protected override embeddingModel(): string {\n return this.configuredEmbeddingModel ?? EMBEDDING_MODELS.ollama;\n }\n}\n","/**\n * MiniMax LLM provider implementation.\n *\n * Extends OpenAIProvider since MiniMax exposes an OpenAI-compatible API.\n * Overrides only the constructor to set MiniMax's base URL and API key.\n */\n\nimport { OpenAIProvider } from \"./openai.js\";\n\n/** MiniMax API base URL. */\nconst MINIMAX_BASE_URL = \"https://api.minimax.io/v1\";\n\n/** MiniMax-backed LLM provider using the OpenAI-compatible endpoint. */\nexport class MiniMaxProvider extends OpenAIProvider {\n constructor(model: string, apiKey: string) {\n super(model, { baseURL: MINIMAX_BASE_URL, apiKey });\n }\n}\n","/**\n * Claude settings fallback helpers.\n *\n * Provides a narrow, read-only integration with `~/.claude/settings.json`.\n * We only read the `env` object and only extract Anthropic-related values that\n * llmwiki can safely consume. Explicit process env values remain higher priority.\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\n\nconst CLAUDE_SETTINGS_PATH_ENV = \"LLMWIKI_CLAUDE_SETTINGS_PATH\";\n\ninterface ClaudeSettingsEnv {\n ANTHROPIC_API_KEY?: string;\n ANTHROPIC_AUTH_TOKEN?: string;\n ANTHROPIC_BASE_URL?: string;\n ANTHROPIC_MODEL?: string;\n}\n\ninterface AnthropicAuthConfig {\n apiKey?: string;\n authToken?: string;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction normalize(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction resolveClaudeSettingsPath(env: NodeJS.ProcessEnv): string {\n return env[CLAUDE_SETTINGS_PATH_ENV] ?? path.join(homedir(), \".claude\", \"settings.json\");\n}\n\nfunction readClaudeSettingsFile(settingsPath: string): string | undefined {\n try {\n return readFileSync(settingsPath, \"utf8\");\n } catch (err) {\n if (isRecord(err) && err.code === \"ENOENT\") {\n return undefined;\n }\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(`Failed to read Claude settings at \"${settingsPath}\": ${message}`);\n }\n}\n\nexport function readClaudeSettingsEnv(env: NodeJS.ProcessEnv = process.env): ClaudeSettingsEnv | undefined {\n const settingsPath = resolveClaudeSettingsPath(env);\n const raw = readClaudeSettingsFile(settingsPath);\n if (!raw) return undefined;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(`Failed to parse Claude settings at \"${settingsPath}\": ${message}`);\n }\n\n if (!isRecord(parsed) || !isRecord(parsed.env)) {\n return undefined;\n }\n\n const values: ClaudeSettingsEnv = {\n ANTHROPIC_API_KEY: normalize(parsed.env.ANTHROPIC_API_KEY),\n ANTHROPIC_AUTH_TOKEN: normalize(parsed.env.ANTHROPIC_AUTH_TOKEN),\n ANTHROPIC_BASE_URL: normalize(parsed.env.ANTHROPIC_BASE_URL),\n ANTHROPIC_MODEL: normalize(parsed.env.ANTHROPIC_MODEL),\n };\n\n if (!values.ANTHROPIC_API_KEY && !values.ANTHROPIC_AUTH_TOKEN && !values.ANTHROPIC_BASE_URL && !values.ANTHROPIC_MODEL) {\n return undefined;\n }\n return values;\n}\n\nfunction tryReadClaudeSettingsEnv(env: NodeJS.ProcessEnv): ClaudeSettingsEnv | undefined {\n try {\n return readClaudeSettingsEnv(env);\n } catch {\n return undefined;\n }\n}\n\nfunction validateAnthropicBaseURL(value: string): string {\n const normalized = value.trim();\n try {\n const parsed = new URL(normalized);\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n throw new Error(\"Must use http:// or https:// protocol.\");\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Must be a valid http(s) URL.\";\n throw new Error(`Invalid ANTHROPIC_BASE_URL: \"${normalized}\". ${message}`);\n }\n return normalized;\n}\n\nexport function resolveAnthropicAuthFromEnv(env: NodeJS.ProcessEnv = process.env): AnthropicAuthConfig {\n const explicitApiKey = normalize(env.ANTHROPIC_API_KEY);\n if (explicitApiKey) return { apiKey: explicitApiKey };\n\n const explicitAuthToken = normalize(env.ANTHROPIC_AUTH_TOKEN);\n if (explicitAuthToken) return { authToken: explicitAuthToken };\n\n const fallback = readClaudeSettingsEnv(env);\n if (fallback?.ANTHROPIC_API_KEY) return { apiKey: fallback.ANTHROPIC_API_KEY };\n if (fallback?.ANTHROPIC_AUTH_TOKEN) return { authToken: fallback.ANTHROPIC_AUTH_TOKEN };\n return {};\n}\n\nexport function resolveAnthropicModelFromEnv(env: NodeJS.ProcessEnv = process.env): string | undefined {\n const explicitModel = env.LLMWIKI_MODEL;\n if (explicitModel !== undefined) return explicitModel;\n return tryReadClaudeSettingsEnv(env)?.ANTHROPIC_MODEL;\n}\n\nexport function resolveAnthropicBaseURLFromEnv(env: NodeJS.ProcessEnv = process.env): string | undefined {\n const explicitBaseURL = normalize(env.ANTHROPIC_BASE_URL);\n if (explicitBaseURL) return validateAnthropicBaseURL(explicitBaseURL);\n\n const fallbackBaseURL = tryReadClaudeSettingsEnv(env)?.ANTHROPIC_BASE_URL;\n if (!fallbackBaseURL) return undefined;\n return validateAnthropicBaseURL(fallbackBaseURL);\n}\n","/**\n * LLM provider abstraction layer.\n *\n * Defines the LLMProvider interface and a factory function that reads\n * LLMWIKI_PROVIDER and LLMWIKI_MODEL env vars to instantiate the\n * appropriate backend (Anthropic, OpenAI, Ollama, or MiniMax).\n */\n\nimport { DEFAULT_PROVIDER, PROVIDER_MODELS, OLLAMA_DEFAULT_HOST } from \"./constants.js\";\nimport { AnthropicProvider } from \"../providers/anthropic.js\";\nimport { OpenAIProvider } from \"../providers/openai.js\";\nimport { OllamaProvider } from \"../providers/ollama.js\";\nimport { MiniMaxProvider } from \"../providers/minimax.js\";\nimport {\n resolveAnthropicAuthFromEnv,\n resolveAnthropicBaseURLFromEnv,\n resolveAnthropicModelFromEnv,\n} from \"./claude-settings.js\";\n\n/** A single message in an LLM conversation. */\nexport interface LLMMessage {\n role: \"user\" | \"assistant\";\n content: string;\n}\n\n/** A tool definition in Anthropic-style format (used as the canonical shape). */\nexport interface LLMTool {\n name: string;\n description: string;\n input_schema: Record<string, unknown>;\n}\n\n/** Provider-agnostic interface for LLM backends. */\nexport interface LLMProvider {\n complete(system: string, messages: LLMMessage[], maxTokens: number): Promise<string>;\n stream(\n system: string,\n messages: LLMMessage[],\n maxTokens: number,\n onToken?: (text: string) => void,\n ): Promise<string>;\n toolCall(\n system: string,\n messages: LLMMessage[],\n tools: LLMTool[],\n maxTokens: number,\n ): Promise<string>;\n /** Return a single embedding vector for the given text. */\n embed(text: string): Promise<number[]>;\n}\n\nconst SUPPORTED_PROVIDERS: ReadonlySet<string> = new Set([\"anthropic\", \"openai\", \"ollama\", \"minimax\"]);\n\n/**\n * Factory that returns the appropriate LLMProvider based on env vars.\n * Reads LLMWIKI_PROVIDER (default \"anthropic\") and LLMWIKI_MODEL\n * (defaults per provider from PROVIDER_MODELS).\n *\n * Direct process.env access is acceptable here as this is a system boundary.\n */\nexport function getProvider(): LLMProvider {\n const providerName = getProviderName();\n\n switch (providerName) {\n case \"anthropic\":\n return getAnthropicProvider();\n case \"openai\":\n return new OpenAIProvider(getModelForProvider(\"openai\"), {\n baseURL: readOptionalEnv(\"OPENAI_BASE_URL\"),\n embeddingsBaseURL: readOptionalEnv(\"OPENAI_EMBEDDINGS_BASE_URL\"),\n embeddingModel: readOptionalEnv(\"LLMWIKI_EMBEDDING_MODEL\"),\n });\n case \"ollama\":\n return new OllamaProvider(getModelForProvider(\"ollama\"), {\n baseURL: readOptionalEnv(\"OLLAMA_HOST\") ?? OLLAMA_DEFAULT_HOST,\n embeddingsBaseURL: readOptionalEnv(\"OLLAMA_EMBEDDINGS_HOST\"),\n embeddingModel: readOptionalEnv(\"LLMWIKI_EMBEDDING_MODEL\"),\n });\n case \"minimax\":\n return getMiniMaxProvider();\n default:\n throw new Error(`Unhandled provider: ${providerName}`);\n }\n}\n\nfunction readOptionalEnv(name: string): string | undefined {\n const value = process.env[name]?.trim();\n return value ? value : undefined;\n}\n\nfunction getModelForProvider(providerName: \"openai\" | \"ollama\" | \"minimax\"): string {\n return process.env.LLMWIKI_MODEL ?? PROVIDER_MODELS[providerName];\n}\n\nfunction getMiniMaxProvider(): MiniMaxProvider {\n const apiKey = process.env.MINIMAX_API_KEY;\n if (!apiKey) {\n throw new Error(\n \"MiniMax provider requires MINIMAX_API_KEY environment variable.\\n\" +\n ' Set it with: export MINIMAX_API_KEY=your_key',\n );\n }\n return new MiniMaxProvider(getModelForProvider(\"minimax\"), apiKey);\n}\n\nfunction getAnthropicProvider(): AnthropicProvider {\n const model = resolveAnthropicModelFromEnv() ?? PROVIDER_MODELS.anthropic;\n const baseURL = resolveAnthropicBaseURLFromEnv();\n const auth = resolveAnthropicAuthFromEnv();\n\n return new AnthropicProvider(model, {\n baseURL,\n ...auth,\n });\n}\n\nfunction getProviderName(): string {\n const providerName = process.env.LLMWIKI_PROVIDER ?? DEFAULT_PROVIDER;\n if (!SUPPORTED_PROVIDERS.has(providerName)) {\n throw new Error(\n `Unknown provider \"${providerName}\". Supported: ${[...SUPPORTED_PROVIDERS].join(\", \")}`,\n );\n }\n return providerName;\n}\n\n/** Expose the resolved provider name for callers that need model lookup. */\nexport function getActiveProviderName(): string {\n return getProviderName();\n}\n","/**\n * Shared LLM helper with provider abstraction.\n *\n * Provides callClaude() for backward compatibility — delegates to the\n * active LLMProvider while preserving retry logic with exponential backoff.\n * The provider is selected via LLMWIKI_PROVIDER env var (see provider.ts).\n */\n\nimport { RETRY_COUNT, RETRY_BASE_MS, RETRY_MULTIPLIER } from \"./constants.js\";\nimport { getProvider } from \"./provider.js\";\nimport type { LLMMessage, LLMTool } from \"./provider.js\";\n\n/** Sleep for a given number of milliseconds. */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\ninterface CallClaudeOptions {\n system: string;\n messages: LLMMessage[];\n tools?: LLMTool[];\n maxTokens?: number;\n stream?: boolean;\n onToken?: (text: string) => void;\n}\n\n/**\n * Call the active LLM provider with retry logic.\n * Supports streaming, tool-use, and basic completion modes.\n * Preserves the original callClaude interface for backward compatibility.\n */\nexport async function callClaude(options: CallClaudeOptions): Promise<string> {\n const { system, messages, tools, maxTokens = 4096, stream = false, onToken } = options;\n const provider = getProvider();\n\n for (let attempt = 0; attempt <= RETRY_COUNT; attempt++) {\n try {\n if (stream) {\n return await provider.stream(system, messages, maxTokens, onToken);\n }\n\n if (tools && tools.length > 0) {\n return await provider.toolCall(system, messages, tools, maxTokens);\n }\n\n return await provider.complete(system, messages, maxTokens);\n } catch (error) {\n if (attempt === RETRY_COUNT) throw error;\n\n const delayMs = RETRY_BASE_MS * Math.pow(RETRY_MULTIPLIER, attempt);\n const errMsg = error instanceof Error ? error.message : String(error);\n console.warn(`⚠ API call failed (attempt ${attempt + 1}/${RETRY_COUNT + 1}): ${errMsg}`);\n console.warn(` Retrying in ${delayMs / 1000}s...`);\n await sleep(delayMs);\n }\n }\n\n throw new Error(\"Unreachable\");\n}\n","/**\n * PID-based lock file for preventing concurrent compilation.\n *\n * Fresh acquisition uses O_CREAT | O_EXCL (the 'wx' flag) for atomic lock\n * creation — the kernel guarantees only one process can create the file.\n *\n * Stale lock reclamation uses a two-lock protocol:\n * 1. Acquire a reclamation lock (.llmwiki/lock.reclaim) via 'wx' to serialize\n * all processes attempting to reclaim the same stale main lock.\n * 2. Re-verify the main lock is still stale (another reclaimer may have\n * already fixed it).\n * 3. unlink + tryCreateLock('wx') on the main lock — safe because we hold\n * exclusive reclamation access.\n * 4. Release the reclamation lock in a finally block.\n *\n * The reclamation lock itself can become stale if a process crashes during\n * the brief reclamation window. When that happens, acquireReclaimLock only\n * cleans up the stale file — it does NOT retry acquisition in the same call.\n * This eliminates the unlink-then-create race that would allow two processes\n * to both hold the reclaim lock. The outer retry loop in acquireLock handles\n * convergence: first pass cleans up the stale reclaim lock, second pass\n * acquires it cleanly via 'wx'.\n */\n\nimport { open, readFile, unlink, mkdir } from \"fs/promises\";\nimport path from \"path\";\nimport { LLMWIKI_DIR, LOCK_FILE } from \"./constants.js\";\nimport * as output from \"./output.js\";\n\nconst RECLAIM_SUFFIX = \".reclaim\";\nconst MAX_ACQUIRE_ATTEMPTS = 2;\n\n/** Check whether a process with the given PID is still running. */\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Acquire the compilation lock. Returns true if acquired, false if busy.\n *\n * Retries up to MAX_ACQUIRE_ATTEMPTS times to handle the case where the\n * first attempt cleans up a stale reclamation lock but cannot acquire it\n * in the same call (to avoid the double-winner race).\n */\nexport async function acquireLock(root: string): Promise<boolean> {\n const lockPath = path.join(root, LOCK_FILE);\n await mkdir(path.join(root, LLMWIKI_DIR), { recursive: true });\n\n for (let attempt = 0; attempt < MAX_ACQUIRE_ATTEMPTS; attempt++) {\n // Try atomic create — fails if file already exists\n const created = await tryCreateLock(lockPath);\n if (created) return true;\n\n // Lock exists. Check if the holding process is dead.\n const stale = await isLockStale(lockPath);\n if (!stale) {\n output.status(\"!\", output.warn(\"Another compilation is running.\"));\n return false;\n }\n\n // Stale lock — serialize reclamation via a second lock.\n const reclaimed = await reclaimStaleLock(root, lockPath);\n if (reclaimed) return true;\n\n // Reclamation failed (e.g. cleaned up stale reclaim lock). Retry.\n }\n\n output.status(\"!\", output.warn(\"Could not acquire lock after retrying.\"));\n return false;\n}\n\n/**\n * Reclaim a stale main lock using a serialized two-lock protocol.\n *\n * Acquires .llmwiki/lock.reclaim (via 'wx') so that only one process performs\n * the unlink + recreate sequence at a time. Re-verifies staleness under\n * the reclamation lock in case another process already fixed it.\n * @param root - Project root directory.\n * @param lockPath - Absolute path to the main lock file.\n */\nasync function reclaimStaleLock(root: string, lockPath: string): Promise<boolean> {\n const reclaimPath = lockPath + RECLAIM_SUFFIX;\n\n const gotReclaimLock = await acquireReclaimLock(reclaimPath);\n if (!gotReclaimLock) return false;\n\n try {\n // Re-verify under exclusive reclamation access.\n // Another reclaimer may have already fixed the main lock.\n if (!(await isLockStale(lockPath))) {\n return false;\n }\n\n // Still stale. Safe to reclaim — we're the only reclaimer.\n try { await unlink(lockPath); } catch { /* already gone */ }\n\n const acquired = await tryCreateLock(lockPath);\n if (acquired) {\n output.status(\"i\", output.dim(\"Reclaimed stale lock from dead process.\"));\n }\n return acquired;\n } finally {\n try { await unlink(reclaimPath); } catch { /* cleanup best-effort */ }\n }\n}\n\n/**\n * Acquire the reclamation lock. Uses 'wx' for atomic creation.\n *\n * If the reclaim lock is stale (holder crashed during reclamation), this\n * function ONLY cleans up the stale file and returns false. It does NOT\n * retry acquisition in the same call. This is the key safety property:\n * unlink and create never happen in the same call, so two processes that\n * both see a stale reclaim lock will both clean up (harmless — second\n * unlink gets ENOENT) and both return false. Neither holds the reclaim\n * lock, so neither proceeds to touch the main lock. The outer retry loop\n * in acquireLock converges on the next attempt via a clean 'wx'.\n * @param reclaimPath - Absolute path to the reclamation lock file.\n */\nasync function acquireReclaimLock(reclaimPath: string): Promise<boolean> {\n if (await tryCreateLock(reclaimPath)) return true;\n\n // Reclaim lock exists. If its holder is alive, back off.\n if (!(await isLockStale(reclaimPath))) return false;\n\n // Stale reclaim lock — clean it up but do NOT retry in this call.\n // Retrying here would reintroduce the unlink+create race.\n try { await unlink(reclaimPath); } catch { /* already gone */ }\n return false;\n}\n\n/**\n * Atomically create the lock file with our PID.\n * Returns true if we created it, false if it already exists.\n */\nasync function tryCreateLock(lockPath: string): Promise<boolean> {\n try {\n const fd = await open(lockPath, \"wx\");\n await fd.writeFile(String(process.pid), \"utf-8\");\n await fd.close();\n return true;\n } catch (err: unknown) {\n if (err instanceof Error && \"code\" in err && (err as NodeJS.ErrnoException).code === \"EEXIST\") {\n return false;\n }\n throw err;\n }\n}\n\n/** Check if an existing lock is stale (holding process is dead). */\nasync function isLockStale(lockPath: string): Promise<boolean> {\n try {\n const content = await readFile(lockPath, \"utf-8\");\n const pid = parseInt(content.trim(), 10);\n if (isNaN(pid)) return true;\n return !isProcessAlive(pid);\n } catch {\n return true;\n }\n}\n\n/** Release the compilation lock. Safe to call even if lock doesn't exist. */\nexport async function releaseLock(root: string): Promise<void> {\n const lockPath = path.join(root, LOCK_FILE);\n try {\n await unlink(lockPath);\n } catch {\n // Lock already removed or never existed\n }\n}\n","/**\n * LLM prompt templates and tool schemas for the compilation pipeline.\n * Contains the Anthropic tool definition for concept extraction,\n * prompt builders for both extraction and page generation phases,\n * and a parser for the structured tool output.\n */\n\nimport type {\n ContradictionRef,\n ExtractedConcept,\n ProvenanceState,\n} from \"../utils/types.js\";\nimport type { PageKindRule, SeedPage } from \"../schema/index.js\";\n\n/** Allowed provenance state strings emitted by the LLM tool schema. */\nconst PROVENANCE_STATE_VALUES: ProvenanceState[] = [\n \"extracted\",\n \"merged\",\n \"inferred\",\n \"ambiguous\",\n];\n\n/**\n * Anthropic Tool definition for extracting knowledge concepts from a source.\n * Used with callClaude's tool_use mode to get structured concept data.\n */\nexport const CONCEPT_EXTRACTION_TOOL = {\n name: \"extract_concepts\",\n description: \"Extract knowledge concepts from a source document\",\n input_schema: {\n type: \"object\" as const,\n properties: {\n concepts: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n concept: {\n type: \"string\",\n description: \"Human-readable concept title\",\n },\n summary: {\n type: \"string\",\n description: \"One-line description\",\n },\n is_new: {\n type: \"boolean\",\n description: \"True if this is a new concept not in existing wiki\",\n },\n tags: {\n type: \"array\",\n items: { type: \"string\" },\n description:\n \"2-4 categorical tags for organizing this concept (e.g., 'machine-learning', 'optimization')\",\n },\n confidence: {\n type: \"number\",\n description:\n \"Confidence in this concept on a 0..1 scale (1 = directly stated, 0 = highly speculative).\",\n },\n provenance_state: {\n type: \"string\",\n enum: PROVENANCE_STATE_VALUES,\n description:\n \"How this concept was produced: 'extracted' (direct from source), 'merged' (synthesised across sources), 'inferred' (model deduction), or 'ambiguous' (sources disagree).\",\n },\n contradicted_by: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n slug: { type: \"string\", description: \"Slug of the contradicting concept.\" },\n reason: { type: \"string\", description: \"Brief reason for the contradiction.\" },\n },\n required: [\"slug\"],\n },\n description: \"Slugs of other concepts whose evidence contradicts this one.\",\n },\n inferred_paragraphs: {\n type: \"integer\",\n description:\n \"Estimated number of paragraphs in the page that will be inferred rather than directly cited.\",\n },\n },\n required: [\"concept\", \"summary\", \"is_new\"],\n },\n },\n },\n required: [\"concepts\"],\n },\n};\n\n/**\n * Build the system prompt for the concept extraction phase.\n * Instructs the LLM to analyze a source document and identify distinct concepts.\n * @param sourceContent - The full text of the source document.\n * @param existingIndex - The current wiki index.md contents (may be empty).\n * @returns System prompt string for the extraction call.\n */\nexport function buildExtractionPrompt(\n sourceContent: string,\n existingIndex: string,\n): string {\n const indexSection = existingIndex\n ? `\\n\\nHere is the existing wiki index — avoid duplicating concepts already covered:\\n\\n${existingIndex}`\n : \"\\n\\nNo existing wiki pages yet.\";\n\n return [\n \"You are a knowledge extraction engine. Analyze the following source document\",\n \"and identify 3-8 distinct, meaningful concepts worth documenting as wiki pages.\",\n \"Each concept should be a standalone topic that someone might look up.\",\n \"Focus on key ideas, techniques, patterns, or entities — not trivial details.\",\n \"Use the extract_concepts tool to return your findings.\",\n \"\",\n \"For every concept, emit provenance metadata so downstream tools can reason\",\n \"about reliability:\",\n \" - confidence: 0..1 — how certain you are the source supports this concept.\",\n \" - provenance_state: 'extracted' if directly stated, 'merged' if synthesised\",\n \" from multiple parts of the source, 'inferred' if reasoned from context,\",\n \" or 'ambiguous' if the source is contradictory or unclear.\",\n \" - contradicted_by: slugs of other concepts (in this batch or the index)\",\n \" whose evidence conflicts with this one.\",\n \" - inferred_paragraphs: estimated number of paragraphs in the resulting\",\n \" page that will be inferred rather than directly citable.\",\n indexSection,\n \"\\n\\n--- SOURCE DOCUMENT ---\\n\\n\",\n sourceContent,\n ].join(\"\\n\");\n}\n\n/**\n * Build the system prompt for wiki page generation.\n * Instructs the LLM to write a complete wiki page for a single concept.\n * @param concept - The concept title to write about.\n * @param sourceContent - The source material to draw from.\n * @param existingPage - The current page content if updating (empty for new pages).\n * @param relatedPages - Concatenated content of related wiki pages for context.\n * @returns System prompt string for the page generation call.\n */\nexport function buildPagePrompt(\n concept: string,\n sourceContent: string,\n existingPage: string,\n relatedPages: string,\n): string {\n const existingSection = existingPage\n ? `\\n\\nExisting page to update:\\n\\n${existingPage}`\n : \"\";\n\n const relatedSection = relatedPages\n ? `\\n\\nRelated wiki pages for cross-referencing:\\n\\n${relatedPages}`\n : \"\";\n\n return [\n `You are a wiki author. Write a clear, well-structured markdown page about \"${concept}\".`,\n \"Draw facts only from the provided source material.\",\n \"Include a ## Sources section at the end listing the source document.\",\n \"Suggest [[wikilinks]] to related concepts where appropriate.\",\n \"Write in a neutral, informative tone. Be concise but thorough.\",\n \"\",\n \"Source attribution: at the end of each prose paragraph, append a citation\",\n \"marker showing which source file(s) the paragraph drew from.\",\n \"Format: ^[filename.md] for single-source, ^[source-a.md, source-b.md] for multi-source.\",\n \"When a single sentence makes a specific factual claim and you can identify the\",\n \"exact line range it came from, you may use the claim-level form\",\n \"^[filename.md:START-END] (or ^[filename.md#LSTART-LEND]) at the end of that\",\n \"sentence — START and END are 1-indexed line numbers in the source file.\",\n \"Paragraph-level citations remain the default; only switch to claim-level form\",\n \"when it materially improves verifiability and the line range is unambiguous.\",\n \"Place citations only at the end of prose paragraphs or sentences — not on\",\n \"headings, list items, or code blocks.\",\n \"Source filenames are visible as `--- SOURCE: filename.md ---` headers in the content below.\",\n \"\",\n \"If a paragraph is your inference rather than a direct extraction, leave it\",\n \"uncited — downstream lint rules will count uncited paragraphs as 'inferred'\",\n \"to compute the page's provenance metadata.\",\n existingSection,\n relatedSection,\n \"\\n\\n--- SOURCE MATERIAL ---\\n\\n\",\n sourceContent,\n ].join(\"\\n\");\n}\n\n/** Raw concept shape as it arrives from the tool JSON. */\ninterface RawConcept {\n concept: unknown;\n summary: unknown;\n is_new: unknown;\n tags?: unknown;\n confidence?: unknown;\n provenance_state?: unknown;\n contradicted_by?: unknown;\n inferred_paragraphs?: unknown;\n}\n\n/** True if the raw concept has the required string/boolean fields. */\nfunction isValidRawConcept(c: RawConcept): boolean {\n return (\n typeof c.concept === \"string\" &&\n typeof c.summary === \"string\" &&\n typeof c.is_new === \"boolean\" &&\n (c.tags === undefined || Array.isArray(c.tags))\n );\n}\n\n/** Coerce raw contradiction entries from the tool into typed refs. */\nfunction coerceContradictedBy(raw: unknown): ContradictionRef[] | undefined {\n if (!Array.isArray(raw)) return undefined;\n const refs: ContradictionRef[] = [];\n for (const entry of raw) {\n if (!entry || typeof entry !== \"object\") continue;\n const obj = entry as { slug?: unknown; reason?: unknown };\n if (typeof obj.slug !== \"string\" || obj.slug.trim().length === 0) continue;\n const ref: ContradictionRef = { slug: obj.slug.trim() };\n if (typeof obj.reason === \"string\") ref.reason = obj.reason;\n refs.push(ref);\n }\n return refs.length > 0 ? refs : undefined;\n}\n\n/** Map a validated raw concept into an ExtractedConcept. */\nfunction mapRawConcept(c: RawConcept): ExtractedConcept {\n const provenance = typeof c.provenance_state === \"string\" &&\n PROVENANCE_STATE_VALUES.includes(c.provenance_state as ProvenanceState)\n ? (c.provenance_state as ProvenanceState)\n : undefined;\n return {\n concept: c.concept as string,\n summary: c.summary as string,\n is_new: c.is_new as boolean,\n tags: Array.isArray(c.tags) ? (c.tags as string[]) : undefined,\n confidence: typeof c.confidence === \"number\" ? c.confidence : undefined,\n provenanceState: provenance,\n contradictedBy: coerceContradictedBy(c.contradicted_by),\n inferredParagraphs: typeof c.inferred_paragraphs === \"number\" &&\n Number.isInteger(c.inferred_paragraphs) && c.inferred_paragraphs >= 0\n ? c.inferred_paragraphs\n : undefined,\n };\n}\n\n/**\n * Build a system prompt for generating a seed page (overview / comparison /\n * entity) declared in the project's schema config. Seed pages weave together\n * material from related concept pages rather than from raw source files.\n * @param seed - Seed page definition pulled from the schema.\n * @param rule - Per-kind rule (used for the description and link minimum).\n * @param relatedPagesContent - Concatenated content of related concept pages.\n * @returns System prompt string for the page generation call.\n */\nexport function buildSeedPagePrompt(\n seed: SeedPage,\n rule: PageKindRule,\n relatedPagesContent: string,\n): string {\n const minLinks = rule.minWikilinks;\n const linkExpectation = minLinks > 0\n ? `Include at least ${minLinks} [[wikilinks]] to related pages.`\n : \"Use [[wikilinks]] when referencing other pages.\";\n return [\n `You are a wiki author. Write a ${seed.kind} page titled \"${seed.title}\".`,\n `Page-kind guidance: ${rule.description}`,\n `Summary line for context: ${seed.summary}`,\n \"Draw facts only from the related wiki pages provided below.\",\n linkExpectation,\n \"Write in a neutral, informative tone. Be concise but thorough.\",\n \"\\n\\n--- RELATED PAGES ---\\n\\n\",\n relatedPagesContent,\n ].join(\"\\n\");\n}\n\n/**\n * Parse the JSON tool output from concept extraction into typed objects.\n * @param toolOutput - Raw JSON string returned from the extract_concepts tool.\n * @returns Array of ExtractedConcept objects.\n */\nexport function parseConcepts(toolOutput: string): ExtractedConcept[] {\n try {\n const parsed = JSON.parse(toolOutput);\n const concepts: RawConcept[] = parsed.concepts ?? [];\n return concepts.filter(isValidRawConcept).map(mapRawConcept);\n } catch {\n return [];\n }\n}\n","/**\n * Type definitions for the wiki schema layer.\n *\n * The schema layer turns llmwiki from a flat compiler pipeline into a shaped\n * knowledge system. It declares the kinds of pages a project supports\n * (`concept`, `entity`, `comparison`, `overview`) and the cross-link\n * expectations that lint and review enforce per kind.\n *\n * Types live in their own module so that compile, lint, CLI, and tests can\n * depend on the schema vocabulary without pulling in YAML/JSON loaders.\n */\n\n/** All page kinds the schema layer recognises. */\nexport type PageKind = \"concept\" | \"entity\" | \"comparison\" | \"overview\";\n\n/** All recognised page kinds, exported for validation and CLI display. */\nexport const PAGE_KINDS: readonly PageKind[] = [\n \"concept\",\n \"entity\",\n \"comparison\",\n \"overview\",\n] as const;\n\n/** Per-kind policy: minimum cross-links and a human description used in prompts. */\nexport interface PageKindRule {\n /** Minimum number of [[wikilinks]] a page of this kind should contain. */\n minWikilinks: number;\n /** Short human-readable description; surfaced in prompts and review output. */\n description: string;\n}\n\n/** Optional declarative seed for non-concept pages the compiler can generate. */\nexport interface SeedPage {\n /** Display title; also used to derive the page slug. */\n title: string;\n /** Page kind — must be one of `PAGE_KINDS`. */\n kind: PageKind;\n /** One-line summary written into frontmatter. */\n summary: string;\n /**\n * For `overview` and `comparison` kinds, the slugs the page should weave\n * together. The compiler passes these to the LLM as the source material.\n */\n relatedSlugs?: string[];\n}\n\n/** Resolved schema config the rest of the compiler reads. */\nexport interface SchemaConfig {\n /** Schema format version. Currently always `1`. */\n version: 1;\n /** Kind assigned to pages that don't declare a kind in frontmatter. */\n defaultKind: PageKind;\n /** Per-kind rules keyed by `PageKind`. */\n kinds: Record<PageKind, PageKindRule>;\n /** Optional seed pages the compiler should materialise on each run. */\n seedPages: SeedPage[];\n /** Path the schema was loaded from, or `null` when defaults are used. */\n loadedFrom: string | null;\n}\n\n/** Raw schema file contents — every field is optional so partial files work. */\nexport interface PartialSchemaFile {\n version?: number;\n defaultKind?: string;\n kinds?: Partial<Record<string, Partial<PageKindRule>>>;\n seedPages?: Array<Partial<SeedPage>>;\n}\n","/**\n * Default schema constants.\n *\n * Projects without a schema file fall back to these defaults so the compiler\n * keeps working on day one. Every existing wiki — created before the schema\n * layer existed — is treated as a wiki of `concept` pages with no\n * cross-link minimums, preserving backward compatibility.\n */\n\nimport type { PageKind, PageKindRule, SchemaConfig } from \"./types.js\";\n\n/** Minimum cross-links per kind, chosen to match each kind's purpose. */\nconst DEFAULT_MIN_LINKS: Record<PageKind, number> = {\n concept: 0,\n entity: 1,\n comparison: 2,\n overview: 3,\n};\n\n/** Human-readable descriptions used in prompts and review output. */\nconst DEFAULT_DESCRIPTIONS: Record<PageKind, string> = {\n concept: \"A standalone idea, technique, or pattern worth documenting.\",\n entity: \"A specific thing — a person, product, organization, or named artifact.\",\n comparison: \"A side-by-side analysis weighing two or more concepts or entities.\",\n overview: \"A top-down map page that situates several concepts within a domain.\",\n};\n\n/** Build the default per-kind rule table. */\nfunction buildDefaultKindRules(): Record<PageKind, PageKindRule> {\n return {\n concept: { minWikilinks: DEFAULT_MIN_LINKS.concept, description: DEFAULT_DESCRIPTIONS.concept },\n entity: { minWikilinks: DEFAULT_MIN_LINKS.entity, description: DEFAULT_DESCRIPTIONS.entity },\n comparison: {\n minWikilinks: DEFAULT_MIN_LINKS.comparison,\n description: DEFAULT_DESCRIPTIONS.comparison,\n },\n overview: {\n minWikilinks: DEFAULT_MIN_LINKS.overview,\n description: DEFAULT_DESCRIPTIONS.overview,\n },\n };\n}\n\n/** The schema returned when no schema file exists. */\nexport function buildDefaultSchema(): SchemaConfig {\n return {\n version: 1,\n defaultKind: \"concept\",\n kinds: buildDefaultKindRules(),\n seedPages: [],\n loadedFrom: null,\n };\n}\n","/**\n * Schema config loader.\n *\n * Discovers a project's schema file from a fixed list of candidate paths,\n * parses it (JSON or YAML), and merges it onto the default schema. Missing\n * files are not an error — the compiler falls back to defaults so existing\n * projects continue to work without any migration.\n */\n\nimport { existsSync } from \"fs\";\nimport { readFile } from \"fs/promises\";\nimport path from \"path\";\nimport yaml from \"js-yaml\";\nimport type {\n PageKind,\n PageKindRule,\n PartialSchemaFile,\n SchemaConfig,\n SeedPage,\n} from \"./types.js\";\nimport { PAGE_KINDS } from \"./types.js\";\nimport { buildDefaultSchema } from \"./defaults.js\";\n\n/** Candidate schema file paths searched in priority order. */\nconst SCHEMA_CANDIDATE_PATHS = [\n \".llmwiki/schema.json\",\n \".llmwiki/schema.yaml\",\n \".llmwiki/schema.yml\",\n \"wiki/.schema.yaml\",\n \"wiki/.schema.yml\",\n];\n\n/** Find the first existing schema candidate path under `root`, or null. */\nfunction findSchemaPath(root: string): string | null {\n for (const candidate of SCHEMA_CANDIDATE_PATHS) {\n const absolute = path.join(root, candidate);\n if (existsSync(absolute)) return absolute;\n }\n return null;\n}\n\n/** Decide whether to parse the file as JSON or YAML based on its extension. */\nfunction parseSchemaFile(filePath: string, content: string): PartialSchemaFile {\n const isJson = filePath.endsWith(\".json\");\n const parsed = isJson ? JSON.parse(content) : yaml.load(content);\n if (parsed && typeof parsed === \"object\") return parsed as PartialSchemaFile;\n return {};\n}\n\n/** Type-guard checking whether a string is one of the supported page kinds. */\nfunction isPageKind(value: unknown): value is PageKind {\n return typeof value === \"string\" && (PAGE_KINDS as readonly string[]).includes(value);\n}\n\n/** Merge a single per-kind rule from the file onto the default rule. */\nfunction mergeKindRule(\n defaults: PageKindRule,\n override: Partial<PageKindRule> | undefined,\n): PageKindRule {\n if (!override) return defaults;\n const minWikilinks = typeof override.minWikilinks === \"number\"\n ? override.minWikilinks\n : defaults.minWikilinks;\n const description = typeof override.description === \"string\"\n ? override.description\n : defaults.description;\n return { minWikilinks, description };\n}\n\n/** Merge per-kind rule overrides onto the default rule table. */\nfunction mergeKinds(\n defaults: Record<PageKind, PageKindRule>,\n overrides: PartialSchemaFile[\"kinds\"],\n): Record<PageKind, PageKindRule> {\n const merged = { ...defaults };\n if (!overrides) return merged;\n\n for (const kind of PAGE_KINDS) {\n merged[kind] = mergeKindRule(defaults[kind], overrides[kind]);\n }\n return merged;\n}\n\n/** Validate and coerce a single seed page entry. Returns null when invalid. */\nfunction normalizeSeedPage(entry: Partial<SeedPage>): SeedPage | null {\n if (typeof entry.title !== \"string\" || entry.title.trim() === \"\") return null;\n if (!isPageKind(entry.kind)) return null;\n const summary = typeof entry.summary === \"string\" ? entry.summary : \"\";\n const relatedSlugs = Array.isArray(entry.relatedSlugs)\n ? entry.relatedSlugs.filter((slug): slug is string => typeof slug === \"string\")\n : undefined;\n return { title: entry.title, kind: entry.kind, summary, relatedSlugs };\n}\n\n/** Coerce raw seed page entries into validated SeedPage objects. */\nfunction normalizeSeedPages(entries: PartialSchemaFile[\"seedPages\"]): SeedPage[] {\n if (!Array.isArray(entries)) return [];\n return entries\n .map(normalizeSeedPage)\n .filter((entry): entry is SeedPage => entry !== null);\n}\n\n/** Apply a parsed partial-schema onto the defaults, returning the resolved config. */\nfunction applyOverrides(\n defaults: SchemaConfig,\n overrides: PartialSchemaFile,\n loadedFrom: string,\n): SchemaConfig {\n const defaultKind = isPageKind(overrides.defaultKind)\n ? overrides.defaultKind\n : defaults.defaultKind;\n return {\n version: 1,\n defaultKind,\n kinds: mergeKinds(defaults.kinds, overrides.kinds),\n seedPages: normalizeSeedPages(overrides.seedPages),\n loadedFrom,\n };\n}\n\n/**\n * Load the schema for `root`, falling back to defaults when no file is present.\n * Throws on parse failure so the user sees a clear error rather than a silent\n * default — silent fallback would mask real config bugs.\n * @param root - Project root directory.\n * @returns Resolved schema config.\n */\nexport async function loadSchema(root: string): Promise<SchemaConfig> {\n const defaults = buildDefaultSchema();\n const schemaPath = findSchemaPath(root);\n if (!schemaPath) return defaults;\n\n const raw = await readFile(schemaPath, \"utf-8\");\n const parsed = parseSchemaFile(schemaPath, raw);\n return applyOverrides(defaults, parsed, schemaPath);\n}\n\n/** Expose candidate paths so the CLI `schema init` command can pick one. */\nexport function defaultSchemaInitPath(root: string): string {\n return path.join(root, SCHEMA_CANDIDATE_PATHS[0]);\n}\n","/**\n * Schema helper utilities shared by compile, lint, and CLI.\n *\n * Kept separate from `loader.ts` so callers that just need to interpret a\n * page's kind or count its wikilinks don't pull the YAML/JSON parser into\n * their dependency graph.\n */\n\nimport yaml from \"js-yaml\";\nimport type { PageKind, SchemaConfig } from \"./types.js\";\nimport { PAGE_KINDS } from \"./types.js\";\n\n/** Pattern matching [[Wikilink Title]] references in markdown content. */\nconst WIKILINK_PATTERN = /\\[\\[([^\\]]+)\\]\\]/g;\n\n/**\n * Resolve a page's kind from its raw frontmatter value, falling back to the\n * schema default when no explicit kind is set or the value is invalid.\n * @param rawKind - Raw `kind` value pulled from frontmatter (untyped).\n * @param schema - Resolved schema config.\n * @returns The resolved page kind.\n */\nexport function resolvePageKind(rawKind: unknown, schema: SchemaConfig): PageKind {\n if (typeof rawKind === \"string\" && (PAGE_KINDS as readonly string[]).includes(rawKind)) {\n return rawKind as PageKind;\n }\n return schema.defaultKind;\n}\n\n/**\n * Count the [[wikilinks]] in a page body.\n * Pure function so the linter can apply per-kind minimums without re-parsing.\n * @param body - Markdown body text.\n * @returns Number of wikilink references found.\n */\nexport function countWikilinks(body: string): number {\n const matches = body.match(WIKILINK_PATTERN);\n return matches ? matches.length : 0;\n}\n\n/**\n * Serialise a schema config to YAML for `llmwiki schema init` to write to disk.\n * The `loadedFrom` field is omitted because it's a runtime-only annotation.\n * @param schema - Resolved schema config.\n * @returns YAML string suitable for writing to a schema file.\n */\nexport function serializeSchemaToYaml(schema: SchemaConfig): string {\n const serializable = {\n version: schema.version,\n defaultKind: schema.defaultKind,\n kinds: schema.kinds,\n seedPages: schema.seedPages,\n };\n return yaml.dump(serializable, { lineWidth: -1, quotingType: '\"' });\n}\n","/**\n * Semantic dependency tracking for cross-source concept sharing.\n *\n * When multiple source files contribute to the same concept, a change in one\n * source should trigger recompilation of that concept using content from ALL\n * contributing sources. This module builds a reverse index from concepts back\n * to their source files, then identifies which unchanged sources are affected\n * by changes to other sources that share concepts with them.\n *\n * Without this, if sources A and B both produce concept X and source A changes,\n * concept X would be regenerated using only source A's content — losing source\n * B's contribution entirely.\n */\n\nimport { readState, updateSourceState, writeState } from \"../utils/state.js\";\nimport { slugify } from \"../utils/markdown.js\";\nimport * as output from \"../utils/output.js\";\nimport type { WikiState, SourceChange, ExtractedConcept } from \"../utils/types.js\";\n\nexport interface ExtractionResult {\n sourceFile: string;\n sourcePath: string;\n sourceContent: string;\n concepts: ExtractedConcept[];\n}\n\n/**\n * Build a reverse map from concept slugs to the source files that produced them.\n * @param sources - The sources record from WikiState.\n * @returns Map where keys are concept slugs and values are arrays of source filenames.\n */\nfunction buildConceptToSourcesMap(\n sources: WikiState[\"sources\"],\n): Map<string, string[]> {\n const conceptMap = new Map<string, string[]>();\n\n for (const [sourceFile, entry] of Object.entries(sources)) {\n for (const slug of entry.concepts) {\n const existing = conceptMap.get(slug);\n if (existing) {\n existing.push(sourceFile);\n } else {\n conceptMap.set(slug, [sourceFile]);\n }\n }\n }\n\n return conceptMap;\n}\n\n/** Extract filenames from changes matching a given status. */\nfunction filesByStatus(\n changes: SourceChange[],\n ...statuses: SourceChange[\"status\"][]\n): Set<string> {\n const statusSet = new Set(statuses);\n return new Set(\n changes.filter((c) => statusSet.has(c.status)).map((c) => c.file),\n );\n}\n\n/**\n * Collect co-contributors for a source's concepts, skipping files in the\n * exclusion sets. Mutates `out` by adding newly discovered contributors.\n */\nfunction collectSharedContributors(\n sourceFile: string,\n state: WikiState,\n conceptMap: Map<string, string[]>,\n excludeSets: Set<string>[],\n out: Set<string>,\n): void {\n const sourceEntry = state.sources[sourceFile];\n if (!sourceEntry) return;\n\n for (const slug of sourceEntry.concepts) {\n const contributors = conceptMap.get(slug);\n if (!contributors || contributors.length < 2) continue;\n\n for (const contributor of contributors) {\n const isExcluded = excludeSets.some((s) => s.has(contributor));\n if (!isExcluded) out.add(contributor);\n }\n }\n}\n\n/**\n * Identify unchanged sources that need recompilation because they share\n * concepts with directly changed sources. This enables correct cross-source\n * concept regeneration — ensuring shared concepts are rebuilt with content\n * from ALL contributing sources.\n *\n * Deleted sources are intentionally excluded: recompiling a concept-mate of\n * a deleted source would regenerate the page from fewer sources, losing\n * content. Shared concepts from deleted sources are preserved as-is by\n * markOrphaned (which skips shared concepts).\n *\n * @param state - The current persisted WikiState.\n * @param directChanges - Changes detected by hash comparison.\n * @returns Filenames of indirectly affected sources not already in the changed list.\n */\nexport function findAffectedSources(\n state: WikiState,\n directChanges: SourceChange[],\n): string[] {\n const changedFiles = filesByStatus(directChanges, \"new\", \"changed\");\n const deletedFiles = filesByStatus(directChanges, \"deleted\");\n const conceptMap = buildConceptToSourcesMap(state.sources);\n const affected = new Set<string>();\n\n for (const changedFile of changedFiles) {\n collectSharedContributors(\n changedFile, state, conceptMap,\n [changedFiles, deletedFiles, affected],\n affected,\n );\n }\n\n return Array.from(affected);\n}\n\n/**\n * Find concept slugs that must NOT be regenerated during this compile batch.\n * A slug is \"frozen\" when it was shared between a deleted source and at least\n * one surviving source. Regenerating it would overwrite the existing page\n * (which has combined content from all prior contributors) with content from\n * only the surviving sources, silently losing the deleted source's contribution.\n * @param state - Current persisted state.\n * @param changes - All detected source changes in this batch.\n * @returns Set of concept slugs that compileSource should skip.\n */\nexport function findFrozenSlugs(\n state: WikiState,\n changes: SourceChange[],\n): Set<string> {\n // Start with persisted frozen slugs from prior batches.\n const frozen = new Set<string>(state.frozenSlugs ?? []);\n\n // Add new frozen slugs from deletions in this batch.\n const deletedFiles = changes\n .filter((c) => c.status === \"deleted\")\n .map((c) => c.file);\n\n const conceptMap = buildConceptToSourcesMap(state.sources);\n\n for (const file of deletedFiles) {\n const entry = state.sources[file];\n if (!entry) continue;\n\n for (const slug of entry.concepts) {\n const contributors = conceptMap.get(slug);\n if (contributors && contributors.length > 1) {\n frozen.add(slug);\n }\n }\n }\n\n return frozen;\n}\n\n/**\n * Unfreeze slugs that were successfully regenerated by all their current\n * contributors, then persist the remaining frozen set to state.\n * A slug is safe to unfreeze when every source that claims it in state\n * was compiled in this batch and successfully extracted it.\n */\nexport async function persistFrozenSlugs(\n root: string,\n frozenSlugs: Set<string>,\n successfulExtractions: ExtractionResult[],\n): Promise<void> {\n const currentState = await readState(root);\n const conceptMap = buildConceptToSourcesMap(currentState.sources);\n\n // Concepts successfully extracted in this batch, keyed by slug.\n const extractedBy = new Set<string>();\n for (const result of successfulExtractions) {\n if (result.concepts.length === 0) continue;\n for (const c of result.concepts) {\n extractedBy.add(slugify(c.concept));\n }\n }\n const compiledFiles = new Set(\n successfulExtractions\n .filter((r) => r.concepts.length > 0)\n .map((r) => r.sourceFile),\n );\n\n const remaining = new Set<string>();\n for (const slug of frozenSlugs) {\n const owners = conceptMap.get(slug) ?? [];\n // Unfreeze only if ALL current owners were compiled and extracted it.\n const allOwnersCompiled = owners.length > 0\n && owners.every((f) => compiledFiles.has(f))\n && extractedBy.has(slug);\n\n if (!allOwnersCompiled) remaining.add(slug);\n }\n\n const stateToSave = { ...currentState, frozenSlugs: Array.from(remaining) };\n await writeState(root, stateToSave);\n}\n\n/**\n * Collect concept slugs from extractions that were not in the source's\n * previous concept list — these are \"newly gained\" concepts that\n * findAffectedSources could not have matched pre-extraction.\n */\nfunction collectFreshSlugs(\n extractions: ExtractionResult[],\n state: WikiState,\n): Set<string> {\n const freshSlugs = new Set<string>();\n\n for (const result of extractions) {\n const oldConcepts = new Set(state.sources[result.sourceFile]?.concepts ?? []);\n for (const c of result.concepts) {\n const slug = slugify(c.concept);\n if (!oldConcepts.has(slug)) freshSlugs.add(slug);\n }\n }\n\n return freshSlugs;\n}\n\n/**\n * Find unchanged sources that own any of the given slugs, excluding files\n * present in the provided exclusion sets.\n */\nfunction findSlugOwners(\n slugs: Set<string>,\n conceptMap: Map<string, string[]>,\n excludeSets: Set<string>[],\n): string[] {\n const affected = new Set<string>();\n\n for (const slug of slugs) {\n const owners = conceptMap.get(slug);\n if (!owners) continue;\n for (const owner of owners) {\n const isExcluded = excludeSets.some((s) => s.has(owner));\n if (!isExcluded) affected.add(owner);\n }\n }\n\n return Array.from(affected);\n}\n\n/**\n * Post-extraction check for compiled sources whose freshly extracted concepts\n * overlap with unchanged sources not already in the batch. Covers two cases\n * that findAffectedSources (pre-extraction) cannot detect:\n * 1. New sources have no state entry, so their concepts are unknown.\n * 2. Changed sources may gain concepts they didn't previously have.\n * @param extractions - Results from Phase 1 extraction.\n * @param state - Current persisted state.\n * @param allChanges - Full changes array including deleted/unchanged entries.\n * @returns Filenames of unchanged sources that share concepts with compiled sources.\n */\nexport function findLateAffectedSources(\n extractions: ExtractionResult[],\n state: WikiState,\n allChanges: SourceChange[],\n): string[] {\n const compilingFiles = filesByStatus(allChanges, \"new\", \"changed\");\n const deletedFiles = filesByStatus(allChanges, \"deleted\");\n const conceptMap = buildConceptToSourcesMap(state.sources);\n const freshSlugs = collectFreshSlugs(extractions, state);\n\n return findSlugOwners(freshSlugs, conceptMap, [compilingFiles, deletedFiles]);\n}\n\n/**\n * Find concept slugs from a source that are also produced by other sources.\n * Used by markOrphaned to skip orphaning shared concepts when a source is\n * deleted — preserving combined content from prior compilations.\n * @param sourceFile - The source being checked.\n * @param state - Current persisted state.\n * @returns Set of slugs that have at least one other contributing source.\n */\nexport function findSharedConcepts(\n sourceFile: string,\n state: WikiState,\n): Set<string> {\n const shared = new Set<string>();\n const sourceEntry = state.sources[sourceFile];\n if (!sourceEntry) return shared;\n\n const conceptMap = buildConceptToSourcesMap(state.sources);\n\n for (const slug of sourceEntry.concepts) {\n const contributors = conceptMap.get(slug);\n if (contributors && contributors.length > 1) {\n shared.add(slug);\n }\n }\n\n return shared;\n}\n\n/**\n * Freeze concepts from failed extractions and persist their state with a\n * blank hash so they retry on the next compile. Preserves old concept lists\n * to keep dependency tracking intact.\n */\nexport async function freezeFailedExtractions(\n root: string,\n results: ExtractionResult[],\n frozenSlugs: Set<string>,\n): Promise<void> {\n for (const result of results) {\n if (result.concepts.length > 0) continue;\n\n output.status(\"!\", output.warn(`${result.sourceFile}: no concepts — will retry.`));\n const currentState = await readState(root);\n const oldConcepts = currentState.sources[result.sourceFile]?.concepts ?? [];\n for (const slug of oldConcepts) frozenSlugs.add(slug);\n\n await updateSourceState(root, result.sourceFile, {\n hash: \"\",\n concepts: oldConcepts,\n compiledAt: new Date().toISOString(),\n });\n }\n}\n","/**\n * Orphan management for deleted source files.\n *\n * When a source is deleted, its exclusively-owned concept pages are marked\n * orphaned (orphaned: true in frontmatter). Shared concepts are preserved\n * to avoid losing combined content from prior compilations.\n *\n * After compilation, frozen slugs (shared concepts that lost a contributor)\n * are checked against the updated state. Any that lost ALL owners are\n * orphaned as a cleanup pass.\n */\n\nimport path from \"path\";\nimport { readState, removeSourceState } from \"../utils/state.js\";\nimport {\n atomicWrite,\n safeReadFile,\n parseFrontmatter,\n} from \"../utils/markdown.js\";\nimport { findSharedConcepts } from \"./deps.js\";\nimport * as output from \"../utils/output.js\";\nimport { CONCEPTS_DIR } from \"../utils/constants.js\";\n\n/**\n * Mark wiki pages as orphaned when their source is deleted.\n * Only orphans concepts exclusively owned by the deleted source.\n * Shared concepts (contributed to by other live sources) are preserved\n * as-is to avoid losing combined content from prior compilations.\n */\nexport async function markOrphaned(\n root: string,\n sourceFile: string,\n state: Awaited<ReturnType<typeof readState>>,\n): Promise<void> {\n const sourceEntry = state.sources[sourceFile];\n if (!sourceEntry) return;\n\n const sharedSlugs = findSharedConcepts(sourceFile, state);\n\n for (const slug of sourceEntry.concepts) {\n if (sharedSlugs.has(slug)) {\n output.status(\"i\", output.dim(`Kept: ${slug}.md (shared with other sources)`));\n continue;\n }\n\n await orphanPage(root, slug, \"source deleted\");\n }\n\n await removeSourceState(root, sourceFile);\n}\n\n/**\n * Check frozen slugs against the updated state after compilation.\n * If no source still claims a frozen slug, orphan its page so it doesn't\n * linger as an untracked stale file.\n */\nexport async function orphanUnownedFrozenPages(\n root: string,\n frozenSlugs: Set<string>,\n): Promise<void> {\n const currentState = await readState(root);\n const ownedSlugs = new Set<string>();\n for (const entry of Object.values(currentState.sources)) {\n for (const slug of entry.concepts) ownedSlugs.add(slug);\n }\n\n for (const slug of frozenSlugs) {\n if (ownedSlugs.has(slug)) continue;\n await orphanPage(root, slug, \"no remaining sources\");\n }\n}\n\n/**\n * Mark a single concept page as orphaned if it exists and isn't already marked.\n * @param root - Project root directory.\n * @param slug - Concept slug to orphan.\n * @param reason - Human-readable reason for the log message.\n */\nasync function orphanPage(root: string, slug: string, reason: string): Promise<void> {\n const pagePath = path.join(root, CONCEPTS_DIR, `${slug}.md`);\n const content = await safeReadFile(pagePath);\n if (!content) return;\n\n const { meta } = parseFrontmatter(content);\n if (meta.orphaned === true) return;\n\n const updated = content.replace(\"---\\n\", \"---\\norphaned: true\\n\");\n await atomicWrite(pagePath, updated);\n output.status(\"⚠\", output.warn(`Orphaned: ${slug}.md (${reason})`));\n}\n","/**\n * Interlink resolution for wiki pages.\n *\n * Rule-based (not LLM-based) pass that scans wiki pages for concept title\n * mentions and wraps them in [[slug|Title]] wikilinks. The piped alias form\n * keeps Obsidian link resolution stable when a page's filename (slug) differs\n * from its display title.\n *\n * Complexity: O(changed * total) per incremental compile.\n * Full recompile degrades to O(total^2).\n */\n\nimport { readdir, readFile } from \"fs/promises\";\nimport path from \"path\";\nimport { existsSync } from \"fs\";\nimport { atomicWrite, parseFrontmatter } from \"../utils/markdown.js\";\nimport { CONCEPTS_DIR } from \"../utils/constants.js\";\nimport * as output from \"../utils/output.js\";\n\ninterface PageInfo {\n slug: string;\n title: string;\n filePath: string;\n}\n\n/** Build an index of all wiki page titles from the concepts directory. */\nasync function buildTitleIndex(root: string): Promise<PageInfo[]> {\n const conceptsDir = path.join(root, CONCEPTS_DIR);\n if (!existsSync(conceptsDir)) return [];\n\n const files = await readdir(conceptsDir);\n const pages: PageInfo[] = [];\n\n for (const file of files) {\n if (!file.endsWith(\".md\")) continue;\n\n const filePath = path.join(conceptsDir, file);\n const content = await readFile(filePath, \"utf-8\");\n const { meta } = parseFrontmatter(content);\n\n if (meta.title && typeof meta.title === \"string\" && !meta.orphaned) {\n pages.push({\n slug: file.replace(/\\.md$/, \"\"),\n title: meta.title,\n filePath,\n });\n }\n }\n\n return pages;\n}\n\n/** Check if a position is inside an existing [[wikilink]]. */\nfunction isInsideWikilink(text: string, position: number): boolean {\n const before = text.lastIndexOf(\"[[\", position);\n const after = text.indexOf(\"]]\", position);\n if (before === -1 || after === -1) return false;\n\n const closeBefore = text.indexOf(\"]]\", before);\n return closeBefore >= position;\n}\n\n/** Check if a position is inside a ^[...] citation marker. */\nfunction isInsideCitation(text: string, position: number): boolean {\n const before = text.lastIndexOf(\"^[\", position);\n const after = text.indexOf(\"]\", position);\n if (before === -1 || after === -1) return false;\n\n const closeBefore = text.indexOf(\"]\", before);\n return closeBefore >= position;\n}\n\n/** Check if a match is at a word boundary. */\nfunction isWordBoundary(text: string, start: number, end: number): boolean {\n const before = start === 0 || /[\\s,.:;!?()\\[\\]{}/\"']/.test(text[start - 1]);\n const after = end >= text.length || /[\\s,.:;!?()\\[\\]{}/\"']/.test(text[end]);\n return before && after;\n}\n\n/** Find all regex matches for a title in the text, returned as position spans. */\nfunction findTitleMatches(text: string, title: string): { start: number; end: number }[] {\n const escaped = title.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const regex = new RegExp(escaped, \"gi\");\n const matches: { start: number; end: number }[] = [];\n let match;\n\n while ((match = regex.exec(text)) !== null) {\n matches.push({ start: match.index, end: match.index + match[0].length });\n }\n\n return matches;\n}\n\n/** Determine whether a match position is eligible for wikilink insertion. */\nfunction isLinkablePosition(text: string, start: number, end: number): boolean {\n if (isInsideWikilink(text, start)) return false;\n if (isInsideCitation(text, start)) return false;\n return isWordBoundary(text, start, end);\n}\n\n/**\n * Add [[wikilinks]] to a page's body for any title mentions.\n * Skips already-linked text and non-word-boundary matches.\n */\nfunction addWikilinks(body: string, titles: PageInfo[], selfTitle: string): string {\n let result = body;\n const selfLower = selfTitle.toLowerCase();\n\n for (const page of titles) {\n if (page.title.toLowerCase() === selfLower) continue;\n\n const matches = findTitleMatches(result, page.title);\n\n // Process matches in reverse to preserve positions\n for (const m of matches.reverse()) {\n if (!isLinkablePosition(result, m.start, m.end)) continue;\n result = result.slice(0, m.start) + `[[${page.slug}|${page.title}]]` + result.slice(m.end);\n }\n }\n\n return result;\n}\n\n/**\n * Run interlink resolution on changed and affected pages.\n *\n * Two passes:\n * 1. Outbound: changed pages get [[wikilinks]] for any title they mention.\n * 2. Inbound: ALL pages get scanned for mentions of newly created titles.\n * This ensures existing pages link to new concepts without a full recompile.\n *\n * Complexity: O(changed * total) for outbound, O(newTitles * total) for inbound.\n */\nexport async function resolveLinks(\n root: string,\n changedSlugs: string[],\n newSlugs: string[],\n): Promise<number> {\n const titleIndex = await buildTitleIndex(root);\n if (titleIndex.length === 0) return 0;\n\n let linkCount = 0;\n\n // Pass 1: outbound links on changed pages\n linkCount += await resolveOutboundLinks(titleIndex, changedSlugs);\n\n // Pass 2: inbound links on all pages for new titles\n linkCount += await resolveInboundLinks(titleIndex, newSlugs);\n\n if (linkCount > 0) {\n output.status(\"🔗\", output.dim(`Resolved links in ${linkCount} page(s)`));\n }\n\n return linkCount;\n}\n\n/** Add outbound [[wikilinks]] to changed pages for any title they mention. */\nasync function resolveOutboundLinks(\n titleIndex: PageInfo[],\n changedSlugs: string[],\n): Promise<number> {\n let count = 0;\n\n for (const page of titleIndex) {\n if (!changedSlugs.includes(page.slug)) continue;\n const didLink = await linkPage(page, titleIndex);\n if (didLink) count++;\n }\n\n return count;\n}\n\n/** Scan ALL pages for mentions of newly created concept titles. */\nasync function resolveInboundLinks(\n titleIndex: PageInfo[],\n newSlugs: string[],\n): Promise<number> {\n if (newSlugs.length === 0) return 0;\n\n const newTitles = titleIndex.filter((p) => newSlugs.includes(p.slug));\n if (newTitles.length === 0) return 0;\n\n let count = 0;\n\n for (const page of titleIndex) {\n // Skip pages that were already processed in outbound pass\n if (newSlugs.includes(page.slug)) continue;\n\n const content = await readFile(page.filePath, \"utf-8\");\n const { body } = parseFrontmatter(content);\n const linked = addWikilinks(body, newTitles, page.title);\n\n if (linked !== body) {\n const newContent = content.replace(body, linked);\n await atomicWrite(page.filePath, newContent);\n count++;\n }\n }\n\n return count;\n}\n\n/** Add wikilinks to a single page, writing atomically if changed. */\nasync function linkPage(page: PageInfo, titleIndex: PageInfo[]): Promise<boolean> {\n const content = await readFile(page.filePath, \"utf-8\");\n const { body } = parseFrontmatter(content);\n const linked = addWikilinks(body, titleIndex, page.title);\n\n if (linked === body) return false;\n\n const newContent = content.replace(body, linked);\n await atomicWrite(page.filePath, newContent);\n return true;\n}\n","/**\n * Wiki index generator.\n *\n * Scans all concept pages in wiki/concepts/, extracts frontmatter metadata,\n * and produces wiki/index.md with a sorted list of all concepts and their\n * summaries. Used after each compilation pass.\n */\n\nimport { readdir } from \"fs/promises\";\nimport path from \"path\";\nimport { atomicWrite, safeReadFile, parseFrontmatter } from \"../utils/markdown.js\";\nimport { CONCEPTS_DIR, QUERIES_DIR, INDEX_FILE } from \"../utils/constants.js\";\nimport * as output from \"../utils/output.js\";\nimport type { PageSummary } from \"../utils/types.js\";\n\n/**\n * Generate the wiki/index.md listing all concept pages with summaries.\n * @param root - Project root directory.\n */\nexport async function generateIndex(root: string): Promise<void> {\n output.status(\"*\", output.info(\"Generating index...\"));\n\n const conceptsPath = path.join(root, CONCEPTS_DIR);\n const queriesPath = path.join(root, QUERIES_DIR);\n const concepts = await collectPageSummaries(conceptsPath);\n const queries = await collectPageSummaries(queriesPath);\n\n concepts.sort((a, b) => a.title.localeCompare(b.title));\n queries.sort((a, b) => a.title.localeCompare(b.title));\n\n const indexContent = buildIndexContent(concepts, queries);\n const indexPath = path.join(root, INDEX_FILE);\n await atomicWrite(indexPath, indexContent);\n\n const total = concepts.length + queries.length;\n output.status(\"+\", output.success(`Index updated with ${total} pages.`));\n}\n\n/** A scanned page paired with its parsed frontmatter. */\ninterface ScannedPage {\n slug: string;\n meta: Record<string, unknown>;\n}\n\n/**\n * Scan a wiki directory and return every .md page paired with its parsed\n * frontmatter. Read-only utility shared by index generation and the MCP\n * server's status tool.\n * @param dirPath - Absolute path to a wiki page directory.\n * @returns Array of {slug, meta} entries — empty when the directory is missing.\n */\nexport async function scanWikiPages(dirPath: string): Promise<ScannedPage[]> {\n let files: string[];\n try {\n files = await readdir(dirPath);\n } catch {\n return [];\n }\n\n const scanned: ScannedPage[] = [];\n for (const file of files.filter((f) => f.endsWith(\".md\"))) {\n const content = await safeReadFile(path.join(dirPath, file));\n const { meta } = parseFrontmatter(content);\n scanned.push({ slug: file.replace(/\\.md$/, \"\"), meta });\n }\n return scanned;\n}\n\n/**\n * Project a wiki directory into PageSummary entries (excludes orphaned and\n * untitled pages). Built on top of scanWikiPages so the MCP server can share\n * the underlying scan logic without re-reading the directory.\n * @param conceptsPath - Absolute path to wiki/concepts/.\n * @returns Array of page summary objects.\n */\nexport async function collectPageSummaries(\n conceptsPath: string,\n): Promise<PageSummary[]> {\n const scanned = await scanWikiPages(conceptsPath);\n return scanned\n .filter(({ meta }) => meta.title && typeof meta.title === \"string\" && !meta.orphaned)\n .map(({ slug, meta }) => ({\n title: meta.title as string,\n slug,\n summary: typeof meta.summary === \"string\" ? meta.summary : \"\",\n }));\n}\n\n/** Strip [[wikilink]] brackets from text, leaving the inner text intact. */\nfunction stripWikilinks(text: string): string {\n return text.replace(/\\[\\[([^\\]]+)\\]\\]/g, \"$1\");\n}\n\n/**\n * Build the index.md markdown content from page summaries.\n * @param pages - Sorted array of page summaries.\n * @returns Full index.md content string.\n */\nfunction buildIndexContent(concepts: PageSummary[], queries: PageSummary[]): string {\n const lines = [\"# Knowledge Wiki\", \"\", \"## Concepts\", \"\"];\n\n for (const page of concepts) {\n lines.push(`- **[[${page.slug}|${page.title}]]** — ${stripWikilinks(page.summary)}`);\n }\n\n if (queries.length > 0) {\n lines.push(\"\", \"## Saved Queries\", \"\");\n for (const page of queries) {\n lines.push(`- **[[${page.slug}|${page.title}]]** — ${stripWikilinks(page.summary)}`);\n }\n }\n\n const total = concepts.length + queries.length;\n lines.push(\"\");\n lines.push(`_${total} pages | Generated ${new Date().toISOString()}_`);\n lines.push(\"\");\n\n return lines.join(\"\\n\");\n}\n","/**\n * Obsidian integration helpers for the llmwiki knowledge compiler.\n *\n * Provides two capabilities:\n * 1. Enriching wiki page frontmatter with tags and aliases for better\n * Obsidian graph navigation and search.\n * 2. Generating a Map of Content (MOC) page that groups concept pages\n * by tag for easy browsing.\n */\n\nimport { readdir } from \"fs/promises\";\nimport path from \"path\";\nimport { slugify, atomicWrite, safeReadFile, parseFrontmatter } from \"../utils/markdown.js\";\nimport { CONCEPTS_DIR, MOC_FILE } from \"../utils/constants.js\";\n\n/** Minimum word count to generate an abbreviation alias. */\nconst ABBREVIATION_MIN_WORDS = 3;\n\n/** Conjunctions that trigger a word-swap alias. */\nconst SWAP_CONJUNCTIONS = [\" and \", \" or \"];\n\n/**\n * Enrich a frontmatter object with Obsidian-specific tags and aliases.\n * Mutates the frontmatter object in place.\n * @param frontmatter - The frontmatter object to enrich.\n * @param conceptTitle - The human-readable concept title.\n * @param tags - Tags from extraction (may be empty).\n */\nexport function addObsidianMeta(\n frontmatter: Record<string, unknown>,\n conceptTitle: string,\n tags: string[],\n): void {\n frontmatter.tags = tags;\n frontmatter.aliases = generateAliases(conceptTitle);\n}\n\n/**\n * Generate deterministic aliases from a concept title.\n * Produces up to three alias variants:\n * - Slug form (e.g., \"gradient-descent\")\n * - Word-swap around conjunctions (e.g., \"Optimization and Gradient Descent\")\n * - Abbreviation from first letters for 3+ word titles (e.g., \"RAG\")\n * @param title - The concept title to derive aliases from.\n * @returns Array of aliases that differ from the original title.\n */\nfunction generateAliases(title: string): string[] {\n const aliases: string[] = [];\n const slug = slugify(title);\n\n if (slug !== title) {\n aliases.push(slug);\n }\n\n const swapAlias = generateSwapAlias(title);\n if (swapAlias) {\n aliases.push(swapAlias);\n }\n\n const abbreviation = generateAbbreviation(title);\n if (abbreviation) {\n aliases.push(abbreviation);\n }\n\n return aliases;\n}\n\n/**\n * Generate a word-swap alias by reversing parts around a conjunction.\n * E.g., \"Gradient Descent and Optimization\" becomes \"Optimization and Gradient Descent\".\n * @param title - The concept title.\n * @returns The swapped alias, or null if no conjunction found.\n */\nfunction generateSwapAlias(title: string): string | null {\n for (const conjunction of SWAP_CONJUNCTIONS) {\n const index = title.toLowerCase().indexOf(conjunction);\n if (index === -1) continue;\n\n const before = title.slice(0, index);\n const after = title.slice(index + conjunction.length);\n const originalConjunction = title.slice(index, index + conjunction.length);\n return `${after}${originalConjunction}${before}`;\n }\n return null;\n}\n\n/**\n * Generate an abbreviation from first letters of each word for titles with 3+ words.\n * E.g., \"Retrieval Augmented Generation\" becomes \"RAG\".\n * @param title - The concept title.\n * @returns The abbreviation, or null if title has fewer than 3 words.\n */\nfunction generateAbbreviation(title: string): string | null {\n const words = title.split(/\\s+/);\n if (words.length < ABBREVIATION_MIN_WORDS) return null;\n\n const abbreviation = words.map((w) => w[0].toUpperCase()).join(\"\");\n if (abbreviation === title) return null;\n\n return abbreviation;\n}\n\n/**\n * Generate a Map of Content (MOC) page grouping concept pages by tag.\n * Reads all concept pages, extracts their tags from frontmatter, and writes\n * a structured MOC.md with sections per tag and an Uncategorized section.\n * @param root - Project root directory.\n */\nexport async function generateMOC(root: string): Promise<void> {\n const conceptsPath = path.join(root, CONCEPTS_DIR);\n const pages = await loadConceptPages(conceptsPath);\n\n const tagGroups = groupPagesByTag(pages);\n const content = buildMOCContent(tagGroups);\n\n await atomicWrite(path.join(root, MOC_FILE), content);\n}\n\n/** Minimal page info needed for MOC generation. */\ninterface PageInfo {\n slug: string;\n title: string;\n tags: string[];\n}\n\n/**\n * Load all concept pages and extract their title and tags.\n * @param conceptsPath - Absolute path to the concepts directory.\n * @returns Array of page info objects.\n */\nasync function loadConceptPages(conceptsPath: string): Promise<PageInfo[]> {\n let files: string[];\n try {\n files = await readdir(conceptsPath);\n } catch {\n return [];\n }\n\n const pages: PageInfo[] = [];\n for (const file of files) {\n if (!file.endsWith(\".md\")) continue;\n\n const content = await safeReadFile(path.join(conceptsPath, file));\n if (!content) continue;\n\n const { meta } = parseFrontmatter(content);\n if (meta.orphaned) continue;\n\n const slug = file.replace(/\\.md$/, \"\");\n const title = typeof meta.title === \"string\" ? meta.title : slug;\n const tags = Array.isArray(meta.tags) ? (meta.tags as string[]) : [];\n pages.push({ slug, title, tags });\n }\n\n return pages;\n}\n\n/**\n * Group pages by their tags into a map. Pages with no tags go under \"Uncategorized\".\n * @param pages - Array of page info objects.\n * @returns Map of tag name to array of page titles.\n */\nfunction groupPagesByTag(pages: PageInfo[]): Map<string, PageInfo[]> {\n const groups = new Map<string, PageInfo[]>();\n\n for (const page of pages) {\n if (page.tags.length === 0) {\n appendToGroup(groups, \"Uncategorized\", page);\n continue;\n }\n\n for (const tag of page.tags) {\n appendToGroup(groups, tag, page);\n }\n }\n\n return groups;\n}\n\n/** Append a page to a group, creating the group if needed. */\nfunction appendToGroup(groups: Map<string, PageInfo[]>, key: string, page: PageInfo): void {\n const existing = groups.get(key);\n if (existing) {\n existing.push(page);\n } else {\n groups.set(key, [page]);\n }\n}\n\n/**\n * Build the MOC markdown content from grouped pages.\n * @param tagGroups - Map of tag name to array of page titles.\n * @returns Complete MOC markdown string.\n */\nfunction buildMOCContent(tagGroups: Map<string, PageInfo[]>): string {\n const lines: string[] = [\"# Map of Content\", \"\"];\n\n const sortedTags = [...tagGroups.keys()].sort((a, b) => {\n // \"Uncategorized\" always goes last\n if (a === \"Uncategorized\") return 1;\n if (b === \"Uncategorized\") return -1;\n return a.localeCompare(b);\n });\n\n for (const tag of sortedTags) {\n const pages = tagGroups.get(tag) ?? [];\n lines.push(`## ${tag}`, \"\");\n for (const page of pages.sort((a, b) => a.title.localeCompare(b.title))) {\n lines.push(`- [[${page.slug}|${page.title}]]`);\n }\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\");\n}\n","/**\n * Embedding-based semantic search utilities.\n *\n * Maintains a persistent store of page embeddings in .llmwiki/embeddings.json\n * and provides cosine-similarity retrieval so the query command can narrow\n * hundreds of pages down to a small top-K before calling the selection LLM.\n *\n * The store is additive: successful embedding calls update entries; failures\n * degrade gracefully (caller falls back to full-index selection).\n */\n\nimport { readFile, readdir } from \"fs/promises\";\nimport { existsSync } from \"fs\";\nimport path from \"path\";\nimport { getProvider, getActiveProviderName } from \"./provider.js\";\nimport { atomicWrite, safeReadFile, parseFrontmatter } from \"./markdown.js\";\nimport {\n CONCEPTS_DIR,\n QUERIES_DIR,\n EMBEDDINGS_FILE,\n EMBEDDING_TOP_K,\n EMBEDDING_MODELS,\n} from \"./constants.js\";\nimport * as output from \"./output.js\";\n\n/** A single embedded page record. */\nexport interface EmbeddingEntry {\n slug: string;\n title: string;\n summary: string;\n vector: number[];\n updatedAt: string;\n}\n\n/** Root shape of .llmwiki/embeddings.json. */\nexport interface EmbeddingStore {\n version: 1;\n model: string;\n dimensions: number;\n entries: EmbeddingEntry[];\n}\n\n/** A retrievable page record on disk (concepts/ or queries/). */\ninterface PageRecord {\n slug: string;\n title: string;\n summary: string;\n}\n\n/**\n * Cosine similarity between two equal-length vectors.\n * Returns 0 when either vector has zero magnitude (safer than NaN for ranking).\n */\nexport function cosineSimilarity(a: number[], b: number[]): number {\n if (a.length !== b.length || a.length === 0) return 0;\n\n let dot = 0;\n let magA = 0;\n let magB = 0;\n for (let i = 0; i < a.length; i++) {\n dot += a[i] * b[i];\n magA += a[i] * a[i];\n magB += b[i] * b[i];\n }\n\n if (magA === 0 || magB === 0) return 0;\n return dot / (Math.sqrt(magA) * Math.sqrt(magB));\n}\n\n/** Return the top-K entries most similar to the query vector, sorted descending. */\nexport function findTopK(\n queryVec: number[],\n store: EmbeddingStore,\n k: number,\n): EmbeddingEntry[] {\n const scored = store.entries.map((entry) => ({\n entry,\n score: cosineSimilarity(queryVec, entry.vector),\n }));\n scored.sort((left, right) => right.score - left.score);\n return scored.slice(0, k).map((item) => item.entry);\n}\n\n/** Read .llmwiki/embeddings.json, returning null if it does not exist. */\nexport async function readEmbeddingStore(root: string): Promise<EmbeddingStore | null> {\n const filePath = path.join(root, EMBEDDINGS_FILE);\n if (!existsSync(filePath)) return null;\n const raw = await readFile(filePath, \"utf-8\");\n return JSON.parse(raw) as EmbeddingStore;\n}\n\n/** Atomically persist the embedding store. */\nexport async function writeEmbeddingStore(root: string, store: EmbeddingStore): Promise<void> {\n const filePath = path.join(root, EMBEDDINGS_FILE);\n await atomicWrite(filePath, JSON.stringify(store, null, 2));\n}\n\n/**\n * Embed the question, look up top-K matches, and return lightweight page records.\n * Returns [] when no store exists so callers can transparently fall back.\n */\nexport async function findRelevantPages(\n root: string,\n question: string,\n): Promise<Array<{ slug: string; title: string; summary: string }>> {\n const store = await readEmbeddingStore(root);\n if (!store || store.entries.length === 0) return [];\n const activeModel = resolveEmbeddingModel();\n if (store.model !== activeModel) {\n warnStaleEmbeddingStore(store.model, activeModel);\n return [];\n }\n\n const queryVec = await getProvider().embed(question);\n return findTopK(queryVec, store, EMBEDDING_TOP_K).map((entry) => ({\n slug: entry.slug,\n title: entry.title,\n summary: entry.summary,\n }));\n}\n\n/** Scan concepts/ and queries/ directories, returning retrievable pages. */\nasync function collectPageRecords(root: string): Promise<PageRecord[]> {\n const records: PageRecord[] = [];\n for (const dir of [CONCEPTS_DIR, QUERIES_DIR]) {\n const absDir = path.join(root, dir);\n let files: string[];\n try {\n files = await readdir(absDir);\n } catch {\n continue;\n }\n for (const file of files.filter((f) => f.endsWith(\".md\"))) {\n const content = await safeReadFile(path.join(absDir, file));\n const { meta } = parseFrontmatter(content);\n if (meta.orphaned || typeof meta.title !== \"string\") continue;\n records.push({\n slug: file.replace(/\\.md$/, \"\"),\n title: meta.title,\n summary: typeof meta.summary === \"string\" ? meta.summary : \"\",\n });\n }\n }\n return records;\n}\n\n/** Build the text that represents a page in the embedding space. */\nfunction buildEmbeddingText(record: PageRecord): string {\n return record.summary\n ? `${record.title}\\n\\n${record.summary}`\n : record.title;\n}\n\n/**\n * Embed every page in `records` whose slug appears in `slugsToEmbed`,\n * returning the new entries. Failures bubble up to the caller.\n */\nasync function embedPages(\n records: PageRecord[],\n slugsToEmbed: Set<string>,\n): Promise<EmbeddingEntry[]> {\n const provider = getProvider();\n const now = new Date().toISOString();\n const fresh: EmbeddingEntry[] = [];\n\n for (const record of records) {\n if (!slugsToEmbed.has(record.slug)) continue;\n const vector = await provider.embed(buildEmbeddingText(record));\n fresh.push({\n slug: record.slug,\n title: record.title,\n summary: record.summary,\n vector,\n updatedAt: now,\n });\n }\n return fresh;\n}\n\n/** Tracks which (stored, active) model pairs have already been warned about. */\nconst warnedStaleModels = new Set<string>();\n\n/** Warn once per (stored, active) model pair so queries stay quiet on repeat runs. */\nfunction warnStaleEmbeddingStore(storedModel: string, activeModel: string): void {\n const key = `${storedModel}→${activeModel}`;\n if (warnedStaleModels.has(key)) return;\n warnedStaleModels.add(key);\n output.status(\n \"!\",\n output.warn(\n `Embedding store was built with \"${storedModel}\" but active embedding model is \"${activeModel}\". ` +\n `Falling back to full-index selection. Run 'llmwiki compile' to rebuild embeddings.`,\n ),\n );\n}\n\n/** Test-only hook: clear the warned-pair cache so each test sees a fresh warning. */\nexport function resetStaleEmbeddingWarnings(): void {\n warnedStaleModels.clear();\n}\n\n/** Choose the active embedding model name, defaulting to anthropic's voyage model. */\nexport function resolveEmbeddingModel(): string {\n const providerName = getActiveProviderName();\n const configuredModel = process.env.LLMWIKI_EMBEDDING_MODEL?.trim();\n if (configuredModel && (providerName === \"openai\" || providerName === \"ollama\")) {\n return configuredModel;\n }\n return EMBEDDING_MODELS[providerName] ?? EMBEDDING_MODELS.anthropic;\n}\n\n/** Merge fresh embeddings into an existing store, dropping slugs not in liveSlugs. */\nfunction mergeEntries(\n existing: EmbeddingEntry[],\n fresh: EmbeddingEntry[],\n liveSlugs: Set<string>,\n): EmbeddingEntry[] {\n const bySlug = new Map<string, EmbeddingEntry>();\n for (const entry of existing) {\n if (liveSlugs.has(entry.slug)) bySlug.set(entry.slug, entry);\n }\n for (const entry of fresh) {\n bySlug.set(entry.slug, entry);\n }\n return Array.from(bySlug.values());\n}\n\n/**\n * Re-embed the given changed slugs and prune any entries whose pages no longer\n * exist on disk. Changed slugs not present as live pages are silently skipped.\n */\nexport async function updateEmbeddings(root: string, changedSlugs: string[]): Promise<void> {\n const records = await collectPageRecords(root);\n const liveSlugs = new Set(records.map((r) => r.slug));\n const embeddingModel = resolveEmbeddingModel();\n const existingStore = await readEmbeddingStore(root);\n const modelChanged = Boolean(existingStore && existingStore.model !== embeddingModel);\n const toEmbed = new Set(changedSlugs.filter((slug) => liveSlugs.has(slug)));\n const previousEntries = modelChanged ? [] : existingStore?.entries ?? [];\n\n // Cold start: embed every page so the store is immediately useful.\n if (!existingStore || modelChanged) {\n for (const record of records) toEmbed.add(record.slug);\n }\n\n if (!modelChanged && toEmbed.size === 0 && previousEntries.every((e) => liveSlugs.has(e.slug))) {\n return;\n }\n\n const freshEntries = await embedPages(records, toEmbed);\n const mergedEntries = mergeEntries(previousEntries, freshEntries, liveSlugs);\n\n const dimensions = mergedEntries[0]?.vector.length ?? 0;\n const store: EmbeddingStore = {\n version: 1,\n model: embeddingModel,\n dimensions,\n entries: mergedEntries,\n };\n await writeEmbeddingStore(root, store);\n output.status(\"*\", output.dim(`Embeddings updated (${mergedEntries.length} pages).`));\n}\n","/**\n * Review candidate persistence for the llmwiki compile pipeline.\n *\n * When `llmwiki compile --review` runs, generated wiki pages are routed\n * here as JSON candidate records under `.llmwiki/candidates/` instead of\n * being written directly to `wiki/`. Reviewers then approve or reject the\n * proposals via the `llmwiki review` subcommands.\n *\n * Candidates are deliberately kept as standalone JSON so they survive across\n * compile runs and can be inspected manually without the CLI. Each record\n * stores the full page body so approval is a pure copy — the LLM is never\n * called again at approval time.\n */\n\nimport { readdir, rename, unlink, writeFile, mkdir } from \"fs/promises\";\nimport { existsSync } from \"fs\";\nimport path from \"path\";\nimport { randomBytes } from \"crypto\";\nimport { atomicWrite, safeReadFile } from \"../utils/markdown.js\";\nimport * as output from \"../utils/output.js\";\nimport {\n CANDIDATES_DIR,\n CANDIDATES_ARCHIVE_DIR,\n} from \"../utils/constants.js\";\nimport type { ReviewCandidate, SourceState } from \"../utils/types.js\";\nimport type { LintResult } from \"../linter/types.js\";\n\n/** Length (bytes) of the random suffix appended to candidate ids. */\nconst ID_SUFFIX_BYTES = 4;\n\n/** Filesystem extension used for candidate JSON files. */\nconst CANDIDATE_EXT = \".json\";\n\n/** Input shape for creating a new candidate (id + timestamp generated here). */\ninterface CandidateDraft {\n title: string;\n slug: string;\n summary: string;\n sources: string[];\n body: string;\n /**\n * Per-source state entries to persist into `.llmwiki/state.json` when this\n * candidate is approved. Keyed by source filename. Optional so callers that\n * never need incremental tracking (legacy / tests) can omit it.\n */\n sourceStates?: Record<string, SourceState>;\n /**\n * Schema lint violations for the candidate body detected at compile time.\n * Omit (or pass `undefined`) when the candidate body is clean.\n */\n schemaViolations?: LintResult[];\n}\n\n/** Build a deterministic-but-unique id from a slug and a short random suffix. */\nfunction buildCandidateId(slug: string): string {\n const suffix = randomBytes(ID_SUFFIX_BYTES).toString(\"hex\");\n return `${slug}-${suffix}`;\n}\n\n/** Absolute path to a candidate's JSON file. */\nfunction candidatePath(root: string, id: string): string {\n return path.join(root, CANDIDATES_DIR, `${id}${CANDIDATE_EXT}`);\n}\n\n/** Absolute path to the archived JSON file for a rejected candidate. */\nfunction archivePath(root: string, id: string): string {\n return path.join(root, CANDIDATES_ARCHIVE_DIR, `${id}${CANDIDATE_EXT}`);\n}\n\n/**\n * Persist a new candidate record and return it. The id is generated from the\n * slug plus a short random suffix so multiple compile runs can co-exist.\n * @param root - Project root directory.\n * @param draft - The candidate fields to persist.\n * @returns The full ReviewCandidate (with id + generatedAt populated).\n */\nexport async function writeCandidate(\n root: string,\n draft: CandidateDraft,\n): Promise<ReviewCandidate> {\n const candidate: ReviewCandidate = {\n id: buildCandidateId(draft.slug),\n title: draft.title,\n slug: draft.slug,\n summary: draft.summary,\n sources: draft.sources,\n body: draft.body,\n generatedAt: new Date().toISOString(),\n ...(draft.sourceStates ? { sourceStates: draft.sourceStates } : {}),\n ...(draft.schemaViolations ? { schemaViolations: draft.schemaViolations } : {}),\n };\n\n await atomicWrite(candidatePath(root, candidate.id), JSON.stringify(candidate, null, 2));\n return candidate;\n}\n\n/**\n * Emit a CLI error, set exit code 1, and return null. Used by candidate load\n * helpers to avoid duplicating the error-path boilerplate.\n * @param message - Error message to display.\n */\nfunction failWithError(message: string): null {\n output.status(\"!\", output.error(message));\n process.exitCode = 1;\n return null;\n}\n\n/**\n * Load a candidate by id and, if missing, emit the standard \"not found\" CLI\n * error and set process.exitCode = 1. Returns null when the candidate is\n * missing so callers can early-return without re-implementing the same\n * error block in every review subcommand.\n * @param root - Project root directory.\n * @param id - Candidate id to look up.\n */\nexport async function loadCandidateOrFail(\n root: string,\n id: string,\n): Promise<ReviewCandidate | null> {\n const candidate = await readCandidate(root, id);\n if (!candidate) return failWithError(`Candidate not found: ${id}`);\n return candidate;\n}\n\n/**\n * Re-read a candidate under the lock and abort if it has disappeared.\n *\n * This is the authoritative TOCTOU guard: a concurrent approve or reject may\n * have removed the candidate after the pre-lock fast-fail but before the lock\n * was acquired. Returning `null` signals the caller to abort without writing\n * any output artefact.\n * @param root - Project root directory.\n * @param id - Candidate id to load.\n * @returns The candidate if still present, or `null` after setting exit code 1.\n */\nexport async function loadCandidateUnderLockOrFail(\n root: string,\n id: string,\n): Promise<ReviewCandidate | null> {\n const candidate = await readCandidate(root, id);\n if (!candidate) {\n return failWithError(`Candidate ${id} was removed by another process during review.`);\n }\n return candidate;\n}\n\n/** Parse a single candidate JSON file. Returns null when the file is missing or malformed. */\nexport async function readCandidate(\n root: string,\n id: string,\n): Promise<ReviewCandidate | null> {\n const raw = await safeReadFile(candidatePath(root, id));\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as ReviewCandidate;\n if (!isValidCandidate(parsed)) return null;\n return parsed;\n } catch {\n return null;\n }\n}\n\n/** Defensive type-guard so corrupted candidate files don't blow up the CLI. */\nfunction isValidCandidate(value: unknown): value is ReviewCandidate {\n if (!value || typeof value !== \"object\") return false;\n const candidate = value as Record<string, unknown>;\n return (\n typeof candidate.id === \"string\" &&\n typeof candidate.title === \"string\" &&\n typeof candidate.slug === \"string\" &&\n typeof candidate.body === \"string\" &&\n Array.isArray(candidate.sources)\n );\n}\n\n/**\n * List every candidate currently pending review, sorted by generation time.\n * Skips files that aren't candidate JSON (e.g. the archive subdirectory).\n * @param root - Project root directory.\n * @returns All pending review candidates.\n */\nexport async function listCandidates(root: string): Promise<ReviewCandidate[]> {\n const dir = path.join(root, CANDIDATES_DIR);\n if (!existsSync(dir)) return [];\n\n const entries = await readdir(dir, { withFileTypes: true });\n const candidates: ReviewCandidate[] = [];\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith(CANDIDATE_EXT)) continue;\n const id = entry.name.slice(0, -CANDIDATE_EXT.length);\n const candidate = await readCandidate(root, id);\n if (candidate) candidates.push(candidate);\n }\n\n candidates.sort((a, b) => a.generatedAt.localeCompare(b.generatedAt));\n return candidates;\n}\n\n/**\n * Count pending candidates using the same validity filter as listCandidates,\n * so consumers (e.g. `wiki_status.pendingCandidates`) never report counts\n * that disagree with what `review list` actually shows. Malformed JSON files\n * are skipped here exactly as they are by listCandidates.\n */\nexport async function countCandidates(root: string): Promise<number> {\n const candidates = await listCandidates(root);\n return candidates.length;\n}\n\n/** Remove a pending candidate from disk. Returns false when nothing existed to remove. */\nexport async function deleteCandidate(root: string, id: string): Promise<boolean> {\n const filePath = candidatePath(root, id);\n if (!existsSync(filePath)) return false;\n await unlink(filePath);\n return true;\n}\n\n/**\n * Move a candidate from the pending area into the archive subdirectory so\n * rejected proposals stay auditable without touching `wiki/`.\n * @param root - Project root directory.\n * @param id - Candidate id to archive.\n * @returns True when the candidate was found and archived.\n */\nexport async function archiveCandidate(root: string, id: string): Promise<boolean> {\n const sourcePath = candidatePath(root, id);\n if (!existsSync(sourcePath)) return false;\n\n const target = archivePath(root, id);\n await mkdir(path.dirname(target), { recursive: true });\n // Copy via writeFile + unlink to support cross-filesystem rename failures.\n try {\n await rename(sourcePath, target);\n } catch {\n const raw = await safeReadFile(sourcePath);\n await writeFile(target, raw, \"utf-8\");\n await unlink(sourcePath);\n }\n return true;\n}\n","/**\n * Lint rules for wiki quality checks.\n *\n * Each rule is a function that takes a project root path and returns\n * an array of LintResult diagnostics. Rules perform pure static analysis\n * with no LLM calls — they inspect frontmatter, wikilinks, citations,\n * and file structure to find potential issues.\n */\n\nimport { readdir, readFile } from \"fs/promises\";\nimport { existsSync } from \"fs\";\nimport path from \"path\";\nimport {\n isMalformedCitationEntry,\n parseFrontmatter,\n parseProvenanceMetadata,\n safeReadFile,\n slugify,\n} from \"../utils/markdown.js\";\nimport {\n CONCEPTS_DIR,\n LOW_CONFIDENCE_THRESHOLD,\n MAX_INFERRED_PARAGRAPHS_WITHOUT_CITATIONS,\n QUERIES_DIR,\n SOURCES_DIR,\n} from \"../utils/constants.js\";\nimport type { LintResult } from \"./types.js\";\nimport {\n countWikilinks,\n resolvePageKind,\n type SchemaConfig,\n} from \"../schema/index.js\";\n\n/** Minimum body length (in characters) for a page to be considered non-empty. */\nconst MIN_BODY_LENGTH = 50;\n\n/** Pattern matching [[Wikilink Title]] references in markdown content. */\nconst WIKILINK_PATTERN = /\\[\\[([^\\]]+)\\]\\]/g;\n\n/** Pattern matching ^[filename.md] citation markers in markdown content. */\nconst CITATION_PATTERN = /\\^\\[([^\\]]+)\\]/g;\n\n/** Match result with its line number and captured group. */\ninterface LineMatch {\n captured: string;\n line: number;\n}\n\n/**\n * Scan all lines of a page's content and return regex matches with line numbers.\n * Shared by rules that need to locate patterns within page bodies.\n */\nfunction findMatchesInContent(content: string, pattern: RegExp): LineMatch[] {\n const results: LineMatch[] = [];\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const matches = lines[i].matchAll(pattern);\n for (const match of matches) {\n results.push({ captured: match[1], line: i + 1 });\n }\n }\n return results;\n}\n\n/**\n * Read all .md files from a directory, returning their paths and parsed content.\n * Returns an empty array if the directory does not exist.\n */\nasync function readMarkdownFiles(\n dirPath: string,\n): Promise<Array<{ filePath: string; content: string }>> {\n if (!existsSync(dirPath)) return [];\n\n const entries = await readdir(dirPath);\n const mdFiles = entries.filter((f) => f.endsWith(\".md\"));\n\n const results = await Promise.all(\n mdFiles.map(async (fileName) => {\n const filePath = path.join(dirPath, fileName);\n const content = await readFile(filePath, \"utf-8\");\n return { filePath, content };\n }),\n );\n\n return results;\n}\n\n/**\n * Collect all wiki pages from both concepts/ and queries/ directories.\n */\nasync function collectAllPages(\n root: string,\n): Promise<Array<{ filePath: string; content: string }>> {\n const conceptPages = await readMarkdownFiles(path.join(root, CONCEPTS_DIR));\n const queryPages = await readMarkdownFiles(path.join(root, QUERIES_DIR));\n return [...conceptPages, ...queryPages];\n}\n\n/**\n * Build a set of slugs for all existing wiki pages.\n * Used to verify that wikilink targets actually exist.\n */\nfunction buildPageSlugSet(\n pages: Array<{ filePath: string }>,\n): Set<string> {\n const slugs = new Set<string>();\n for (const page of pages) {\n const baseName = path.basename(page.filePath, \".md\");\n slugs.add(baseName.toLowerCase());\n }\n return slugs;\n}\n\n/** Find [[Title]] wikilinks that don't match any existing wiki page. */\nexport async function checkBrokenWikilinks(root: string): Promise<LintResult[]> {\n const pages = await collectAllPages(root);\n const existingSlugs = buildPageSlugSet(pages);\n const results: LintResult[] = [];\n\n for (const page of pages) {\n for (const { captured, line } of findMatchesInContent(page.content, WIKILINK_PATTERN)) {\n const linkSlug = slugify(captured);\n if (!existingSlugs.has(linkSlug)) {\n results.push({\n rule: \"broken-wikilink\",\n severity: \"error\",\n file: page.filePath,\n message: `Broken wikilink [[${captured}]] — no matching page found`,\n line,\n });\n }\n }\n }\n\n return results;\n}\n\n/** Find pages with `orphaned: true` in their frontmatter. */\nexport async function checkOrphanedPages(root: string): Promise<LintResult[]> {\n const pages = await collectAllPages(root);\n const results: LintResult[] = [];\n\n for (const page of pages) {\n const { meta } = parseFrontmatter(page.content);\n if (meta.orphaned === true) {\n results.push({\n rule: \"orphaned-page\",\n severity: \"warning\",\n file: page.filePath,\n message: `Page is marked as orphaned`,\n });\n }\n }\n\n return results;\n}\n\n/** Find pages with empty or missing `summary` in frontmatter. */\nexport async function checkMissingSummaries(root: string): Promise<LintResult[]> {\n const pages = await collectAllPages(root);\n const results: LintResult[] = [];\n\n for (const page of pages) {\n const { meta } = parseFrontmatter(page.content);\n const summary = meta.summary;\n const isMissing = !summary || (typeof summary === \"string\" && summary.trim() === \"\");\n\n if (isMissing) {\n results.push({\n rule: \"missing-summary\",\n severity: \"warning\",\n file: page.filePath,\n message: `Page has no summary in frontmatter`,\n });\n }\n }\n\n return results;\n}\n\n/** Find multiple pages whose titles match case-insensitively. */\nexport async function checkDuplicateConcepts(root: string): Promise<LintResult[]> {\n const pages = await collectAllPages(root);\n const titleMap = new Map<string, string[]>();\n\n for (const page of pages) {\n const { meta } = parseFrontmatter(page.content);\n const title = typeof meta.title === \"string\" ? meta.title : \"\";\n if (!title) continue;\n\n const normalizedTitle = title.toLowerCase().trim();\n const existing = titleMap.get(normalizedTitle) ?? [];\n existing.push(page.filePath);\n titleMap.set(normalizedTitle, existing);\n }\n\n const results: LintResult[] = [];\n for (const [title, files] of titleMap) {\n if (files.length <= 1) continue;\n for (const file of files) {\n results.push({\n rule: \"duplicate-concept\",\n severity: \"error\",\n file,\n message: `Duplicate title \"${title}\" — also in ${files.filter((f) => f !== file).join(\", \")}`,\n });\n }\n }\n\n return results;\n}\n\n/** Find pages with frontmatter but very short or empty body content. */\nexport async function checkEmptyPages(root: string): Promise<LintResult[]> {\n const pages = await collectAllPages(root);\n const results: LintResult[] = [];\n\n for (const page of pages) {\n const { meta, body } = parseFrontmatter(page.content);\n const hasTitle = typeof meta.title === \"string\" && meta.title.trim() !== \"\";\n const isBodyEmpty = body.trim().length < MIN_BODY_LENGTH;\n\n if (hasTitle && isBodyEmpty) {\n results.push({\n rule: \"empty-page\",\n severity: \"warning\",\n file: page.filePath,\n message: `Page body is empty or too short (< ${MIN_BODY_LENGTH} chars)`,\n });\n }\n }\n\n return results;\n}\n\n/** Strip an optional `:start-end` or `#Lstart-Lend` span suffix from a citation entry. */\nfunction stripSpanSuffix(entry: string): string {\n const colonIdx = entry.indexOf(\":\");\n const hashIdx = entry.indexOf(\"#\");\n const cuts = [colonIdx, hashIdx].filter((i) => i >= 0);\n if (cuts.length === 0) return entry;\n return entry.slice(0, Math.min(...cuts));\n}\n\n/**\n * Flag pages whose frontmatter declares confidence below the threshold.\n * Pages without a confidence field are silently skipped to preserve\n * backward-compatibility with pre-existing wikis.\n */\nexport async function checkLowConfidencePages(root: string): Promise<LintResult[]> {\n const pages = await collectAllPages(root);\n const results: LintResult[] = [];\n\n for (const page of pages) {\n const { meta } = parseFrontmatter(page.content);\n const { confidence } = parseProvenanceMetadata(meta);\n if (confidence === undefined || confidence >= LOW_CONFIDENCE_THRESHOLD) continue;\n results.push({\n rule: \"low-confidence\",\n severity: \"warning\",\n file: page.filePath,\n message: `Page confidence ${confidence.toFixed(2)} is below ${LOW_CONFIDENCE_THRESHOLD}`,\n });\n }\n\n return results;\n}\n\n/** Flag pages whose frontmatter records contradictions with other pages. */\nexport async function checkContradictedPages(root: string): Promise<LintResult[]> {\n const pages = await collectAllPages(root);\n const results: LintResult[] = [];\n\n for (const page of pages) {\n const { meta } = parseFrontmatter(page.content);\n const { contradictedBy } = parseProvenanceMetadata(meta);\n if (!contradictedBy || contradictedBy.length === 0) continue;\n const slugs = contradictedBy.map((r) => r.slug).join(\", \");\n results.push({\n rule: \"contradicted-page\",\n severity: \"warning\",\n file: page.filePath,\n message: `Page contradicts: ${slugs}`,\n });\n }\n\n return results;\n}\n\n/**\n * Flag pages with too many inferred paragraphs unsupported by direct citations.\n * Uses the metadata-reported count when present and falls back to counting\n * uncited prose paragraphs in the body.\n */\nexport async function checkInferredWithoutCitations(root: string): Promise<LintResult[]> {\n const pages = await collectAllPages(root);\n const results: LintResult[] = [];\n\n for (const page of pages) {\n const { meta, body } = parseFrontmatter(page.content);\n const provenance = parseProvenanceMetadata(meta);\n const inferred = provenance.inferredParagraphs ?? countUncitedProseParagraphs(body);\n if (inferred <= MAX_INFERRED_PARAGRAPHS_WITHOUT_CITATIONS) continue;\n results.push({\n rule: \"excess-inferred-paragraphs\",\n severity: \"warning\",\n file: page.filePath,\n message: `Page has ${inferred} inferred paragraphs without citations (max ${MAX_INFERRED_PARAGRAPHS_WITHOUT_CITATIONS})`,\n });\n }\n\n return results;\n}\n\n/** Match a paragraph that looks like prose (not a heading, list, or code block). */\nconst PROSE_PARAGRAPH_LEAD = /^[A-Za-z]/;\n\n/** Count prose paragraphs in a body that lack a ^[citation] marker. */\nfunction countUncitedProseParagraphs(body: string): number {\n const paragraphs = body.split(/\\n\\s*\\n/);\n let count = 0;\n for (const block of paragraphs) {\n const trimmed = block.trim();\n if (trimmed.length === 0) continue;\n if (!PROSE_PARAGRAPH_LEAD.test(trimmed)) continue;\n if (CITATION_PATTERN.test(trimmed)) {\n CITATION_PATTERN.lastIndex = 0;\n continue;\n }\n CITATION_PATTERN.lastIndex = 0;\n count += 1;\n }\n return count;\n}\n\n/** Regex matching the `:start-end` span suffix on a citation entry. */\nconst COLON_SPAN_PATTERN = /^[^:#]+:(\\d+)(?:-(\\d+))?$/;\n\n/** Regex matching the `#Lstart-Lend` span suffix on a citation entry. */\nconst HASH_SPAN_PATTERN = /^[^:#]+#L(\\d+)(?:-L(\\d+))?$/;\n\n/** Parsed line range from a citation entry, or null if no range is present. */\ninterface ParsedLineRange {\n start: number;\n end: number;\n}\n\n/**\n * Enforce per-kind cross-link minimums declared in the schema.\n * For each page, resolve its kind, look up the rule, and warn when the page\n * body has fewer wikilinks than the rule requires. Pages with kind `concept`\n * and a minimum of 0 (the default) generate no diagnostics, so existing\n * projects without a schema file see no behaviour change.\n * @param root - Project root directory.\n * @param schema - Resolved schema config.\n */\nexport async function checkSchemaCrossLinks(\n root: string,\n schema: SchemaConfig,\n): Promise<LintResult[]> {\n const pages = await collectAllPages(root);\n const results: LintResult[] = [];\n\n for (const page of pages) {\n const { meta, body } = parseFrontmatter(page.content);\n const kind = resolvePageKind(meta.kind, schema);\n const rule = schema.kinds[kind];\n if (rule.minWikilinks <= 0) continue;\n\n const linkCount = countWikilinks(body);\n if (linkCount >= rule.minWikilinks) continue;\n\n results.push({\n rule: \"schema-cross-link-minimum\",\n severity: \"warning\",\n file: page.filePath,\n message:\n `Page kind \"${kind}\" requires at least ${rule.minWikilinks} ` +\n `[[wikilinks]] but only ${linkCount} found.`,\n });\n }\n\n return results;\n}\n\n/**\n * Check cross-link minimums for a single page given as a raw content string.\n *\n * Unlike `checkSchemaCrossLinks`, this function operates on content already in\n * memory without reading from disk. Used by the review pipeline to attach\n * schema violations to a candidate at write time so `review show` can surface\n * them before the reviewer approves the page.\n *\n * The `filePath` parameter is embedded verbatim in each `LintResult.file` so\n * callers control how the candidate is identified in diagnostic output.\n *\n * @param content - Full page content including frontmatter.\n * @param filePath - Logical file path to embed in diagnostics (may be virtual).\n * @param schema - Resolved schema config.\n * @returns Lint results for this single page, empty when no violations found.\n */\nexport function checkPageCrossLinks(\n content: string,\n filePath: string,\n schema: SchemaConfig,\n): LintResult[] {\n const { meta, body } = parseFrontmatter(content);\n const kind = resolvePageKind(meta.kind, schema);\n const rule = schema.kinds[kind];\n if (rule.minWikilinks <= 0) return [];\n\n const linkCount = countWikilinks(body);\n if (linkCount >= rule.minWikilinks) return [];\n\n return [\n {\n rule: \"schema-cross-link-minimum\",\n severity: \"warning\",\n file: filePath,\n message:\n `Page kind \"${kind}\" requires at least ${rule.minWikilinks} ` +\n `[[wikilinks]] but only ${linkCount} found.`,\n },\n ];\n}\n\n/** Extract the line range from a citation entry string, or return null if there is none. */\nfunction parseLineRange(entry: string): ParsedLineRange | null {\n const colonMatch = COLON_SPAN_PATTERN.exec(entry);\n if (colonMatch) {\n const start = Number(colonMatch[1]);\n const end = colonMatch[2] !== undefined ? Number(colonMatch[2]) : start;\n return { start, end };\n }\n const hashMatch = HASH_SPAN_PATTERN.exec(entry);\n if (hashMatch) {\n const start = Number(hashMatch[1]);\n const end = hashMatch[2] !== undefined ? Number(hashMatch[2]) : start;\n return { start, end };\n }\n return null;\n}\n\n/** Count the number of lines in a file's text content. */\nfunction countLines(content: string): number {\n if (content.length === 0) return 0;\n return content.split(\"\\n\").length;\n}\n\n/**\n * Find ^[filename.md] citations referencing source files that don't exist, and\n * flag claim-level spans whose line ranges exceed the source file's actual length.\n * Handles both single-source ^[file.md] and multi-source ^[a.md, b.md] forms,\n * plus the claim-level extension `^[file.md:42-58]` / `^[file.md#L42-L58]`.\n * Line counts are cached per source file to avoid redundant reads.\n */\nexport async function checkBrokenCitations(root: string): Promise<LintResult[]> {\n const pages = await collectAllPages(root);\n const sourcesDir = path.join(root, SOURCES_DIR);\n const results: LintResult[] = [];\n /** Cache of source filename → line count to avoid repeated reads. */\n const lineCountCache = new Map<string, number>();\n\n for (const page of pages) {\n for (const { captured, line } of findMatchesInContent(page.content, CITATION_PATTERN)) {\n await collectBrokenForMarker(captured, line, page.filePath, sourcesDir, lineCountCache, results);\n }\n }\n\n return results;\n}\n\n/** Append broken-citation diagnostics for every entry inside a single ^[...] marker. */\nasync function collectBrokenForMarker(\n captured: string,\n line: number,\n pageFile: string,\n sourcesDir: string,\n lineCountCache: Map<string, number>,\n out: LintResult[],\n): Promise<void> {\n for (const part of captured.split(\",\")) {\n const trimmed = part.trim();\n if (trimmed.length === 0) continue;\n const filename = stripSpanSuffix(trimmed);\n const citedPath = path.join(sourcesDir, filename);\n if (!existsSync(citedPath)) {\n out.push({\n rule: \"broken-citation\",\n severity: \"error\",\n file: pageFile,\n message: `Broken citation ^[${filename}] — source file not found`,\n line,\n });\n continue;\n }\n const range = parseLineRange(trimmed);\n if (range === null) continue;\n const lineCount = await resolveLineCount(citedPath, filename, lineCountCache);\n if (range.end <= lineCount) continue;\n out.push({\n rule: \"broken-citation\",\n severity: \"error\",\n file: pageFile,\n message: `Claim-level span ^[${trimmed}] is out of bounds (source has only ${lineCount} lines)`,\n line,\n });\n }\n}\n\n/** Return the line count for a source file, reading and caching if necessary. */\nasync function resolveLineCount(\n citedPath: string,\n filename: string,\n cache: Map<string, number>,\n): Promise<number> {\n const cached = cache.get(filename);\n if (cached !== undefined) return cached;\n const content = await safeReadFile(citedPath);\n const lineCount = countLines(content);\n cache.set(filename, lineCount);\n return lineCount;\n}\n\n/**\n * Find ^[...] markers whose entries do not parse against the documented\n * paragraph or claim-level grammar (e.g. `^[file.md:abc]` or `^[file.md#X]`).\n * Detects malformed claim-level citations without breaking the paragraph form.\n */\nexport async function checkMalformedClaimCitations(root: string): Promise<LintResult[]> {\n const pages = await collectAllPages(root);\n const results: LintResult[] = [];\n\n for (const page of pages) {\n for (const { captured, line } of findMatchesInContent(page.content, CITATION_PATTERN)) {\n for (const part of captured.split(\",\")) {\n if (!isMalformedCitationEntry(part)) continue;\n results.push({\n rule: \"malformed-claim-citation\",\n severity: \"error\",\n file: page.filePath,\n message: `Malformed claim citation ^[${captured}] — expected file.md, file.md:N-N, or file.md#LN-LN`,\n line,\n });\n }\n }\n }\n\n return results;\n}\n","/**\n * Wiki page rendering for the llmwiki compile pipeline.\n *\n * Encapsulates the single-page generation step: gather related pages, call\n * the LLM, build frontmatter, and produce the final markdown blob. Splitting\n * this away from the orchestrator (`compiler/index.ts`) keeps the orchestrator\n * focused on phase sequencing and lets the review-candidate code path reuse\n * the exact same renderer used for direct writes.\n */\n\nimport { readdir } from \"fs/promises\";\nimport path from \"path\";\nimport {\n buildFrontmatter,\n parseFrontmatter,\n safeReadFile,\n} from \"../utils/markdown.js\";\nimport { callClaude } from \"../utils/llm.js\";\nimport { buildPagePrompt } from \"./prompts.js\";\nimport { addObsidianMeta } from \"./obsidian.js\";\nimport { addProvenanceMeta, reportContradictionWarnings } from \"./provenance.js\";\nimport { CONCEPTS_DIR } from \"../utils/constants.js\";\nimport type { SchemaConfig } from \"../schema/index.js\";\nimport type { ExtractedConcept } from \"../utils/types.js\";\n\n/** Maximum number of existing concept pages to include as cross-reference context. */\nconst RELATED_PAGE_CONTEXT_LIMIT = 5;\n\n/** A merged-concept input from the orchestrator (multiple sources merged into one). */\ninterface RenderableConcept {\n slug: string;\n concept: ExtractedConcept;\n sourceFiles: string[];\n combinedContent: string;\n}\n\n/**\n * Render a wiki page (frontmatter + body) for a merged concept by calling\n * the LLM with cross-referencing context from existing concept pages.\n * @param root - Project root directory.\n * @param entry - The merged concept to render.\n * @param schema - Resolved schema config, used to stamp `kind` on frontmatter.\n * @returns Full markdown content (frontmatter + body, trailing newline).\n */\nexport async function renderMergedPageContent(\n root: string,\n entry: RenderableConcept,\n schema: SchemaConfig,\n): Promise<string> {\n const pagePath = path.join(root, CONCEPTS_DIR, `${entry.slug}.md`);\n const existingPage = await safeReadFile(pagePath);\n const relatedPages = await loadRelatedPages(root, entry.slug);\n\n const system = buildPagePrompt(\n entry.concept.concept,\n entry.combinedContent,\n existingPage,\n relatedPages,\n );\n\n const pageBody = await callClaude({\n system,\n messages: [\n { role: \"user\", content: `Write the wiki page for \"${entry.concept.concept}\".` },\n ],\n });\n\n const frontmatter = buildMergedFrontmatter(entry, existingPage, schema);\n reportContradictionWarnings(entry.concept.concept, entry.concept);\n return `${frontmatter}\\n\\n${pageBody}\\n`;\n}\n\n/**\n * Construct the frontmatter block for a merged concept, preserving createdAt\n * and stamping the `kind` field from the schema's default kind.\n */\nfunction buildMergedFrontmatter(\n entry: RenderableConcept,\n existingPage: string,\n schema: SchemaConfig,\n): string {\n const now = new Date().toISOString();\n const existing = existingPage ? parseFrontmatter(existingPage) : null;\n const createdAt = (existing?.meta.createdAt && typeof existing.meta.createdAt === \"string\")\n ? existing.meta.createdAt\n : now;\n const frontmatterFields: Record<string, unknown> = {\n title: entry.concept.concept,\n summary: entry.concept.summary,\n sources: entry.sourceFiles,\n kind: schema.defaultKind,\n createdAt,\n updatedAt: now,\n };\n addObsidianMeta(frontmatterFields, entry.concept.concept, entry.concept.tags ?? []);\n addProvenanceMeta(frontmatterFields, entry.concept);\n return buildFrontmatter(frontmatterFields);\n}\n\n/**\n * Load related wiki pages to provide cross-referencing context.\n * Returns concatenated content of up to RELATED_PAGE_CONTEXT_LIMIT pages.\n * @param root - Project root directory.\n * @param excludeSlug - Slug of the current page to exclude.\n * @returns Concatenated related page contents (empty when concepts dir is missing).\n */\nasync function loadRelatedPages(root: string, excludeSlug: string): Promise<string> {\n const conceptsPath = path.join(root, CONCEPTS_DIR);\n let files: string[];\n\n try {\n files = await readdir(conceptsPath);\n } catch {\n return \"\";\n }\n\n const related = files\n .filter((f) => f.endsWith(\".md\") && f !== `${excludeSlug}.md`)\n .slice(0, RELATED_PAGE_CONTEXT_LIMIT);\n\n const contents: string[] = [];\n for (const f of related) {\n const content = await safeReadFile(path.join(conceptsPath, f));\n if (!content) continue;\n const { meta } = parseFrontmatter(content);\n if (meta.orphaned) continue;\n contents.push(content);\n }\n\n return contents.join(\"\\n\\n---\\n\\n\");\n}\n","/**\n * Helpers for surfacing provenance metadata during compilation.\n *\n * Keeps the compile orchestrator small by isolating the logic that copies\n * confidence/contradiction signals from extracted concepts onto wiki page\n * frontmatter and emits compile-time warnings when contradictions are\n * reported.\n */\n\nimport * as output from \"../utils/output.js\";\nimport type { ExtractedConcept } from \"../utils/types.js\";\n\n/**\n * Copy provenance metadata fields from an extracted concept onto the\n * frontmatter record, omitting fields the LLM did not provide so existing\n * pages without these fields stay clean.\n * @param fields - Mutable frontmatter record being assembled for a page.\n * @param concept - Source concept whose provenance metadata to apply.\n */\nexport function addProvenanceMeta(\n fields: Record<string, unknown>,\n concept: ExtractedConcept,\n): void {\n if (typeof concept.confidence === \"number\") {\n fields.confidence = concept.confidence;\n }\n if (concept.provenanceState) {\n fields.provenanceState = concept.provenanceState;\n }\n if (concept.contradictedBy && concept.contradictedBy.length > 0) {\n fields.contradictedBy = concept.contradictedBy;\n }\n if (typeof concept.inferredParagraphs === \"number\") {\n fields.inferredParagraphs = concept.inferredParagraphs;\n }\n}\n\n/**\n * Print a compile-time warning when a concept reports contradictions with\n * other pages. Returns silently when there is nothing to report.\n * @param conceptTitle - Human-readable title of the concept being compiled.\n * @param concept - The extracted concept whose contradictions to surface.\n */\nexport function reportContradictionWarnings(\n conceptTitle: string,\n concept: ExtractedConcept,\n): void {\n const refs = concept.contradictedBy;\n if (!refs || refs.length === 0) return;\n const slugs = refs.map((r) => r.slug).join(\", \");\n output.status(\n \"!\",\n output.warn(`Contradiction reported on \"${conceptTitle}\" — conflicts with: ${slugs}`),\n );\n}\n","/**\n * Commander action for `llmwiki query <question>`.\n * Two-step LLM-powered wiki query that first selects relevant pages from the\n * wiki index, then streams an answer grounded in those pages. Optionally saves\n * the response as a new page in wiki/queries/.\n *\n * Step 1 - Page Selection: Reads wiki/index.md and asks Claude (via tool_use)\n * to pick the most relevant concept pages for the question.\n *\n * Step 2 - Answer Generation: Loads the selected pages in full and streams\n * a cited answer to the terminal.\n */\n\nimport { existsSync } from \"fs\";\nimport path from \"path\";\nimport { callClaude } from \"../utils/llm.js\";\nimport type { LLMTool } from \"../utils/provider.js\";\nimport { atomicWrite, safeReadFile, slugify, buildFrontmatter, parseFrontmatter } from \"../utils/markdown.js\";\nimport { generateIndex } from \"../compiler/indexgen.js\";\nimport * as output from \"../utils/output.js\";\nimport { QUERY_PAGE_LIMIT, INDEX_FILE, CONCEPTS_DIR, QUERIES_DIR } from \"../utils/constants.js\";\nimport { findRelevantPages, updateEmbeddings } from \"../utils/embeddings.js\";\nimport type { QueryResult } from \"../utils/types.js\";\n\n/** Directories to search when loading selected pages, in priority order. */\nconst PAGE_DIRS = [CONCEPTS_DIR, QUERIES_DIR];\n\n/** Tool schema for page selection (provider-agnostic). */\nconst PAGE_SELECTION_TOOL: LLMTool = {\n name: \"select_pages\",\n description: \"Select the most relevant wiki pages to answer a question\",\n input_schema: {\n type: \"object\" as const,\n properties: {\n pages: {\n type: \"array\",\n items: {\n type: \"string\",\n description: \"Slug of a relevant wiki page (e.g. 'llm-knowledge-bases')\",\n },\n maxItems: QUERY_PAGE_LIMIT,\n },\n reasoning: {\n type: \"string\",\n description: \"Brief explanation of why these pages were selected\",\n },\n },\n required: [\"pages\", \"reasoning\"],\n },\n};\n\ninterface PageSelectionResult {\n pages: string[];\n reasoning: string;\n}\n\n/**\n * Select the most relevant wiki pages for a question using Claude tool_use.\n * @param question - The user's natural language question.\n * @param indexContent - The full text of wiki/index.md.\n * @returns Parsed page slugs and reasoning from Claude.\n */\nexport async function selectPages(\n question: string,\n indexContent: string,\n): Promise<PageSelectionResult> {\n const systemPrompt =\n \"You are a knowledge base assistant. Given a question and a wiki index, select the most relevant pages.\";\n\n const userMessage = `Question: ${question}\\n\\nWiki Index:\\n${indexContent}`;\n\n const rawResult = await callClaude({\n system: systemPrompt,\n messages: [{ role: \"user\", content: userMessage }],\n tools: [PAGE_SELECTION_TOOL],\n });\n\n try {\n const parsed = JSON.parse(rawResult);\n return {\n pages: Array.isArray(parsed.pages) ? parsed.pages.filter((p: unknown) => typeof p === \"string\") : [],\n reasoning: typeof parsed.reasoning === \"string\" ? parsed.reasoning : \"No reasoning provided\",\n };\n } catch {\n return { pages: [], reasoning: \"Failed to parse page selection response\" };\n }\n}\n\n/** Render a list of candidate pages in the same bullet format selectPages() consumes. */\nfunction buildFilteredIndex(\n candidates: Array<{ slug: string; title: string; summary: string }>,\n): string {\n return candidates\n .map((entry) => `- **${entry.slug}**: ${entry.title} — ${entry.summary}`)\n .join(\"\\n\");\n}\n\ninterface SelectedPages {\n pages: string[];\n rawPages: string[];\n reasoning: string;\n}\n\n/**\n * Pick relevant pages using embedding pre-filter when available.\n * Falls back to sending the full wiki index when no embeddings store exists\n * or when the embedding call fails.\n */\nasync function selectRelevantPages(root: string, question: string): Promise<SelectedPages> {\n const candidates = await tryFindRelevantPages(root, question);\n\n if (candidates.length > 0) {\n const filteredIndex = buildFilteredIndex(candidates);\n const { pages: rawPages, reasoning } = await selectPages(question, filteredIndex);\n // Tool output holds slugs directly in the semantic path — no slugify needed.\n return { pages: rawPages, rawPages, reasoning };\n }\n\n const indexContent = await safeReadFile(path.join(root, INDEX_FILE));\n const { pages: rawPages, reasoning } = await selectPages(question, indexContent);\n return { pages: rawPages.map((p) => slugify(p)), rawPages, reasoning };\n}\n\n/** Embedding-based candidate lookup that never throws. */\nasync function tryFindRelevantPages(\n root: string,\n question: string,\n): Promise<Array<{ slug: string; title: string; summary: string }>> {\n try {\n return await findRelevantPages(root, question);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n output.status(\"!\", output.dim(`Semantic pre-filter unavailable (${message}); using full index.`));\n return [];\n }\n}\n\n/**\n * Load the full content of each selected wiki page.\n * Skips pages that don't exist and warns the user.\n * @param root - Absolute path to the project root directory.\n * @param slugs - Array of page slugs to load from wiki/concepts/.\n * @returns Combined page contents with slug headers for context.\n */\nexport async function loadSelectedPages(root: string, slugs: string[]): Promise<string> {\n const sections: string[] = [];\n\n for (const slug of slugs) {\n let content = \"\";\n for (const dir of PAGE_DIRS) {\n const candidate = await safeReadFile(path.join(root, dir, `${slug}.md`));\n if (!candidate) continue;\n const { meta } = parseFrontmatter(candidate);\n if (meta.orphaned) continue;\n content = candidate;\n break;\n }\n\n if (!content) {\n output.status(\"?\", output.warn(`Page not found: ${slug}.md — skipping`));\n continue;\n }\n\n sections.push(`--- Page: ${slug} ---\\n${content}`);\n }\n\n return sections.join(\"\\n\\n\");\n}\n\n/** Shared system prompt for the answer-generation step. */\nconst ANSWER_SYSTEM_PROMPT =\n \"You are a knowledge assistant. Answer the question using ONLY the wiki content provided. \" +\n \"Cite specific pages using [[Page Title]] wikilinks. \" +\n \"If the wiki doesn't contain enough information, say so.\";\n\n/**\n * Call the LLM with the loaded wiki pages as grounding context.\n * Streams to the provided onToken callback when one is supplied,\n * otherwise returns the full answer without streaming.\n */\nasync function callAnswerLLM(\n question: string,\n pagesContent: string,\n onToken?: (text: string) => void,\n): Promise<string> {\n const userMessage = `Question: ${question}\\n\\nRelevant wiki pages:\\n${pagesContent}`;\n return callClaude({\n system: ANSWER_SYSTEM_PROMPT,\n messages: [{ role: \"user\", content: userMessage }],\n stream: Boolean(onToken),\n onToken,\n });\n}\n\n/**\n * Generate a one-line summary from the answer for use in the wiki index.\n * Takes the first sentence (up to 120 chars) so the page-selection LLM\n * has retrieval signal beyond just the title.\n * @param answer - The full answer text.\n * @returns A short summary string.\n */\nexport function summarizeAnswer(answer: string): string {\n const firstLine = answer.trim().split(/\\n/)[0] ?? \"\";\n const firstSentence = firstLine.split(/(?<=[.!?])\\s/)[0] ?? firstLine;\n return firstSentence.slice(0, 120);\n}\n\n/**\n * Save a query answer as a wiki page in the queries/ directory,\n * then regenerate the wiki index so the answer is immediately retrievable.\n * @param root - Absolute path to the project root directory.\n * @param question - The original question used as the page title.\n * @param answer - The generated answer body.\n */\nasync function saveQueryPage(root: string, question: string, answer: string): Promise<string> {\n const slug = slugify(question);\n const filePath = path.join(root, QUERIES_DIR, `${slug}.md`);\n\n const frontmatter = buildFrontmatter({\n title: question,\n summary: summarizeAnswer(answer),\n type: \"query\",\n createdAt: new Date().toISOString(),\n });\n\n const document = `${frontmatter}\\n\\n${answer}\\n`;\n await atomicWrite(filePath, document);\n\n output.status(\n \"+\",\n output.success(`Saved query → ${output.source(filePath)}`),\n );\n\n // Regenerate the index so the saved query is immediately discoverable\n // by the next query's page-selection step.\n await generateIndex(root);\n\n // Index the new query so semantic search retrieves it on the next question.\n // Non-critical: embedding failures (e.g. missing VOYAGE_API_KEY) don't block save.\n try {\n await updateEmbeddings(root, [slug]);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n output.status(\"!\", output.warn(`Skipped embeddings update: ${message}`));\n }\n\n return slug;\n}\n\n/** Options for generateAnswer — programmatic-friendly. */\ninterface GenerateAnswerOptions {\n /** Persist the answer as a wiki query page when set. */\n save?: boolean;\n /** Per-token callback for streaming. Omit for non-streaming usage. */\n onToken?: (text: string) => void;\n /** Callback fired once page selection completes — lets CLIs print reasoning before streaming. */\n onPageSelection?: (pages: string[], reasoning: string) => void;\n}\n\n/**\n * Run the two-step page-selection + answer-generation pipeline and return\n * a structured QueryResult. This is the programmatic entry point used by\n * the MCP server and any non-CLI consumer.\n *\n * @param root - Absolute path to the project root directory.\n * @param question - The natural language question to answer.\n * @param options - Streaming + save behaviour controls.\n * @returns Answer text, selected slugs, reasoning, and saved slug if applicable.\n */\nexport async function generateAnswer(\n root: string,\n question: string,\n options: GenerateAnswerOptions = {},\n): Promise<QueryResult> {\n if (!existsSync(path.join(root, INDEX_FILE))) {\n throw new Error(\"Wiki index not found. Run `llmwiki compile` first.\");\n }\n\n const { pages, reasoning } = await selectRelevantPages(root, question);\n options.onPageSelection?.(pages, reasoning);\n\n const pagesContent = await loadSelectedPages(root, pages);\n\n if (!pagesContent) {\n return { answer: \"\", selectedPages: pages, reasoning };\n }\n\n const answer = await callAnswerLLM(question, pagesContent, options.onToken);\n\n let saved: string | undefined;\n if (options.save) {\n saved = await saveQueryPage(root, question, answer);\n }\n\n return { answer, selectedPages: pages, reasoning, saved };\n}\n\n/**\n * Run a two-step LLM-powered query against the knowledge wiki.\n * @param root - Absolute path to the project root directory.\n * @param question - The natural language question to answer.\n * @param options - Command options (e.g. --save to persist the answer).\n */\nexport default async function queryCommand(\n root: string,\n question: string,\n options: { save?: boolean },\n): Promise<void> {\n if (!existsSync(path.join(root, INDEX_FILE))) {\n output.status(\"!\", output.error(\"Wiki index not found. Run `llmwiki compile` first.\"));\n return;\n }\n\n output.header(\"Selecting relevant pages\");\n\n const result = await generateAnswer(root, question, {\n save: options.save,\n onToken: (text) => process.stdout.write(text),\n onPageSelection: (pages, reasoning) => {\n output.status(\"i\", output.dim(`Reasoning: ${reasoning}`));\n output.status(\"*\", output.info(`Selected ${pages.length} page(s): ${pages.join(\", \")}`));\n output.header(\"Generating answer\");\n },\n });\n\n // Newline after streamed answer so subsequent terminal output formats cleanly.\n process.stdout.write(\"\\n\");\n\n if (!result.answer) {\n output.status(\"!\", output.error(\"No matching pages found. Try refining your question.\"));\n return;\n }\n\n if (result.saved) {\n output.status(\"→\", output.dim(\"Saved. Future queries will use this answer as context.\"));\n } else {\n output.status(\"→\", output.dim(\"Tip: use --save to add this answer to your wiki\"));\n }\n}\n","/**\n * Commander action for `llmwiki watch`.\n *\n * Monitors sources/ for file changes via chokidar and triggers incremental\n * recompilation automatically. Uses a debounce to batch rapid changes into\n * a single compile pass. Respects the .llmwiki/lock file — queues changes\n * if a compile is already running.\n */\n\nimport { watch as chokidarWatch } from \"chokidar\";\nimport { existsSync } from \"fs\";\nimport path from \"path\";\nimport { compile } from \"../compiler/index.js\";\nimport { SOURCES_DIR } from \"../utils/constants.js\";\nimport * as output from \"../utils/output.js\";\n\nconst DEBOUNCE_MS = 500;\n\n/**\n * Start watching sources/ for changes and auto-recompile.\n * Runs until the process is killed (Ctrl+C).\n */\nexport default async function watchCommand(): Promise<void> {\n const sourcesPath = path.resolve(SOURCES_DIR);\n\n if (!existsSync(sourcesPath)) {\n output.status(\n \"!\",\n output.warn('No sources/ directory found. Run `llmwiki ingest <url>` first.'),\n );\n return;\n }\n\n output.header(\"llmwiki watch\");\n output.status(\"👁\", output.info(`Watching ${sourcesPath} for changes...`));\n output.status(\"i\", output.dim(\"Press Ctrl+C to stop.\\n\"));\n\n let compiling = false;\n let pendingRecompile = false;\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n const triggerCompile = async () => {\n if (compiling) {\n pendingRecompile = true;\n return;\n }\n\n compiling = true;\n try {\n await compile(process.cwd());\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n output.status(\"!\", output.error(`Compile failed: ${msg}`));\n }\n\n compiling = false;\n\n // If changes arrived during compilation, recompile\n if (pendingRecompile) {\n pendingRecompile = false;\n await triggerCompile();\n }\n };\n\n const scheduleCompile = (eventPath: string, event: string) => {\n output.status(\n \"~\",\n output.dim(`${event}: ${path.basename(eventPath)}`),\n );\n\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(triggerCompile, DEBOUNCE_MS);\n };\n\n const watcher = chokidarWatch(sourcesPath, {\n ignoreInitial: true,\n awaitWriteFinish: { stabilityThreshold: 200 },\n });\n\n watcher\n .on(\"add\", (p) => scheduleCompile(p, \"added\"))\n .on(\"change\", (p) => scheduleCompile(p, \"changed\"))\n .on(\"unlink\", (p) => scheduleCompile(p, \"deleted\"));\n\n // Keep process alive\n await new Promise<void>(() => {});\n}\n","/**\n * Wiki linter orchestrator.\n *\n * Imports all lint rules, runs them concurrently, and aggregates\n * results into a summary with error/warning/info counts.\n * This is the main entry point for programmatic lint access.\n */\n\nimport type { LintResult, LintRule, LintSummary, SchemaAwareLintRule } from \"./types.js\";\nimport {\n checkBrokenWikilinks,\n checkOrphanedPages,\n checkMissingSummaries,\n checkDuplicateConcepts,\n checkEmptyPages,\n checkBrokenCitations,\n checkMalformedClaimCitations,\n checkLowConfidencePages,\n checkContradictedPages,\n checkInferredWithoutCitations,\n checkSchemaCrossLinks,\n} from \"./rules.js\";\nimport { loadSchema } from \"../schema/index.js\";\n\n/** Rule-only lint checks that don't depend on the schema layer. */\nconst RULES_WITHOUT_SCHEMA: LintRule[] = [\n checkBrokenWikilinks,\n checkOrphanedPages,\n checkMissingSummaries,\n checkDuplicateConcepts,\n checkEmptyPages,\n checkBrokenCitations,\n checkMalformedClaimCitations,\n checkLowConfidencePages,\n checkContradictedPages,\n checkInferredWithoutCitations,\n];\n\n/** Lint rules that need the resolved schema to know per-kind expectations. */\nconst RULES_WITH_SCHEMA: SchemaAwareLintRule[] = [checkSchemaCrossLinks];\n\n/**\n * Count occurrences of a specific severity level in the results.\n */\nfunction countBySeverity(\n results: LintResult[],\n severity: LintResult[\"severity\"],\n): number {\n return results.filter((r) => r.severity === severity).length;\n}\n\n/**\n * Run all lint rules concurrently against the wiki at the given root.\n * Loads the project schema (or defaults) so schema-aware rules can enforce\n * per-kind cross-link minimums alongside structural checks.\n * @param root - Absolute path to the project root directory.\n * @returns A summary containing all diagnostics and severity counts.\n */\nexport async function lint(root: string): Promise<LintSummary> {\n const schema = await loadSchema(root);\n const [plainResults, schemaResults] = await Promise.all([\n Promise.all(RULES_WITHOUT_SCHEMA.map((rule) => rule(root))),\n Promise.all(RULES_WITH_SCHEMA.map((rule) => rule(root, schema))),\n ]);\n\n const results = [...plainResults.flat(), ...schemaResults.flat()];\n\n return {\n errors: countBySeverity(results, \"error\"),\n warnings: countBySeverity(results, \"warning\"),\n info: countBySeverity(results, \"info\"),\n results,\n };\n}\n","/**\n * Commander action for `llmwiki lint`.\n *\n * Runs rule-based quality checks against the wiki without any LLM calls.\n * Prints colored diagnostics grouped by severity and exits with code 1\n * if any errors are found.\n */\n\nimport { lint } from \"../linter/index.js\";\nimport * as output from \"../utils/output.js\";\nimport type { LintResult } from \"../linter/types.js\";\nimport { loadSchema } from \"../schema/index.js\";\n\n/** Map severity levels to output formatting functions. */\nconst SEVERITY_FORMATTERS: Record<LintResult[\"severity\"], (text: string) => string> = {\n error: output.error,\n warning: output.warn,\n info: output.info,\n};\n\n/** Map severity levels to display icons. */\nconst SEVERITY_ICONS: Record<LintResult[\"severity\"], string> = {\n error: \"x\",\n warning: \"!\",\n info: \"i\",\n};\n\n/** Print a single lint result with colored output. */\nfunction printResult(result: LintResult): void {\n const formatter = SEVERITY_FORMATTERS[result.severity];\n const icon = SEVERITY_ICONS[result.severity];\n const location = result.line ? `${result.file}:${result.line}` : result.file;\n output.status(icon, `${formatter(result.severity)} ${output.dim(location)} ${result.message}`);\n}\n\n/**\n * Run the lint command: execute all rules and print results.\n * Exits with code 1 if any errors are found.\n */\nexport default async function lintCommand(): Promise<void> {\n output.header(\"Linting wiki\");\n\n const schema = await loadSchema(process.cwd());\n const schemaSource = schema.loadedFrom ?? \"defaults (no schema file)\";\n output.status(\"i\", output.dim(`Schema: ${schemaSource}`));\n\n const summary = await lint(process.cwd());\n\n for (const result of summary.results) {\n printResult(result);\n }\n\n console.log();\n const summaryLine = [\n output.error(`${summary.errors} error(s)`),\n output.warn(`${summary.warnings} warning(s)`),\n output.info(`${summary.info} info`),\n ].join(\", \");\n output.status(\"*\", summaryLine);\n\n if (summary.errors > 0) {\n process.exit(1);\n }\n}\n","/**\n * Commander actions for `llmwiki schema` subcommands.\n *\n * Exposes two operations:\n * - `schema init` writes a starter schema file seeded with sensible defaults\n * so users can customise page kinds and cross-link minimums without\n * hand-rolling the format.\n * - `schema show` prints the resolved schema a project would use, including\n * which file (if any) it was loaded from — helpful for debugging.\n */\n\nimport { existsSync } from \"fs\";\nimport { mkdir, writeFile } from \"fs/promises\";\nimport path from \"path\";\nimport * as output from \"../utils/output.js\";\nimport {\n buildDefaultSchema,\n defaultSchemaInitPath,\n loadSchema,\n serializeSchemaToYaml,\n} from \"../schema/index.js\";\n\n/**\n * Write a starter schema file to `.llmwiki/schema.json` under the project root.\n * Refuses to overwrite an existing file so `schema init` is safe to re-run.\n */\nexport async function schemaInitCommand(): Promise<void> {\n const root = process.cwd();\n const defaults = buildDefaultSchema();\n const targetPath = defaultSchemaInitPath(root);\n\n if (existsSync(targetPath)) {\n output.status(\"!\", output.warn(`Schema file already exists at ${targetPath}`));\n return;\n }\n\n await mkdir(path.dirname(targetPath), { recursive: true });\n const serializable = {\n version: defaults.version,\n defaultKind: defaults.defaultKind,\n kinds: defaults.kinds,\n seedPages: defaults.seedPages,\n };\n await writeFile(targetPath, `${JSON.stringify(serializable, null, 2)}\\n`, \"utf-8\");\n output.status(\"+\", output.success(`Wrote schema to ${targetPath}`));\n}\n\n/**\n * Print the resolved schema for the current project, showing defaults and\n * whichever file (if any) supplied overrides.\n */\nexport async function schemaShowCommand(): Promise<void> {\n const schema = await loadSchema(process.cwd());\n const loadedFrom = schema.loadedFrom ?? \"(defaults — no schema file found)\";\n output.header(`Schema (${loadedFrom})`);\n console.log(serializeSchemaToYaml(schema));\n}\n","/**\n * Commander action for `llmwiki review list`.\n *\n * Prints every pending review candidate (id, slug, sources, generated time)\n * so reviewers can pick one to inspect with `llmwiki review show <id>`.\n */\n\nimport { listCandidates } from \"../compiler/candidates.js\";\nimport * as output from \"../utils/output.js\";\n\n/** List every pending candidate from .llmwiki/candidates/. */\nexport default async function reviewListCommand(): Promise<void> {\n output.header(\"Pending review candidates\");\n\n const candidates = await listCandidates(process.cwd());\n if (candidates.length === 0) {\n output.status(\"✓\", output.success(\"No pending candidates.\"));\n return;\n }\n\n for (const candidate of candidates) {\n const sources = candidate.sources.join(\", \");\n const meta = output.dim(`${candidate.generatedAt} | sources: ${sources}`);\n output.status(\"?\", `${output.info(candidate.id)} → ${candidate.slug} ${meta}`);\n }\n\n output.status(\n \"→\",\n output.dim(`Use \\`llmwiki review show <id>\\` to inspect a candidate.`),\n );\n}\n","/**\n * Commander action for `llmwiki review show <id>`.\n *\n * Prints a single candidate's metadata header followed by its full body so\n * reviewers can read the proposed page before approving or rejecting.\n */\n\nimport { loadCandidateOrFail } from \"../compiler/candidates.js\";\nimport * as output from \"../utils/output.js\";\n\n/** Print a single candidate's full content to stdout. */\nexport default async function reviewShowCommand(id: string): Promise<void> {\n const candidate = await loadCandidateOrFail(process.cwd(), id);\n if (!candidate) return;\n\n output.header(`Candidate ${candidate.id}`);\n output.status(\"i\", output.dim(`title: ${candidate.title}`));\n output.status(\"i\", output.dim(`slug: ${candidate.slug}`));\n output.status(\"i\", output.dim(`summary: ${candidate.summary}`));\n output.status(\"i\", output.dim(`sources: ${candidate.sources.join(\", \")}`));\n output.status(\"i\", output.dim(`generated: ${candidate.generatedAt}`));\n\n console.log();\n console.log(candidate.body);\n\n if (candidate.schemaViolations && candidate.schemaViolations.length > 0) {\n console.log();\n output.header(\"Schema violations\");\n for (const v of candidate.schemaViolations) {\n output.status(\"!\", output.warn(`[${v.severity}] ${v.message}`));\n }\n }\n}\n","/**\n * Commander action for `llmwiki review approve <id>`.\n *\n * Promotes a pending candidate into the live wiki: writes the page body to\n * wiki/concepts/<slug>.md, refreshes the index/MOC, updates embeddings, and\n * removes the candidate file. Approval never re-invokes the LLM — the body\n * stored in the candidate is written verbatim.\n *\n * All mutations are performed under `.llmwiki/lock` to prevent races with a\n * concurrent compile or sibling approve/reject. The candidate is re-read under\n * the lock (TOCTOU guard) — if it disappears between the fast-fail check and\n * lock acquisition (e.g. a concurrent reject ran first), the approval aborts\n * cleanly rather than writing a page from a stale in-memory snapshot.\n */\n\nimport path from \"path\";\nimport {\n atomicWrite,\n validateWikiPage,\n} from \"../utils/markdown.js\";\nimport {\n deleteCandidate,\n listCandidates,\n} from \"../compiler/candidates.js\";\nimport { generateIndex } from \"../compiler/indexgen.js\";\nimport { generateMOC } from \"../compiler/obsidian.js\";\nimport { resolveLinks } from \"../compiler/resolver.js\";\nimport { updateEmbeddings } from \"../utils/embeddings.js\";\nimport { updateSourceState } from \"../utils/state.js\";\nimport { CONCEPTS_DIR } from \"../utils/constants.js\";\nimport * as output from \"../utils/output.js\";\nimport type { ReviewCandidate } from \"../utils/types.js\";\nimport { runReviewUnderLock, readCandidateUnderLock } from \"./review-helpers.js\";\n\n/** Approve a pending candidate by promoting its body into wiki/concepts/. */\nexport default async function reviewApproveCommand(id: string): Promise<void> {\n await runReviewUnderLock(id, approveUnderLock);\n}\n\n/**\n * Perform all wiki mutations for an approval while holding the lock.\n *\n * Re-reads the candidate under the lock so that a concurrent reject that ran\n * between the pre-lock fast-fail and lock acquisition is detected. Aborts with\n * exit code 1 if the candidate has disappeared or fails page validation.\n */\nasync function approveUnderLock(root: string, id: string): Promise<void> {\n const candidate = await readCandidateUnderLock(root, id);\n if (!candidate) return;\n\n if (!validateWikiPage(candidate.body)) {\n output.status(\"!\", output.error(`Candidate ${id} failed page validation; not approved.`));\n process.exitCode = 1;\n return;\n }\n\n const pagePath = path.join(root, CONCEPTS_DIR, `${candidate.slug}.md`);\n await atomicWrite(pagePath, candidate.body);\n output.status(\"+\", output.success(`Approved → ${output.source(pagePath)}`));\n\n await persistCandidateSourceStates(root, candidate);\n await refreshWikiAfterApproval(root, candidate.slug);\n await deleteCandidate(root, id);\n output.status(\"✓\", output.dim(`Candidate ${id} cleared.`));\n}\n\n/**\n * Flush the source-state snapshot stored on the candidate into\n * `.llmwiki/state.json` so the contributing source files are marked\n * compiled. Without this, approved candidates would re-appear on the next\n * `compile` run because the source still looks \"new\" or \"changed\" to the\n * change detector.\n *\n * When a single source produced multiple candidates (e.g. an extraction\n * yielded several concepts), persisting state on the first approval would\n * mark the source as fully compiled and silently strand the remaining\n * pending candidates — the next `compile --review` would skip the source\n * entirely. To avoid that, we only persist a source's state when no OTHER\n * pending candidate still references that source filename.\n */\nasync function persistCandidateSourceStates(\n root: string,\n candidate: ReviewCandidate,\n): Promise<void> {\n const states = candidate.sourceStates;\n if (!states) return;\n const otherSources = await collectOtherCandidateSources(root, candidate.id);\n for (const [sourceFile, entry] of Object.entries(states)) {\n if (otherSources.has(sourceFile)) continue;\n await updateSourceState(root, sourceFile, entry);\n }\n}\n\n/**\n * Build the set of source filenames referenced by every pending candidate\n * other than the one currently being approved. Used to defer source-state\n * persistence until the LAST candidate from a given source is reviewed.\n */\nasync function collectOtherCandidateSources(\n root: string,\n approvingId: string,\n): Promise<Set<string>> {\n const pending = await listCandidates(root);\n const sources = new Set<string>();\n for (const candidate of pending) {\n if (candidate.id === approvingId) continue;\n for (const source of candidate.sources) sources.add(source);\n }\n return sources;\n}\n\n/** Refresh interlinks, index, MOC, and embeddings after writing a candidate. */\nasync function refreshWikiAfterApproval(root: string, slug: string): Promise<void> {\n await resolveLinks(root, [slug], [slug]);\n await generateIndex(root);\n await generateMOC(root);\n await safelyUpdateEmbeddings(root, [slug]);\n}\n\n/**\n * Refresh the embeddings store without failing approval.\n * Mirrors the compiler's tolerance: missing API keys / transient provider\n * failures should warn, not abort the approval flow.\n */\nasync function safelyUpdateEmbeddings(root: string, slugs: string[]): Promise<void> {\n try {\n await updateEmbeddings(root, slugs);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n output.status(\"!\", output.warn(`Skipped embeddings update: ${message}`));\n }\n}\n","/**\n * Shared helpers for review subcommands (approve and reject).\n *\n * Both commands follow the same pattern:\n * 1. Fast-fail: read the candidate before locking (cheap early exit for bad ids).\n * 2. Acquire lock: serialize against concurrent compile / approve / reject.\n * 3. Under-lock re-read: authoritative TOCTOU guard — abort if the candidate\n * was removed between steps 1 and 2 (e.g. a concurrent reject ran first).\n * 4. Run the mutation.\n * 5. Release lock.\n *\n * Extracting this pattern avoids duplicating the acquire/release boilerplate\n * in both approve and reject.\n */\n\nimport {\n loadCandidateOrFail,\n loadCandidateUnderLockOrFail,\n} from \"../compiler/candidates.js\";\nimport { acquireLock, releaseLock } from \"../utils/lock.js\";\nimport * as output from \"../utils/output.js\";\n\n/** Re-export for use by the under-lock mutation functions in approve/reject. */\nexport { loadCandidateUnderLockOrFail as readCandidateUnderLock };\n\n/**\n * Run a review mutation under the `.llmwiki/lock`.\n *\n * Performs the pre-lock fast-fail, acquires the lock, then delegates to the\n * provided `underLock` callback. The lock is released in a `finally` block.\n *\n * @param id - Candidate id to review.\n * @param underLock - Async mutation to run while holding the lock.\n */\nexport async function runReviewUnderLock(\n id: string,\n underLock: (root: string, id: string) => Promise<void>,\n): Promise<void> {\n const root = process.cwd();\n\n // Fast-fail: surface a clear error for obviously missing ids.\n // The authoritative check happens under the lock via loadCandidateUnderLockOrFail.\n const preCheck = await loadCandidateOrFail(root, id);\n if (!preCheck) return;\n\n const locked = await acquireLock(root);\n if (!locked) {\n output.status(\"!\", output.error(\"Could not acquire lock. Try again later.\"));\n process.exitCode = 1;\n return;\n }\n\n try {\n await underLock(root, id);\n } finally {\n await releaseLock(root);\n }\n}\n","/**\n * Commander action for `llmwiki review reject <id>`.\n *\n * Removes a candidate from the pending area without touching `wiki/`.\n * Rejected candidates are moved into .llmwiki/candidates/archive/ so they\n * remain auditable but never appear in `llmwiki review list` again.\n *\n * The archive mutation is performed under `.llmwiki/lock` to serialize\n * concurrent approve/reject and approve-vs-compile operations, matching\n * the lock discipline used by compile and approve.\n *\n * The candidate is re-read under the lock (TOCTOU guard) — if it disappears\n * between the pre-lock fast-fail and lock acquisition, the rejection aborts\n * cleanly rather than silently succeeding on a stale handle.\n */\n\nimport { archiveCandidate } from \"../compiler/candidates.js\";\nimport * as output from \"../utils/output.js\";\nimport { runReviewUnderLock, readCandidateUnderLock } from \"./review-helpers.js\";\n\n/** Reject a pending candidate by archiving its JSON record. */\nexport default async function reviewRejectCommand(id: string): Promise<void> {\n await runReviewUnderLock(id, rejectUnderLock);\n}\n\n/**\n * Perform the archive mutation while holding the lock.\n *\n * Re-reads the candidate under the lock so that a concurrent approve that ran\n * between the pre-lock fast-fail and lock acquisition is detected. Aborts with\n * exit code 1 if the candidate has disappeared.\n */\nasync function rejectUnderLock(root: string, id: string): Promise<void> {\n const candidate = await readCandidateUnderLock(root, id);\n if (!candidate) return;\n\n await archiveCandidate(root, id);\n output.status(\n \"-\",\n output.warn(`Rejected candidate ${id} (${candidate.slug}) — archived, wiki unchanged.`),\n );\n}\n","/**\n * MCP (Model Context Protocol) server entry point for llmwiki.\n *\n * Exposes llmwiki's automated pipelines (ingest, compile, query, search,\n * lint, read, status) as MCP tools so AI agents can drive the compiler\n * without scraping CLI output. Read-only wiki views are exposed as\n * MCP resources for direct context injection.\n *\n * Transport: stdio. The server reads JSON-RPC messages on stdin and\n * writes responses on stdout, which is the standard surface area for\n * Claude Desktop, Cursor, and other MCP-aware clients.\n */\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { registerWikiTools } from \"./tools.js\";\nimport { registerWikiResources } from \"./resources.js\";\n\ninterface ServerOptions {\n /** Project root directory the server operates on. */\n root: string;\n /** Server version surfaced to MCP clients in the initialize handshake. */\n version: string;\n}\n\n/**\n * Start the MCP server bound to stdio transport.\n * Resolves once the transport closes (typically when the parent process exits).\n *\n * @param options - Root directory and server version (the CLI passes its own\n * version so the server doesn't need to read package.json).\n */\nexport async function startMCPServer(options: ServerOptions): Promise<void> {\n const { root, version } = options;\n const server = new McpServer({ name: \"llmwiki\", version }, {\n instructions:\n \"llmwiki is a knowledge compiler. Use ingest_source to add raw sources, \" +\n \"compile_wiki to run the LLM pipeline, query_wiki for grounded answers, \" +\n \"and search_pages to retrieve relevant pages. read_page, lint_wiki, and \" +\n \"wiki_status work without an API key.\",\n });\n\n registerWikiTools(server, root);\n registerWikiResources(server, root);\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n","/**\n * MCP tool registrations for llmwiki.\n *\n * Each tool wraps an existing pipeline function (ingest, compile, query,\n * search, read, lint, status) and converts its structured result into\n * an MCP CallToolResult. Tools that need an LLM provider validate the\n * provider lazily — the server itself starts without credentials so\n * read-only tools always work.\n */\n\nimport path from \"path\";\nimport { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { ingestSource } from \"../commands/ingest.js\";\nimport { compileAndReport } from \"../compiler/index.js\";\nimport { generateAnswer, selectPages } from \"../commands/query.js\";\nimport { lint } from \"../linter/index.js\";\nimport { collectPageSummaries, scanWikiPages } from \"../compiler/indexgen.js\";\nimport { detectChanges } from \"../compiler/hasher.js\";\nimport { countCandidates } from \"../compiler/candidates.js\";\nimport { readState } from \"../utils/state.js\";\nimport { safeReadFile, parseFrontmatter } from \"../utils/markdown.js\";\nimport { findRelevantPages } from \"../utils/embeddings.js\";\nimport {\n CONCEPTS_DIR,\n INDEX_FILE,\n QUERIES_DIR,\n} from \"../utils/constants.js\";\nimport { ensureProviderAvailable } from \"./provider-check.js\";\n\n/** Directories searched (in priority order) when resolving a page slug. */\nconst PAGE_DIRS = [CONCEPTS_DIR, QUERIES_DIR];\n\n/** Shape returned by search_pages for each matching page. */\ninterface PageRecord {\n slug: string;\n title: string;\n summary: string;\n body: string;\n}\n\n/**\n * Wrap an arbitrary JSON value as the standard MCP CallToolResult.\n * MCP requires content blocks even for structured payloads, so we mirror\n * the JSON in a text block for clients that don't read structuredContent.\n */\nfunction jsonResult(payload: unknown): {\n content: Array<{ type: \"text\"; text: string }>;\n structuredContent: { result: unknown };\n} {\n return {\n content: [{ type: \"text\" as const, text: JSON.stringify(payload, null, 2) }],\n structuredContent: { result: payload },\n };\n}\n\n/** Register all 7 wiki tools on the given MCP server instance. */\nexport function registerWikiTools(server: McpServer, root: string): void {\n registerIngestTool(server, root);\n registerCompileTool(server, root);\n registerQueryTool(server, root);\n registerSearchTool(server, root);\n registerReadTool(server, root);\n registerLintTool(server, root);\n registerStatusTool(server, root);\n}\n\nfunction registerIngestTool(server: McpServer, root: string): void {\n server.registerTool(\n \"ingest_source\",\n {\n title: \"Ingest Source\",\n description:\n \"Fetch a URL or copy a local file into sources/. Returns the saved filename, \" +\n \"character count, and whether content was truncated to fit the size limit.\",\n inputSchema: {\n source: z\n .string()\n .describe(\"URL (http/https) or absolute path to a .md/.txt file\"),\n },\n },\n async ({ source }) => {\n const previousCwd = process.cwd();\n try {\n process.chdir(root);\n const result = await ingestSource(source);\n return jsonResult(result);\n } finally {\n process.chdir(previousCwd);\n }\n },\n );\n}\n\nfunction registerCompileTool(server: McpServer, root: string): void {\n server.registerTool(\n \"compile_wiki\",\n {\n title: \"Compile Wiki\",\n description:\n \"Run the incremental compile pipeline: extract concepts from new/changed \" +\n \"sources, generate wiki pages, resolve interlinks, and rebuild the index. \" +\n \"Requires an LLM provider with credentials.\",\n inputSchema: {},\n },\n async () => {\n ensureProviderAvailable();\n const result = await compileAndReport(root);\n return jsonResult(result);\n },\n );\n}\n\nfunction registerQueryTool(server: McpServer, root: string): void {\n server.registerTool(\n \"query_wiki\",\n {\n title: \"Query Wiki\",\n description:\n \"Ask a natural-language question. Selects relevant pages with the LLM, \" +\n \"loads them, and returns a grounded answer with citations. Set save=true \" +\n \"to persist the answer as a wiki page. Requires an LLM provider.\",\n inputSchema: {\n question: z.string().describe(\"The natural-language question to answer.\"),\n save: z\n .boolean()\n .optional()\n .describe(\"Persist the answer as a wiki/queries/ page when true.\"),\n },\n },\n async ({ question, save }) => {\n ensureProviderAvailable();\n const result = await generateAnswer(root, question, { save });\n return jsonResult(result);\n },\n );\n}\n\nfunction registerSearchTool(server: McpServer, root: string): void {\n server.registerTool(\n \"search_pages\",\n {\n title: \"Search Pages\",\n description:\n \"Select pages relevant to a question and return their full content. \" +\n \"Uses semantic embeddings when available, falling back to LLM-based \" +\n \"selection over the wiki index. Requires an LLM provider.\",\n inputSchema: {\n question: z.string().describe(\"The query used to rank pages.\"),\n },\n },\n async ({ question }) => {\n ensureProviderAvailable();\n const slugs = await pickSearchSlugs(root, question);\n const records = await loadPageRecords(root, slugs);\n return jsonResult({ pages: records });\n },\n );\n}\n\n/** Resolve search candidates: prefer semantic search, fall back to LLM selection. */\nasync function pickSearchSlugs(root: string, question: string): Promise<string[]> {\n try {\n const candidates = await findRelevantPages(root, question);\n if (candidates.length > 0) return candidates.map((c) => c.slug);\n } catch {\n // Embeddings unavailable — fall through to index-based selection.\n }\n\n const indexContent = await safeReadFile(path.join(root, INDEX_FILE));\n const { pages } = await selectPages(question, indexContent);\n return pages;\n}\n\nfunction registerReadTool(server: McpServer, root: string): void {\n server.registerTool(\n \"read_page\",\n {\n title: \"Read Page\",\n description:\n \"Read a single wiki page by slug. Searches concepts/ first, then queries/. \" +\n \"Returns the parsed frontmatter and body. No LLM call required.\",\n inputSchema: {\n slug: z.string().describe(\"Page slug, without .md extension.\"),\n },\n },\n async ({ slug }) => {\n const page = await readPage(root, slug);\n if (!page) {\n throw new Error(`Page not found: ${slug}`);\n }\n return jsonResult(page);\n },\n );\n}\n\nfunction registerLintTool(server: McpServer, root: string): void {\n server.registerTool(\n \"lint_wiki\",\n {\n title: \"Lint Wiki\",\n description:\n \"Run rule-based quality checks (broken wikilinks, orphans, duplicates, \" +\n \"empty pages, broken citations). Returns structured diagnostics. No LLM call.\",\n inputSchema: {},\n },\n async () => {\n const summary = await lint(root);\n return jsonResult(summary);\n },\n );\n}\n\nfunction registerStatusTool(server: McpServer, root: string): void {\n server.registerTool(\n \"wiki_status\",\n {\n title: \"Wiki Status\",\n description:\n \"Summarize the wiki: page count, source count, last compile time, \" +\n \"orphaned pages, and pending source changes. Read-only — never \" +\n \"modifies the workspace.\",\n inputSchema: {},\n },\n async () => jsonResult(await collectStatus(root)),\n );\n}\n\n/** Read-only status snapshot used by the wiki_status tool. */\nasync function collectStatus(root: string): Promise<WikiStatus> {\n const concepts = await collectPageSummaries(path.join(root, CONCEPTS_DIR));\n const queries = await collectPageSummaries(path.join(root, QUERIES_DIR));\n const state = await readState(root);\n const changes = await detectChanges(root, state);\n const orphans = await findOrphanedSlugs(root);\n const pendingCandidates = await countCandidates(root);\n const compileTimes = Object.values(state.sources).map((s) => s.compiledAt);\n const lastCompile = compileTimes.length > 0\n ? compileTimes.sort().slice(-1)[0]\n : null;\n\n return {\n pages: { concepts: concepts.length, queries: queries.length, total: concepts.length + queries.length },\n sources: Object.keys(state.sources).length,\n lastCompiledAt: lastCompile,\n orphanedPages: orphans,\n pendingCandidates,\n pendingChanges: changes\n .filter((c) => c.status !== \"unchanged\")\n .map((c) => ({ file: c.file, status: c.status })),\n };\n}\n\ninterface WikiStatus {\n pages: { concepts: number; queries: number; total: number };\n sources: number;\n lastCompiledAt: string | null;\n orphanedPages: string[];\n /** Number of compile candidates awaiting human review. */\n pendingCandidates: number;\n pendingChanges: Array<{ file: string; status: string }>;\n}\n\n/** Find concept slugs whose pages are flagged as orphaned. */\nasync function findOrphanedSlugs(root: string): Promise<string[]> {\n const scanned = await scanWikiPages(path.join(root, CONCEPTS_DIR));\n return scanned.filter(({ meta }) => meta.orphaned).map(({ slug }) => slug);\n}\n\n/** Load full content for a list of slugs, skipping missing/orphaned pages. */\nasync function loadPageRecords(root: string, slugs: string[]): Promise<PageRecord[]> {\n const records: PageRecord[] = [];\n for (const slug of slugs) {\n const page = await readPage(root, slug);\n if (page) records.push(page);\n }\n return records;\n}\n\n/**\n * Locate a page by slug across the priority-ordered page directories,\n * skipping orphaned entries to match the query pipeline's behaviour.\n */\nexport async function readPage(root: string, slug: string): Promise<PageRecord | null> {\n for (const dir of PAGE_DIRS) {\n const content = await safeReadFile(path.join(root, dir, `${slug}.md`));\n if (!content) continue;\n\n const { meta, body } = parseFrontmatter(content);\n if (meta.orphaned) continue;\n\n return {\n slug,\n title: typeof meta.title === \"string\" ? meta.title : slug,\n summary: typeof meta.summary === \"string\" ? meta.summary : \"\",\n body: body.trim(),\n };\n }\n return null;\n}\n\n","/**\n * Per-tool provider validation for the MCP server.\n *\n * The MCP server starts without any API key check so read-only tools\n * (read_page, lint_wiki, wiki_status) and the ingest tool always work.\n * Tools that need an LLM call (compile, query, search) invoke this guard\n * to surface a clean error if credentials are missing.\n */\n\nimport { DEFAULT_PROVIDER } from \"../utils/constants.js\";\nimport { resolveAnthropicAuthFromEnv } from \"../utils/claude-settings.js\";\n\n/** Map of provider name to the env var that satisfies it. Null = no key needed. */\nconst PROVIDER_KEY_VARS: Record<string, string | null> = {\n anthropic: \"ANTHROPIC_API_KEY\",\n openai: \"OPENAI_API_KEY\",\n ollama: null,\n minimax: \"MINIMAX_API_KEY\",\n};\n\n/**\n * Throw if the active LLM provider is missing credentials.\n * Anthropic accepts either ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN\n * (resolved through the Claude Code settings fallback chain).\n */\nexport function ensureProviderAvailable(): void {\n const provider = process.env.LLMWIKI_PROVIDER ?? DEFAULT_PROVIDER;\n\n if (provider === \"anthropic\") {\n const auth = resolveAnthropicAuthFromEnv();\n if (!auth.apiKey && !auth.authToken) {\n throw new Error(\n 'Anthropic credentials are required for the \"anthropic\" provider. ' +\n \"Set ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN.\",\n );\n }\n return;\n }\n\n const keyVar = PROVIDER_KEY_VARS[provider];\n if (keyVar === undefined) {\n throw new Error(\n `Unknown provider \"${provider}\". Supported: ${Object.keys(PROVIDER_KEY_VARS).join(\", \")}`,\n );\n }\n\n if (keyVar && !process.env[keyVar]) {\n throw new Error(\n `${keyVar} environment variable is required for the \"${provider}\" provider.`,\n );\n }\n}\n","/**\n * MCP resource registrations for llmwiki.\n *\n * Resources expose read-only views of the wiki under the llmwiki:// URI\n * scheme. Hosts can attach these as context without invoking a tool —\n * useful for letting agents browse the wiki passively.\n */\n\nimport path from \"path\";\nimport { readdir } from \"fs/promises\";\nimport { McpServer, ResourceTemplate } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport {\n CONCEPTS_DIR,\n INDEX_FILE,\n QUERIES_DIR,\n SOURCES_DIR,\n STATE_FILE,\n} from \"../utils/constants.js\";\nimport { safeReadFile, parseFrontmatter } from \"../utils/markdown.js\";\nimport { readState } from \"../utils/state.js\";\n\n/** Standard JSON content block for an MCP resource read result. */\nfunction jsonContent(uri: URL, payload: unknown): {\n uri: string;\n mimeType: string;\n text: string;\n} {\n return {\n uri: uri.href,\n mimeType: \"application/json\",\n text: JSON.stringify(payload, null, 2),\n };\n}\n\n/** Standard markdown content block for an MCP resource read result. */\nfunction markdownContent(uri: URL, text: string): {\n uri: string;\n mimeType: string;\n text: string;\n} {\n return {\n uri: uri.href,\n mimeType: \"text/markdown\",\n text,\n };\n}\n\n/** Register all 5 read-only wiki resources on the given MCP server. */\nexport function registerWikiResources(server: McpServer, root: string): void {\n registerIndexResource(server, root);\n registerSourcesResource(server, root);\n registerStateResource(server, root);\n registerConceptResource(server, root);\n registerQueryResource(server, root);\n}\n\nfunction registerIndexResource(server: McpServer, root: string): void {\n server.registerResource(\n \"wiki-index\",\n \"llmwiki://index\",\n {\n title: \"Wiki Index\",\n description: \"Full content of wiki/index.md (auto-generated table of contents).\",\n mimeType: \"text/markdown\",\n },\n async (uri) => {\n const content = await safeReadFile(path.join(root, INDEX_FILE));\n return { contents: [markdownContent(uri, content)] };\n },\n );\n}\n\nfunction registerSourcesResource(server: McpServer, root: string): void {\n server.registerResource(\n \"wiki-sources\",\n \"llmwiki://sources\",\n {\n title: \"Wiki Sources\",\n description: \"List of ingested source files with frontmatter metadata.\",\n mimeType: \"application/json\",\n },\n async (uri) => ({\n contents: [jsonContent(uri, await listSources(root))],\n }),\n );\n}\n\nfunction registerStateResource(server: McpServer, root: string): void {\n server.registerResource(\n \"wiki-state\",\n \"llmwiki://state\",\n {\n title: \"Compilation State\",\n description: \"Per-source hashes, concepts, and last compile times from .llmwiki/state.json.\",\n mimeType: \"application/json\",\n },\n async (uri) => {\n const state = await readState(root);\n return { contents: [jsonContent(uri, state)] };\n },\n );\n}\n\nfunction registerConceptResource(server: McpServer, root: string): void {\n server.registerResource(\n \"wiki-concept\",\n new ResourceTemplate(\"llmwiki://concept/{slug}\", {\n list: async () => listPagesUnder(root, CONCEPTS_DIR, \"concept\"),\n }),\n {\n title: \"Wiki Concept\",\n description: \"A single concept page from wiki/concepts/ — frontmatter plus body.\",\n mimeType: \"application/json\",\n },\n async (uri, { slug }) => ({\n contents: [jsonContent(uri, await loadPageWithMeta(root, CONCEPTS_DIR, String(slug)))],\n }),\n );\n}\n\nfunction registerQueryResource(server: McpServer, root: string): void {\n server.registerResource(\n \"wiki-query\",\n new ResourceTemplate(\"llmwiki://query/{slug}\", {\n list: async () => listPagesUnder(root, QUERIES_DIR, \"query\"),\n }),\n {\n title: \"Wiki Query\",\n description: \"A single saved query page from wiki/queries/ — frontmatter plus body.\",\n mimeType: \"application/json\",\n },\n async (uri, { slug }) => ({\n contents: [jsonContent(uri, await loadPageWithMeta(root, QUERIES_DIR, String(slug)))],\n }),\n );\n}\n\n/** Source listing: filename, frontmatter (truncation, source URL, etc.). */\nasync function listSources(root: string): Promise<Array<Record<string, unknown>>> {\n const sourcesPath = path.join(root, SOURCES_DIR);\n let files: string[];\n try {\n files = await readdir(sourcesPath);\n } catch {\n return [];\n }\n\n const records: Array<Record<string, unknown>> = [];\n for (const file of files.filter((f) => f.endsWith(\".md\"))) {\n const content = await safeReadFile(path.join(sourcesPath, file));\n const { meta } = parseFrontmatter(content);\n records.push({ filename: file, ...meta });\n }\n return records;\n}\n\n/** Read a single page and return a structured payload (slug, meta, body). */\nasync function loadPageWithMeta(\n root: string,\n dir: string,\n slug: string,\n): Promise<{ slug: string; meta: Record<string, unknown>; body: string }> {\n const filePath = path.join(root, dir, `${slug}.md`);\n const content = await safeReadFile(filePath);\n if (!content) {\n throw new Error(`Page not found: ${dir}/${slug}.md`);\n }\n\n const { meta, body } = parseFrontmatter(content);\n return { slug, meta, body: body.trim() };\n}\n\n/** Build a resource list payload by enumerating .md files in a wiki directory. */\nasync function listPagesUnder(\n root: string,\n dir: string,\n scheme: \"concept\" | \"query\",\n): Promise<{ resources: Array<{ uri: string; name: string }> }> {\n const pagesPath = path.join(root, dir);\n let files: string[];\n try {\n files = await readdir(pagesPath);\n } catch {\n return { resources: [] };\n }\n\n const resources = files\n .filter((f) => f.endsWith(\".md\"))\n .map((f) => {\n const slug = f.replace(/\\.md$/, \"\");\n return { uri: `llmwiki://${scheme}/${slug}`, name: slug };\n });\n\n return { resources };\n}\n"],"mappings":";;;AAQA,OAAO;AACP,SAAS,qBAAqB;AAC9B,SAAS,eAAe;;;ACHxB,OAAOA,WAAU;AACjB,SAAS,SAAAC,QAAO,aAAAC,kBAAiB;;;ACFjC,SAAS,WAAW,QAAQ,UAAU,aAAa;AACnD,OAAO,UAAU;AACjB,OAAO,UAAU;AAYjB,IAAM,sBAAsB;AAG5B,IAAM,kBAAkB;AAGxB,IAAM,0BAAwD,oBAAI,IAAI;AAAA,EACpE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAWM,SAAS,QAAQ,OAAuB;AAC7C,SAAO,MACJ,YAAY,EACZ,QAAQ,SAAS,EAAE,EACnB,QAAQ,aAAa,EAAE,EACvB,QAAQ,QAAQ,GAAG,EACnB,QAAQ,OAAO,GAAG,EAClB,QAAQ,UAAU,EAAE;AACzB;AAGO,SAAS,iBAAiB,QAAyC;AACxE,QAAM,SAAS,KAAK,KAAK,QAAQ,EAAE,WAAW,IAAI,aAAa,IAAI,CAAC,EAAE,QAAQ;AAC9E,SAAO;AAAA,EAAQ,MAAM;AAAA;AACvB;AAGO,SAAS,iBAAiB,SAG/B;AACA,QAAM,QAAQ,QAAQ,MAAM,oCAAoC;AAChE,MAAI,CAAC,OAAO;AACV,WAAO,EAAE,MAAM,CAAC,GAAG,MAAM,QAAQ;AAAA,EACnC;AAEA,MAAI,OAAgC,CAAC;AACrC,MAAI;AACF,UAAM,SAAS,KAAK,KAAK,MAAM,CAAC,CAAC;AACjC,QAAI,UAAU,OAAO,WAAW,UAAU;AACxC,aAAO;AAAA,IACT;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO,EAAE,MAAM,MAAM,MAAM,CAAC,EAAE;AAChC;AAGA,eAAsB,YAAY,UAAkB,SAAgC;AAClF,QAAM,MAAM,KAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AACvD,QAAM,UAAU,WAAW;AAC3B,QAAM,UAAU,SAAS,SAAS,OAAO;AACzC,QAAM,OAAO,SAAS,QAAQ;AAChC;AA4EA,SAAS,iBAAiB,OAAe,KAAsB;AAC7D,SAAO,SAAS,mBAAmB,OAAO;AAC5C;AAQO,SAAS,yBAAyB,OAAwB;AAC/D,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,MAAI,CAAC,QAAQ,SAAS,GAAG,KAAK,CAAC,QAAQ,SAAS,GAAG,EAAG,QAAO;AAC7D,QAAM,QAAQ,oBAAoB,KAAK,OAAO;AAC9C,MAAI,CAAC,SAAS,CAAC,MAAM,OAAQ,QAAO;AACpC,QAAM,EAAE,YAAY,UAAU,WAAW,QAAQ,IAAI,MAAM;AAC3D,QAAM,QAAQ,cAAc;AAC5B,QAAM,MAAM,YAAY;AACxB,MAAI,UAAU,OAAW,QAAO;AAChC,QAAM,YAAY,OAAO,KAAK;AAC9B,QAAM,UAAU,QAAQ,SAAY,YAAY,OAAO,GAAG;AAC1D,SAAO,CAAC,iBAAiB,WAAW,OAAO;AAC7C;AAgCA,eAAsB,aAAa,UAAmC;AACpE,MAAI;AACF,WAAO,MAAM,SAAS,UAAU,OAAO;AAAA,EACzC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,SAAS,gBAAgB,KAAkC;AACzD,MAAI,OAAO,QAAQ,YAAY,CAAC,OAAO,SAAS,GAAG,EAAG,QAAO;AAC7D,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,MAAM,EAAG,QAAO;AACpB,SAAO;AACT;AAGA,SAAS,qBAAqB,KAA2C;AACvE,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,SAAO,wBAAwB,IAAI,GAAsB,IACpD,MACD;AACN;AAGA,SAAS,yBAAyB,OAAyC;AACzE,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS,GAAG;AACxD,WAAO,EAAE,MAAM,MAAM,KAAK,EAAE;AAAA,EAC9B;AACA,MAAI,SAAS,OAAO,UAAU,YAAY,UAAU,OAAO;AACzD,UAAM,MAAM;AACZ,QAAI,OAAO,IAAI,SAAS,YAAY,IAAI,KAAK,KAAK,EAAE,WAAW,EAAG,QAAO;AACzE,UAAM,MAAwB,EAAE,MAAM,IAAI,KAAK,KAAK,EAAE;AACtD,QAAI,OAAO,IAAI,WAAW,SAAU,KAAI,SAAS,IAAI;AACrD,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAGA,SAAS,oBAAoB,KAA8C;AACzE,MAAI,CAAC,MAAM,QAAQ,GAAG,EAAG,QAAO;AAChC,QAAM,OAAO,IACV,IAAI,wBAAwB,EAC5B,OAAO,CAAC,QAAiC,QAAQ,IAAI;AACxD,SAAO,KAAK,SAAS,IAAI,OAAO;AAClC;AAGA,SAAS,wBAAwB,KAAkC;AACjE,MAAI,OAAO,QAAQ,YAAY,CAAC,OAAO,UAAU,GAAG,KAAK,MAAM,EAAG,QAAO;AACzE,SAAO;AACT;AASO,SAAS,wBACd,MACoB;AACpB,SAAO;AAAA,IACL,YAAY,gBAAgB,KAAK,UAAU;AAAA,IAC3C,iBAAiB,qBAAqB,KAAK,eAAe;AAAA,IAC1D,gBAAgB,oBAAoB,KAAK,cAAc;AAAA,IACvD,oBAAoB,wBAAwB,KAAK,kBAAkB;AAAA,EACrE;AACF;AAMO,SAAS,iBAAiB,SAA0B;AACzD,MAAI,CAAC,WAAW,QAAQ,KAAK,EAAE,WAAW,EAAG,QAAO;AAEpD,QAAM,EAAE,MAAM,KAAK,IAAI,iBAAiB,OAAO;AAC/C,MAAI,CAAC,KAAK,MAAO,QAAO;AACxB,MAAI,KAAK,KAAK,EAAE,WAAW,EAAG,QAAO;AAErC,SAAO;AACT;;;ACvSO,IAAM,mBAAmB;AAGzB,IAAM,mBAAmB;AAGzB,IAAM,mBAAmB;AAGzB,IAAM,sBAAsB;AAG5B,IAAM,cAAc;AACpB,IAAM,gBAAgB;AACtB,IAAM,mBAAmB;AAGzB,IAAM,mBAAmB;AAGzB,IAAM,kBAA0C;AAAA,EACrD,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AACX;AAGO,IAAM,sBAAsB;AAM5B,IAAM,4BAA4B,KAAK,KAAK;AAQ5C,IAAM,4BAA4B,KAAK,KAAK;AAG5C,IAAM,cAAc;AACpB,IAAM,eAAe;AACrB,IAAM,cAAc;AACpB,IAAM,cAAc;AACpB,IAAM,aAAa;AACnB,IAAM,YAAY;AAClB,IAAM,aAAa;AACnB,IAAM,WAAW;AACjB,IAAM,kBAAkB;AAGxB,IAAM,iBAAiB;AAGvB,IAAM,yBAAyB;AAG/B,IAAM,kBAAkB;AAGxB,IAAM,2BAA2B;AACjC,IAAM,4CAA4C;AAGlD,IAAM,mBAA2C;AAAA,EACtD,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,QAAQ;AACV;;;ACzEA,IAAM,QAAQ;AACd,IAAM,OAAO;AACb,IAAM,MAAM;AACZ,IAAM,QAAQ;AACd,IAAM,SAAS;AACf,IAAM,OAAO;AAEb,IAAM,OAAO;AACb,IAAM,MAAM;AAEL,SAAS,KAAK,MAAsB;AACzC,SAAO,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK;AAC/B;AAEO,SAAS,IAAI,MAAsB;AACxC,SAAO,GAAG,GAAG,GAAG,IAAI,GAAG,KAAK;AAC9B;AAEO,SAAS,QAAQ,MAAsB;AAC5C,SAAO,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK;AAChC;AAEO,SAAS,KAAK,MAAsB;AACzC,SAAO,GAAG,MAAM,GAAG,IAAI,GAAG,KAAK;AACjC;AAEO,SAAS,KAAK,MAAsB;AACzC,SAAO,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK;AAC/B;AAEO,SAAS,MAAM,MAAsB;AAC1C,SAAO,GAAG,GAAG,GAAG,IAAI,GAAG,KAAK;AAC9B;AAEO,SAAS,OAAO,MAAsB;AAC3C,SAAO,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK;AAC/B;AAGO,SAAS,OAAO,MAAc,SAAuB;AAC1D,UAAQ,IAAI,GAAG,IAAI,IAAI,OAAO,EAAE;AAClC;AAGO,SAAS,OAAO,OAAqB;AAC1C,UAAQ,IAAI;AAAA,EAAK,IAAI,GAAG,KAAK,GAAG,KAAK,EAAE;AACvC,UAAQ,IAAI,IAAI,SAAI,OAAO,KAAK,IAAI,MAAM,SAAS,GAAG,EAAE,CAAC,CAAC,CAAC;AAC7D;;;AC5CA,SAAS,aAAa;AACtB,SAAS,mBAAmB;AAC5B,OAAO,qBAAqB;AAQ5B,eAAe,cAAc,KAAgC;AAC3D,QAAM,WAAW,MAAM,MAAM,GAAG;AAChC,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,MAAM,mBAAmB,GAAG,UAAU,SAAS,MAAM,EAAE;AAAA,EACnE;AACA,SAAO;AACT;AAGA,SAAS,uBAAuB,MAAc,KAAqD;AACjG,QAAM,MAAM,IAAI,MAAM,MAAM,EAAE,IAAI,CAAC;AACnC,QAAM,SAAS,IAAI,YAAY,IAAI,OAAO,QAAQ;AAClD,QAAM,UAAU,OAAO,MAAM;AAE7B,MAAI,CAAC,WAAW,CAAC,QAAQ,SAAS;AAChC,UAAM,IAAI,MAAM,2CAA2C,GAAG,EAAE;AAAA,EAClE;AAEA,SAAO;AAAA,IACL,OAAO,QAAQ,SAAS;AAAA,IACxB,aAAa,QAAQ;AAAA,EACvB;AACF;AAGA,SAAS,kBAAkB,MAAsB;AAC/C,QAAM,WAAW,IAAI,gBAAgB,EAAE,cAAc,MAAM,CAAC;AAC5D,SAAO,SAAS,SAAS,IAAI;AAC/B;AAQA,eAAO,UAAiC,KAAuC;AAC7E,QAAM,WAAW,MAAM,cAAc,GAAG;AACxC,QAAM,OAAO,MAAM,SAAS,KAAK;AACjC,QAAM,EAAE,OAAO,YAAY,IAAI,uBAAuB,MAAM,GAAG;AAC/D,QAAM,UAAU,kBAAkB,WAAW;AAE7C,SAAO,EAAE,OAAO,QAAQ;AAC1B;;;ACvDA,SAAS,YAAAC,iBAAgB;AACzB,OAAOC,WAAU;AAEjB,IAAM,uBAAuB,oBAAI,IAAI,CAAC,OAAO,MAAM,CAAC;AAQpD,SAAS,kBAAkB,UAA0B;AACnD,QAAM,WAAWA,MAAK,SAAS,UAAUA,MAAK,QAAQ,QAAQ,CAAC;AAC/D,SAAO,SAAS,QAAQ,UAAU,GAAG,EAAE,KAAK;AAC9C;AAGA,SAAS,cAAc,MAAsB;AAC3C,SAAO;AAAA,EAAW,IAAI;AAAA;AACxB;AAQA,eAAO,WAAkC,UAA6C;AACpF,QAAM,MAAMA,MAAK,QAAQ,QAAQ,EAAE,YAAY;AAE/C,MAAI,CAAC,qBAAqB,IAAI,GAAG,GAAG;AAClC,UAAM,IAAI;AAAA,MACR,0BAA0B,GAAG;AAAA,IAC/B;AAAA,EACF;AAEA,QAAM,MAAM,MAAMD,UAAS,UAAU,OAAO;AAC5C,QAAM,QAAQ,kBAAkB,QAAQ;AACxC,QAAM,UAAU,QAAQ,QAAQ,MAAM,cAAc,GAAG;AAEvD,SAAO,EAAE,OAAO,QAAQ;AAC1B;;;AL/BA,SAAS,MAAME,SAAyB;AACtC,SAAOA,QAAO,WAAW,SAAS,KAAKA,QAAO,WAAW,UAAU;AACrE;AAUO,SAAS,iBAAiB,SAAiC;AAChE,MAAI,QAAQ,UAAU,kBAAkB;AACtC,WAAO,EAAE,SAAS,WAAW,OAAO,eAAe,QAAQ,OAAO;AAAA,EACpE;AAEA,EAAO;AAAA,IACL;AAAA,IACO;AAAA,MACL,0BAA0B,QAAQ,OAAO,eAAe,CAAC,OAAO,iBAAiB,eAAe,CAAC;AAAA,IACnG;AAAA,EACF;AACA,SAAO;AAAA,IACL,SAAS,QAAQ,MAAM,GAAG,gBAAgB;AAAA,IAC1C,WAAW;AAAA,IACX,eAAe,QAAQ;AAAA,EACzB;AACF;AAGA,SAAS,kBAAkB,SAAuB;AAChD,QAAM,SAAS,QAAQ,KAAK,EAAE;AAE9B,MAAI,WAAW,GAAG;AAChB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,MAAI,SAAS,kBAAkB;AAC7B,IAAO;AAAA,MACL;AAAA,MACO;AAAA,QACL,6BAA6B,MAAM,kCAAkC,gBAAgB;AAAA,MACvF;AAAA,IACF;AAAA,EACF;AACF;AAGO,SAAS,cACd,OACAA,SACA,QACQ;AACR,QAAM,OAAgC;AAAA,IACpC;AAAA,IACA,QAAAA;AAAA,IACA,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,EACrC;AACA,MAAI,OAAO,WAAW;AACpB,SAAK,YAAY;AACjB,SAAK,gBAAgB,OAAO;AAAA,EAC9B;AACA,QAAM,cAAc,iBAAiB,IAAI;AAEzC,SAAO,GAAG,WAAW;AAAA;AAAA,EAAO,OAAO,OAAO;AAAA;AAC5C;AAGA,eAAe,WAAW,OAAe,UAAmC;AAC1E,QAAM,WAAW,GAAG,QAAQ,KAAK,CAAC;AAClC,QAAM,WAAWC,MAAK,KAAK,aAAa,QAAQ;AAEhD,QAAMC,OAAM,aAAa,EAAE,WAAW,KAAK,CAAC;AAC5C,QAAMC,WAAU,UAAU,UAAU,OAAO;AAE3C,SAAO;AACT;AAUA,eAAsB,aAAaH,SAAuC;AACxE,EAAO,OAAO,KAAY,KAAK,cAAcA,OAAM,EAAE,CAAC;AAEtD,QAAM,EAAE,OAAO,QAAQ,IAAI,MAAMA,OAAM,IACnC,MAAM,UAAUA,OAAM,IACtB,MAAM,WAAWA,OAAM;AAE3B,QAAM,SAAS,iBAAiB,OAAO;AACvC,oBAAkB,OAAO,OAAO;AAChC,QAAM,WAAW,cAAc,OAAOA,SAAQ,MAAM;AACpD,QAAM,YAAY,MAAM,WAAW,OAAO,QAAQ;AAElD,SAAO;AAAA,IACL,UAAUC,MAAK,SAAS,SAAS;AAAA,IACjC,WAAW,OAAO,QAAQ;AAAA,IAC1B,WAAW,OAAO;AAAA,IAClB,QAAAD;AAAA,EACF;AACF;AAMA,eAAO,OAA8BA,SAA+B;AAClE,QAAM,SAAS,MAAM,aAAaA,OAAM;AACxC,QAAM,YAAYC,MAAK,KAAK,aAAa,OAAO,QAAQ;AAExD,EAAO;AAAA,IACL;AAAA,IACO,QAAQ,SAAgB,KAAK,OAAO,QAAQ,CAAC,WAAa,OAAO,SAAS,CAAC,EAAE;AAAA,EACtF;AACA,EAAO,OAAO,UAAY,IAAI,uBAAuB,CAAC;AACxD;;;AMrIA,SAAS,cAAAG,mBAAkB;;;ACI3B,SAAS,YAAAC,kBAAgB;AACzB,OAAOC,YAAU;;;ACFjB,SAAS,YAAAC,WAAU,aAAAC,YAAW,UAAAC,SAAQ,SAAAC,QAAO,gBAAgB;AAC7D,SAAS,kBAAkB;AAC3B,OAAOC,WAAU;AAIjB,SAAS,aAAwB;AAC/B,SAAO,EAAE,SAAS,GAAG,WAAW,IAAI,SAAS,CAAC,EAAE;AAClD;AAGA,eAAsB,UAAU,MAAkC;AAChE,QAAM,WAAWC,MAAK,KAAK,MAAM,UAAU;AAE3C,MAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,WAAO,WAAW;AAAA,EACpB;AAEA,MAAI;AACF,UAAM,MAAM,MAAMC,UAAS,UAAU,OAAO;AAC5C,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,UAAM,UAAU,WAAW;AAC3B,YAAQ,KAAK,iDAAuC,OAAO,mBAAmB;AAC9E,UAAM,SAAS,UAAU,OAAO;AAChC,WAAO,WAAW;AAAA,EACpB;AACF;AAGA,eAAsB,WAAW,MAAc,OAAiC;AAC9E,QAAM,MAAMD,MAAK,KAAK,MAAM,WAAW;AACvC,QAAME,OAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AAEpC,QAAM,WAAWF,MAAK,KAAK,MAAM,UAAU;AAC3C,QAAM,UAAU,WAAW;AAE3B,QAAMG,WAAU,SAAS,KAAK,UAAU,OAAO,MAAM,CAAC,GAAG,OAAO;AAChE,QAAMC,QAAO,SAAS,QAAQ;AAChC;AAMA,eAAsB,kBACpB,MACA,YACA,OACe;AACf,QAAM,QAAQ,MAAM,UAAU,IAAI;AAClC,QAAM,QAAQ,UAAU,IAAI;AAC5B,QAAM,WAAW,MAAM,KAAK;AAC9B;AAGA,eAAsB,kBACpB,MACA,YACe;AACf,QAAM,QAAQ,MAAM,UAAU,IAAI;AAClC,SAAO,MAAM,QAAQ,UAAU;AAC/B,QAAM,WAAW,MAAM,KAAK;AAC9B;;;ACxDA,OAAOC,WAAU;;;ACRjB,SAAS,kBAAkB;AAC3B,SAAS,YAAAC,WAAU,eAAe;AAClC,OAAOC,WAAU;AASjB,eAAsB,SAAS,UAAmC;AAChE,QAAM,UAAU,MAAMC,UAAS,UAAU,OAAO;AAChD,SAAO,WAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK;AAC1D;AASA,eAAsB,cACpB,MACA,WACyB;AACzB,QAAM,cAAcC,MAAK,KAAK,MAAM,WAAW;AAC/C,QAAM,eAAe,MAAM,gBAAgB,WAAW;AACtD,QAAM,UAA0B,CAAC;AAEjC,aAAW,QAAQ,cAAc;AAC/B,UAAMC,UAAS,MAAM,aAAa,MAAM,MAAM,SAAS;AACvD,YAAQ,KAAK,EAAE,MAAM,QAAAA,QAAO,CAAC;AAAA,EAC/B;AAEA,QAAM,iBAAiB,iBAAiB,cAAc,SAAS;AAC/D,UAAQ,KAAK,GAAG,cAAc;AAE9B,SAAO;AACT;AAOA,eAAe,gBAAgB,aAAwC;AACrE,MAAI;AACF,UAAM,UAAU,MAAM,QAAQ,WAAW;AACzC,WAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC;AAAA,EAChD,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AASA,eAAe,aACb,MACA,MACA,WACiC;AACjC,QAAM,WAAWD,MAAK,KAAK,MAAM,aAAa,IAAI;AAClD,QAAM,OAAO,MAAM,SAAS,QAAQ;AACpC,QAAM,OAAO,UAAU,QAAQ,IAAI;AAEnC,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,KAAK,SAAS,KAAM,QAAO;AAC/B,SAAO;AACT;AAQA,SAAS,iBACP,cACA,WACgB;AAChB,QAAM,aAAa,IAAI,IAAI,YAAY;AACvC,SAAO,OAAO,KAAK,UAAU,OAAO,EACjC,OAAO,CAAC,SAAS,CAAC,WAAW,IAAI,IAAI,CAAC,EACtC,IAAI,CAAC,UAAU,EAAE,MAAM,QAAQ,UAAmB,EAAE;AACzD;;;AD/DA,eAAsB,4BACpB,MACA,aACsC;AACtC,QAAM,WAAwC,CAAC;AAC/C,QAAM,cAAa,oBAAI,KAAK,GAAE,YAAY;AAE1C,aAAW,UAAU,aAAa;AAChC,QAAI,OAAO,SAAS,WAAW,EAAG;AAClC,aAAS,OAAO,UAAU,IAAI,MAAM,WAAW,MAAM,QAAQ,UAAU;AAAA,EACzE;AAEA,SAAO;AACT;AAGA,eAAe,WACb,MACA,QACA,YACsB;AACtB,QAAM,WAAWE,MAAK,KAAK,MAAM,aAAa,OAAO,UAAU;AAC/D,QAAM,OAAO,MAAM,SAAS,QAAQ;AACpC,SAAO;AAAA,IACL;AAAA,IACA,UAAU,OAAO,SAAS,IAAI,CAAC,YAAY,QAAQ,QAAQ,OAAO,CAAC;AAAA,IACnE;AAAA,EACF;AACF;AAWO,SAAS,qBACd,WACA,aAC6B;AAC7B,QAAM,SAAsC,CAAC;AAC7C,aAAW,QAAQ,aAAa;AAC9B,UAAM,QAAQ,UAAU,IAAI;AAC5B,QAAI,MAAO,QAAO,IAAI,IAAI;AAAA,EAC5B;AACA,SAAO;AACT;;;AE9EA,OAAO,eAAuC;AAI9C,IAAM,wBAAwB;AAcvB,SAAS,4BACd,UAAoC,CAAC,GACtB;AACf,QAAM,iBAAiB,QAAQ,SAAS,KAAK;AAC7C,QAAM,gBAAgB,QAAQ,QAAQ,KAAK;AAC3C,QAAM,mBAAmB,QAAQ,WAAW,KAAK;AAEjD,QAAM,SAAwB,CAAC;AAE/B,MAAI,eAAe;AACjB,WAAO,SAAS;AAAA,EAClB;AACA,MAAI,kBAAkB;AACpB,WAAO,YAAY;AAAA,EACrB;AAEA,MAAI,CAAC,gBAAgB;AACnB,WAAO;AAAA,EACT;AAEA,QAAM,oBACJ,eAAe,SAAS,GAAG,KAAK,eAAe,SAAS,IACpD,eAAe,MAAM,GAAG,EAAE,IAC1B;AAEN,SAAO,UAAU;AACjB,SAAO;AACT;AAIO,IAAM,oBAAN,MAA+C;AAAA,EACnC;AAAA,EACA;AAAA,EAEjB,YAAY,OAAe,UAAoC,CAAC,GAAG;AACjE,SAAK,QAAQ;AACb,SAAK,SAAS,IAAI,UAAU,4BAA4B,OAAO,CAAC;AAAA,EAClE;AAAA;AAAA,EAGA,MAAM,SAAS,QAAgB,UAAwB,WAAoC;AACzF,UAAM,WAAW,MAAM,KAAK,OAAO,SAAS,OAAO;AAAA,MACjD,OAAO,KAAK;AAAA,MACZ,YAAY;AAAA,MACZ;AAAA,MACA;AAAA,IACF,CAAC;AAED,UAAM,YAAY,SAAS,QAAQ,KAAK,CAAC,UAAU,MAAM,SAAS,MAAM;AACxE,WAAO,WAAW,SAAS,SAAS,UAAU,OAAO;AAAA,EACvD;AAAA;AAAA,EAGA,MAAM,OACJ,QACA,UACA,WACA,SACiB;AACjB,UAAM,SAAS,KAAK,OAAO,SAAS,OAAO;AAAA,MACzC,OAAO,KAAK;AAAA,MACZ,YAAY;AAAA,MACZ;AAAA,MACA;AAAA,IACF,CAAC;AAED,QAAI,WAAW;AACf,qBAAiB,SAAS,QAAQ;AAChC,UAAI,MAAM,SAAS,yBAAyB,MAAM,MAAM,SAAS,cAAc;AAC7E,oBAAY,MAAM,MAAM;AACxB,kBAAU,MAAM,MAAM,IAAI;AAAA,MAC5B;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,SACJ,QACA,UACA,OACA,WACiB;AACjB,UAAM,iBAAmC,MAAM,IAAI,CAAC,OAAO;AAAA,MACzD,MAAM,EAAE;AAAA,MACR,aAAa,EAAE;AAAA,MACf,cAAc,EAAE;AAAA,IAClB,EAAE;AAEF,UAAM,WAAW,MAAM,KAAK,OAAO,SAAS,OAAO;AAAA,MACjD,OAAO,KAAK;AAAA,MACZ,YAAY;AAAA,MACZ;AAAA,MACA;AAAA,MACA,OAAO;AAAA,IACT,CAAC;AAED,UAAM,YAAY,SAAS,QAAQ,KAAK,CAAC,UAAU,MAAM,SAAS,UAAU;AAC5E,QAAI,WAAW,SAAS,YAAY;AAClC,aAAO,KAAK,UAAU,UAAU,KAAK;AAAA,IACvC;AAEA,UAAM,YAAY,SAAS,QAAQ,KAAK,CAAC,UAAU,MAAM,SAAS,MAAM;AACxE,WAAO,WAAW,SAAS,SAAS,UAAU,OAAO;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,MAAM,MAAiC;AAC3C,UAAM,SAAS,QAAQ,IAAI,gBAAgB,KAAK;AAChD,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WAAW,MAAM,MAAM,uBAAuB;AAAA,MAClD,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,eAAe,UAAU,MAAM;AAAA,MACjC;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,OAAO,MAAM,OAAO,iBAAiB,UAAU,CAAC;AAAA,IACzE,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,SAAS,MAAM,SAAS,KAAK;AACnC,YAAM,IAAI,MAAM,qCAAqC,SAAS,MAAM,MAAM,MAAM,EAAE;AAAA,IACpF;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,UAAM,SAAS,KAAK,OAAO,CAAC,GAAG;AAC/B,QAAI,CAAC,MAAM,QAAQ,MAAM,GAAG;AAC1B,YAAM,IAAI,MAAM,sDAAsD;AAAA,IACxE;AACA,WAAO;AAAA,EACT;AACF;;;ACjKA,OAAO,YAAY;AAyBZ,SAAS,eAAe,MAAkC;AAC/D,QAAM,MAAM,QAAQ,IAAI,IAAI,GAAG,KAAK;AACpC,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,SAAS,OAAO,GAAG;AACzB,SAAO,OAAO,SAAS,MAAM,KAAK,SAAS,IAAI,SAAS;AAC1D;AAGA,SAAS,yBAA6C;AACpD,SAAO,eAAe,4BAA4B;AACpD;AAGO,SAAS,sBACd,MAC2B;AAC3B,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU;AAAA,MACR,MAAM,KAAK;AAAA,MACX,aAAa,KAAK;AAAA,MAClB,YAAY,KAAK;AAAA,IACnB;AAAA,EACF;AACF;AAGO,IAAM,iBAAN,MAA4C;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEnB,YAAY,OAAe,UAAiC,CAAC,GAAG;AAC9D,SAAK,QAAQ;AACb,SAAK,2BAA2B,QAAQ;AAGxC,UAAM,cAAc,QAAQ,UAAU,QAAQ,IAAI,kBAAkB;AACpE,UAAM,UAAU,QAAQ,aAAa,uBAAuB,KAAK;AACjE,SAAK,SAAS,IAAI,OAAO;AAAA,MACvB,QAAQ;AAAA,MACR,SAAS,QAAQ,WAAW;AAAA,MAC5B;AAAA,IACF,CAAC;AACD,SAAK,mBAAmB,QAAQ,oBAC5B,IAAI,OAAO,EAAE,QAAQ,aAAa,SAAS,QAAQ,mBAAmB,QAAQ,CAAC,IAC/E,KAAK;AAAA,EACX;AAAA;AAAA,EAGA,MAAM,SAAS,QAAgB,UAAwB,WAAoC;AACzF,UAAM,WAAW,MAAM,KAAK,OAAO,KAAK,YAAY,OAAO;AAAA,MACzD,OAAO,KAAK;AAAA,MACZ,YAAY;AAAA,MACZ,UAAU,CAAC,EAAE,MAAM,UAAU,SAAS,OAAO,GAAG,GAAG,QAAQ;AAAA,IAC7D,CAAC;AAED,WAAO,SAAS,QAAQ,CAAC,GAAG,SAAS,WAAW;AAAA,EAClD;AAAA;AAAA,EAGA,MAAM,OACJ,QACA,UACA,WACA,SACiB;AACjB,UAAM,SAAS,MAAM,KAAK,OAAO,KAAK,YAAY,OAAO;AAAA,MACvD,OAAO,KAAK;AAAA,MACZ,YAAY;AAAA,MACZ,UAAU,CAAC,EAAE,MAAM,UAAU,SAAS,OAAO,GAAG,GAAG,QAAQ;AAAA,MAC3D,QAAQ;AAAA,IACV,CAAC;AAED,QAAI,WAAW;AACf,qBAAiB,SAAS,QAAQ;AAChC,YAAM,QAAQ,MAAM,QAAQ,CAAC,GAAG,OAAO;AACvC,UAAI,OAAO;AACT,oBAAY;AACZ,kBAAU,KAAK;AAAA,MACjB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,SACJ,QACA,UACA,OACA,WACiB;AACjB,UAAM,cAAc,MAAM,IAAI,qBAAqB;AAEnD,UAAM,WAAW,MAAM,KAAK,OAAO,KAAK,YAAY,OAAO;AAAA,MACzD,OAAO,KAAK;AAAA,MACZ,YAAY;AAAA,MACZ,UAAU,CAAC,EAAE,MAAM,UAAU,SAAS,OAAO,GAAG,GAAG,QAAQ;AAAA,MAC3D,OAAO;AAAA,IACT,CAAC;AAED,UAAM,YAAY,SAAS,QAAQ,CAAC,GAAG,SAAS;AAChD,QAAI,aAAa,UAAU,SAAS,GAAG;AACrC,aAAO,UAAU,CAAC,EAAE,SAAS;AAAA,IAC/B;AAEA,WAAO,SAAS,QAAQ,CAAC,GAAG,SAAS,WAAW;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,MAAM,MAAiC;AAC3C,UAAM,WAAW,MAAM,KAAK,iBAAiB,WAAW,OAAO;AAAA,MAC7D,OAAO,KAAK,eAAe;AAAA,MAC3B,OAAO;AAAA,IACT,CAAC;AAED,UAAM,SAAS,SAAS,KAAK,CAAC,GAAG;AACjC,QAAI,CAAC,MAAM,QAAQ,MAAM,GAAG;AAC1B,YAAM,IAAI,MAAM,sDAAsD;AAAA,IACxE;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGU,iBAAyB;AACjC,WAAO,KAAK,4BAA4B,iBAAiB;AAAA,EAC3D;AACF;;;AC3IA,SAAS,uBAAuB,UAA2B;AACzD,SACE,YACA,eAAe,mBAAmB,KAClC,eAAe,4BAA4B,KAC3C;AAEJ;AAGO,IAAM,iBAAN,cAA6B,eAAe;AAAA,EACjD,YAAY,OAAe,SAAgC;AACzD,UAAM,OAAO;AAAA,MACX,SAAS,QAAQ;AAAA,MACjB,QAAQ;AAAA,MACR,mBAAmB,QAAQ;AAAA,MAC3B,gBAAgB,QAAQ;AAAA,MACxB,WAAW,uBAAuB,QAAQ,SAAS;AAAA,IACrD,CAAC;AAAA,EACH;AAAA;AAAA,EAGmB,iBAAyB;AAC1C,WAAO,KAAK,4BAA4B,iBAAiB;AAAA,EAC3D;AACF;;;ACxCA,IAAM,mBAAmB;AAGlB,IAAM,kBAAN,cAA8B,eAAe;AAAA,EAClD,YAAY,OAAe,QAAgB;AACzC,UAAM,OAAO,EAAE,SAAS,kBAAkB,OAAO,CAAC;AAAA,EACpD;AACF;;;ACTA,SAAS,oBAAoB;AAC7B,SAAS,eAAe;AACxB,OAAOC,WAAU;AAEjB,IAAM,2BAA2B;AAcjC,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAEA,SAAS,UAAU,OAAoC;AACrD,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,UAAU,MAAM,KAAK;AAC3B,SAAO,QAAQ,SAAS,IAAI,UAAU;AACxC;AAEA,SAAS,0BAA0B,KAAgC;AACjE,SAAO,IAAI,wBAAwB,KAAKA,MAAK,KAAK,QAAQ,GAAG,WAAW,eAAe;AACzF;AAEA,SAAS,uBAAuB,cAA0C;AACxE,MAAI;AACF,WAAO,aAAa,cAAc,MAAM;AAAA,EAC1C,SAAS,KAAK;AACZ,QAAI,SAAS,GAAG,KAAK,IAAI,SAAS,UAAU;AAC1C,aAAO;AAAA,IACT;AACA,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,UAAM,IAAI,MAAM,sCAAsC,YAAY,MAAM,OAAO,EAAE;AAAA,EACnF;AACF;AAEO,SAAS,sBAAsB,MAAyB,QAAQ,KAAoC;AACzG,QAAM,eAAe,0BAA0B,GAAG;AAClD,QAAM,MAAM,uBAAuB,YAAY;AAC/C,MAAI,CAAC,IAAK,QAAO;AAEjB,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,GAAG;AAAA,EACzB,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,UAAM,IAAI,MAAM,uCAAuC,YAAY,MAAM,OAAO,EAAE;AAAA,EACpF;AAEA,MAAI,CAAC,SAAS,MAAM,KAAK,CAAC,SAAS,OAAO,GAAG,GAAG;AAC9C,WAAO;AAAA,EACT;AAEA,QAAM,SAA4B;AAAA,IAChC,mBAAmB,UAAU,OAAO,IAAI,iBAAiB;AAAA,IACzD,sBAAsB,UAAU,OAAO,IAAI,oBAAoB;AAAA,IAC/D,oBAAoB,UAAU,OAAO,IAAI,kBAAkB;AAAA,IAC3D,iBAAiB,UAAU,OAAO,IAAI,eAAe;AAAA,EACvD;AAEA,MAAI,CAAC,OAAO,qBAAqB,CAAC,OAAO,wBAAwB,CAAC,OAAO,sBAAsB,CAAC,OAAO,iBAAiB;AACtH,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,yBAAyB,KAAuD;AACvF,MAAI;AACF,WAAO,sBAAsB,GAAG;AAAA,EAClC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,yBAAyB,OAAuB;AACvD,QAAM,aAAa,MAAM,KAAK;AAC9B,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,UAAU;AACjC,QAAI,OAAO,aAAa,WAAW,OAAO,aAAa,UAAU;AAC/D,YAAM,IAAI,MAAM,wCAAwC;AAAA,IAC1D;AAAA,EACF,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU;AACrD,UAAM,IAAI,MAAM,gCAAgC,UAAU,MAAM,OAAO,EAAE;AAAA,EAC3E;AACA,SAAO;AACT;AAEO,SAAS,4BAA4B,MAAyB,QAAQ,KAA0B;AACrG,QAAM,iBAAiB,UAAU,IAAI,iBAAiB;AACtD,MAAI,eAAgB,QAAO,EAAE,QAAQ,eAAe;AAEpD,QAAM,oBAAoB,UAAU,IAAI,oBAAoB;AAC5D,MAAI,kBAAmB,QAAO,EAAE,WAAW,kBAAkB;AAE7D,QAAM,WAAW,sBAAsB,GAAG;AAC1C,MAAI,UAAU,kBAAmB,QAAO,EAAE,QAAQ,SAAS,kBAAkB;AAC7E,MAAI,UAAU,qBAAsB,QAAO,EAAE,WAAW,SAAS,qBAAqB;AACtF,SAAO,CAAC;AACV;AAEO,SAAS,6BAA6B,MAAyB,QAAQ,KAAyB;AACrG,QAAM,gBAAgB,IAAI;AAC1B,MAAI,kBAAkB,OAAW,QAAO;AACxC,SAAO,yBAAyB,GAAG,GAAG;AACxC;AAEO,SAAS,+BAA+B,MAAyB,QAAQ,KAAyB;AACvG,QAAM,kBAAkB,UAAU,IAAI,kBAAkB;AACxD,MAAI,gBAAiB,QAAO,yBAAyB,eAAe;AAEpE,QAAM,kBAAkB,yBAAyB,GAAG,GAAG;AACvD,MAAI,CAAC,gBAAiB,QAAO;AAC7B,SAAO,yBAAyB,eAAe;AACjD;;;AC/EA,IAAM,sBAA2C,oBAAI,IAAI,CAAC,aAAa,UAAU,UAAU,SAAS,CAAC;AAS9F,SAAS,cAA2B;AACzC,QAAM,eAAe,gBAAgB;AAErC,UAAQ,cAAc;AAAA,IACpB,KAAK;AACH,aAAO,qBAAqB;AAAA,IAC9B,KAAK;AACH,aAAO,IAAI,eAAe,oBAAoB,QAAQ,GAAG;AAAA,QACvD,SAAS,gBAAgB,iBAAiB;AAAA,QAC1C,mBAAmB,gBAAgB,4BAA4B;AAAA,QAC/D,gBAAgB,gBAAgB,yBAAyB;AAAA,MAC3D,CAAC;AAAA,IACH,KAAK;AACH,aAAO,IAAI,eAAe,oBAAoB,QAAQ,GAAG;AAAA,QACvD,SAAS,gBAAgB,aAAa,KAAK;AAAA,QAC3C,mBAAmB,gBAAgB,wBAAwB;AAAA,QAC3D,gBAAgB,gBAAgB,yBAAyB;AAAA,MAC3D,CAAC;AAAA,IACH,KAAK;AACH,aAAO,mBAAmB;AAAA,IAC5B;AACE,YAAM,IAAI,MAAM,uBAAuB,YAAY,EAAE;AAAA,EACzD;AACF;AAEA,SAAS,gBAAgB,MAAkC;AACzD,QAAM,QAAQ,QAAQ,IAAI,IAAI,GAAG,KAAK;AACtC,SAAO,QAAQ,QAAQ;AACzB;AAEA,SAAS,oBAAoB,cAAuD;AAClF,SAAO,QAAQ,IAAI,iBAAiB,gBAAgB,YAAY;AAClE;AAEA,SAAS,qBAAsC;AAC7C,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,SAAO,IAAI,gBAAgB,oBAAoB,SAAS,GAAG,MAAM;AACnE;AAEA,SAAS,uBAA0C;AACjD,QAAM,QAAQ,6BAA6B,KAAK,gBAAgB;AAChE,QAAM,UAAU,+BAA+B;AAC/C,QAAM,OAAO,4BAA4B;AAEzC,SAAO,IAAI,kBAAkB,OAAO;AAAA,IAClC;AAAA,IACA,GAAG;AAAA,EACL,CAAC;AACH;AAEA,SAAS,kBAA0B;AACjC,QAAM,eAAe,QAAQ,IAAI,oBAAoB;AACrD,MAAI,CAAC,oBAAoB,IAAI,YAAY,GAAG;AAC1C,UAAM,IAAI;AAAA,MACR,qBAAqB,YAAY,iBAAiB,CAAC,GAAG,mBAAmB,EAAE,KAAK,IAAI,CAAC;AAAA,IACvF;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,wBAAgC;AAC9C,SAAO,gBAAgB;AACzB;;;ACpHA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAgBA,eAAsB,WAAW,SAA6C;AAC5E,QAAM,EAAE,QAAQ,UAAU,OAAO,YAAY,MAAM,SAAS,OAAO,QAAQ,IAAI;AAC/E,QAAM,WAAW,YAAY;AAE7B,WAAS,UAAU,GAAG,WAAW,aAAa,WAAW;AACvD,QAAI;AACF,UAAI,QAAQ;AACV,eAAO,MAAM,SAAS,OAAO,QAAQ,UAAU,WAAW,OAAO;AAAA,MACnE;AAEA,UAAI,SAAS,MAAM,SAAS,GAAG;AAC7B,eAAO,MAAM,SAAS,SAAS,QAAQ,UAAU,OAAO,SAAS;AAAA,MACnE;AAEA,aAAO,MAAM,SAAS,SAAS,QAAQ,UAAU,SAAS;AAAA,IAC5D,SAASC,QAAO;AACd,UAAI,YAAY,YAAa,OAAMA;AAEnC,YAAM,UAAU,gBAAgB,KAAK,IAAI,kBAAkB,OAAO;AAClE,YAAM,SAASA,kBAAiB,QAAQA,OAAM,UAAU,OAAOA,MAAK;AACpE,cAAQ,KAAK,mCAA8B,UAAU,CAAC,IAAI,cAAc,CAAC,MAAM,MAAM,EAAE;AACvF,cAAQ,KAAK,iBAAiB,UAAU,GAAI,MAAM;AAClD,YAAM,MAAM,OAAO;AAAA,IACrB;AAAA,EACF;AAEA,QAAM,IAAI,MAAM,aAAa;AAC/B;;;AClCA,SAAS,MAAM,YAAAC,WAAU,QAAQ,SAAAC,cAAa;AAC9C,OAAOC,WAAU;AAIjB,IAAM,iBAAiB;AACvB,IAAM,uBAAuB;AAG7B,SAAS,eAAe,KAAsB;AAC5C,MAAI;AACF,YAAQ,KAAK,KAAK,CAAC;AACnB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AASA,eAAsB,YAAY,MAAgC;AAChE,QAAM,WAAWC,MAAK,KAAK,MAAM,SAAS;AAC1C,QAAMC,OAAMD,MAAK,KAAK,MAAM,WAAW,GAAG,EAAE,WAAW,KAAK,CAAC;AAE7D,WAAS,UAAU,GAAG,UAAU,sBAAsB,WAAW;AAE/D,UAAM,UAAU,MAAM,cAAc,QAAQ;AAC5C,QAAI,QAAS,QAAO;AAGpB,UAAM,QAAQ,MAAM,YAAY,QAAQ;AACxC,QAAI,CAAC,OAAO;AACV,MAAO,OAAO,KAAY,KAAK,iCAAiC,CAAC;AACjE,aAAO;AAAA,IACT;AAGA,UAAM,YAAY,MAAM,iBAAiB,MAAM,QAAQ;AACvD,QAAI,UAAW,QAAO;AAAA,EAGxB;AAEA,EAAO,OAAO,KAAY,KAAK,wCAAwC,CAAC;AACxE,SAAO;AACT;AAWA,eAAe,iBAAiB,MAAc,UAAoC;AAChF,QAAM,cAAc,WAAW;AAE/B,QAAM,iBAAiB,MAAM,mBAAmB,WAAW;AAC3D,MAAI,CAAC,eAAgB,QAAO;AAE5B,MAAI;AAGF,QAAI,CAAE,MAAM,YAAY,QAAQ,GAAI;AAClC,aAAO;AAAA,IACT;AAGA,QAAI;AAAE,YAAM,OAAO,QAAQ;AAAA,IAAG,QAAQ;AAAA,IAAqB;AAE3D,UAAM,WAAW,MAAM,cAAc,QAAQ;AAC7C,QAAI,UAAU;AACZ,MAAO,OAAO,KAAY,IAAI,yCAAyC,CAAC;AAAA,IAC1E;AACA,WAAO;AAAA,EACT,UAAE;AACA,QAAI;AAAE,YAAM,OAAO,WAAW;AAAA,IAAG,QAAQ;AAAA,IAA4B;AAAA,EACvE;AACF;AAeA,eAAe,mBAAmB,aAAuC;AACvE,MAAI,MAAM,cAAc,WAAW,EAAG,QAAO;AAG7C,MAAI,CAAE,MAAM,YAAY,WAAW,EAAI,QAAO;AAI9C,MAAI;AAAE,UAAM,OAAO,WAAW;AAAA,EAAG,QAAQ;AAAA,EAAqB;AAC9D,SAAO;AACT;AAMA,eAAe,cAAc,UAAoC;AAC/D,MAAI;AACF,UAAM,KAAK,MAAM,KAAK,UAAU,IAAI;AACpC,UAAM,GAAG,UAAU,OAAO,QAAQ,GAAG,GAAG,OAAO;AAC/C,UAAM,GAAG,MAAM;AACf,WAAO;AAAA,EACT,SAAS,KAAc;AACrB,QAAI,eAAe,SAAS,UAAU,OAAQ,IAA8B,SAAS,UAAU;AAC7F,aAAO;AAAA,IACT;AACA,UAAM;AAAA,EACR;AACF;AAGA,eAAe,YAAY,UAAoC;AAC7D,MAAI;AACF,UAAM,UAAU,MAAME,UAAS,UAAU,OAAO;AAChD,UAAM,MAAM,SAAS,QAAQ,KAAK,GAAG,EAAE;AACvC,QAAI,MAAM,GAAG,EAAG,QAAO;AACvB,WAAO,CAAC,eAAe,GAAG;AAAA,EAC5B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,eAAsB,YAAY,MAA6B;AAC7D,QAAM,WAAWF,MAAK,KAAK,MAAM,SAAS;AAC1C,MAAI;AACF,UAAM,OAAO,QAAQ;AAAA,EACvB,QAAQ;AAAA,EAER;AACF;;;AC/JA,IAAM,0BAA6C;AAAA,EACjD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAMO,IAAM,0BAA0B;AAAA,EACrC,MAAM;AAAA,EACN,aAAa;AAAA,EACb,cAAc;AAAA,IACZ,MAAM;AAAA,IACN,YAAY;AAAA,MACV,UAAU;AAAA,QACR,MAAM;AAAA,QACN,OAAO;AAAA,UACL,MAAM;AAAA,UACN,YAAY;AAAA,YACV,SAAS;AAAA,cACP,MAAM;AAAA,cACN,aAAa;AAAA,YACf;AAAA,YACA,SAAS;AAAA,cACP,MAAM;AAAA,cACN,aAAa;AAAA,YACf;AAAA,YACA,QAAQ;AAAA,cACN,MAAM;AAAA,cACN,aAAa;AAAA,YACf;AAAA,YACA,MAAM;AAAA,cACJ,MAAM;AAAA,cACN,OAAO,EAAE,MAAM,SAAS;AAAA,cACxB,aACE;AAAA,YACJ;AAAA,YACA,YAAY;AAAA,cACV,MAAM;AAAA,cACN,aACE;AAAA,YACJ;AAAA,YACA,kBAAkB;AAAA,cAChB,MAAM;AAAA,cACN,MAAM;AAAA,cACN,aACE;AAAA,YACJ;AAAA,YACA,iBAAiB;AAAA,cACf,MAAM;AAAA,cACN,OAAO;AAAA,gBACL,MAAM;AAAA,gBACN,YAAY;AAAA,kBACV,MAAM,EAAE,MAAM,UAAU,aAAa,qCAAqC;AAAA,kBAC1E,QAAQ,EAAE,MAAM,UAAU,aAAa,sCAAsC;AAAA,gBAC/E;AAAA,gBACA,UAAU,CAAC,MAAM;AAAA,cACnB;AAAA,cACA,aAAa;AAAA,YACf;AAAA,YACA,qBAAqB;AAAA,cACnB,MAAM;AAAA,cACN,aACE;AAAA,YACJ;AAAA,UACF;AAAA,UACA,UAAU,CAAC,WAAW,WAAW,QAAQ;AAAA,QAC3C;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU,CAAC,UAAU;AAAA,EACvB;AACF;AASO,SAAS,sBACd,eACA,eACQ;AACR,QAAM,eAAe,gBACjB;AAAA;AAAA;AAAA;AAAA,EAAwF,aAAa,KACrG;AAEJ,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAWO,SAAS,gBACd,SACA,eACA,cACA,cACQ;AACR,QAAM,kBAAkB,eACpB;AAAA;AAAA;AAAA;AAAA,EAAmC,YAAY,KAC/C;AAEJ,QAAM,iBAAiB,eACnB;AAAA;AAAA;AAAA;AAAA,EAAoD,YAAY,KAChE;AAEJ,SAAO;AAAA,IACL,8EAA8E,OAAO;AAAA,IACrF;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAeA,SAAS,kBAAkB,GAAwB;AACjD,SACE,OAAO,EAAE,YAAY,YACrB,OAAO,EAAE,YAAY,YACrB,OAAO,EAAE,WAAW,cACnB,EAAE,SAAS,UAAa,MAAM,QAAQ,EAAE,IAAI;AAEjD;AAGA,SAAS,qBAAqB,KAA8C;AAC1E,MAAI,CAAC,MAAM,QAAQ,GAAG,EAAG,QAAO;AAChC,QAAM,OAA2B,CAAC;AAClC,aAAW,SAAS,KAAK;AACvB,QAAI,CAAC,SAAS,OAAO,UAAU,SAAU;AACzC,UAAM,MAAM;AACZ,QAAI,OAAO,IAAI,SAAS,YAAY,IAAI,KAAK,KAAK,EAAE,WAAW,EAAG;AAClE,UAAM,MAAwB,EAAE,MAAM,IAAI,KAAK,KAAK,EAAE;AACtD,QAAI,OAAO,IAAI,WAAW,SAAU,KAAI,SAAS,IAAI;AACrD,SAAK,KAAK,GAAG;AAAA,EACf;AACA,SAAO,KAAK,SAAS,IAAI,OAAO;AAClC;AAGA,SAAS,cAAc,GAAiC;AACtD,QAAM,aAAa,OAAO,EAAE,qBAAqB,YAC/C,wBAAwB,SAAS,EAAE,gBAAmC,IACnE,EAAE,mBACH;AACJ,SAAO;AAAA,IACL,SAAS,EAAE;AAAA,IACX,SAAS,EAAE;AAAA,IACX,QAAQ,EAAE;AAAA,IACV,MAAM,MAAM,QAAQ,EAAE,IAAI,IAAK,EAAE,OAAoB;AAAA,IACrD,YAAY,OAAO,EAAE,eAAe,WAAW,EAAE,aAAa;AAAA,IAC9D,iBAAiB;AAAA,IACjB,gBAAgB,qBAAqB,EAAE,eAAe;AAAA,IACtD,oBAAoB,OAAO,EAAE,wBAAwB,YACnD,OAAO,UAAU,EAAE,mBAAmB,KAAK,EAAE,uBAAuB,IAClE,EAAE,sBACF;AAAA,EACN;AACF;AAWO,SAAS,oBACd,MACA,MACA,qBACQ;AACR,QAAM,WAAW,KAAK;AACtB,QAAM,kBAAkB,WAAW,IAC/B,oBAAoB,QAAQ,qCAC5B;AACJ,SAAO;AAAA,IACL,kCAAkC,KAAK,IAAI,iBAAiB,KAAK,KAAK;AAAA,IACtE,uBAAuB,KAAK,WAAW;AAAA,IACvC,6BAA6B,KAAK,OAAO;AAAA,IACzC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAOO,SAAS,cAAc,YAAwC;AACpE,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,UAAU;AACpC,UAAM,WAAyB,OAAO,YAAY,CAAC;AACnD,WAAO,SAAS,OAAO,iBAAiB,EAAE,IAAI,aAAa;AAAA,EAC7D,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;;;AC5QO,IAAM,aAAkC;AAAA,EAC7C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;ACTA,IAAM,oBAA8C;AAAA,EAClD,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,UAAU;AACZ;AAGA,IAAM,uBAAiD;AAAA,EACrD,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,UAAU;AACZ;AAGA,SAAS,wBAAwD;AAC/D,SAAO;AAAA,IACL,SAAS,EAAE,cAAc,kBAAkB,SAAS,aAAa,qBAAqB,QAAQ;AAAA,IAC9F,QAAQ,EAAE,cAAc,kBAAkB,QAAQ,aAAa,qBAAqB,OAAO;AAAA,IAC3F,YAAY;AAAA,MACV,cAAc,kBAAkB;AAAA,MAChC,aAAa,qBAAqB;AAAA,IACpC;AAAA,IACA,UAAU;AAAA,MACR,cAAc,kBAAkB;AAAA,MAChC,aAAa,qBAAqB;AAAA,IACpC;AAAA,EACF;AACF;AAGO,SAAS,qBAAmC;AACjD,SAAO;AAAA,IACL,SAAS;AAAA,IACT,aAAa;AAAA,IACb,OAAO,sBAAsB;AAAA,IAC7B,WAAW,CAAC;AAAA,IACZ,YAAY;AAAA,EACd;AACF;;;AC3CA,SAAS,cAAAG,mBAAkB;AAC3B,SAAS,YAAAC,iBAAgB;AACzB,OAAOC,WAAU;AACjB,OAAOC,WAAU;AAYjB,IAAM,yBAAyB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGA,SAAS,eAAe,MAA6B;AACnD,aAAW,aAAa,wBAAwB;AAC9C,UAAM,WAAWC,MAAK,KAAK,MAAM,SAAS;AAC1C,QAAIC,YAAW,QAAQ,EAAG,QAAO;AAAA,EACnC;AACA,SAAO;AACT;AAGA,SAAS,gBAAgB,UAAkB,SAAoC;AAC7E,QAAM,SAAS,SAAS,SAAS,OAAO;AACxC,QAAM,SAAS,SAAS,KAAK,MAAM,OAAO,IAAIC,MAAK,KAAK,OAAO;AAC/D,MAAI,UAAU,OAAO,WAAW,SAAU,QAAO;AACjD,SAAO,CAAC;AACV;AAGA,SAAS,WAAW,OAAmC;AACrD,SAAO,OAAO,UAAU,YAAa,WAAiC,SAAS,KAAK;AACtF;AAGA,SAAS,cACP,UACA,UACc;AACd,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,eAAe,OAAO,SAAS,iBAAiB,WAClD,SAAS,eACT,SAAS;AACb,QAAM,cAAc,OAAO,SAAS,gBAAgB,WAChD,SAAS,cACT,SAAS;AACb,SAAO,EAAE,cAAc,YAAY;AACrC;AAGA,SAAS,WACP,UACA,WACgC;AAChC,QAAM,SAAS,EAAE,GAAG,SAAS;AAC7B,MAAI,CAAC,UAAW,QAAO;AAEvB,aAAW,QAAQ,YAAY;AAC7B,WAAO,IAAI,IAAI,cAAc,SAAS,IAAI,GAAG,UAAU,IAAI,CAAC;AAAA,EAC9D;AACA,SAAO;AACT;AAGA,SAAS,kBAAkB,OAA2C;AACpE,MAAI,OAAO,MAAM,UAAU,YAAY,MAAM,MAAM,KAAK,MAAM,GAAI,QAAO;AACzE,MAAI,CAAC,WAAW,MAAM,IAAI,EAAG,QAAO;AACpC,QAAM,UAAU,OAAO,MAAM,YAAY,WAAW,MAAM,UAAU;AACpE,QAAM,eAAe,MAAM,QAAQ,MAAM,YAAY,IACjD,MAAM,aAAa,OAAO,CAAC,SAAyB,OAAO,SAAS,QAAQ,IAC5E;AACJ,SAAO,EAAE,OAAO,MAAM,OAAO,MAAM,MAAM,MAAM,SAAS,aAAa;AACvE;AAGA,SAAS,mBAAmB,SAAqD;AAC/E,MAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO,CAAC;AACrC,SAAO,QACJ,IAAI,iBAAiB,EACrB,OAAO,CAAC,UAA6B,UAAU,IAAI;AACxD;AAGA,SAAS,eACP,UACA,WACA,YACc;AACd,QAAM,cAAc,WAAW,UAAU,WAAW,IAChD,UAAU,cACV,SAAS;AACb,SAAO;AAAA,IACL,SAAS;AAAA,IACT;AAAA,IACA,OAAO,WAAW,SAAS,OAAO,UAAU,KAAK;AAAA,IACjD,WAAW,mBAAmB,UAAU,SAAS;AAAA,IACjD;AAAA,EACF;AACF;AASA,eAAsB,WAAW,MAAqC;AACpE,QAAM,WAAW,mBAAmB;AACpC,QAAM,aAAa,eAAe,IAAI;AACtC,MAAI,CAAC,WAAY,QAAO;AAExB,QAAM,MAAM,MAAMC,UAAS,YAAY,OAAO;AAC9C,QAAM,SAAS,gBAAgB,YAAY,GAAG;AAC9C,SAAO,eAAe,UAAU,QAAQ,UAAU;AACpD;AAGO,SAAS,sBAAsB,MAAsB;AAC1D,SAAOH,MAAK,KAAK,MAAM,uBAAuB,CAAC,CAAC;AAClD;;;ACpIA,OAAOI,WAAU;AAKjB,IAAM,mBAAmB;AASlB,SAAS,gBAAgB,SAAkB,QAAgC;AAChF,MAAI,OAAO,YAAY,YAAa,WAAiC,SAAS,OAAO,GAAG;AACtF,WAAO;AAAA,EACT;AACA,SAAO,OAAO;AAChB;AAQO,SAAS,eAAe,MAAsB;AACnD,QAAM,UAAU,KAAK,MAAM,gBAAgB;AAC3C,SAAO,UAAU,QAAQ,SAAS;AACpC;AAQO,SAAS,sBAAsB,QAA8B;AAClE,QAAM,eAAe;AAAA,IACnB,SAAS,OAAO;AAAA,IAChB,aAAa,OAAO;AAAA,IACpB,OAAO,OAAO;AAAA,IACd,WAAW,OAAO;AAAA,EACpB;AACA,SAAOC,MAAK,KAAK,cAAc,EAAE,WAAW,IAAI,aAAa,IAAI,CAAC;AACpE;;;ACvBA,SAAS,yBACP,SACuB;AACvB,QAAM,aAAa,oBAAI,IAAsB;AAE7C,aAAW,CAAC,YAAY,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AACzD,eAAW,QAAQ,MAAM,UAAU;AACjC,YAAM,WAAW,WAAW,IAAI,IAAI;AACpC,UAAI,UAAU;AACZ,iBAAS,KAAK,UAAU;AAAA,MAC1B,OAAO;AACL,mBAAW,IAAI,MAAM,CAAC,UAAU,CAAC;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAGA,SAAS,cACP,YACG,UACU;AACb,QAAM,YAAY,IAAI,IAAI,QAAQ;AAClC,SAAO,IAAI;AAAA,IACT,QAAQ,OAAO,CAAC,MAAM,UAAU,IAAI,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,EAClE;AACF;AAMA,SAAS,0BACP,YACA,OACA,YACA,aACA,KACM;AACN,QAAM,cAAc,MAAM,QAAQ,UAAU;AAC5C,MAAI,CAAC,YAAa;AAElB,aAAW,QAAQ,YAAY,UAAU;AACvC,UAAM,eAAe,WAAW,IAAI,IAAI;AACxC,QAAI,CAAC,gBAAgB,aAAa,SAAS,EAAG;AAE9C,eAAW,eAAe,cAAc;AACtC,YAAM,aAAa,YAAY,KAAK,CAAC,MAAM,EAAE,IAAI,WAAW,CAAC;AAC7D,UAAI,CAAC,WAAY,KAAI,IAAI,WAAW;AAAA,IACtC;AAAA,EACF;AACF;AAiBO,SAAS,oBACd,OACA,eACU;AACV,QAAM,eAAe,cAAc,eAAe,OAAO,SAAS;AAClE,QAAM,eAAe,cAAc,eAAe,SAAS;AAC3D,QAAM,aAAa,yBAAyB,MAAM,OAAO;AACzD,QAAM,WAAW,oBAAI,IAAY;AAEjC,aAAW,eAAe,cAAc;AACtC;AAAA,MACE;AAAA,MAAa;AAAA,MAAO;AAAA,MACpB,CAAC,cAAc,cAAc,QAAQ;AAAA,MACrC;AAAA,IACF;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,QAAQ;AAC5B;AAYO,SAAS,gBACd,OACA,SACa;AAEb,QAAM,SAAS,IAAI,IAAY,MAAM,eAAe,CAAC,CAAC;AAGtD,QAAM,eAAe,QAClB,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,EACpC,IAAI,CAAC,MAAM,EAAE,IAAI;AAEpB,QAAM,aAAa,yBAAyB,MAAM,OAAO;AAEzD,aAAW,QAAQ,cAAc;AAC/B,UAAM,QAAQ,MAAM,QAAQ,IAAI;AAChC,QAAI,CAAC,MAAO;AAEZ,eAAW,QAAQ,MAAM,UAAU;AACjC,YAAM,eAAe,WAAW,IAAI,IAAI;AACxC,UAAI,gBAAgB,aAAa,SAAS,GAAG;AAC3C,eAAO,IAAI,IAAI;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAQA,eAAsB,mBACpB,MACA,aACA,uBACe;AACf,QAAM,eAAe,MAAM,UAAU,IAAI;AACzC,QAAM,aAAa,yBAAyB,aAAa,OAAO;AAGhE,QAAM,cAAc,oBAAI,IAAY;AACpC,aAAW,UAAU,uBAAuB;AAC1C,QAAI,OAAO,SAAS,WAAW,EAAG;AAClC,eAAW,KAAK,OAAO,UAAU;AAC/B,kBAAY,IAAI,QAAQ,EAAE,OAAO,CAAC;AAAA,IACpC;AAAA,EACF;AACA,QAAM,gBAAgB,IAAI;AAAA,IACxB,sBACG,OAAO,CAAC,MAAM,EAAE,SAAS,SAAS,CAAC,EACnC,IAAI,CAAC,MAAM,EAAE,UAAU;AAAA,EAC5B;AAEA,QAAM,YAAY,oBAAI,IAAY;AAClC,aAAW,QAAQ,aAAa;AAC9B,UAAM,SAAS,WAAW,IAAI,IAAI,KAAK,CAAC;AAExC,UAAM,oBAAoB,OAAO,SAAS,KACrC,OAAO,MAAM,CAAC,MAAM,cAAc,IAAI,CAAC,CAAC,KACxC,YAAY,IAAI,IAAI;AAEzB,QAAI,CAAC,kBAAmB,WAAU,IAAI,IAAI;AAAA,EAC5C;AAEA,QAAM,cAAc,EAAE,GAAG,cAAc,aAAa,MAAM,KAAK,SAAS,EAAE;AAC1E,QAAM,WAAW,MAAM,WAAW;AACpC;AAOA,SAAS,kBACP,aACA,OACa;AACb,QAAM,aAAa,oBAAI,IAAY;AAEnC,aAAW,UAAU,aAAa;AAChC,UAAM,cAAc,IAAI,IAAI,MAAM,QAAQ,OAAO,UAAU,GAAG,YAAY,CAAC,CAAC;AAC5E,eAAW,KAAK,OAAO,UAAU;AAC/B,YAAM,OAAO,QAAQ,EAAE,OAAO;AAC9B,UAAI,CAAC,YAAY,IAAI,IAAI,EAAG,YAAW,IAAI,IAAI;AAAA,IACjD;AAAA,EACF;AAEA,SAAO;AACT;AAMA,SAAS,eACP,OACA,YACA,aACU;AACV,QAAM,WAAW,oBAAI,IAAY;AAEjC,aAAW,QAAQ,OAAO;AACxB,UAAM,SAAS,WAAW,IAAI,IAAI;AAClC,QAAI,CAAC,OAAQ;AACb,eAAW,SAAS,QAAQ;AAC1B,YAAM,aAAa,YAAY,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC;AACvD,UAAI,CAAC,WAAY,UAAS,IAAI,KAAK;AAAA,IACrC;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,QAAQ;AAC5B;AAaO,SAAS,wBACd,aACA,OACA,YACU;AACV,QAAM,iBAAiB,cAAc,YAAY,OAAO,SAAS;AACjE,QAAM,eAAe,cAAc,YAAY,SAAS;AACxD,QAAM,aAAa,yBAAyB,MAAM,OAAO;AACzD,QAAM,aAAa,kBAAkB,aAAa,KAAK;AAEvD,SAAO,eAAe,YAAY,YAAY,CAAC,gBAAgB,YAAY,CAAC;AAC9E;AAUO,SAAS,mBACd,YACA,OACa;AACb,QAAM,SAAS,oBAAI,IAAY;AAC/B,QAAM,cAAc,MAAM,QAAQ,UAAU;AAC5C,MAAI,CAAC,YAAa,QAAO;AAEzB,QAAM,aAAa,yBAAyB,MAAM,OAAO;AAEzD,aAAW,QAAQ,YAAY,UAAU;AACvC,UAAM,eAAe,WAAW,IAAI,IAAI;AACxC,QAAI,gBAAgB,aAAa,SAAS,GAAG;AAC3C,aAAO,IAAI,IAAI;AAAA,IACjB;AAAA,EACF;AAEA,SAAO;AACT;AAOA,eAAsB,wBACpB,MACA,SACA,aACe;AACf,aAAW,UAAU,SAAS;AAC5B,QAAI,OAAO,SAAS,SAAS,EAAG;AAEhC,IAAO,OAAO,KAAY,KAAK,GAAG,OAAO,UAAU,kCAA6B,CAAC;AACjF,UAAM,eAAe,MAAM,UAAU,IAAI;AACzC,UAAM,cAAc,aAAa,QAAQ,OAAO,UAAU,GAAG,YAAY,CAAC;AAC1E,eAAW,QAAQ,YAAa,aAAY,IAAI,IAAI;AAEpD,UAAM,kBAAkB,MAAM,OAAO,YAAY;AAAA,MAC/C,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,IACrC,CAAC;AAAA,EACH;AACF;;;ACxTA,OAAOC,YAAU;AAiBjB,eAAsB,aACpB,MACA,YACA,OACe;AACf,QAAM,cAAc,MAAM,QAAQ,UAAU;AAC5C,MAAI,CAAC,YAAa;AAElB,QAAM,cAAc,mBAAmB,YAAY,KAAK;AAExD,aAAW,QAAQ,YAAY,UAAU;AACvC,QAAI,YAAY,IAAI,IAAI,GAAG;AACzB,MAAO,OAAO,KAAY,IAAI,SAAS,IAAI,iCAAiC,CAAC;AAC7E;AAAA,IACF;AAEA,UAAM,WAAW,MAAM,MAAM,gBAAgB;AAAA,EAC/C;AAEA,QAAM,kBAAkB,MAAM,UAAU;AAC1C;AAOA,eAAsB,yBACpB,MACA,aACe;AACf,QAAM,eAAe,MAAM,UAAU,IAAI;AACzC,QAAM,aAAa,oBAAI,IAAY;AACnC,aAAW,SAAS,OAAO,OAAO,aAAa,OAAO,GAAG;AACvD,eAAW,QAAQ,MAAM,SAAU,YAAW,IAAI,IAAI;AAAA,EACxD;AAEA,aAAW,QAAQ,aAAa;AAC9B,QAAI,WAAW,IAAI,IAAI,EAAG;AAC1B,UAAM,WAAW,MAAM,MAAM,sBAAsB;AAAA,EACrD;AACF;AAQA,eAAe,WAAW,MAAc,MAAc,QAA+B;AACnF,QAAM,WAAWC,OAAK,KAAK,MAAM,cAAc,GAAG,IAAI,KAAK;AAC3D,QAAM,UAAU,MAAM,aAAa,QAAQ;AAC3C,MAAI,CAAC,QAAS;AAEd,QAAM,EAAE,KAAK,IAAI,iBAAiB,OAAO;AACzC,MAAI,KAAK,aAAa,KAAM;AAE5B,QAAM,UAAU,QAAQ,QAAQ,SAAS,uBAAuB;AAChE,QAAM,YAAY,UAAU,OAAO;AACnC,EAAO,OAAO,UAAY,KAAK,aAAa,IAAI,QAAQ,MAAM,GAAG,CAAC;AACpE;;;AC7EA,SAAS,WAAAC,UAAS,YAAAC,iBAAgB;AAClC,OAAOC,YAAU;AACjB,SAAS,cAAAC,mBAAkB;AAY3B,eAAe,gBAAgB,MAAmC;AAChE,QAAM,cAAcC,OAAK,KAAK,MAAM,YAAY;AAChD,MAAI,CAACC,YAAW,WAAW,EAAG,QAAO,CAAC;AAEtC,QAAM,QAAQ,MAAMC,SAAQ,WAAW;AACvC,QAAM,QAAoB,CAAC;AAE3B,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,KAAK,SAAS,KAAK,EAAG;AAE3B,UAAM,WAAWF,OAAK,KAAK,aAAa,IAAI;AAC5C,UAAM,UAAU,MAAMG,UAAS,UAAU,OAAO;AAChD,UAAM,EAAE,KAAK,IAAI,iBAAiB,OAAO;AAEzC,QAAI,KAAK,SAAS,OAAO,KAAK,UAAU,YAAY,CAAC,KAAK,UAAU;AAClE,YAAM,KAAK;AAAA,QACT,MAAM,KAAK,QAAQ,SAAS,EAAE;AAAA,QAC9B,OAAO,KAAK;AAAA,QACZ;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAGA,SAAS,iBAAiB,MAAc,UAA2B;AACjE,QAAM,SAAS,KAAK,YAAY,MAAM,QAAQ;AAC9C,QAAM,QAAQ,KAAK,QAAQ,MAAM,QAAQ;AACzC,MAAI,WAAW,MAAM,UAAU,GAAI,QAAO;AAE1C,QAAM,cAAc,KAAK,QAAQ,MAAM,MAAM;AAC7C,SAAO,eAAe;AACxB;AAGA,SAAS,iBAAiB,MAAc,UAA2B;AACjE,QAAM,SAAS,KAAK,YAAY,MAAM,QAAQ;AAC9C,QAAM,QAAQ,KAAK,QAAQ,KAAK,QAAQ;AACxC,MAAI,WAAW,MAAM,UAAU,GAAI,QAAO;AAE1C,QAAM,cAAc,KAAK,QAAQ,KAAK,MAAM;AAC5C,SAAO,eAAe;AACxB;AAGA,SAAS,eAAe,MAAc,OAAe,KAAsB;AACzE,QAAM,SAAS,UAAU,KAAK,wBAAwB,KAAK,KAAK,QAAQ,CAAC,CAAC;AAC1E,QAAM,QAAQ,OAAO,KAAK,UAAU,wBAAwB,KAAK,KAAK,GAAG,CAAC;AAC1E,SAAO,UAAU;AACnB;AAGA,SAAS,iBAAiB,MAAc,OAAiD;AACvF,QAAM,UAAU,MAAM,QAAQ,uBAAuB,MAAM;AAC3D,QAAM,QAAQ,IAAI,OAAO,SAAS,IAAI;AACtC,QAAM,UAA4C,CAAC;AACnD,MAAI;AAEJ,UAAQ,QAAQ,MAAM,KAAK,IAAI,OAAO,MAAM;AAC1C,YAAQ,KAAK,EAAE,OAAO,MAAM,OAAO,KAAK,MAAM,QAAQ,MAAM,CAAC,EAAE,OAAO,CAAC;AAAA,EACzE;AAEA,SAAO;AACT;AAGA,SAAS,mBAAmB,MAAc,OAAe,KAAsB;AAC7E,MAAI,iBAAiB,MAAM,KAAK,EAAG,QAAO;AAC1C,MAAI,iBAAiB,MAAM,KAAK,EAAG,QAAO;AAC1C,SAAO,eAAe,MAAM,OAAO,GAAG;AACxC;AAMA,SAAS,aAAa,MAAc,QAAoB,WAA2B;AACjF,MAAI,SAAS;AACb,QAAM,YAAY,UAAU,YAAY;AAExC,aAAW,QAAQ,QAAQ;AACzB,QAAI,KAAK,MAAM,YAAY,MAAM,UAAW;AAE5C,UAAM,UAAU,iBAAiB,QAAQ,KAAK,KAAK;AAGnD,eAAW,KAAK,QAAQ,QAAQ,GAAG;AACjC,UAAI,CAAC,mBAAmB,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAG;AACjD,eAAS,OAAO,MAAM,GAAG,EAAE,KAAK,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,OAAO,OAAO,MAAM,EAAE,GAAG;AAAA,IAC3F;AAAA,EACF;AAEA,SAAO;AACT;AAYA,eAAsB,aACpB,MACA,cACA,UACiB;AACjB,QAAM,aAAa,MAAM,gBAAgB,IAAI;AAC7C,MAAI,WAAW,WAAW,EAAG,QAAO;AAEpC,MAAI,YAAY;AAGhB,eAAa,MAAM,qBAAqB,YAAY,YAAY;AAGhE,eAAa,MAAM,oBAAoB,YAAY,QAAQ;AAE3D,MAAI,YAAY,GAAG;AACjB,IAAO,OAAO,aAAa,IAAI,qBAAqB,SAAS,UAAU,CAAC;AAAA,EAC1E;AAEA,SAAO;AACT;AAGA,eAAe,qBACb,YACA,cACiB;AACjB,MAAI,QAAQ;AAEZ,aAAW,QAAQ,YAAY;AAC7B,QAAI,CAAC,aAAa,SAAS,KAAK,IAAI,EAAG;AACvC,UAAM,UAAU,MAAM,SAAS,MAAM,UAAU;AAC/C,QAAI,QAAS;AAAA,EACf;AAEA,SAAO;AACT;AAGA,eAAe,oBACb,YACA,UACiB;AACjB,MAAI,SAAS,WAAW,EAAG,QAAO;AAElC,QAAM,YAAY,WAAW,OAAO,CAAC,MAAM,SAAS,SAAS,EAAE,IAAI,CAAC;AACpE,MAAI,UAAU,WAAW,EAAG,QAAO;AAEnC,MAAI,QAAQ;AAEZ,aAAW,QAAQ,YAAY;AAE7B,QAAI,SAAS,SAAS,KAAK,IAAI,EAAG;AAElC,UAAM,UAAU,MAAMA,UAAS,KAAK,UAAU,OAAO;AACrD,UAAM,EAAE,KAAK,IAAI,iBAAiB,OAAO;AACzC,UAAM,SAAS,aAAa,MAAM,WAAW,KAAK,KAAK;AAEvD,QAAI,WAAW,MAAM;AACnB,YAAM,aAAa,QAAQ,QAAQ,MAAM,MAAM;AAC/C,YAAM,YAAY,KAAK,UAAU,UAAU;AAC3C;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAGA,eAAe,SAAS,MAAgB,YAA0C;AAChF,QAAM,UAAU,MAAMA,UAAS,KAAK,UAAU,OAAO;AACrD,QAAM,EAAE,KAAK,IAAI,iBAAiB,OAAO;AACzC,QAAM,SAAS,aAAa,MAAM,YAAY,KAAK,KAAK;AAExD,MAAI,WAAW,KAAM,QAAO;AAE5B,QAAM,aAAa,QAAQ,QAAQ,MAAM,MAAM;AAC/C,QAAM,YAAY,KAAK,UAAU,UAAU;AAC3C,SAAO;AACT;;;AC7MA,SAAS,WAAAC,gBAAe;AACxB,OAAOC,YAAU;AAUjB,eAAsB,cAAc,MAA6B;AAC/D,EAAO,OAAO,KAAY,KAAK,qBAAqB,CAAC;AAErD,QAAM,eAAeC,OAAK,KAAK,MAAM,YAAY;AACjD,QAAM,cAAcA,OAAK,KAAK,MAAM,WAAW;AAC/C,QAAM,WAAW,MAAM,qBAAqB,YAAY;AACxD,QAAM,UAAU,MAAM,qBAAqB,WAAW;AAEtD,WAAS,KAAK,CAAC,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,KAAK,CAAC;AACtD,UAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,KAAK,CAAC;AAErD,QAAM,eAAe,kBAAkB,UAAU,OAAO;AACxD,QAAM,YAAYA,OAAK,KAAK,MAAM,UAAU;AAC5C,QAAM,YAAY,WAAW,YAAY;AAEzC,QAAM,QAAQ,SAAS,SAAS,QAAQ;AACxC,EAAO,OAAO,KAAY,QAAQ,sBAAsB,KAAK,SAAS,CAAC;AACzE;AAeA,eAAsB,cAAc,SAAyC;AAC3E,MAAI;AACJ,MAAI;AACF,YAAQ,MAAMC,SAAQ,OAAO;AAAA,EAC/B,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,UAAyB,CAAC;AAChC,aAAW,QAAQ,MAAM,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,GAAG;AACzD,UAAM,UAAU,MAAM,aAAaD,OAAK,KAAK,SAAS,IAAI,CAAC;AAC3D,UAAM,EAAE,KAAK,IAAI,iBAAiB,OAAO;AACzC,YAAQ,KAAK,EAAE,MAAM,KAAK,QAAQ,SAAS,EAAE,GAAG,KAAK,CAAC;AAAA,EACxD;AACA,SAAO;AACT;AASA,eAAsB,qBACpB,cACwB;AACxB,QAAM,UAAU,MAAM,cAAc,YAAY;AAChD,SAAO,QACJ,OAAO,CAAC,EAAE,KAAK,MAAM,KAAK,SAAS,OAAO,KAAK,UAAU,YAAY,CAAC,KAAK,QAAQ,EACnF,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO;AAAA,IACxB,OAAO,KAAK;AAAA,IACZ;AAAA,IACA,SAAS,OAAO,KAAK,YAAY,WAAW,KAAK,UAAU;AAAA,EAC7D,EAAE;AACN;AAGA,SAAS,eAAe,MAAsB;AAC5C,SAAO,KAAK,QAAQ,qBAAqB,IAAI;AAC/C;AAOA,SAAS,kBAAkB,UAAyB,SAAgC;AAClF,QAAM,QAAQ,CAAC,oBAAoB,IAAI,eAAe,EAAE;AAExD,aAAW,QAAQ,UAAU;AAC3B,UAAM,KAAK,SAAS,KAAK,IAAI,IAAI,KAAK,KAAK,eAAU,eAAe,KAAK,OAAO,CAAC,EAAE;AAAA,EACrF;AAEA,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,KAAK,IAAI,oBAAoB,EAAE;AACrC,eAAW,QAAQ,SAAS;AAC1B,YAAM,KAAK,SAAS,KAAK,IAAI,IAAI,KAAK,KAAK,eAAU,eAAe,KAAK,OAAO,CAAC,EAAE;AAAA,IACrF;AAAA,EACF;AAEA,QAAM,QAAQ,SAAS,SAAS,QAAQ;AACxC,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,IAAI,KAAK,uBAAsB,oBAAI,KAAK,GAAE,YAAY,CAAC,GAAG;AACrE,QAAM,KAAK,EAAE;AAEb,SAAO,MAAM,KAAK,IAAI;AACxB;;;AC5GA,SAAS,WAAAE,gBAAe;AACxB,OAAOC,YAAU;AAKjB,IAAM,yBAAyB;AAG/B,IAAM,oBAAoB,CAAC,SAAS,MAAM;AASnC,SAAS,gBACd,aACA,cACA,MACM;AACN,cAAY,OAAO;AACnB,cAAY,UAAU,gBAAgB,YAAY;AACpD;AAWA,SAAS,gBAAgB,OAAyB;AAChD,QAAM,UAAoB,CAAC;AAC3B,QAAM,OAAO,QAAQ,KAAK;AAE1B,MAAI,SAAS,OAAO;AAClB,YAAQ,KAAK,IAAI;AAAA,EACnB;AAEA,QAAM,YAAY,kBAAkB,KAAK;AACzC,MAAI,WAAW;AACb,YAAQ,KAAK,SAAS;AAAA,EACxB;AAEA,QAAM,eAAe,qBAAqB,KAAK;AAC/C,MAAI,cAAc;AAChB,YAAQ,KAAK,YAAY;AAAA,EAC3B;AAEA,SAAO;AACT;AAQA,SAAS,kBAAkB,OAA8B;AACvD,aAAW,eAAe,mBAAmB;AAC3C,UAAM,QAAQ,MAAM,YAAY,EAAE,QAAQ,WAAW;AACrD,QAAI,UAAU,GAAI;AAElB,UAAM,SAAS,MAAM,MAAM,GAAG,KAAK;AACnC,UAAM,QAAQ,MAAM,MAAM,QAAQ,YAAY,MAAM;AACpD,UAAM,sBAAsB,MAAM,MAAM,OAAO,QAAQ,YAAY,MAAM;AACzE,WAAO,GAAG,KAAK,GAAG,mBAAmB,GAAG,MAAM;AAAA,EAChD;AACA,SAAO;AACT;AAQA,SAAS,qBAAqB,OAA8B;AAC1D,QAAM,QAAQ,MAAM,MAAM,KAAK;AAC/B,MAAI,MAAM,SAAS,uBAAwB,QAAO;AAElD,QAAM,eAAe,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,YAAY,CAAC,EAAE,KAAK,EAAE;AACjE,MAAI,iBAAiB,MAAO,QAAO;AAEnC,SAAO;AACT;AAQA,eAAsB,YAAY,MAA6B;AAC7D,QAAM,eAAeC,OAAK,KAAK,MAAM,YAAY;AACjD,QAAM,QAAQ,MAAM,iBAAiB,YAAY;AAEjD,QAAM,YAAY,gBAAgB,KAAK;AACvC,QAAM,UAAU,gBAAgB,SAAS;AAEzC,QAAM,YAAYA,OAAK,KAAK,MAAM,QAAQ,GAAG,OAAO;AACtD;AAcA,eAAe,iBAAiB,cAA2C;AACzE,MAAI;AACJ,MAAI;AACF,YAAQ,MAAMC,SAAQ,YAAY;AAAA,EACpC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,QAAoB,CAAC;AAC3B,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,KAAK,SAAS,KAAK,EAAG;AAE3B,UAAM,UAAU,MAAM,aAAaD,OAAK,KAAK,cAAc,IAAI,CAAC;AAChE,QAAI,CAAC,QAAS;AAEd,UAAM,EAAE,KAAK,IAAI,iBAAiB,OAAO;AACzC,QAAI,KAAK,SAAU;AAEnB,UAAM,OAAO,KAAK,QAAQ,SAAS,EAAE;AACrC,UAAM,QAAQ,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;AAC5D,UAAM,OAAO,MAAM,QAAQ,KAAK,IAAI,IAAK,KAAK,OAAoB,CAAC;AACnE,UAAM,KAAK,EAAE,MAAM,OAAO,KAAK,CAAC;AAAA,EAClC;AAEA,SAAO;AACT;AAOA,SAAS,gBAAgB,OAA4C;AACnE,QAAM,SAAS,oBAAI,IAAwB;AAE3C,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,KAAK,WAAW,GAAG;AAC1B,oBAAc,QAAQ,iBAAiB,IAAI;AAC3C;AAAA,IACF;AAEA,eAAW,OAAO,KAAK,MAAM;AAC3B,oBAAc,QAAQ,KAAK,IAAI;AAAA,IACjC;AAAA,EACF;AAEA,SAAO;AACT;AAGA,SAAS,cAAc,QAAiC,KAAa,MAAsB;AACzF,QAAM,WAAW,OAAO,IAAI,GAAG;AAC/B,MAAI,UAAU;AACZ,aAAS,KAAK,IAAI;AAAA,EACpB,OAAO;AACL,WAAO,IAAI,KAAK,CAAC,IAAI,CAAC;AAAA,EACxB;AACF;AAOA,SAAS,gBAAgB,WAA4C;AACnE,QAAM,QAAkB,CAAC,oBAAoB,EAAE;AAE/C,QAAM,aAAa,CAAC,GAAG,UAAU,KAAK,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM;AAEtD,QAAI,MAAM,gBAAiB,QAAO;AAClC,QAAI,MAAM,gBAAiB,QAAO;AAClC,WAAO,EAAE,cAAc,CAAC;AAAA,EAC1B,CAAC;AAED,aAAW,OAAO,YAAY;AAC5B,UAAM,QAAQ,UAAU,IAAI,GAAG,KAAK,CAAC;AACrC,UAAM,KAAK,MAAM,GAAG,IAAI,EAAE;AAC1B,eAAW,QAAQ,MAAM,KAAK,CAAC,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,KAAK,CAAC,GAAG;AACvE,YAAM,KAAK,OAAO,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI;AAAA,IAC/C;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;;;AC3MA,SAAS,YAAAE,WAAU,WAAAC,gBAAe;AAClC,SAAS,cAAAC,mBAAkB;AAC3B,OAAOC,YAAU;AAwCV,SAAS,iBAAiB,GAAa,GAAqB;AACjE,MAAI,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAG,QAAO;AAEpD,MAAI,MAAM;AACV,MAAI,OAAO;AACX,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AACjC,WAAO,EAAE,CAAC,IAAI,EAAE,CAAC;AACjB,YAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;AAClB,YAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;AAAA,EACpB;AAEA,MAAI,SAAS,KAAK,SAAS,EAAG,QAAO;AACrC,SAAO,OAAO,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI;AAChD;AAGO,SAAS,SACd,UACA,OACA,GACkB;AAClB,QAAM,SAAS,MAAM,QAAQ,IAAI,CAAC,WAAW;AAAA,IAC3C;AAAA,IACA,OAAO,iBAAiB,UAAU,MAAM,MAAM;AAAA,EAChD,EAAE;AACF,SAAO,KAAK,CAAC,MAAM,UAAU,MAAM,QAAQ,KAAK,KAAK;AACrD,SAAO,OAAO,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK;AACpD;AAGA,eAAsB,mBAAmB,MAA8C;AACrF,QAAM,WAAWC,OAAK,KAAK,MAAM,eAAe;AAChD,MAAI,CAACC,YAAW,QAAQ,EAAG,QAAO;AAClC,QAAM,MAAM,MAAMC,UAAS,UAAU,OAAO;AAC5C,SAAO,KAAK,MAAM,GAAG;AACvB;AAGA,eAAsB,oBAAoB,MAAc,OAAsC;AAC5F,QAAM,WAAWF,OAAK,KAAK,MAAM,eAAe;AAChD,QAAM,YAAY,UAAU,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAC5D;AAMA,eAAsB,kBACpB,MACA,UACkE;AAClE,QAAM,QAAQ,MAAM,mBAAmB,IAAI;AAC3C,MAAI,CAAC,SAAS,MAAM,QAAQ,WAAW,EAAG,QAAO,CAAC;AAClD,QAAM,cAAc,sBAAsB;AAC1C,MAAI,MAAM,UAAU,aAAa;AAC/B,4BAAwB,MAAM,OAAO,WAAW;AAChD,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,WAAW,MAAM,YAAY,EAAE,MAAM,QAAQ;AACnD,SAAO,SAAS,UAAU,OAAO,eAAe,EAAE,IAAI,CAAC,WAAW;AAAA,IAChE,MAAM,MAAM;AAAA,IACZ,OAAO,MAAM;AAAA,IACb,SAAS,MAAM;AAAA,EACjB,EAAE;AACJ;AAGA,eAAe,mBAAmB,MAAqC;AACrE,QAAM,UAAwB,CAAC;AAC/B,aAAW,OAAO,CAAC,cAAc,WAAW,GAAG;AAC7C,UAAM,SAASA,OAAK,KAAK,MAAM,GAAG;AAClC,QAAI;AACJ,QAAI;AACF,cAAQ,MAAMG,SAAQ,MAAM;AAAA,IAC9B,QAAQ;AACN;AAAA,IACF;AACA,eAAW,QAAQ,MAAM,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,GAAG;AACzD,YAAM,UAAU,MAAM,aAAaH,OAAK,KAAK,QAAQ,IAAI,CAAC;AAC1D,YAAM,EAAE,KAAK,IAAI,iBAAiB,OAAO;AACzC,UAAI,KAAK,YAAY,OAAO,KAAK,UAAU,SAAU;AACrD,cAAQ,KAAK;AAAA,QACX,MAAM,KAAK,QAAQ,SAAS,EAAE;AAAA,QAC9B,OAAO,KAAK;AAAA,QACZ,SAAS,OAAO,KAAK,YAAY,WAAW,KAAK,UAAU;AAAA,MAC7D,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO;AACT;AAGA,SAAS,mBAAmB,QAA4B;AACtD,SAAO,OAAO,UACV,GAAG,OAAO,KAAK;AAAA;AAAA,EAAO,OAAO,OAAO,KACpC,OAAO;AACb;AAMA,eAAe,WACb,SACA,cAC2B;AAC3B,QAAM,WAAW,YAAY;AAC7B,QAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,QAAM,QAA0B,CAAC;AAEjC,aAAW,UAAU,SAAS;AAC5B,QAAI,CAAC,aAAa,IAAI,OAAO,IAAI,EAAG;AACpC,UAAM,SAAS,MAAM,SAAS,MAAM,mBAAmB,MAAM,CAAC;AAC9D,UAAM,KAAK;AAAA,MACT,MAAM,OAAO;AAAA,MACb,OAAO,OAAO;AAAA,MACd,SAAS,OAAO;AAAA,MAChB;AAAA,MACA,WAAW;AAAA,IACb,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAGA,IAAM,oBAAoB,oBAAI,IAAY;AAG1C,SAAS,wBAAwB,aAAqB,aAA2B;AAC/E,QAAM,MAAM,GAAG,WAAW,SAAI,WAAW;AACzC,MAAI,kBAAkB,IAAI,GAAG,EAAG;AAChC,oBAAkB,IAAI,GAAG;AACzB,EAAO;AAAA,IACL;AAAA,IACO;AAAA,MACL,mCAAmC,WAAW,oCAAoC,WAAW;AAAA,IAE/F;AAAA,EACF;AACF;AAQO,SAAS,wBAAgC;AAC9C,QAAM,eAAe,sBAAsB;AAC3C,QAAM,kBAAkB,QAAQ,IAAI,yBAAyB,KAAK;AAClE,MAAI,oBAAoB,iBAAiB,YAAY,iBAAiB,WAAW;AAC/E,WAAO;AAAA,EACT;AACA,SAAO,iBAAiB,YAAY,KAAK,iBAAiB;AAC5D;AAGA,SAAS,aACP,UACA,OACA,WACkB;AAClB,QAAM,SAAS,oBAAI,IAA4B;AAC/C,aAAW,SAAS,UAAU;AAC5B,QAAI,UAAU,IAAI,MAAM,IAAI,EAAG,QAAO,IAAI,MAAM,MAAM,KAAK;AAAA,EAC7D;AACA,aAAW,SAAS,OAAO;AACzB,WAAO,IAAI,MAAM,MAAM,KAAK;AAAA,EAC9B;AACA,SAAO,MAAM,KAAK,OAAO,OAAO,CAAC;AACnC;AAMA,eAAsB,iBAAiB,MAAc,cAAuC;AAC1F,QAAM,UAAU,MAAM,mBAAmB,IAAI;AAC7C,QAAM,YAAY,IAAI,IAAI,QAAQ,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AACpD,QAAM,iBAAiB,sBAAsB;AAC7C,QAAM,gBAAgB,MAAM,mBAAmB,IAAI;AACnD,QAAM,eAAe,QAAQ,iBAAiB,cAAc,UAAU,cAAc;AACpF,QAAM,UAAU,IAAI,IAAI,aAAa,OAAO,CAAC,SAAS,UAAU,IAAI,IAAI,CAAC,CAAC;AAC1E,QAAM,kBAAkB,eAAe,CAAC,IAAI,eAAe,WAAW,CAAC;AAGvE,MAAI,CAAC,iBAAiB,cAAc;AAClC,eAAW,UAAU,QAAS,SAAQ,IAAI,OAAO,IAAI;AAAA,EACvD;AAEA,MAAI,CAAC,gBAAgB,QAAQ,SAAS,KAAK,gBAAgB,MAAM,CAAC,MAAM,UAAU,IAAI,EAAE,IAAI,CAAC,GAAG;AAC9F;AAAA,EACF;AAEA,QAAM,eAAe,MAAM,WAAW,SAAS,OAAO;AACtD,QAAM,gBAAgB,aAAa,iBAAiB,cAAc,SAAS;AAE3E,QAAM,aAAa,cAAc,CAAC,GAAG,OAAO,UAAU;AACtD,QAAM,QAAwB;AAAA,IAC5B,SAAS;AAAA,IACT,OAAO;AAAA,IACP;AAAA,IACA,SAAS;AAAA,EACX;AACA,QAAM,oBAAoB,MAAM,KAAK;AACrC,EAAO,OAAO,KAAY,IAAI,uBAAuB,cAAc,MAAM,UAAU,CAAC;AACtF;;;ACvPA,SAAS,WAAAI,UAAS,UAAAC,SAAQ,UAAAC,SAAQ,aAAAC,YAAW,SAAAC,cAAa;AAC1D,SAAS,cAAAC,mBAAkB;AAC3B,OAAOC,YAAU;AACjB,SAAS,mBAAmB;AAW5B,IAAM,kBAAkB;AAGxB,IAAM,gBAAgB;AAuBtB,SAAS,iBAAiB,MAAsB;AAC9C,QAAM,SAAS,YAAY,eAAe,EAAE,SAAS,KAAK;AAC1D,SAAO,GAAG,IAAI,IAAI,MAAM;AAC1B;AAGA,SAAS,cAAc,MAAc,IAAoB;AACvD,SAAOC,OAAK,KAAK,MAAM,gBAAgB,GAAG,EAAE,GAAG,aAAa,EAAE;AAChE;AAGA,SAAS,YAAY,MAAc,IAAoB;AACrD,SAAOA,OAAK,KAAK,MAAM,wBAAwB,GAAG,EAAE,GAAG,aAAa,EAAE;AACxE;AASA,eAAsB,eACpB,MACA,OAC0B;AAC1B,QAAM,YAA6B;AAAA,IACjC,IAAI,iBAAiB,MAAM,IAAI;AAAA,IAC/B,OAAO,MAAM;AAAA,IACb,MAAM,MAAM;AAAA,IACZ,SAAS,MAAM;AAAA,IACf,SAAS,MAAM;AAAA,IACf,MAAM,MAAM;AAAA,IACZ,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,IACpC,GAAI,MAAM,eAAe,EAAE,cAAc,MAAM,aAAa,IAAI,CAAC;AAAA,IACjE,GAAI,MAAM,mBAAmB,EAAE,kBAAkB,MAAM,iBAAiB,IAAI,CAAC;AAAA,EAC/E;AAEA,QAAM,YAAY,cAAc,MAAM,UAAU,EAAE,GAAG,KAAK,UAAU,WAAW,MAAM,CAAC,CAAC;AACvF,SAAO;AACT;AAOA,SAAS,cAAc,SAAuB;AAC5C,EAAO,OAAO,KAAY,MAAM,OAAO,CAAC;AACxC,UAAQ,WAAW;AACnB,SAAO;AACT;AAUA,eAAsB,oBACpB,MACA,IACiC;AACjC,QAAM,YAAY,MAAM,cAAc,MAAM,EAAE;AAC9C,MAAI,CAAC,UAAW,QAAO,cAAc,wBAAwB,EAAE,EAAE;AACjE,SAAO;AACT;AAaA,eAAsB,6BACpB,MACA,IACiC;AACjC,QAAM,YAAY,MAAM,cAAc,MAAM,EAAE;AAC9C,MAAI,CAAC,WAAW;AACd,WAAO,cAAc,aAAa,EAAE,gDAAgD;AAAA,EACtF;AACA,SAAO;AACT;AAGA,eAAsB,cACpB,MACA,IACiC;AACjC,QAAM,MAAM,MAAM,aAAa,cAAc,MAAM,EAAE,CAAC;AACtD,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,QAAI,CAAC,iBAAiB,MAAM,EAAG,QAAO;AACtC,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,SAAS,iBAAiB,OAA0C;AAClE,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,YAAY;AAClB,SACE,OAAO,UAAU,OAAO,YACxB,OAAO,UAAU,UAAU,YAC3B,OAAO,UAAU,SAAS,YAC1B,OAAO,UAAU,SAAS,YAC1B,MAAM,QAAQ,UAAU,OAAO;AAEnC;AAQA,eAAsB,eAAe,MAA0C;AAC7E,QAAM,MAAMA,OAAK,KAAK,MAAM,cAAc;AAC1C,MAAI,CAACC,YAAW,GAAG,EAAG,QAAO,CAAC;AAE9B,QAAM,UAAU,MAAMC,SAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAC1D,QAAM,aAAgC,CAAC;AACvC,aAAW,SAAS,SAAS;AAC3B,QAAI,CAAC,MAAM,OAAO,KAAK,CAAC,MAAM,KAAK,SAAS,aAAa,EAAG;AAC5D,UAAM,KAAK,MAAM,KAAK,MAAM,GAAG,CAAC,cAAc,MAAM;AACpD,UAAM,YAAY,MAAM,cAAc,MAAM,EAAE;AAC9C,QAAI,UAAW,YAAW,KAAK,SAAS;AAAA,EAC1C;AAEA,aAAW,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,cAAc,EAAE,WAAW,CAAC;AACpE,SAAO;AACT;AAQA,eAAsB,gBAAgB,MAA+B;AACnE,QAAM,aAAa,MAAM,eAAe,IAAI;AAC5C,SAAO,WAAW;AACpB;AAGA,eAAsB,gBAAgB,MAAc,IAA8B;AAChF,QAAM,WAAW,cAAc,MAAM,EAAE;AACvC,MAAI,CAACD,YAAW,QAAQ,EAAG,QAAO;AAClC,QAAME,QAAO,QAAQ;AACrB,SAAO;AACT;AASA,eAAsB,iBAAiB,MAAc,IAA8B;AACjF,QAAM,aAAa,cAAc,MAAM,EAAE;AACzC,MAAI,CAACF,YAAW,UAAU,EAAG,QAAO;AAEpC,QAAM,SAAS,YAAY,MAAM,EAAE;AACnC,QAAMG,OAAMJ,OAAK,QAAQ,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;AAErD,MAAI;AACF,UAAMK,QAAO,YAAY,MAAM;AAAA,EACjC,QAAQ;AACN,UAAM,MAAM,MAAM,aAAa,UAAU;AACzC,UAAMC,WAAU,QAAQ,KAAK,OAAO;AACpC,UAAMH,QAAO,UAAU;AAAA,EACzB;AACA,SAAO;AACT;;;ACtOA,SAAS,WAAAI,UAAS,YAAAC,iBAAgB;AAClC,SAAS,cAAAC,mBAAkB;AAC3B,OAAOC,YAAU;AAuBjB,IAAM,kBAAkB;AAGxB,IAAMC,oBAAmB;AAGzB,IAAM,mBAAmB;AAYzB,SAAS,qBAAqB,SAAiB,SAA8B;AAC3E,QAAM,UAAuB,CAAC;AAC9B,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,UAAU,MAAM,CAAC,EAAE,SAAS,OAAO;AACzC,eAAW,SAAS,SAAS;AAC3B,cAAQ,KAAK,EAAE,UAAU,MAAM,CAAC,GAAG,MAAM,IAAI,EAAE,CAAC;AAAA,IAClD;AAAA,EACF;AACA,SAAO;AACT;AAMA,eAAe,kBACb,SACuD;AACvD,MAAI,CAACC,YAAW,OAAO,EAAG,QAAO,CAAC;AAElC,QAAM,UAAU,MAAMC,SAAQ,OAAO;AACrC,QAAM,UAAU,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC;AAEvD,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,QAAQ,IAAI,OAAO,aAAa;AAC9B,YAAM,WAAWC,OAAK,KAAK,SAAS,QAAQ;AAC5C,YAAM,UAAU,MAAMC,UAAS,UAAU,OAAO;AAChD,aAAO,EAAE,UAAU,QAAQ;AAAA,IAC7B,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAKA,eAAe,gBACb,MACuD;AACvD,QAAM,eAAe,MAAM,kBAAkBD,OAAK,KAAK,MAAM,YAAY,CAAC;AAC1E,QAAM,aAAa,MAAM,kBAAkBA,OAAK,KAAK,MAAM,WAAW,CAAC;AACvE,SAAO,CAAC,GAAG,cAAc,GAAG,UAAU;AACxC;AAMA,SAAS,iBACP,OACa;AACb,QAAM,QAAQ,oBAAI,IAAY;AAC9B,aAAW,QAAQ,OAAO;AACxB,UAAM,WAAWA,OAAK,SAAS,KAAK,UAAU,KAAK;AACnD,UAAM,IAAI,SAAS,YAAY,CAAC;AAAA,EAClC;AACA,SAAO;AACT;AAGA,eAAsB,qBAAqB,MAAqC;AAC9E,QAAM,QAAQ,MAAM,gBAAgB,IAAI;AACxC,QAAM,gBAAgB,iBAAiB,KAAK;AAC5C,QAAM,UAAwB,CAAC;AAE/B,aAAW,QAAQ,OAAO;AACxB,eAAW,EAAE,UAAU,KAAK,KAAK,qBAAqB,KAAK,SAASH,iBAAgB,GAAG;AACrF,YAAM,WAAW,QAAQ,QAAQ;AACjC,UAAI,CAAC,cAAc,IAAI,QAAQ,GAAG;AAChC,gBAAQ,KAAK;AAAA,UACX,MAAM;AAAA,UACN,UAAU;AAAA,UACV,MAAM,KAAK;AAAA,UACX,SAAS,qBAAqB,QAAQ;AAAA,UACtC;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAGA,eAAsB,mBAAmB,MAAqC;AAC5E,QAAM,QAAQ,MAAM,gBAAgB,IAAI;AACxC,QAAM,UAAwB,CAAC;AAE/B,aAAW,QAAQ,OAAO;AACxB,UAAM,EAAE,KAAK,IAAI,iBAAiB,KAAK,OAAO;AAC9C,QAAI,KAAK,aAAa,MAAM;AAC1B,cAAQ,KAAK;AAAA,QACX,MAAM;AAAA,QACN,UAAU;AAAA,QACV,MAAM,KAAK;AAAA,QACX,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAGA,eAAsB,sBAAsB,MAAqC;AAC/E,QAAM,QAAQ,MAAM,gBAAgB,IAAI;AACxC,QAAM,UAAwB,CAAC;AAE/B,aAAW,QAAQ,OAAO;AACxB,UAAM,EAAE,KAAK,IAAI,iBAAiB,KAAK,OAAO;AAC9C,UAAM,UAAU,KAAK;AACrB,UAAM,YAAY,CAAC,WAAY,OAAO,YAAY,YAAY,QAAQ,KAAK,MAAM;AAEjF,QAAI,WAAW;AACb,cAAQ,KAAK;AAAA,QACX,MAAM;AAAA,QACN,UAAU;AAAA,QACV,MAAM,KAAK;AAAA,QACX,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAGA,eAAsB,uBAAuB,MAAqC;AAChF,QAAM,QAAQ,MAAM,gBAAgB,IAAI;AACxC,QAAM,WAAW,oBAAI,IAAsB;AAE3C,aAAW,QAAQ,OAAO;AACxB,UAAM,EAAE,KAAK,IAAI,iBAAiB,KAAK,OAAO;AAC9C,UAAM,QAAQ,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;AAC5D,QAAI,CAAC,MAAO;AAEZ,UAAM,kBAAkB,MAAM,YAAY,EAAE,KAAK;AACjD,UAAM,WAAW,SAAS,IAAI,eAAe,KAAK,CAAC;AACnD,aAAS,KAAK,KAAK,QAAQ;AAC3B,aAAS,IAAI,iBAAiB,QAAQ;AAAA,EACxC;AAEA,QAAM,UAAwB,CAAC;AAC/B,aAAW,CAAC,OAAO,KAAK,KAAK,UAAU;AACrC,QAAI,MAAM,UAAU,EAAG;AACvB,eAAW,QAAQ,OAAO;AACxB,cAAQ,KAAK;AAAA,QACX,MAAM;AAAA,QACN,UAAU;AAAA,QACV;AAAA,QACA,SAAS,oBAAoB,KAAK,oBAAe,MAAM,OAAO,CAAC,MAAM,MAAM,IAAI,EAAE,KAAK,IAAI,CAAC;AAAA,MAC7F,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAGA,eAAsB,gBAAgB,MAAqC;AACzE,QAAM,QAAQ,MAAM,gBAAgB,IAAI;AACxC,QAAM,UAAwB,CAAC;AAE/B,aAAW,QAAQ,OAAO;AACxB,UAAM,EAAE,MAAM,KAAK,IAAI,iBAAiB,KAAK,OAAO;AACpD,UAAM,WAAW,OAAO,KAAK,UAAU,YAAY,KAAK,MAAM,KAAK,MAAM;AACzE,UAAM,cAAc,KAAK,KAAK,EAAE,SAAS;AAEzC,QAAI,YAAY,aAAa;AAC3B,cAAQ,KAAK;AAAA,QACX,MAAM;AAAA,QACN,UAAU;AAAA,QACV,MAAM,KAAK;AAAA,QACX,SAAS,sCAAsC,eAAe;AAAA,MAChE,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAGA,SAAS,gBAAgB,OAAuB;AAC9C,QAAM,WAAW,MAAM,QAAQ,GAAG;AAClC,QAAM,UAAU,MAAM,QAAQ,GAAG;AACjC,QAAM,OAAO,CAAC,UAAU,OAAO,EAAE,OAAO,CAAC,MAAM,KAAK,CAAC;AACrD,MAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,SAAO,MAAM,MAAM,GAAG,KAAK,IAAI,GAAG,IAAI,CAAC;AACzC;AAOA,eAAsB,wBAAwB,MAAqC;AACjF,QAAM,QAAQ,MAAM,gBAAgB,IAAI;AACxC,QAAM,UAAwB,CAAC;AAE/B,aAAW,QAAQ,OAAO;AACxB,UAAM,EAAE,KAAK,IAAI,iBAAiB,KAAK,OAAO;AAC9C,UAAM,EAAE,WAAW,IAAI,wBAAwB,IAAI;AACnD,QAAI,eAAe,UAAa,cAAc,yBAA0B;AACxE,YAAQ,KAAK;AAAA,MACX,MAAM;AAAA,MACN,UAAU;AAAA,MACV,MAAM,KAAK;AAAA,MACX,SAAS,mBAAmB,WAAW,QAAQ,CAAC,CAAC,aAAa,wBAAwB;AAAA,IACxF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAGA,eAAsB,uBAAuB,MAAqC;AAChF,QAAM,QAAQ,MAAM,gBAAgB,IAAI;AACxC,QAAM,UAAwB,CAAC;AAE/B,aAAW,QAAQ,OAAO;AACxB,UAAM,EAAE,KAAK,IAAI,iBAAiB,KAAK,OAAO;AAC9C,UAAM,EAAE,eAAe,IAAI,wBAAwB,IAAI;AACvD,QAAI,CAAC,kBAAkB,eAAe,WAAW,EAAG;AACpD,UAAM,QAAQ,eAAe,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,IAAI;AACzD,YAAQ,KAAK;AAAA,MACX,MAAM;AAAA,MACN,UAAU;AAAA,MACV,MAAM,KAAK;AAAA,MACX,SAAS,qBAAqB,KAAK;AAAA,IACrC,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAOA,eAAsB,8BAA8B,MAAqC;AACvF,QAAM,QAAQ,MAAM,gBAAgB,IAAI;AACxC,QAAM,UAAwB,CAAC;AAE/B,aAAW,QAAQ,OAAO;AACxB,UAAM,EAAE,MAAM,KAAK,IAAI,iBAAiB,KAAK,OAAO;AACpD,UAAM,aAAa,wBAAwB,IAAI;AAC/C,UAAM,WAAW,WAAW,sBAAsB,4BAA4B,IAAI;AAClF,QAAI,YAAY,0CAA2C;AAC3D,YAAQ,KAAK;AAAA,MACX,MAAM;AAAA,MACN,UAAU;AAAA,MACV,MAAM,KAAK;AAAA,MACX,SAAS,YAAY,QAAQ,+CAA+C,yCAAyC;AAAA,IACvH,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAGA,IAAM,uBAAuB;AAG7B,SAAS,4BAA4B,MAAsB;AACzD,QAAM,aAAa,KAAK,MAAM,SAAS;AACvC,MAAI,QAAQ;AACZ,aAAW,SAAS,YAAY;AAC9B,UAAM,UAAU,MAAM,KAAK;AAC3B,QAAI,QAAQ,WAAW,EAAG;AAC1B,QAAI,CAAC,qBAAqB,KAAK,OAAO,EAAG;AACzC,QAAI,iBAAiB,KAAK,OAAO,GAAG;AAClC,uBAAiB,YAAY;AAC7B;AAAA,IACF;AACA,qBAAiB,YAAY;AAC7B,aAAS;AAAA,EACX;AACA,SAAO;AACT;AAGA,IAAM,qBAAqB;AAG3B,IAAM,oBAAoB;AAiB1B,eAAsB,sBACpB,MACA,QACuB;AACvB,QAAM,QAAQ,MAAM,gBAAgB,IAAI;AACxC,QAAM,UAAwB,CAAC;AAE/B,aAAW,QAAQ,OAAO;AACxB,UAAM,EAAE,MAAM,KAAK,IAAI,iBAAiB,KAAK,OAAO;AACpD,UAAM,OAAO,gBAAgB,KAAK,MAAM,MAAM;AAC9C,UAAM,OAAO,OAAO,MAAM,IAAI;AAC9B,QAAI,KAAK,gBAAgB,EAAG;AAE5B,UAAM,YAAY,eAAe,IAAI;AACrC,QAAI,aAAa,KAAK,aAAc;AAEpC,YAAQ,KAAK;AAAA,MACX,MAAM;AAAA,MACN,UAAU;AAAA,MACV,MAAM,KAAK;AAAA,MACX,SACE,cAAc,IAAI,uBAAuB,KAAK,YAAY,2BAChC,SAAS;AAAA,IACvC,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAkBO,SAAS,oBACd,SACA,UACA,QACc;AACd,QAAM,EAAE,MAAM,KAAK,IAAI,iBAAiB,OAAO;AAC/C,QAAM,OAAO,gBAAgB,KAAK,MAAM,MAAM;AAC9C,QAAM,OAAO,OAAO,MAAM,IAAI;AAC9B,MAAI,KAAK,gBAAgB,EAAG,QAAO,CAAC;AAEpC,QAAM,YAAY,eAAe,IAAI;AACrC,MAAI,aAAa,KAAK,aAAc,QAAO,CAAC;AAE5C,SAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,UAAU;AAAA,MACV,MAAM;AAAA,MACN,SACE,cAAc,IAAI,uBAAuB,KAAK,YAAY,2BAChC,SAAS;AAAA,IACvC;AAAA,EACF;AACF;AAGA,SAAS,eAAe,OAAuC;AAC7D,QAAM,aAAa,mBAAmB,KAAK,KAAK;AAChD,MAAI,YAAY;AACd,UAAM,QAAQ,OAAO,WAAW,CAAC,CAAC;AAClC,UAAM,MAAM,WAAW,CAAC,MAAM,SAAY,OAAO,WAAW,CAAC,CAAC,IAAI;AAClE,WAAO,EAAE,OAAO,IAAI;AAAA,EACtB;AACA,QAAM,YAAY,kBAAkB,KAAK,KAAK;AAC9C,MAAI,WAAW;AACb,UAAM,QAAQ,OAAO,UAAU,CAAC,CAAC;AACjC,UAAM,MAAM,UAAU,CAAC,MAAM,SAAY,OAAO,UAAU,CAAC,CAAC,IAAI;AAChE,WAAO,EAAE,OAAO,IAAI;AAAA,EACtB;AACA,SAAO;AACT;AAGA,SAAS,WAAW,SAAyB;AAC3C,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,SAAO,QAAQ,MAAM,IAAI,EAAE;AAC7B;AASA,eAAsB,qBAAqB,MAAqC;AAC9E,QAAM,QAAQ,MAAM,gBAAgB,IAAI;AACxC,QAAM,aAAaG,OAAK,KAAK,MAAM,WAAW;AAC9C,QAAM,UAAwB,CAAC;AAE/B,QAAM,iBAAiB,oBAAI,IAAoB;AAE/C,aAAW,QAAQ,OAAO;AACxB,eAAW,EAAE,UAAU,KAAK,KAAK,qBAAqB,KAAK,SAAS,gBAAgB,GAAG;AACrF,YAAM,uBAAuB,UAAU,MAAM,KAAK,UAAU,YAAY,gBAAgB,OAAO;AAAA,IACjG;AAAA,EACF;AAEA,SAAO;AACT;AAGA,eAAe,uBACb,UACA,MACA,UACA,YACA,gBACA,KACe;AACf,aAAW,QAAQ,SAAS,MAAM,GAAG,GAAG;AACtC,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,QAAQ,WAAW,EAAG;AAC1B,UAAM,WAAW,gBAAgB,OAAO;AACxC,UAAM,YAAYA,OAAK,KAAK,YAAY,QAAQ;AAChD,QAAI,CAACF,YAAW,SAAS,GAAG;AAC1B,UAAI,KAAK;AAAA,QACP,MAAM;AAAA,QACN,UAAU;AAAA,QACV,MAAM;AAAA,QACN,SAAS,qBAAqB,QAAQ;AAAA,QACtC;AAAA,MACF,CAAC;AACD;AAAA,IACF;AACA,UAAM,QAAQ,eAAe,OAAO;AACpC,QAAI,UAAU,KAAM;AACpB,UAAM,YAAY,MAAM,iBAAiB,WAAW,UAAU,cAAc;AAC5E,QAAI,MAAM,OAAO,UAAW;AAC5B,QAAI,KAAK;AAAA,MACP,MAAM;AAAA,MACN,UAAU;AAAA,MACV,MAAM;AAAA,MACN,SAAS,sBAAsB,OAAO,uCAAuC,SAAS;AAAA,MACtF;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAGA,eAAe,iBACb,WACA,UACA,OACiB;AACjB,QAAM,SAAS,MAAM,IAAI,QAAQ;AACjC,MAAI,WAAW,OAAW,QAAO;AACjC,QAAM,UAAU,MAAM,aAAa,SAAS;AAC5C,QAAM,YAAY,WAAW,OAAO;AACpC,QAAM,IAAI,UAAU,SAAS;AAC7B,SAAO;AACT;AAOA,eAAsB,6BAA6B,MAAqC;AACtF,QAAM,QAAQ,MAAM,gBAAgB,IAAI;AACxC,QAAM,UAAwB,CAAC;AAE/B,aAAW,QAAQ,OAAO;AACxB,eAAW,EAAE,UAAU,KAAK,KAAK,qBAAqB,KAAK,SAAS,gBAAgB,GAAG;AACrF,iBAAW,QAAQ,SAAS,MAAM,GAAG,GAAG;AACtC,YAAI,CAAC,yBAAyB,IAAI,EAAG;AACrC,gBAAQ,KAAK;AAAA,UACX,MAAM;AAAA,UACN,UAAU;AAAA,UACV,MAAM,KAAK;AAAA,UACX,SAAS,8BAA8B,QAAQ;AAAA,UAC/C;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;AC3hBA,SAAS,WAAAI,gBAAe;AACxB,OAAOC,YAAU;;;ACQV,SAAS,kBACd,QACA,SACM;AACN,MAAI,OAAO,QAAQ,eAAe,UAAU;AAC1C,WAAO,aAAa,QAAQ;AAAA,EAC9B;AACA,MAAI,QAAQ,iBAAiB;AAC3B,WAAO,kBAAkB,QAAQ;AAAA,EACnC;AACA,MAAI,QAAQ,kBAAkB,QAAQ,eAAe,SAAS,GAAG;AAC/D,WAAO,iBAAiB,QAAQ;AAAA,EAClC;AACA,MAAI,OAAO,QAAQ,uBAAuB,UAAU;AAClD,WAAO,qBAAqB,QAAQ;AAAA,EACtC;AACF;AAQO,SAAS,4BACd,cACA,SACM;AACN,QAAM,OAAO,QAAQ;AACrB,MAAI,CAAC,QAAQ,KAAK,WAAW,EAAG;AAChC,QAAM,QAAQ,KAAK,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,IAAI;AAC/C,EAAO;AAAA,IACL;AAAA,IACO,KAAK,8BAA8B,YAAY,4BAAuB,KAAK,EAAE;AAAA,EACtF;AACF;;;AD5BA,IAAM,6BAA6B;AAkBnC,eAAsB,wBACpB,MACA,OACA,QACiB;AACjB,QAAM,WAAWC,OAAK,KAAK,MAAM,cAAc,GAAG,MAAM,IAAI,KAAK;AACjE,QAAM,eAAe,MAAM,aAAa,QAAQ;AAChD,QAAM,eAAe,MAAM,iBAAiB,MAAM,MAAM,IAAI;AAE5D,QAAM,SAAS;AAAA,IACb,MAAM,QAAQ;AAAA,IACd,MAAM;AAAA,IACN;AAAA,IACA;AAAA,EACF;AAEA,QAAM,WAAW,MAAM,WAAW;AAAA,IAChC;AAAA,IACA,UAAU;AAAA,MACR,EAAE,MAAM,QAAQ,SAAS,4BAA4B,MAAM,QAAQ,OAAO,KAAK;AAAA,IACjF;AAAA,EACF,CAAC;AAED,QAAM,cAAc,uBAAuB,OAAO,cAAc,MAAM;AACtE,8BAA4B,MAAM,QAAQ,SAAS,MAAM,OAAO;AAChE,SAAO,GAAG,WAAW;AAAA;AAAA,EAAO,QAAQ;AAAA;AACtC;AAMA,SAAS,uBACP,OACA,cACA,QACQ;AACR,QAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,QAAM,WAAW,eAAe,iBAAiB,YAAY,IAAI;AACjE,QAAM,YAAa,UAAU,KAAK,aAAa,OAAO,SAAS,KAAK,cAAc,WAC9E,SAAS,KAAK,YACd;AACJ,QAAM,oBAA6C;AAAA,IACjD,OAAO,MAAM,QAAQ;AAAA,IACrB,SAAS,MAAM,QAAQ;AAAA,IACvB,SAAS,MAAM;AAAA,IACf,MAAM,OAAO;AAAA,IACb;AAAA,IACA,WAAW;AAAA,EACb;AACA,kBAAgB,mBAAmB,MAAM,QAAQ,SAAS,MAAM,QAAQ,QAAQ,CAAC,CAAC;AAClF,oBAAkB,mBAAmB,MAAM,OAAO;AAClD,SAAO,iBAAiB,iBAAiB;AAC3C;AASA,eAAe,iBAAiB,MAAc,aAAsC;AAClF,QAAM,eAAeA,OAAK,KAAK,MAAM,YAAY;AACjD,MAAI;AAEJ,MAAI;AACF,YAAQ,MAAMC,SAAQ,YAAY;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,MACb,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,KAAK,MAAM,GAAG,WAAW,KAAK,EAC5D,MAAM,GAAG,0BAA0B;AAEtC,QAAM,WAAqB,CAAC;AAC5B,aAAW,KAAK,SAAS;AACvB,UAAM,UAAU,MAAM,aAAaD,OAAK,KAAK,cAAc,CAAC,CAAC;AAC7D,QAAI,CAAC,QAAS;AACd,UAAM,EAAE,KAAK,IAAI,iBAAiB,OAAO;AACzC,QAAI,KAAK,SAAU;AACnB,aAAS,KAAK,OAAO;AAAA,EACvB;AAEA,SAAO,SAAS,KAAK,aAAa;AACpC;;;AzBxEA,OAAO,YAAY;AAgBnB,SAAS,qBAAoC;AAC3C,SAAO,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC,GAAG,OAAO,CAAC,GAAG,QAAQ,CAAC,EAAE;AACpF;AASA,eAAsB,QAAQ,MAAc,UAA0B,CAAC,GAAkB;AACvF,QAAM,iBAAiB,MAAM,OAAO;AACtC;AAWA,eAAsB,iBACpB,MACA,UAA0B,CAAC,GACH;AACxB,EAAO,OAAO,iBAAiB;AAE/B,QAAM,SAAS,MAAM,YAAY,IAAI;AACrC,MAAI,CAAC,QAAQ;AACX,IAAO,OAAO,KAAY,MAAM,0CAA0C,CAAC;AAC3E,WAAO;AAAA,MACL,GAAG,mBAAmB;AAAA,MACtB,QAAQ,CAAC,wEAAmE;AAAA,IAC9E;AAAA,EACF;AAEA,MAAI;AACF,WAAO,MAAM,mBAAmB,MAAM,OAAO;AAAA,EAC/C,UAAE;AACA,UAAM,YAAY,IAAI;AAAA,EACxB;AACF;AAUA,SAAS,cAAc,SAAwC;AAC7D,SAAO;AAAA,IACL,WAAW,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,EAAE,WAAW,SAAS;AAAA,IAC7E,SAAS,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS;AAAA,IACrD,WAAW,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,WAAW;AAAA,EAC3D;AACF;AAWA,eAAe,mBACb,MACA,aACA,aACA,QACA,SAC+B;AAC/B,QAAM,SAAS,iBAAiB,aAAa,WAAW;AAGxD,QAAM,eAAe,QAAQ,SACzB,MAAM,4BAA4B,MAAM,WAAW,IACnD,CAAC;AACL,QAAM,QAAQ,OAAO,mBAAmB;AACxC,QAAM,SAAmB,CAAC;AAC1B,QAAM,aAAuB,CAAC;AAC9B,QAAM,QAAQ,MAAM,QAAQ;AAAA,IAC1B,OAAO,IAAI,CAAC,UAAU,MAAM,YAAY;AACtC,YAAM,SAAS,MAAM,mBAAmB,MAAM,OAAO,QAAQ,SAAS,YAAY;AAClF,UAAI,OAAO,MAAO,QAAO,KAAK,OAAO,KAAK;AAC1C,UAAI,OAAO,YAAa,YAAW,KAAK,OAAO,WAAW;AAC1D,aAAO;AAAA,IACT,CAAC,CAAC;AAAA,EACJ;AACA,SAAO,EAAE,OAAO,QAAQ,WAAW;AACrC;AAGA,eAAe,wBACb,MACA,aACe;AACf,aAAW,UAAU,aAAa;AAChC,QAAI,OAAO,SAAS,WAAW,EAAG;AAClC,UAAM,mBAAmB,MAAM,OAAO,YAAY,OAAO,YAAY,OAAO,QAAQ;AAAA,EACtF;AACF;AAGA,SAAS,iBACP,SACA,YACA,aACA,SACe;AACf,EAAO,OAAO,sBAAsB;AACpC,EAAO,OAAO,UAAY;AAAA,IACxB,GAAG,QAAQ,UAAU,MAAM,cAAc,QAAQ,UAAU,MAAM,aAAa,QAAQ,QAAQ,MAAM;AAAA,EACtG,CAAC;AACD,MAAI,QAAQ,UAAU,WAAW,WAAW,SAAS,GAAG;AACtD,IAAO,OAAO,KAAY;AAAA,MACxB,GAAG,WAAW,WAAW,MAAM;AAAA,IACjC,CAAC;AAAA,EACH,WAAW,QAAQ,UAAU,SAAS,GAAG;AACvC,IAAO,OAAO,UAAY,IAAI,0CAA0C,CAAC;AAAA,EAC3E;AAEA,QAAM,SAAS,CAAC,GAAG,WAAW,MAAM;AACpC,aAAW,UAAU,aAAa;AAChC,QAAI,OAAO,SAAS,WAAW,GAAG;AAChC,aAAO,KAAK,8BAA8B,OAAO,UAAU,EAAE;AAAA,IAC/D;AAAA,EACF;AAEA,QAAM,aAA4B;AAAA,IAChC,UAAU,QAAQ,UAAU;AAAA,IAC5B,SAAS,QAAQ,UAAU;AAAA,IAC3B,SAAS,QAAQ,QAAQ;AAAA,IACzB,UAAU,WAAW,MAAM,IAAI,CAAC,UAAU,MAAM,QAAQ,OAAO;AAAA,IAC/D,OAAO,WAAW,MAAM,IAAI,CAAC,UAAU,MAAM,IAAI;AAAA,IACjD;AAAA,EACF;AACA,MAAI,QAAQ,QAAQ;AAClB,eAAW,aAAa,WAAW;AAAA,EACrC;AACA,SAAO;AACT;AAGA,eAAe,mBACb,MACA,SACwB;AACxB,QAAM,SAAS,MAAM,WAAW,IAAI;AACpC,qBAAmB,MAAM;AACzB,QAAM,QAAQ,MAAM,UAAU,IAAI;AAClC,QAAM,UAAU,MAAM,cAAc,MAAM,KAAK;AAC/C,6BAA2B,SAAS,oBAAoB,OAAO,OAAO,CAAC;AAEvE,QAAM,UAAU,cAAc,OAAO;AACrC,MAAI,QAAQ,UAAU,WAAW,KAAK,QAAQ,QAAQ,WAAW,GAAG;AAClE,IAAO,OAAO,UAAY,QAAQ,mDAA8C,CAAC;AAIjF,QAAI,CAAC,QAAQ,QAAQ;AACnB,YAAM,kBAAwC,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,GAAG,YAAY,CAAC,EAAE;AACtF,YAAM,kBAAkB,MAAM,QAAQ,eAAe;AAGrD,YAAM,aAAa,MAAM,gBAAgB,KAAK;AAC9C,aAAO;AAAA,QACL,GAAG,mBAAmB;AAAA,QACtB,SAAS,QAAQ,UAAU;AAAA,QAC3B,QAAQ,gBAAgB;AAAA,MAC1B;AAAA,IACF;AACA,WAAO,EAAE,GAAG,mBAAmB,GAAG,SAAS,QAAQ,UAAU,OAAO;AAAA,EACtE;AAEA,sBAAoB,OAAO;AAQ3B,MAAI,CAAC,QAAQ,QAAQ;AACnB,UAAM,sBAAsB,MAAM,QAAQ,SAAS,KAAK;AAAA,EAC1D;AAEA,QAAM,cAAc,gBAAgB,OAAO,OAAO;AAClD,oBAAkB,WAAW;AAE7B,QAAM,cAAc,MAAM,oBAAoB,MAAM,QAAQ,WAAW,OAAO,OAAO;AACrF,MAAI,CAAC,QAAQ,QAAQ;AACnB,UAAM,wBAAwB,MAAM,aAAa,WAAW;AAAA,EAC9D;AAEA,QAAM,aAAa,MAAM,mBAAmB,MAAM,aAAa,aAAa,QAAQ,OAAO;AAE3F,MAAI,CAAC,QAAQ,QAAQ;AACnB,UAAM,wBAAwB,MAAM,WAAW;AAC/C,QAAI,YAAY,OAAO,GAAG;AACxB,YAAM,yBAAyB,MAAM,WAAW;AAAA,IAClD;AACA,UAAM,mBAAmB,MAAM,aAAa,WAAW;AAGvD,UAAM,kBAAkB,MAAM,QAAQ,UAAU;AAChD,UAAM,aAAa,MAAM,WAAW,KAAK;AAAA,EAC3C;AACA,SAAO,iBAAiB,SAAS,YAAY,aAAa,OAAO;AACnE;AAGA,SAAS,mBAAmB,QAA4B;AACtD,MAAI,OAAO,YAAY;AACrB,IAAO,OAAO,KAAY,IAAI,WAAW,OAAO,UAAU,EAAE,CAAC;AAAA,EAC/D;AACF;AAGA,SAAS,2BAA2B,SAAyB,UAA0B;AACrF,aAAW,QAAQ,UAAU;AAC3B,IAAO,OAAO,KAAY,KAAK,GAAG,IAAI,+BAA+B,CAAC;AACtE,YAAQ,KAAK,EAAE,MAAM,QAAQ,UAAU,CAAC;AAAA,EAC1C;AACF;AAGA,eAAe,sBACb,MACA,SACA,OACe;AACf,aAAW,OAAO,SAAS;AACzB,UAAM,aAAa,MAAM,IAAI,MAAM,KAAK;AAAA,EAC1C;AACF;AAGA,SAAS,kBAAkB,aAAgC;AACzD,aAAW,QAAQ,aAAa;AAC9B,IAAO,OAAO,KAAY,IAAI,WAAW,IAAI,+BAA+B,CAAC;AAAA,EAC/E;AACF;AAMA,eAAe,oBACb,MACA,WACA,OACA,YAC6B;AAC7B,QAAM,cAAkC,CAAC;AACzC,aAAW,UAAU,WAAW;AAC9B,gBAAY,KAAK,MAAM,iBAAiB,MAAM,OAAO,IAAI,CAAC;AAAA,EAC5D;AAEA,QAAM,eAAe,wBAAwB,aAAa,OAAO,UAAU;AAC3E,aAAW,QAAQ,cAAc;AAC/B,IAAO,OAAO,KAAY,KAAK,GAAG,IAAI,mCAAmC,CAAC;AAC1E,gBAAY,KAAK,MAAM,iBAAiB,MAAM,IAAI,CAAC;AAAA,EACrD;AAEA,SAAO;AACT;AAGA,eAAe,aAAa,MAAc,OAAuC;AAC/E,QAAM,kBAAkB,MAAM,IAAI,CAAC,UAAU,MAAM,IAAI;AACvD,QAAM,cAAc,MAAM,OAAO,CAAC,UAAU,MAAM,QAAQ,MAAM,EAAE,IAAI,CAAC,UAAU,MAAM,IAAI;AAE3F,MAAI,gBAAgB,SAAS,GAAG;AAC9B,IAAO,OAAO,aAAa,KAAK,yBAAyB,CAAC;AAC1D,UAAM,aAAa,MAAM,iBAAiB,WAAW;AAAA,EACvD;AAEA,QAAM,cAAc,IAAI;AACxB,QAAM,YAAY,IAAI;AACtB,QAAM,uBAAuB,MAAM,eAAe;AACpD;AAGA,SAAS,oBAAoB,SAA+B;AAC1D,QAAM,UAAkC;AAAA,IACtC,KAAK;AAAA,IAAK,SAAS;AAAA,IAAK,WAAW;AAAA,IAAK,SAAS;AAAA,EACnD;AACA,QAAM,SAAgD;AAAA,IACpD,KAAY;AAAA,IAAS,SAAgB;AAAA,IAAM,WAAkB;AAAA,IAAK,SAAgB;AAAA,EACpF;AAEA,aAAW,KAAK,SAAS;AACvB,UAAM,OAAO,QAAQ,EAAE,MAAM,KAAK;AAClC,UAAM,MAAM,OAAO,EAAE,MAAM,KAAY;AACvC,IAAO,OAAO,MAAM,IAAI,GAAG,EAAE,IAAI,KAAK,EAAE,MAAM,GAAG,CAAC;AAAA,EACpD;AACF;AAMA,eAAe,iBACb,MACA,YAC2B;AAC3B,EAAO,OAAO,KAAY,KAAK,eAAe,UAAU,EAAE,CAAC;AAE3D,QAAM,aAAaE,OAAK,KAAK,MAAM,aAAa,UAAU;AAC1D,QAAM,gBAAgB,MAAMC,WAAS,YAAY,OAAO;AACxD,QAAM,gBAAgB,MAAM,aAAaD,OAAK,KAAK,MAAM,UAAU,CAAC;AACpE,QAAM,WAAW,MAAM,gBAAgB,eAAe,aAAa;AAEnE,MAAI,SAAS,SAAS,GAAG;AACvB,UAAM,QAAQ,SAAS,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI;AACtD,IAAO,OAAO,KAAY,IAAI,WAAW,SAAS,MAAM,cAAc,KAAK,EAAE,CAAC;AAAA,EAChF;AACA,SAAO,EAAE,YAAY,YAAY,eAAe,SAAS;AAC3D;AAqBO,SAAS,yBACd,UACA,UACkB;AAClB,QAAM,aAAa,EAAE,GAAG,SAAS;AAGjC,MAAI,OAAO,SAAS,eAAe,UAAU;AAC3C,eAAW,aAAa,OAAO,SAAS,eAAe,WACnD,KAAK,IAAI,SAAS,YAAY,SAAS,UAAU,IACjD,SAAS;AAAA,EACf;AAGA,aAAW,kBAAkB;AAG7B,QAAM,OAAO,CAAC,GAAI,SAAS,kBAAkB,CAAC,CAAE;AAChD,QAAM,YAAY,IAAI,IAAI,KAAK,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AACjD,aAAW,OAAO,SAAS,kBAAkB,CAAC,GAAG;AAC/C,QAAI,CAAC,UAAU,IAAI,IAAI,IAAI,GAAG;AAC5B,WAAK,KAAK,GAAG;AACb,gBAAU,IAAI,IAAI,IAAI;AAAA,IACxB;AAAA,EACF;AACA,aAAW,iBAAiB,KAAK,SAAS,IAAI,OAAO;AAGrD,MAAI,OAAO,SAAS,uBAAuB,UAAU;AACnD,eAAW,qBAAqB,OAAO,SAAS,uBAAuB,WACnE,KAAK,IAAI,SAAS,oBAAoB,SAAS,kBAAkB,IACjE,SAAS;AAAA,EACf;AAEA,SAAO;AACT;AAUA,SAAS,iBACP,aACA,aACiB;AACjB,QAAM,SAAS,oBAAI,IAA2B;AAE9C,aAAW,UAAU,aAAa;AAChC,QAAI,OAAO,SAAS,WAAW,EAAG;AAElC,eAAW,WAAW,OAAO,UAAU;AACrC,YAAM,OAAO,QAAQ,QAAQ,OAAO;AACpC,UAAI,YAAY,IAAI,IAAI,EAAG;AAE3B,YAAM,WAAW,OAAO,IAAI,IAAI;AAChC,UAAI,UAAU;AACZ,iBAAS,UAAU,yBAAyB,SAAS,SAAS,OAAO;AACrE,iBAAS,YAAY,KAAK,OAAO,UAAU;AAC3C,iBAAS,mBAAmB;AAAA;AAAA,cAAmB,OAAO,UAAU;AAAA;AAAA,EAAW,OAAO,aAAa;AAAA,MACjG,OAAO;AACL,eAAO,IAAI,MAAM;AAAA,UACf;AAAA,UACA;AAAA,UACA,aAAa,CAAC,OAAO,UAAU;AAAA,UAC/B,iBAAiB,eAAe,OAAO,UAAU;AAAA;AAAA,EAAW,OAAO,aAAa;AAAA,QAClF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,OAAO,OAAO,CAAC;AACnC;AAeA,eAAe,mBACb,MACA,OACA,QACA,SACA,cAC4B;AAC5B,QAAM,WAAW,MAAM,wBAAwB,MAAM,OAAO,MAAM;AAElE,MAAI,QAAQ,QAAQ;AAClB,WAAO,MAAM,uBAAuB,MAAM,OAAO,UAAU,cAAc,MAAM;AAAA,EACjF;AAEA,QAAM,WAAWA,OAAK,KAAK,MAAM,cAAc,GAAG,MAAM,IAAI,KAAK;AACjE,QAAME,SAAQ,MAAM,iBAAiB,UAAU,UAAU,MAAM,QAAQ,OAAO;AAC9E,SAAO,EAAE,OAAOA,UAAS,OAAU;AACrC;AAGA,eAAe,uBACb,MACA,OACA,UACA,cACA,QAC4B;AAI5B,QAAM,cAAc,iBAAiB,MAAM,IAAI;AAC/C,QAAM,aAAa,oBAAoB,UAAU,aAAa,MAAM;AAEpE,QAAM,YAA6B,MAAM,eAAe,MAAM;AAAA,IAC5D,OAAO,MAAM,QAAQ;AAAA,IACrB,MAAM,MAAM;AAAA,IACZ,SAAS,MAAM,QAAQ;AAAA,IACvB,SAAS,MAAM;AAAA,IACf,MAAM;AAAA,IACN,cAAc,qBAAqB,cAAc,MAAM,WAAW;AAAA,IAClE,kBAAkB,WAAW,SAAS,IAAI,aAAa;AAAA,EACzD,CAAC;AACD,EAAO,OAAO,KAAY,KAAK,oBAAoB,UAAU,EAAE,KAAK,MAAM,IAAI,GAAG,CAAC;AAClF,SAAO,EAAE,aAAa,UAAU,GAAG;AACrC;AAYA,eAAe,kBACb,MACA,QACA,YACe;AACf,MAAI,OAAO,UAAU,WAAW,EAAG;AACnC,aAAW,QAAQ,OAAO,WAAW;AACnC,UAAMA,SAAQ,MAAM,uBAAuB,MAAM,QAAQ,IAAI;AAC7D,QAAIA,OAAO,YAAW,OAAO,KAAKA,MAAK;AAAA,EACzC;AACF;AAGA,eAAe,uBACb,MACA,QACA,MACwB;AACxB,QAAM,OAAO,QAAQ,KAAK,KAAK;AAC/B,QAAM,WAAWF,OAAK,KAAK,MAAM,cAAc,GAAG,IAAI,KAAK;AAC3D,QAAM,iBAAiB,MAAM,qBAAqB,MAAM,KAAK,gBAAgB,CAAC,CAAC;AAC/E,QAAM,OAAO,OAAO,MAAM,KAAK,IAAI;AACnC,QAAM,SAAS,oBAAoB,MAAM,MAAM,cAAc;AAC7D,QAAM,WAAW,MAAM,WAAW;AAAA,IAChC;AAAA,IACA,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,aAAa,KAAK,IAAI,iBAAiB,KAAK,KAAK,KAAK,CAAC;AAAA,EAC7F,CAAC;AAED,QAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,QAAM,WAAW,MAAM,aAAa,QAAQ;AAC5C,QAAM,eAAe,WAAW,iBAAiB,QAAQ,EAAE,OAAO;AAClE,QAAM,YAAY,OAAO,cAAc,cAAc,WAAW,aAAa,YAAY;AACzF,QAAM,cAA+B;AAAA,IACnC,OAAO,KAAK;AAAA,IACZ,SAAS,KAAK;AAAA,IACd,SAAS,CAAC;AAAA,IACV,MAAM,KAAK;AAAA,IACX;AAAA,IACA,WAAW;AAAA,EACb;AACA,QAAM,oBAA6C,EAAE,GAAG,YAAY;AACpE,kBAAgB,mBAAmB,KAAK,OAAO,CAAC,CAAC;AACjD,QAAM,cAAc,iBAAiB,iBAAiB;AACtD,SAAO,MAAM,iBAAiB,UAAU,GAAG,WAAW;AAAA;AAAA,EAAO,QAAQ;AAAA,GAAM,KAAK,KAAK;AACvF;AAGA,eAAe,qBAAqB,MAAc,OAAkC;AAClF,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAM,WAAqB,CAAC;AAC5B,aAAW,QAAQ,OAAO;AACxB,UAAM,WAAWA,OAAK,KAAK,MAAM,cAAc,GAAG,IAAI,KAAK;AAC3D,UAAM,UAAU,MAAM,aAAa,QAAQ;AAC3C,QAAI,QAAS,UAAS,KAAK,OAAO;AAAA,EACpC;AACA,SAAO,SAAS,KAAK,aAAa;AACpC;AAQA,eAAe,gBACb,eACA,eAC6B;AAC7B,QAAM,SAAS,sBAAsB,eAAe,aAAa;AACjE,QAAM,YAAY,MAAM,WAAW;AAAA,IACjC;AAAA,IACA,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,6CAA6C,CAAC;AAAA,IAClF,OAAO,CAAC,uBAAuB;AAAA,EACjC,CAAC;AAED,SAAO,cAAc,SAAS;AAChC;AAQA,eAAe,iBACb,UACA,SACA,cACwB;AACxB,MAAI,CAAC,iBAAiB,OAAO,GAAG;AAC9B,IAAO,OAAO,KAAY,KAAK,qBAAqB,YAAY,mBAAc,CAAC;AAC/E,WAAO,qBAAqB,YAAY;AAAA,EAC1C;AAEA,QAAM,YAAY,UAAU,OAAO;AACnC,SAAO;AACT;AAOA,eAAe,uBAAuB,MAAc,cAAuC;AACzF,MAAI;AACF,UAAM,iBAAiB,MAAM,YAAY;AAAA,EAC3C,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,IAAO,OAAO,KAAY,KAAK,8BAA8B,OAAO,EAAE,CAAC;AAAA,EACzE;AACF;AASA,eAAe,mBACb,MACA,YACA,YACA,UACe;AACf,QAAM,OAAO,MAAM,SAAS,UAAU;AACtC,QAAM,QAAqB;AAAA,IACzB;AAAA,IACA,UAAU,SAAS,IAAI,CAAC,MAAM,QAAQ,EAAE,OAAO,CAAC;AAAA,IAChD,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,EACrC;AAEA,QAAM,kBAAkB,MAAM,YAAY,KAAK;AACjD;;;ADzqBA,eAAO,eAAsC,UAA0B,CAAC,GAAkB;AACxF,MAAI,CAACG,YAAW,WAAW,GAAG;AAC5B,IAAO;AAAA,MACL;AAAA,MACO,KAAK,qDAAqD;AAAA,IACnE;AACA;AAAA,EACF;AAEA,QAAM,QAAQ,QAAQ,IAAI,GAAG,OAAO;AACtC;;;A4BdA,SAAS,cAAAC,mBAAkB;AAC3B,OAAOC,YAAU;AAWjB,IAAM,YAAY,CAAC,cAAc,WAAW;AAG5C,IAAM,sBAA+B;AAAA,EACnC,MAAM;AAAA,EACN,aAAa;AAAA,EACb,cAAc;AAAA,IACZ,MAAM;AAAA,IACN,YAAY;AAAA,MACV,OAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAO;AAAA,UACL,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,UAAU;AAAA,MACZ;AAAA,MACA,WAAW;AAAA,QACT,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,UAAU,CAAC,SAAS,WAAW;AAAA,EACjC;AACF;AAaA,eAAsB,YACpB,UACA,cAC8B;AAC9B,QAAM,eACJ;AAEF,QAAM,cAAc,aAAa,QAAQ;AAAA;AAAA;AAAA,EAAoB,YAAY;AAEzE,QAAM,YAAY,MAAM,WAAW;AAAA,IACjC,QAAQ;AAAA,IACR,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,YAAY,CAAC;AAAA,IACjD,OAAO,CAAC,mBAAmB;AAAA,EAC7B,CAAC;AAED,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,SAAS;AACnC,WAAO;AAAA,MACL,OAAO,MAAM,QAAQ,OAAO,KAAK,IAAI,OAAO,MAAM,OAAO,CAAC,MAAe,OAAO,MAAM,QAAQ,IAAI,CAAC;AAAA,MACnG,WAAW,OAAO,OAAO,cAAc,WAAW,OAAO,YAAY;AAAA,IACvE;AAAA,EACF,QAAQ;AACN,WAAO,EAAE,OAAO,CAAC,GAAG,WAAW,0CAA0C;AAAA,EAC3E;AACF;AAGA,SAAS,mBACP,YACQ;AACR,SAAO,WACJ,IAAI,CAAC,UAAU,OAAO,MAAM,IAAI,OAAO,MAAM,KAAK,WAAM,MAAM,OAAO,EAAE,EACvE,KAAK,IAAI;AACd;AAaA,eAAe,oBAAoB,MAAc,UAA0C;AACzF,QAAM,aAAa,MAAM,qBAAqB,MAAM,QAAQ;AAE5D,MAAI,WAAW,SAAS,GAAG;AACzB,UAAM,gBAAgB,mBAAmB,UAAU;AACnD,UAAM,EAAE,OAAOC,WAAU,WAAAC,WAAU,IAAI,MAAM,YAAY,UAAU,aAAa;AAEhF,WAAO,EAAE,OAAOD,WAAU,UAAAA,WAAU,WAAAC,WAAU;AAAA,EAChD;AAEA,QAAM,eAAe,MAAM,aAAaC,OAAK,KAAK,MAAM,UAAU,CAAC;AACnE,QAAM,EAAE,OAAO,UAAU,UAAU,IAAI,MAAM,YAAY,UAAU,YAAY;AAC/E,SAAO,EAAE,OAAO,SAAS,IAAI,CAAC,MAAM,QAAQ,CAAC,CAAC,GAAG,UAAU,UAAU;AACvE;AAGA,eAAe,qBACb,MACA,UACkE;AAClE,MAAI;AACF,WAAO,MAAM,kBAAkB,MAAM,QAAQ;AAAA,EAC/C,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,IAAO,OAAO,KAAY,IAAI,oCAAoC,OAAO,sBAAsB,CAAC;AAChG,WAAO,CAAC;AAAA,EACV;AACF;AASA,eAAsB,kBAAkB,MAAc,OAAkC;AACtF,QAAM,WAAqB,CAAC;AAE5B,aAAW,QAAQ,OAAO;AACxB,QAAI,UAAU;AACd,eAAW,OAAO,WAAW;AAC3B,YAAM,YAAY,MAAM,aAAaA,OAAK,KAAK,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC;AACvE,UAAI,CAAC,UAAW;AAChB,YAAM,EAAE,KAAK,IAAI,iBAAiB,SAAS;AAC3C,UAAI,KAAK,SAAU;AACnB,gBAAU;AACV;AAAA,IACF;AAEA,QAAI,CAAC,SAAS;AACZ,MAAO,OAAO,KAAY,KAAK,mBAAmB,IAAI,qBAAgB,CAAC;AACvE;AAAA,IACF;AAEA,aAAS,KAAK,aAAa,IAAI;AAAA,EAAS,OAAO,EAAE;AAAA,EACnD;AAEA,SAAO,SAAS,KAAK,MAAM;AAC7B;AAGA,IAAM,uBACJ;AASF,eAAe,cACb,UACA,cACA,SACiB;AACjB,QAAM,cAAc,aAAa,QAAQ;AAAA;AAAA;AAAA,EAA6B,YAAY;AAClF,SAAO,WAAW;AAAA,IAChB,QAAQ;AAAA,IACR,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,YAAY,CAAC;AAAA,IACjD,QAAQ,QAAQ,OAAO;AAAA,IACvB;AAAA,EACF,CAAC;AACH;AASO,SAAS,gBAAgB,QAAwB;AACtD,QAAM,YAAY,OAAO,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC,KAAK;AAClD,QAAM,gBAAgB,UAAU,MAAM,cAAc,EAAE,CAAC,KAAK;AAC5D,SAAO,cAAc,MAAM,GAAG,GAAG;AACnC;AASA,eAAe,cAAc,MAAc,UAAkB,QAAiC;AAC5F,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,WAAWA,OAAK,KAAK,MAAM,aAAa,GAAG,IAAI,KAAK;AAE1D,QAAM,cAAc,iBAAiB;AAAA,IACnC,OAAO;AAAA,IACP,SAAS,gBAAgB,MAAM;AAAA,IAC/B,MAAM;AAAA,IACN,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,EACpC,CAAC;AAED,QAAM,WAAW,GAAG,WAAW;AAAA;AAAA,EAAO,MAAM;AAAA;AAC5C,QAAM,YAAY,UAAU,QAAQ;AAEpC,EAAO;AAAA,IACL;AAAA,IACO,QAAQ,sBAAwB,OAAO,QAAQ,CAAC,EAAE;AAAA,EAC3D;AAIA,QAAM,cAAc,IAAI;AAIxB,MAAI;AACF,UAAM,iBAAiB,MAAM,CAAC,IAAI,CAAC;AAAA,EACrC,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,IAAO,OAAO,KAAY,KAAK,8BAA8B,OAAO,EAAE,CAAC;AAAA,EACzE;AAEA,SAAO;AACT;AAsBA,eAAsB,eACpB,MACA,UACA,UAAiC,CAAC,GACZ;AACtB,MAAI,CAACC,YAAWD,OAAK,KAAK,MAAM,UAAU,CAAC,GAAG;AAC5C,UAAM,IAAI,MAAM,oDAAoD;AAAA,EACtE;AAEA,QAAM,EAAE,OAAO,UAAU,IAAI,MAAM,oBAAoB,MAAM,QAAQ;AACrE,UAAQ,kBAAkB,OAAO,SAAS;AAE1C,QAAM,eAAe,MAAM,kBAAkB,MAAM,KAAK;AAExD,MAAI,CAAC,cAAc;AACjB,WAAO,EAAE,QAAQ,IAAI,eAAe,OAAO,UAAU;AAAA,EACvD;AAEA,QAAM,SAAS,MAAM,cAAc,UAAU,cAAc,QAAQ,OAAO;AAE1E,MAAI;AACJ,MAAI,QAAQ,MAAM;AAChB,YAAQ,MAAM,cAAc,MAAM,UAAU,MAAM;AAAA,EACpD;AAEA,SAAO,EAAE,QAAQ,eAAe,OAAO,WAAW,MAAM;AAC1D;AAQA,eAAO,aACL,MACA,UACA,SACe;AACf,MAAI,CAACC,YAAWD,OAAK,KAAK,MAAM,UAAU,CAAC,GAAG;AAC5C,IAAO,OAAO,KAAY,MAAM,oDAAoD,CAAC;AACrF;AAAA,EACF;AAEA,EAAO,OAAO,0BAA0B;AAExC,QAAM,SAAS,MAAM,eAAe,MAAM,UAAU;AAAA,IAClD,MAAM,QAAQ;AAAA,IACd,SAAS,CAAC,SAAS,QAAQ,OAAO,MAAM,IAAI;AAAA,IAC5C,iBAAiB,CAAC,OAAO,cAAc;AACrC,MAAO,OAAO,KAAY,IAAI,cAAc,SAAS,EAAE,CAAC;AACxD,MAAO,OAAO,KAAY,KAAK,YAAY,MAAM,MAAM,aAAa,MAAM,KAAK,IAAI,CAAC,EAAE,CAAC;AACvF,MAAO,OAAO,mBAAmB;AAAA,IACnC;AAAA,EACF,CAAC;AAGD,UAAQ,OAAO,MAAM,IAAI;AAEzB,MAAI,CAAC,OAAO,QAAQ;AAClB,IAAO,OAAO,KAAY,MAAM,sDAAsD,CAAC;AACvF;AAAA,EACF;AAEA,MAAI,OAAO,OAAO;AAChB,IAAO,OAAO,UAAY,IAAI,wDAAwD,CAAC;AAAA,EACzF,OAAO;AACL,IAAO,OAAO,UAAY,IAAI,iDAAiD,CAAC;AAAA,EAClF;AACF;;;ACzUA,SAAS,SAAS,qBAAqB;AACvC,SAAS,cAAAE,mBAAkB;AAC3B,OAAOC,YAAU;AAKjB,IAAM,cAAc;AAMpB,eAAO,eAAqD;AAC1D,QAAM,cAAcC,OAAK,QAAQ,WAAW;AAE5C,MAAI,CAACC,YAAW,WAAW,GAAG;AAC5B,IAAO;AAAA,MACL;AAAA,MACO,KAAK,gEAAgE;AAAA,IAC9E;AACA;AAAA,EACF;AAEA,EAAO,OAAO,eAAe;AAC7B,EAAO,OAAO,aAAa,KAAK,YAAY,WAAW,iBAAiB,CAAC;AACzE,EAAO,OAAO,KAAY,IAAI,yBAAyB,CAAC;AAExD,MAAI,YAAY;AAChB,MAAI,mBAAmB;AACvB,MAAI,gBAAsD;AAE1D,QAAM,iBAAiB,YAAY;AACjC,QAAI,WAAW;AACb,yBAAmB;AACnB;AAAA,IACF;AAEA,gBAAY;AACZ,QAAI;AACF,YAAM,QAAQ,QAAQ,IAAI,CAAC;AAAA,IAC7B,SAAS,KAAK;AACZ,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,MAAO,OAAO,KAAY,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAAA,IAC3D;AAEA,gBAAY;AAGZ,QAAI,kBAAkB;AACpB,yBAAmB;AACnB,YAAM,eAAe;AAAA,IACvB;AAAA,EACF;AAEA,QAAM,kBAAkB,CAAC,WAAmB,UAAkB;AAC5D,IAAO;AAAA,MACL;AAAA,MACO,IAAI,GAAG,KAAK,KAAKD,OAAK,SAAS,SAAS,CAAC,EAAE;AAAA,IACpD;AAEA,QAAI,cAAe,cAAa,aAAa;AAC7C,oBAAgB,WAAW,gBAAgB,WAAW;AAAA,EACxD;AAEA,QAAM,UAAU,cAAc,aAAa;AAAA,IACzC,eAAe;AAAA,IACf,kBAAkB,EAAE,oBAAoB,IAAI;AAAA,EAC9C,CAAC;AAED,UACG,GAAG,OAAO,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAC,EAC5C,GAAG,UAAU,CAAC,MAAM,gBAAgB,GAAG,SAAS,CAAC,EACjD,GAAG,UAAU,CAAC,MAAM,gBAAgB,GAAG,SAAS,CAAC;AAGpD,QAAM,IAAI,QAAc,MAAM;AAAA,EAAC,CAAC;AAClC;;;AC7DA,IAAM,uBAAmC;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGA,IAAM,oBAA2C,CAAC,qBAAqB;AAKvE,SAAS,gBACP,SACA,UACQ;AACR,SAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,aAAa,QAAQ,EAAE;AACxD;AASA,eAAsB,KAAK,MAAoC;AAC7D,QAAM,SAAS,MAAM,WAAW,IAAI;AACpC,QAAM,CAAC,cAAc,aAAa,IAAI,MAAM,QAAQ,IAAI;AAAA,IACtD,QAAQ,IAAI,qBAAqB,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC;AAAA,IAC1D,QAAQ,IAAI,kBAAkB,IAAI,CAAC,SAAS,KAAK,MAAM,MAAM,CAAC,CAAC;AAAA,EACjE,CAAC;AAED,QAAM,UAAU,CAAC,GAAG,aAAa,KAAK,GAAG,GAAG,cAAc,KAAK,CAAC;AAEhE,SAAO;AAAA,IACL,QAAQ,gBAAgB,SAAS,OAAO;AAAA,IACxC,UAAU,gBAAgB,SAAS,SAAS;AAAA,IAC5C,MAAM,gBAAgB,SAAS,MAAM;AAAA,IACrC;AAAA,EACF;AACF;;;AC3DA,IAAM,sBAAgF;AAAA,EACpF;AAAA,EACA,SAAgB;AAAA,EAChB;AACF;AAGA,IAAM,iBAAyD;AAAA,EAC7D,OAAO;AAAA,EACP,SAAS;AAAA,EACT,MAAM;AACR;AAGA,SAAS,YAAY,QAA0B;AAC7C,QAAM,YAAY,oBAAoB,OAAO,QAAQ;AACrD,QAAM,OAAO,eAAe,OAAO,QAAQ;AAC3C,QAAM,WAAW,OAAO,OAAO,GAAG,OAAO,IAAI,IAAI,OAAO,IAAI,KAAK,OAAO;AACxE,EAAO,OAAO,MAAM,GAAG,UAAU,OAAO,QAAQ,CAAC,IAAW,IAAI,QAAQ,CAAC,IAAI,OAAO,OAAO,EAAE;AAC/F;AAMA,eAAO,cAAoD;AACzD,EAAO,OAAO,cAAc;AAE5B,QAAM,SAAS,MAAM,WAAW,QAAQ,IAAI,CAAC;AAC7C,QAAM,eAAe,OAAO,cAAc;AAC1C,EAAO,OAAO,KAAY,IAAI,WAAW,YAAY,EAAE,CAAC;AAExD,QAAM,UAAU,MAAM,KAAK,QAAQ,IAAI,CAAC;AAExC,aAAW,UAAU,QAAQ,SAAS;AACpC,gBAAY,MAAM;AAAA,EACpB;AAEA,UAAQ,IAAI;AACZ,QAAM,cAAc;AAAA,IACX,MAAM,GAAG,QAAQ,MAAM,WAAW;AAAA,IAClC,KAAK,GAAG,QAAQ,QAAQ,aAAa;AAAA,IACrC,KAAK,GAAG,QAAQ,IAAI,OAAO;AAAA,EACpC,EAAE,KAAK,IAAI;AACX,EAAO,OAAO,KAAK,WAAW;AAE9B,MAAI,QAAQ,SAAS,GAAG;AACtB,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;;;ACpDA,SAAS,cAAAE,oBAAkB;AAC3B,SAAS,SAAAC,QAAO,aAAAC,kBAAiB;AACjC,OAAOC,YAAU;AAajB,eAAsB,oBAAmC;AACvD,QAAM,OAAO,QAAQ,IAAI;AACzB,QAAM,WAAW,mBAAmB;AACpC,QAAM,aAAa,sBAAsB,IAAI;AAE7C,MAAIC,aAAW,UAAU,GAAG;AAC1B,IAAO,OAAO,KAAY,KAAK,iCAAiC,UAAU,EAAE,CAAC;AAC7E;AAAA,EACF;AAEA,QAAMC,OAAMC,OAAK,QAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AACzD,QAAM,eAAe;AAAA,IACnB,SAAS,SAAS;AAAA,IAClB,aAAa,SAAS;AAAA,IACtB,OAAO,SAAS;AAAA,IAChB,WAAW,SAAS;AAAA,EACtB;AACA,QAAMC,WAAU,YAAY,GAAG,KAAK,UAAU,cAAc,MAAM,CAAC,CAAC;AAAA,GAAM,OAAO;AACjF,EAAO,OAAO,KAAY,QAAQ,mBAAmB,UAAU,EAAE,CAAC;AACpE;AAMA,eAAsB,oBAAmC;AACvD,QAAM,SAAS,MAAM,WAAW,QAAQ,IAAI,CAAC;AAC7C,QAAM,aAAa,OAAO,cAAc;AACxC,EAAO,OAAO,WAAW,UAAU,GAAG;AACtC,UAAQ,IAAI,sBAAsB,MAAM,CAAC;AAC3C;;;AC7CA,eAAO,oBAA0D;AAC/D,EAAO,OAAO,2BAA2B;AAEzC,QAAM,aAAa,MAAM,eAAe,QAAQ,IAAI,CAAC;AACrD,MAAI,WAAW,WAAW,GAAG;AAC3B,IAAO,OAAO,UAAY,QAAQ,wBAAwB,CAAC;AAC3D;AAAA,EACF;AAEA,aAAW,aAAa,YAAY;AAClC,UAAM,UAAU,UAAU,QAAQ,KAAK,IAAI;AAC3C,UAAM,OAAc,IAAI,GAAG,UAAU,WAAW,eAAe,OAAO,EAAE;AACxE,IAAO,OAAO,KAAK,GAAU,KAAK,UAAU,EAAE,CAAC,WAAM,UAAU,IAAI,IAAI,IAAI,EAAE;AAAA,EAC/E;AAEA,EAAO;AAAA,IACL;AAAA,IACO,IAAI,0DAA0D;AAAA,EACvE;AACF;;;ACnBA,eAAO,kBAAyC,IAA2B;AACzE,QAAM,YAAY,MAAM,oBAAoB,QAAQ,IAAI,GAAG,EAAE;AAC7D,MAAI,CAAC,UAAW;AAEhB,EAAO,OAAO,aAAa,UAAU,EAAE,EAAE;AACzC,EAAO,OAAO,KAAY,IAAI,eAAe,UAAU,KAAK,EAAE,CAAC;AAC/D,EAAO,OAAO,KAAY,IAAI,eAAe,UAAU,IAAI,EAAE,CAAC;AAC9D,EAAO,OAAO,KAAY,IAAI,eAAe,UAAU,OAAO,EAAE,CAAC;AACjE,EAAO,OAAO,KAAY,IAAI,eAAe,UAAU,QAAQ,KAAK,IAAI,CAAC,EAAE,CAAC;AAC5E,EAAO,OAAO,KAAY,IAAI,eAAe,UAAU,WAAW,EAAE,CAAC;AAErE,UAAQ,IAAI;AACZ,UAAQ,IAAI,UAAU,IAAI;AAE1B,MAAI,UAAU,oBAAoB,UAAU,iBAAiB,SAAS,GAAG;AACvE,YAAQ,IAAI;AACZ,IAAO,OAAO,mBAAmB;AACjC,eAAW,KAAK,UAAU,kBAAkB;AAC1C,MAAO,OAAO,KAAY,KAAK,IAAI,EAAE,QAAQ,KAAK,EAAE,OAAO,EAAE,CAAC;AAAA,IAChE;AAAA,EACF;AACF;;;ACjBA,OAAOC,YAAU;;;ACmBjB,eAAsB,mBACpB,IACA,WACe;AACf,QAAM,OAAO,QAAQ,IAAI;AAIzB,QAAM,WAAW,MAAM,oBAAoB,MAAM,EAAE;AACnD,MAAI,CAAC,SAAU;AAEf,QAAM,SAAS,MAAM,YAAY,IAAI;AACrC,MAAI,CAAC,QAAQ;AACX,IAAO,OAAO,KAAY,MAAM,0CAA0C,CAAC;AAC3E,YAAQ,WAAW;AACnB;AAAA,EACF;AAEA,MAAI;AACF,UAAM,UAAU,MAAM,EAAE;AAAA,EAC1B,UAAE;AACA,UAAM,YAAY,IAAI;AAAA,EACxB;AACF;;;ADtBA,eAAO,qBAA4C,IAA2B;AAC5E,QAAM,mBAAmB,IAAI,gBAAgB;AAC/C;AASA,eAAe,iBAAiB,MAAc,IAA2B;AACvE,QAAM,YAAY,MAAM,6BAAuB,MAAM,EAAE;AACvD,MAAI,CAAC,UAAW;AAEhB,MAAI,CAAC,iBAAiB,UAAU,IAAI,GAAG;AACrC,IAAO,OAAO,KAAY,MAAM,aAAa,EAAE,wCAAwC,CAAC;AACxF,YAAQ,WAAW;AACnB;AAAA,EACF;AAEA,QAAM,WAAWC,OAAK,KAAK,MAAM,cAAc,GAAG,UAAU,IAAI,KAAK;AACrE,QAAM,YAAY,UAAU,UAAU,IAAI;AAC1C,EAAO,OAAO,KAAY,QAAQ,mBAAqB,OAAO,QAAQ,CAAC,EAAE,CAAC;AAE1E,QAAM,6BAA6B,MAAM,SAAS;AAClD,QAAM,yBAAyB,MAAM,UAAU,IAAI;AACnD,QAAM,gBAAgB,MAAM,EAAE;AAC9B,EAAO,OAAO,UAAY,IAAI,aAAa,EAAE,WAAW,CAAC;AAC3D;AAgBA,eAAe,6BACb,MACA,WACe;AACf,QAAM,SAAS,UAAU;AACzB,MAAI,CAAC,OAAQ;AACb,QAAM,eAAe,MAAM,6BAA6B,MAAM,UAAU,EAAE;AAC1E,aAAW,CAAC,YAAY,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACxD,QAAI,aAAa,IAAI,UAAU,EAAG;AAClC,UAAM,kBAAkB,MAAM,YAAY,KAAK;AAAA,EACjD;AACF;AAOA,eAAe,6BACb,MACA,aACsB;AACtB,QAAM,UAAU,MAAM,eAAe,IAAI;AACzC,QAAM,UAAU,oBAAI,IAAY;AAChC,aAAW,aAAa,SAAS;AAC/B,QAAI,UAAU,OAAO,YAAa;AAClC,eAAWC,WAAU,UAAU,QAAS,SAAQ,IAAIA,OAAM;AAAA,EAC5D;AACA,SAAO;AACT;AAGA,eAAe,yBAAyB,MAAc,MAA6B;AACjF,QAAM,aAAa,MAAM,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC;AACvC,QAAM,cAAc,IAAI;AACxB,QAAM,YAAY,IAAI;AACtB,QAAMC,wBAAuB,MAAM,CAAC,IAAI,CAAC;AAC3C;AAOA,eAAeA,wBAAuB,MAAc,OAAgC;AAClF,MAAI;AACF,UAAM,iBAAiB,MAAM,KAAK;AAAA,EACpC,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,IAAO,OAAO,KAAY,KAAK,8BAA8B,OAAO,EAAE,CAAC;AAAA,EACzE;AACF;;;AE9GA,eAAO,oBAA2C,IAA2B;AAC3E,QAAM,mBAAmB,IAAI,eAAe;AAC9C;AASA,eAAe,gBAAgB,MAAc,IAA2B;AACtE,QAAM,YAAY,MAAM,6BAAuB,MAAM,EAAE;AACvD,MAAI,CAAC,UAAW;AAEhB,QAAM,iBAAiB,MAAM,EAAE;AAC/B,EAAO;AAAA,IACL;AAAA,IACO,KAAK,sBAAsB,EAAE,KAAK,UAAU,IAAI,oCAA+B;AAAA,EACxF;AACF;;;AC5BA,SAAS,aAAAC,kBAAiB;AAC1B,SAAS,4BAA4B;;;ACJrC,OAAOC,YAAU;AACjB,SAAS,SAAS;;;ACElB,IAAM,oBAAmD;AAAA,EACvD,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AACX;AAOO,SAAS,0BAAgC;AAC9C,QAAM,WAAW,QAAQ,IAAI,oBAAoB;AAEjD,MAAI,aAAa,aAAa;AAC5B,UAAM,OAAO,4BAA4B;AACzC,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,WAAW;AACnC,YAAM,IAAI;AAAA,QACR;AAAA,MAEF;AAAA,IACF;AACA;AAAA,EACF;AAEA,QAAM,SAAS,kBAAkB,QAAQ;AACzC,MAAI,WAAW,QAAW;AACxB,UAAM,IAAI;AAAA,MACR,qBAAqB,QAAQ,iBAAiB,OAAO,KAAK,iBAAiB,EAAE,KAAK,IAAI,CAAC;AAAA,IACzF;AAAA,EACF;AAEA,MAAI,UAAU,CAAC,QAAQ,IAAI,MAAM,GAAG;AAClC,UAAM,IAAI;AAAA,MACR,GAAG,MAAM,8CAA8C,QAAQ;AAAA,IACjE;AAAA,EACF;AACF;;;ADpBA,IAAMC,aAAY,CAAC,cAAc,WAAW;AAe5C,SAAS,WAAW,SAGlB;AACA,SAAO;AAAA,IACL,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,KAAK,UAAU,SAAS,MAAM,CAAC,EAAE,CAAC;AAAA,IAC3E,mBAAmB,EAAE,QAAQ,QAAQ;AAAA,EACvC;AACF;AAGO,SAAS,kBAAkB,QAAmB,MAAoB;AACvE,qBAAmB,QAAQ,IAAI;AAC/B,sBAAoB,QAAQ,IAAI;AAChC,oBAAkB,QAAQ,IAAI;AAC9B,qBAAmB,QAAQ,IAAI;AAC/B,mBAAiB,QAAQ,IAAI;AAC7B,mBAAiB,QAAQ,IAAI;AAC7B,qBAAmB,QAAQ,IAAI;AACjC;AAEA,SAAS,mBAAmB,QAAmB,MAAoB;AACjE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MAEF,aAAa;AAAA,QACX,QAAQ,EACL,OAAO,EACP,SAAS,sDAAsD;AAAA,MACpE;AAAA,IACF;AAAA,IACA,OAAO,EAAE,QAAAC,QAAO,MAAM;AACpB,YAAM,cAAc,QAAQ,IAAI;AAChC,UAAI;AACF,gBAAQ,MAAM,IAAI;AAClB,cAAM,SAAS,MAAM,aAAaA,OAAM;AACxC,eAAO,WAAW,MAAM;AAAA,MAC1B,UAAE;AACA,gBAAQ,MAAM,WAAW;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,oBAAoB,QAAmB,MAAoB;AAClE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MAGF,aAAa,CAAC;AAAA,IAChB;AAAA,IACA,YAAY;AACV,8BAAwB;AACxB,YAAM,SAAS,MAAM,iBAAiB,IAAI;AAC1C,aAAO,WAAW,MAAM;AAAA,IAC1B;AAAA,EACF;AACF;AAEA,SAAS,kBAAkB,QAAmB,MAAoB;AAChE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MAGF,aAAa;AAAA,QACX,UAAU,EAAE,OAAO,EAAE,SAAS,0CAA0C;AAAA,QACxE,MAAM,EACH,QAAQ,EACR,SAAS,EACT,SAAS,uDAAuD;AAAA,MACrE;AAAA,IACF;AAAA,IACA,OAAO,EAAE,UAAU,KAAK,MAAM;AAC5B,8BAAwB;AACxB,YAAM,SAAS,MAAM,eAAe,MAAM,UAAU,EAAE,KAAK,CAAC;AAC5D,aAAO,WAAW,MAAM;AAAA,IAC1B;AAAA,EACF;AACF;AAEA,SAAS,mBAAmB,QAAmB,MAAoB;AACjE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MAGF,aAAa;AAAA,QACX,UAAU,EAAE,OAAO,EAAE,SAAS,+BAA+B;AAAA,MAC/D;AAAA,IACF;AAAA,IACA,OAAO,EAAE,SAAS,MAAM;AACtB,8BAAwB;AACxB,YAAM,QAAQ,MAAM,gBAAgB,MAAM,QAAQ;AAClD,YAAM,UAAU,MAAM,gBAAgB,MAAM,KAAK;AACjD,aAAO,WAAW,EAAE,OAAO,QAAQ,CAAC;AAAA,IACtC;AAAA,EACF;AACF;AAGA,eAAe,gBAAgB,MAAc,UAAqC;AAChF,MAAI;AACF,UAAM,aAAa,MAAM,kBAAkB,MAAM,QAAQ;AACzD,QAAI,WAAW,SAAS,EAAG,QAAO,WAAW,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,EAChE,QAAQ;AAAA,EAER;AAEA,QAAM,eAAe,MAAM,aAAaC,OAAK,KAAK,MAAM,UAAU,CAAC;AACnE,QAAM,EAAE,MAAM,IAAI,MAAM,YAAY,UAAU,YAAY;AAC1D,SAAO;AACT;AAEA,SAAS,iBAAiB,QAAmB,MAAoB;AAC/D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MAEF,aAAa;AAAA,QACX,MAAM,EAAE,OAAO,EAAE,SAAS,mCAAmC;AAAA,MAC/D;AAAA,IACF;AAAA,IACA,OAAO,EAAE,KAAK,MAAM;AAClB,YAAM,OAAO,MAAM,SAAS,MAAM,IAAI;AACtC,UAAI,CAAC,MAAM;AACT,cAAM,IAAI,MAAM,mBAAmB,IAAI,EAAE;AAAA,MAC3C;AACA,aAAO,WAAW,IAAI;AAAA,IACxB;AAAA,EACF;AACF;AAEA,SAAS,iBAAiB,QAAmB,MAAoB;AAC/D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MAEF,aAAa,CAAC;AAAA,IAChB;AAAA,IACA,YAAY;AACV,YAAM,UAAU,MAAM,KAAK,IAAI;AAC/B,aAAO,WAAW,OAAO;AAAA,IAC3B;AAAA,EACF;AACF;AAEA,SAAS,mBAAmB,QAAmB,MAAoB;AACjE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MAGF,aAAa,CAAC;AAAA,IAChB;AAAA,IACA,YAAY,WAAW,MAAM,cAAc,IAAI,CAAC;AAAA,EAClD;AACF;AAGA,eAAe,cAAc,MAAmC;AAC9D,QAAM,WAAW,MAAM,qBAAqBA,OAAK,KAAK,MAAM,YAAY,CAAC;AACzE,QAAM,UAAU,MAAM,qBAAqBA,OAAK,KAAK,MAAM,WAAW,CAAC;AACvE,QAAM,QAAQ,MAAM,UAAU,IAAI;AAClC,QAAM,UAAU,MAAM,cAAc,MAAM,KAAK;AAC/C,QAAM,UAAU,MAAM,kBAAkB,IAAI;AAC5C,QAAM,oBAAoB,MAAM,gBAAgB,IAAI;AACpD,QAAM,eAAe,OAAO,OAAO,MAAM,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,UAAU;AACzE,QAAM,cAAc,aAAa,SAAS,IACtC,aAAa,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,IAC/B;AAEJ,SAAO;AAAA,IACL,OAAO,EAAE,UAAU,SAAS,QAAQ,SAAS,QAAQ,QAAQ,OAAO,SAAS,SAAS,QAAQ,OAAO;AAAA,IACrG,SAAS,OAAO,KAAK,MAAM,OAAO,EAAE;AAAA,IACpC,gBAAgB;AAAA,IAChB,eAAe;AAAA,IACf;AAAA,IACA,gBAAgB,QACb,OAAO,CAAC,MAAM,EAAE,WAAW,WAAW,EACtC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,EAAE,OAAO,EAAE;AAAA,EACpD;AACF;AAaA,eAAe,kBAAkB,MAAiC;AAChE,QAAM,UAAU,MAAM,cAAcA,OAAK,KAAK,MAAM,YAAY,CAAC;AACjE,SAAO,QAAQ,OAAO,CAAC,EAAE,KAAK,MAAM,KAAK,QAAQ,EAAE,IAAI,CAAC,EAAE,KAAK,MAAM,IAAI;AAC3E;AAGA,eAAe,gBAAgB,MAAc,OAAwC;AACnF,QAAM,UAAwB,CAAC;AAC/B,aAAW,QAAQ,OAAO;AACxB,UAAM,OAAO,MAAM,SAAS,MAAM,IAAI;AACtC,QAAI,KAAM,SAAQ,KAAK,IAAI;AAAA,EAC7B;AACA,SAAO;AACT;AAMA,eAAsB,SAAS,MAAc,MAA0C;AACrF,aAAW,OAAOF,YAAW;AAC3B,UAAM,UAAU,MAAM,aAAaE,OAAK,KAAK,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC;AACrE,QAAI,CAAC,QAAS;AAEd,UAAM,EAAE,MAAM,KAAK,IAAI,iBAAiB,OAAO;AAC/C,QAAI,KAAK,SAAU;AAEnB,WAAO;AAAA,MACL;AAAA,MACA,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;AAAA,MACrD,SAAS,OAAO,KAAK,YAAY,WAAW,KAAK,UAAU;AAAA,MAC3D,MAAM,KAAK,KAAK;AAAA,IAClB;AAAA,EACF;AACA,SAAO;AACT;;;AEnSA,OAAOC,YAAU;AACjB,SAAS,WAAAC,gBAAe;AACxB,SAAoB,wBAAwB;AAY5C,SAAS,YAAY,KAAU,SAI7B;AACA,SAAO;AAAA,IACL,KAAK,IAAI;AAAA,IACT,UAAU;AAAA,IACV,MAAM,KAAK,UAAU,SAAS,MAAM,CAAC;AAAA,EACvC;AACF;AAGA,SAAS,gBAAgB,KAAU,MAIjC;AACA,SAAO;AAAA,IACL,KAAK,IAAI;AAAA,IACT,UAAU;AAAA,IACV;AAAA,EACF;AACF;AAGO,SAAS,sBAAsB,QAAmB,MAAoB;AAC3E,wBAAsB,QAAQ,IAAI;AAClC,0BAAwB,QAAQ,IAAI;AACpC,wBAAsB,QAAQ,IAAI;AAClC,0BAAwB,QAAQ,IAAI;AACpC,wBAAsB,QAAQ,IAAI;AACpC;AAEA,SAAS,sBAAsB,QAAmB,MAAoB;AACpE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,OAAO,QAAQ;AACb,YAAM,UAAU,MAAM,aAAaC,OAAK,KAAK,MAAM,UAAU,CAAC;AAC9D,aAAO,EAAE,UAAU,CAAC,gBAAgB,KAAK,OAAO,CAAC,EAAE;AAAA,IACrD;AAAA,EACF;AACF;AAEA,SAAS,wBAAwB,QAAmB,MAAoB;AACtE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,OAAO,SAAS;AAAA,MACd,UAAU,CAAC,YAAY,KAAK,MAAM,YAAY,IAAI,CAAC,CAAC;AAAA,IACtD;AAAA,EACF;AACF;AAEA,SAAS,sBAAsB,QAAmB,MAAoB;AACpE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,OAAO,QAAQ;AACb,YAAM,QAAQ,MAAM,UAAU,IAAI;AAClC,aAAO,EAAE,UAAU,CAAC,YAAY,KAAK,KAAK,CAAC,EAAE;AAAA,IAC/C;AAAA,EACF;AACF;AAEA,SAAS,wBAAwB,QAAmB,MAAoB;AACtE,SAAO;AAAA,IACL;AAAA,IACA,IAAI,iBAAiB,4BAA4B;AAAA,MAC/C,MAAM,YAAY,eAAe,MAAM,cAAc,SAAS;AAAA,IAChE,CAAC;AAAA,IACD;AAAA,MACE,OAAO;AAAA,MACP,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,OAAO,KAAK,EAAE,KAAK,OAAO;AAAA,MACxB,UAAU,CAAC,YAAY,KAAK,MAAM,iBAAiB,MAAM,cAAc,OAAO,IAAI,CAAC,CAAC,CAAC;AAAA,IACvF;AAAA,EACF;AACF;AAEA,SAAS,sBAAsB,QAAmB,MAAoB;AACpE,SAAO;AAAA,IACL;AAAA,IACA,IAAI,iBAAiB,0BAA0B;AAAA,MAC7C,MAAM,YAAY,eAAe,MAAM,aAAa,OAAO;AAAA,IAC7D,CAAC;AAAA,IACD;AAAA,MACE,OAAO;AAAA,MACP,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,OAAO,KAAK,EAAE,KAAK,OAAO;AAAA,MACxB,UAAU,CAAC,YAAY,KAAK,MAAM,iBAAiB,MAAM,aAAa,OAAO,IAAI,CAAC,CAAC,CAAC;AAAA,IACtF;AAAA,EACF;AACF;AAGA,eAAe,YAAY,MAAuD;AAChF,QAAM,cAAcA,OAAK,KAAK,MAAM,WAAW;AAC/C,MAAI;AACJ,MAAI;AACF,YAAQ,MAAMC,SAAQ,WAAW;AAAA,EACnC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,UAA0C,CAAC;AACjD,aAAW,QAAQ,MAAM,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,GAAG;AACzD,UAAM,UAAU,MAAM,aAAaD,OAAK,KAAK,aAAa,IAAI,CAAC;AAC/D,UAAM,EAAE,KAAK,IAAI,iBAAiB,OAAO;AACzC,YAAQ,KAAK,EAAE,UAAU,MAAM,GAAG,KAAK,CAAC;AAAA,EAC1C;AACA,SAAO;AACT;AAGA,eAAe,iBACb,MACA,KACA,MACwE;AACxE,QAAM,WAAWA,OAAK,KAAK,MAAM,KAAK,GAAG,IAAI,KAAK;AAClD,QAAM,UAAU,MAAM,aAAa,QAAQ;AAC3C,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,mBAAmB,GAAG,IAAI,IAAI,KAAK;AAAA,EACrD;AAEA,QAAM,EAAE,MAAM,KAAK,IAAI,iBAAiB,OAAO;AAC/C,SAAO,EAAE,MAAM,MAAM,MAAM,KAAK,KAAK,EAAE;AACzC;AAGA,eAAe,eACb,MACA,KACA,QAC8D;AAC9D,QAAM,YAAYA,OAAK,KAAK,MAAM,GAAG;AACrC,MAAI;AACJ,MAAI;AACF,YAAQ,MAAMC,SAAQ,SAAS;AAAA,EACjC,QAAQ;AACN,WAAO,EAAE,WAAW,CAAC,EAAE;AAAA,EACzB;AAEA,QAAM,YAAY,MACf,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,EAC/B,IAAI,CAAC,MAAM;AACV,UAAM,OAAO,EAAE,QAAQ,SAAS,EAAE;AAClC,WAAO,EAAE,KAAK,aAAa,MAAM,IAAI,IAAI,IAAI,MAAM,KAAK;AAAA,EAC1D,CAAC;AAEH,SAAO,EAAE,UAAU;AACrB;;;AHlKA,eAAsB,eAAe,SAAuC;AAC1E,QAAM,EAAE,MAAM,SAAAC,SAAQ,IAAI;AAC1B,QAAM,SAAS,IAAIC,WAAU,EAAE,MAAM,WAAW,SAAAD,SAAQ,GAAG;AAAA,IACzD,cACE;AAAA,EAIJ,CAAC;AAED,oBAAkB,QAAQ,IAAI;AAC9B,wBAAsB,QAAQ,IAAI;AAElC,QAAM,YAAY,IAAI,qBAAqB;AAC3C,QAAM,OAAO,QAAQ,SAAS;AAChC;;;A7CtBA,IAAME,WAAU,cAAc,YAAY,GAAG;AAC7C,IAAM,EAAE,QAAQ,IAAIA,SAAQ,iBAAiB;AAE7C,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,SAAS,EACd,YAAY,oEAA+D,EAC3E,QAAQ,OAAO;AAElB,QACG,QAAQ,iBAAiB,EACzB,YAAY,0CAA0C,EACtD,OAAO,OAAOC,YAAmB;AAChC,MAAI;AACF,UAAM,OAAcA,OAAM;AAAA,EAC5B,SAAS,KAAK;AACZ,YAAQ,MAAM,yBAAyB,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AACjF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,QACG,QAAQ,SAAS,EACjB,YAAY,2CAA2C,EACvD;AAAA,EACC;AAAA,EACA;AACF,EACC,OAAO,OAAO,YAAkC;AAC/C,MAAI;AACF,oBAAgB;AAChB,UAAM,eAAe,EAAE,QAAQ,QAAQ,OAAO,CAAC;AAAA,EACjD,SAAS,KAAK;AACZ,YAAQ,MAAM,yBAAyB,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AACjF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,IAAM,gBAAgB,QACnB,QAAQ,QAAQ,EAChB,YAAY,sDAAsD;AAErE,cACG,QAAQ,MAAM,EACd,YAAY,gCAAgC,EAC5C,OAAO,YAAY;AAClB,MAAI;AACF,UAAM,kBAAkB;AAAA,EAC1B,SAAS,KAAK;AACZ,YAAQ,MAAM,yBAAyB,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AACjF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,cACG,QAAQ,WAAW,EACnB,YAAY,8CAA8C,EAC1D,OAAO,OAAO,OAAe;AAC5B,MAAI;AACF,UAAM,kBAAkB,EAAE;AAAA,EAC5B,SAAS,KAAK;AACZ,YAAQ,MAAM,yBAAyB,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AACjF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,cACG,QAAQ,cAAc,EACtB,YAAY,wDAAwD,EACpE,OAAO,OAAO,OAAe;AAC5B,MAAI;AACF,UAAM,qBAAqB,EAAE;AAAA,EAC/B,SAAS,KAAK;AACZ,YAAQ,MAAM,yBAAyB,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AACjF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,cACG,QAAQ,aAAa,EACrB,YAAY,0DAA0D,EACtE,OAAO,OAAO,OAAe;AAC5B,MAAI;AACF,UAAM,oBAAoB,EAAE;AAAA,EAC9B,SAAS,KAAK;AACZ,YAAQ,MAAM,yBAAyB,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AACjF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,QACG,QAAQ,kBAAkB,EAC1B,YAAY,iCAAiC,EAC7C,OAAO,UAAU,gCAAgC,EACjD,OAAO,OAAO,UAAkB,YAAgC;AAC/D,MAAI;AACF,oBAAgB;AAChB,UAAM,aAAa,QAAQ,IAAI,GAAG,UAAU,OAAO;AAAA,EACrD,SAAS,KAAK;AACZ,YAAQ,MAAM,yBAAyB,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AACjF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,QACG,QAAQ,OAAO,EACf,YAAY,8CAA8C,EAC1D,OAAO,YAAY;AAClB,MAAI;AACF,oBAAgB;AAChB,UAAM,aAAa;AAAA,EACrB,SAAS,KAAK;AACZ,YAAQ,MAAM,yBAAyB,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AACjF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,YAAY,gDAAgD,EAC5D,OAAO,YAAY;AAClB,MAAI;AACF,UAAM,YAAY;AAAA,EACpB,SAAS,KAAK;AACZ,YAAQ,MAAM,yBAAyB,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AACjF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,IAAM,YAAY,QACf,QAAQ,QAAQ,EAChB,YAAY,wDAAwD;AAEvE,UACG,QAAQ,MAAM,EACd,YAAY,qDAAqD,EACjE,OAAO,YAAY;AAClB,MAAI;AACF,UAAM,kBAAkB;AAAA,EAC1B,SAAS,KAAK;AACZ,YAAQ,MAAM,yBAAyB,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AACjF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,UACG,QAAQ,MAAM,EACd,YAAY,4CAA4C,EACxD,OAAO,YAAY;AAClB,MAAI;AACF,UAAM,kBAAkB;AAAA,EAC1B,SAAS,KAAK;AACZ,YAAQ,MAAM,yBAAyB,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AACjF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,QACG,QAAQ,OAAO,EACf,YAAY,kEAAkE,EAC9E,OAAO,gBAAgB,0BAA0B,QAAQ,IAAI,CAAC,EAC9D,OAAO,OAAO,YAA8B;AAC3C,MAAI;AAGF,UAAM,eAAe,EAAE,MAAM,QAAQ,MAAM,QAAQ,CAAC;AAAA,EACtD,SAAS,KAAK;AACZ,YAAQ,MAAM,yBAAyB,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AACjF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAGH,IAAMC,qBAAmD;AAAA,EACvD,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AACX;AAGA,SAAS,kBAAwB;AAC/B,QAAM,WAAW,QAAQ,IAAI,oBAAoB;AAEjD,MAAI,aAAa,aAAa;AAC5B,UAAM,OAAO,4BAA4B;AACzC,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,WAAW;AACnC,cAAQ;AAAA,QACN;AAAA;AAAA,MAEF;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA;AAAA,EACF;AAEA,QAAM,SAASA,mBAAkB,QAAQ;AAEzC,MAAI,WAAW,QAAW;AACxB,YAAQ;AAAA,MACN,2CAA2C,QAAQ;AAAA,eACjC,OAAO,KAAKA,kBAAiB,EAAE,KAAK,IAAI,CAAC;AAAA,IAC7D;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,UAAU,CAAC,QAAQ,IAAI,MAAM,GAAG;AAClC,YAAQ;AAAA,MACN,yBAAyB,MAAM,8CAA8C,QAAQ;AAAA,wBAC1D,MAAM;AAAA,IACnC;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,QAAQ,MAAM;","names":["path","mkdir","writeFile","readFile","path","source","path","mkdir","writeFile","existsSync","readFile","path","readFile","writeFile","rename","mkdir","path","path","readFile","mkdir","writeFile","rename","path","readFile","path","readFile","path","status","path","path","error","readFile","mkdir","path","path","mkdir","readFile","existsSync","readFile","path","yaml","path","existsSync","yaml","readFile","yaml","yaml","path","path","readdir","readFile","path","existsSync","path","existsSync","readdir","readFile","readdir","path","path","readdir","readdir","path","path","readdir","readFile","readdir","existsSync","path","path","existsSync","readFile","readdir","readdir","rename","unlink","writeFile","mkdir","existsSync","path","path","existsSync","readdir","unlink","mkdir","rename","writeFile","readdir","readFile","existsSync","path","WIKILINK_PATTERN","existsSync","readdir","path","readFile","readdir","path","path","readdir","path","readFile","error","existsSync","existsSync","path","rawPages","reasoning","path","existsSync","existsSync","path","path","existsSync","existsSync","mkdir","writeFile","path","existsSync","mkdir","path","writeFile","path","path","source","safelyUpdateEmbeddings","McpServer","path","PAGE_DIRS","source","path","path","readdir","path","readdir","version","McpServer","require","source","PROVIDER_KEY_VARS"]}