llm-wiki-compiler 0.2.0 → 0.3.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/README.md +111 -8
- package/dist/cli.js +846 -223
- package/dist/cli.js.map +1 -1
- package/package.json +5 -2
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/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/hasher.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/commands/query.ts","../src/commands/watch.ts","../src/linter/rules.ts","../src/linter/index.ts","../src/commands/lint.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 { 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 .action(async () => {\n try {\n requireProvider();\n await compileCommand();\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\";\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/**\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/** Number of most similar pages to return from embedding-based pre-filter. */\nexport const EMBEDDING_TOP_K = 15;\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\";\n\n/**\n * Run the compile command from the current working directory.\n * Exits early if no sources directory exists yet.\n */\nexport default async function compileCommand(): 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());\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, readdir } from \"fs/promises\";\nimport path from \"path\";\nimport { readState, updateSourceState } from \"../utils/state.js\";\nimport {\n atomicWrite,\n safeReadFile,\n validateWikiPage,\n slugify,\n buildFrontmatter,\n parseFrontmatter,\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 buildPagePrompt,\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 { addObsidianMeta, generateMOC } from \"./obsidian.js\";\nimport { updateEmbeddings } from \"../utils/embeddings.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 CompileResult,\n ExtractedConcept,\n SourceChange,\n SourceState,\n WikiState,\n} from \"../utils/types.js\";\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 */\nexport async function compile(root: string): Promise<void> {\n await compileAndReport(root);\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 * @returns Structured result describing what was compiled.\n */\nexport async function compileAndReport(root: string): 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);\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}\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): Promise<PageGenerationResult> {\n const merged = mergeExtractions(extractions, frozenSlugs);\n const limit = pLimit(COMPILE_CONCURRENCY);\n const errors: string[] = [];\n const pages = await Promise.all(\n merged.map((entry) => limit(async () => {\n const writeError = await generateMergedPage(root, entry);\n if (writeError) errors.push(writeError);\n return entry;\n })),\n );\n return { pages, errors };\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): 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 (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 return {\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}\n\n/** Inner pipeline, runs under lock protection. Returns structured CompileResult. */\nasync function runCompilePipeline(root: string): 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 await markDeletedAsOrphaned(root, buckets.deleted, state);\n\n const frozenSlugs = findFrozenSlugs(state, changes);\n reportFrozenSlugs(frozenSlugs);\n\n const extractions = await runExtractionPhases(root, buckets.toCompile, state, changes);\n await freezeFailedExtractions(root, extractions, frozenSlugs);\n\n const generation = await generatePagesPhase(root, extractions, frozenSlugs);\n await persistExtractionStates(root, extractions);\n\n if (frozenSlugs.size > 0) {\n await orphanUnownedFrozenPages(root, frozenSlugs);\n }\n await persistFrozenSlugs(root, frozenSlugs, extractions);\n\n await finalizeWiki(root, generation.pages);\n return summarizeCompile(buckets, generation, extractions);\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 * 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 */\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.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/**\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.\n */\nasync function generateMergedPage(\n root: string,\n entry: MergedConcept,\n): Promise<string | null> {\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 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 const frontmatter = buildFrontmatter(frontmatterFields);\n const fullPage = `${frontmatter}\\n\\n${pageBody}\\n`;\n return await writePageIfValid(pagePath, fullPage, entry.concept.concept);\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 * Load related wiki pages to provide cross-referencing context.\n * Returns concatenated content of up to 5 existing concept pages.\n * @param root - Project root directory.\n * @param excludeSlug - Slug of the current page to exclude.\n * @returns Concatenated related page contents.\n */\nasync function loadRelatedPages(\n root: string,\n excludeSlug: string,\n): 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, 5);\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/**\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 * 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/** 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 model: string;\n\n constructor(model: string, baseURL?: string, apiKey?: string) {\n this.model = model;\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 = apiKey ?? process.env.OPENAI_API_KEY ?? \"\";\n this.client = new OpenAI({\n apiKey: resolvedKey,\n ...(baseURL ? { baseURL } : {}),\n });\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.client.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 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/** Ollama-backed LLM provider using the OpenAI-compatible endpoint. */\nexport class OllamaProvider extends OpenAIProvider {\n constructor(model: string, baseURL: string) {\n super(model, baseURL, \"ollama\");\n }\n\n /** Ollama ships a dedicated embedding model (nomic-embed-text). */\n protected override embeddingModel(): string {\n return 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, 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 case \"ollama\":\n return new OllamaProvider(\n getModelForProvider(\"ollama\"),\n process.env.OLLAMA_HOST ?? OLLAMA_DEFAULT_HOST,\n );\n case \"minimax\":\n return getMiniMaxProvider();\n default:\n throw new Error(`Unhandled provider: ${providerName}`);\n }\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 { ExtractedConcept } from \"../utils/types.js\";\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 },\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 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 existingSection,\n relatedSection,\n \"\\n\\n--- SOURCE MATERIAL ---\\n\\n\",\n sourceContent,\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: ExtractedConcept[] = parsed.concepts ?? [];\n return concepts\n .filter(\n (c) =>\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 .map((c) => ({\n concept: c.concept,\n summary: c.summary,\n is_new: c.is_new,\n tags: Array.isArray(c.tags) ? c.tags : undefined,\n }));\n } catch {\n return [];\n }\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 * 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\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/** Choose the active embedding model name, defaulting to anthropic's voyage model. */\nfunction resolveEmbeddingModel(): string {\n return EMBEDDING_MODELS[getActiveProviderName()] ?? 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 toEmbed = new Set(changedSlugs.filter((slug) => liveSlugs.has(slug)));\n\n const existingStore = await readEmbeddingStore(root);\n const previousEntries = existingStore?.entries ?? [];\n\n // Cold start: embed every page so the store is immediately useful.\n if (!existingStore) {\n for (const record of records) toEmbed.add(record.slug);\n }\n\n if (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: resolveEmbeddingModel(),\n dimensions,\n entries: mergedEntries,\n };\n await writeEmbeddingStore(root, store);\n output.status(\"*\", output.dim(`Embeddings updated (${mergedEntries.length} pages).`));\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, slugify } from \"../utils/markdown.js\";\nimport { CONCEPTS_DIR, QUERIES_DIR, SOURCES_DIR } 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/** Find ^[filename.md] citations referencing source files that don't exist. */\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 const citedPath = path.join(sourcesDir, captured);\n if (!existsSync(citedPath)) {\n results.push({\n rule: \"broken-citation\",\n severity: \"error\",\n file: page.filePath,\n message: `Broken citation ^[${captured}] — source file not found`,\n line,\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} 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];\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 * 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 { 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 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 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 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","readdir","path","readFile","writeFile","rename","mkdir","path","path","readFile","mkdir","writeFile","rename","path","error","readFile","mkdir","path","path","mkdir","readFile","readFile","path","readFile","path","status","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","path","readFile","readdir","existsSync","existsSync","path","rawPages","reasoning","path","existsSync","existsSync","path","path","existsSync","readdir","readFile","existsSync","path","existsSync","readdir","path","readFile","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/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"]}
|