seekstone 0.12.2 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/cli-args.ts","../src/tools/append_note.ts","../../core/src/frontmatter.ts","../src/atomic-write.ts","../src/content-hash.ts","../src/policy.ts","../src/vault-path.ts","../../core/src/extract.ts","../src/tools/context_pack.ts","../src/index/excerpt.ts","../src/index/resolve.ts","../src/tools/search.ts","../src/tools/create_note.ts","../src/index/doc.ts","../src/tools/delete_note.ts","../src/index/backlinks.ts","../src/tools/get_backlinks.ts","../src/tools/get_links.ts","../src/tools/list_notes.ts","../src/tools/list_tags.ts","../src/tools/move_note.ts","../src/tools/rewrite_links.ts","../src/tools/outline_note.ts","../../core/src/outline.ts","../src/tools/patch_frontmatter.ts","../src/tools/patch_note.ts","../src/tools/periodic_note.ts","../src/tools/query_notes.ts","../src/tools/read_note.ts","../src/tools/replace_in_note.ts","../src/dispatch.ts","../src/index/build.ts","../../core/src/walk.ts","../src/init.ts","../src/log.ts","../src/process-guards.ts","../src/tool-list.ts","../src/watcher.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { resolve } from 'node:path';\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n type CallToolResult,\n ListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { helpText, initHelpText, parseCliIntent } from './cli-args.js';\nimport type { ServerContext } from './context.js';\nimport { dispatch } from './dispatch.js';\nimport { buildIndex } from './index/build.js';\nimport { parseInitArgs, runInit } from './init.js';\nimport { createLogger } from './log.js';\nimport { parseWritePolicy } from './policy.js';\nimport { installProcessGuards } from './process-guards.js';\nimport { visibleTools } from './tool-list.js';\nimport { startWatcher } from './watcher.js';\n\n// Inlined at build time by tsup (see tsup.config.ts); falls back in tsx dev.\ndeclare const __SEEKSTONE_VERSION__: string;\nconst VERSION = typeof __SEEKSTONE_VERSION__ === 'string' ? __SEEKSTONE_VERSION__ : '0.0.0-dev';\n\n// CLI subcommands / flags exit before any server setup, printing to stdout\n// (these are explicit invocations, not the MCP stdio session).\nconst intent = parseCliIntent(process.argv.slice(2));\nif (intent === 'version') {\n process.stdout.write(`${VERSION}\\n`);\n process.exit(0);\n}\nif (intent === 'help') {\n process.stdout.write(`${helpText(VERSION)}\\n`);\n process.exit(0);\n}\nif (intent === 'init-help') {\n process.stdout.write(`${initHelpText(VERSION)}\\n`);\n process.exit(0);\n}\nif (intent === 'init') {\n const result = await runInit(parseInitArgs(process.argv.slice(3)), {\n env: process.env,\n platform: process.platform,\n timestamp: new Date().toISOString().replace(/[:.]/g, '-'),\n cwd: process.cwd(),\n });\n process.stdout.write(`${result.output.join('\\n')}\\n`);\n process.exit(result.exitCode);\n}\n\nconst log = createLogger();\n\n// Long-lived stdio session: keep the server alive (and its in-memory index)\n// on a stray unhandled rejection rather than crashing the user's session.\ninstallProcessGuards(log);\n\nconst rawVaultRoot = process.env.SEEKSTONE_VAULT;\nif (!rawVaultRoot) {\n log.error('SEEKSTONE_VAULT env var is required');\n process.exit(1);\n}\n// Normalize once so the containment guard (vault-path.ts) compares against a\n// canonical absolute path — a relative or trailing-slash SEEKSTONE_VAULT must\n// not weaken the prefix boundary.\nconst vaultRoot = resolve(rawVaultRoot);\n\nconst policy = parseWritePolicy(process.env);\nif (policy.readOnly) log.info('read-only mode', { env: 'SEEKSTONE_READ_ONLY' });\nif (policy.writeGlobs) log.info('write paths scoped', { globs: policy.writeGlobs });\n\nlog.info('building index', { vault: vaultRoot });\nconst { index, notes, backlinks, buildMs } = await buildIndex(vaultRoot);\nlog.info('index ready', { notes: notes.size, buildMs });\n\nconst ctx: ServerContext = { vaultRoot, index, notes, backlinks, policy };\n\nconst watcher = startWatcher(ctx, log);\n// Exit handlers must stay synchronous — kick off the close; nothing awaits it.\nprocess.on('exit', () => void watcher.stop());\n\nconst server = new Server({ name: 'seekstone', version: VERSION }, { capabilities: { tools: {} } });\n\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: visibleTools(ctx.policy),\n}));\n\nserver.setRequestHandler(CallToolRequestSchema, async (req): Promise<CallToolResult> => {\n const { name, arguments: args } = req.params;\n return dispatch(ctx, name, args, log);\n});\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\nlog.info('ready', { tools: visibleTools(ctx.policy).length, transport: 'stdio' });\n\nprocess.stderr.write(\n `seekstone: add to Claude Desktop:\\n${JSON.stringify(\n {\n mcpServers: {\n seekstone: {\n command: 'npx',\n args: ['-y', 'seekstone'],\n env: { SEEKSTONE_VAULT: vaultRoot },\n },\n },\n },\n null,\n 2,\n )}\\n`,\n);\n","/**\n * Tiny argument handling for the `seekstone` bin. Kept pure (no process exit,\n * no IO) so it can be unit-tested. The MCP server itself takes no arguments —\n * these are just the conventional `--version` / `--help` niceties expected of a\n * CLI installed via `npx`.\n */\n\nexport type CliIntent = 'version' | 'help' | 'init' | 'init-help' | 'run';\n\nexport function parseCliIntent(argv: readonly string[]): CliIntent {\n // A subcommand, if present, is the first non-flag token. A help flag after\n // the subcommand asks for the subcommand's help, so it must be recognised\n // here — before the init run path — not swallowed by init's own arg parsing.\n if (argv[0] === 'init') {\n for (const arg of argv.slice(1)) {\n if (arg === '--help' || arg === '-h') return 'init-help';\n }\n return 'init';\n }\n for (const arg of argv) {\n if (arg === '--version' || arg === '-v') return 'version';\n if (arg === '--help' || arg === '-h') return 'help';\n }\n return 'run';\n}\n\n// Shared between the top-level help and `seekstone init --help` so the two\n// can't drift apart.\nconst initOptionLines = ` --vault <path> Vault to use (auto-detected from Obsidian if omitted; fallback: $SEEKSTONE_VAULT)\n --client <name> desktop (default) | code | cursor | vscode\n --write Patch the client config in place (backs it up first)`;\n\nexport function helpText(version: string): string {\n return `seekstone ${version} — an Obsidian MCP server (filesystem-direct, low context-tax)\n\nSeekstone runs as a Model Context Protocol stdio server. It is normally\nlaunched by an MCP client (Claude Desktop, Claude Code, Cursor, …), not run by hand.\n\nUsage:\n seekstone Start the MCP server (reads from stdin/stdout)\n seekstone init Validate a vault and print/patch the client MCP config\n seekstone --version Print the version and exit\n seekstone --help Print this help and exit\n\n\"seekstone init\" options:\n${initOptionLines}\n\nRequired environment:\n SEEKSTONE_VAULT Absolute path to your Obsidian vault\n\nOptional environment:\n SEEKSTONE_LOG_LEVEL error | warn | info (default) | debug\n SEEKSTONE_LOG_FILE Absolute path; append JSON-line logs here\n SEEKSTONE_WATCH_POLL Set to 1 to poll for changes (network drives, WSL)\n\nAdd to Claude Code:\n claude mcp add seekstone --env SEEKSTONE_VAULT=/path/to/vault -- npx -y seekstone\n\nDocs: https://github.com/shaqmughal/seekstone`;\n}\n\nexport function initHelpText(version: string): string {\n return `seekstone ${version} — seekstone init\n\nValidate an Obsidian vault and print (or, with --write, patch) the MCP config\nfor the chosen client (Claude Desktop, Claude Code, Cursor, VS Code).\n\nUsage:\n seekstone init [options]\n\nOptions:\n${initOptionLines}\n --help, -h Print this help and exit\n\nExamples:\n seekstone init\n seekstone init --vault \"/Users/you/Obsidian/My Vault\" --client code --write\n\nDocs: https://github.com/shaqmughal/seekstone`;\n}\n","import { readFile } from 'node:fs/promises';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport { z } from 'zod';\nimport { atomicWrite } from '../atomic-write.js';\nimport { assertHashMatch, contentHash } from '../content-hash.js';\nimport type { ServerContext } from '../context.js';\nimport { assertWritable } from '../policy.js';\nimport { resolveVaultPath } from '../vault-path.js';\n\nexport const AppendNoteInput = z.object({\n path: z.string().describe('Vault-relative path to the note.'),\n content: z\n .string()\n .min(1)\n .describe(\n 'Text to append to the note body. Will be separated from existing content by a blank line.',\n ),\n prevHash: z\n .string()\n .optional()\n .describe(\n 'Optional compare-and-swap guard: the contentHash from a prior read. Fails with hash_conflict if the note changed since.',\n ),\n});\nexport type AppendNoteInput = z.infer<typeof AppendNoteInput>;\n\nexport interface AppendNoteResult {\n path: string;\n bytesWritten: number;\n /** sha-256 (hex) of the new content — usable as prevHash for a chained edit. */\n contentHash: string;\n}\n\n/**\n * Append text to a note body without touching the frontmatter.\n *\n * The write contract:\n * - The frontmatter block (if any) is preserved byte-for-byte.\n * - A blank line is inserted before the appended content if the body\n * does not already end with one (keeps Obsidian's visual spacing).\n * - The file is written atomically (temp file + rename).\n */\nexport async function appendNote(\n ctx: ServerContext,\n input: AppendNoteInput,\n): Promise<AppendNoteResult> {\n const absPath = resolveVaultPath(ctx.vaultRoot, input.path);\n assertWritable(ctx.policy, input.path);\n\n const original = await readFile(absPath, 'utf8');\n if (input.prevHash !== undefined) assertHashMatch(original, input.prevHash, input.path);\n const fm = parseFrontmatter(original);\n\n // Ensure body ends with exactly one trailing newline before appending.\n const body = fm.body.endsWith('\\n') ? fm.body : `${fm.body}\\n`;\n const separator = body.endsWith('\\n\\n') ? '' : '\\n';\n const newBody = `${body}${separator}${input.content}`;\n\n // Reconstruct: original header (FM + delimiters) + new body.\n const header = original.slice(0, fm.bodyStart);\n const newContent = `${header}${newBody}`;\n\n await atomicWrite(absPath, newContent);\n\n // Update the in-memory index entry so subsequent searches reflect the change.\n const cached = ctx.notes.get(input.path);\n if (cached) {\n cached.body = newBody;\n cached.raw = newContent;\n }\n\n return {\n path: input.path,\n bytesWritten: Buffer.byteLength(newContent, 'utf8'),\n contentHash: contentHash(newContent),\n };\n}\n","import { parse as parseYaml } from 'yaml';\n\nexport interface FrontmatterResult {\n /** true if the note starts with a YAML block delimited by `---` lines. */\n present: boolean;\n /** Parsed object (null if not present or parse error or block empty). */\n data: Record<string, unknown> | null;\n /** Distinct top-level keys (empty if absent or malformed). */\n keys: string[];\n /** True if a `---` block exists but yaml.parse threw. */\n malformed: boolean;\n /** Byte offset where the body (post-frontmatter) starts. */\n bodyStart: number;\n /** Body text, frontmatter stripped. */\n body: string;\n}\n\n/**\n * Split a note into frontmatter + body without round-tripping through any\n * YAML stringifier. We need to know the *bytes* of the FM block so writers\n * can be byte-faithful — that's the write-safety contract.\n *\n * Obsidian's rule: a YAML block is present iff the file starts with `---`\n * on its own line (no leading whitespace, no BOM stripped silently — though\n * we tolerate a leading BOM since Windows users exist).\n */\nexport function parseFrontmatter(text: string): FrontmatterResult {\n let offset = 0;\n if (text.charCodeAt(0) === 0xfeff) offset = 1; // BOM\n\n // Must start with `---\\n` or `---\\r\\n`\n const head = text.slice(offset, offset + 4);\n if (head !== '---\\n' && text.slice(offset, offset + 5) !== '---\\r\\n') {\n return {\n present: false,\n data: null,\n keys: [],\n malformed: false,\n bodyStart: 0,\n body: text,\n };\n }\n const fmStart = offset + (head === '---\\n' ? 4 : 5);\n\n // Find closing `\\n---\\n` (or CRLF variant) — Obsidian requires `---` on its own line.\n // We search line-by-line from fmStart.\n const closeLF = text.indexOf('\\n---\\n', fmStart - 1);\n const closeCRLF = text.indexOf('\\r\\n---\\r\\n', fmStart - 1);\n let closeIdx = -1;\n let closeLen = 0;\n if (closeLF !== -1 && (closeCRLF === -1 || closeLF < closeCRLF)) {\n closeIdx = closeLF;\n closeLen = 5;\n } else if (closeCRLF !== -1) {\n closeIdx = closeCRLF;\n closeLen = 7;\n }\n if (closeIdx === -1) {\n // Unterminated frontmatter — count as malformed, treat whole file as body.\n return {\n present: true,\n data: null,\n keys: [],\n malformed: true,\n bodyStart: 0,\n body: text,\n };\n }\n\n const yamlText = text.slice(fmStart, closeIdx + 1); // include trailing \\n before ---\n const bodyStart = closeIdx + closeLen;\n const body = text.slice(bodyStart);\n\n let data: Record<string, unknown> | null = null;\n let malformed = false;\n try {\n const parsed = parseYaml(yamlText);\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n data = parsed as Record<string, unknown>;\n } else {\n // YAML parsed but isn't a mapping — Obsidian also treats this as malformed-for-our-purposes.\n malformed = parsed !== null && parsed !== undefined;\n }\n } catch {\n malformed = true;\n }\n\n return {\n present: true,\n data,\n keys: data ? Object.keys(data) : [],\n malformed,\n bodyStart,\n body,\n };\n}\n","import { rename, writeFile } from 'node:fs/promises';\n\n/**\n * Crash-safe file write: write to a same-directory temp file, then rename\n * over the destination. A crash mid-write leaves either the old content or\n * the new content on disk — never a torn file. rename-over-existing works on\n * the full CI OS matrix (proven by patch_note's original implementation).\n *\n * Not a lock: two seekstone processes writing the same note share this temp\n * suffix and can race each other — same exposure as any two writers on one\n * file. CAS (content-hash.ts) is the conflict-detection layer above this.\n */\nexport async function atomicWrite(absPath: string, content: string): Promise<void> {\n const tmpPath = `${absPath}.seekstone-tmp`;\n await writeFile(tmpPath, content, 'utf8');\n await rename(tmpPath, absPath);\n}\n","import { createHash } from 'node:crypto';\n\n/**\n * Content hashing for optional compare-and-swap edits.\n *\n * The hash is sha-256 (hex) of the exact raw string a tool read from disk —\n * disk bytes are the version identity. `read_note` and every mutating tool\n * return `contentHash`; edit tools accept `prevHash` and refuse to write when\n * the note changed since it was read. This is conflict DETECTION, not\n * locking: the window between check and rename is the same one the OS gives\n * any two writers. node:crypto is fully offline — the zero-network guarantee\n * is unaffected.\n */\nexport function contentHash(raw: string | Buffer): string {\n return createHash('sha256').update(raw).digest('hex');\n}\n\n/**\n * Throw the structured hash_conflict error when `prevHash` no longer matches\n * the freshly-read content. Call immediately after the disk read, before any\n * edit is computed.\n */\nexport function assertHashMatch(raw: string, prevHash: string, path: string): void {\n const actual = contentHash(raw);\n if (actual !== prevHash) {\n throw new Error(\n JSON.stringify({\n error: 'hash_conflict',\n path,\n expected: prevHash,\n actual,\n hint: 'Note changed on disk since it was read. Re-read it and retry with the new contentHash.',\n }),\n );\n }\n}\n","import picomatch from 'picomatch';\n\n/**\n * Write policy — read-only mode and write-path scoping, parsed once at boot\n * and enforced at the dispatch layer (tool visibility + rejection) and inside\n * every write handler at the point the final vault-relative path is known\n * (`assertWritable`). Two env vars:\n *\n * SEEKSTONE_READ_ONLY=1 — write tools are unregistered from\n * tools/list AND rejected at dispatch.\n * SEEKSTONE_WRITE_PATHS=<glob,>— comma-separated vault-relative globs;\n * writes are permitted only under matching\n * paths (e.g. \"journal/**,inbox/*.md\").\n *\n * Read-only wins over write paths. Unset write paths = everything writable.\n */\nexport interface WritePolicy {\n readOnly: boolean;\n /** Vault-relative glob allowlist; undefined = all paths writable. */\n writeGlobs?: string[];\n}\n\n/** Default policy for contexts built without env config (tests, benches). */\nexport const PERMISSIVE_POLICY: WritePolicy = { readOnly: false };\n\nexport function parseWritePolicy(env: Record<string, string | undefined>): WritePolicy {\n const ro = (env.SEEKSTONE_READ_ONLY ?? '').toLowerCase();\n const readOnly = ro === '1' || ro === 'true';\n const globs = (env.SEEKSTONE_WRITE_PATHS ?? '')\n .split(',')\n .map((g) => g.trim())\n .filter((g) => g.length > 0);\n return globs.length > 0 ? { readOnly, writeGlobs: globs } : { readOnly };\n}\n\n/** Normalize a vault-relative path for glob matching (Windows separators, ./). */\nfunction normalizeRel(relPath: string): string {\n let p = relPath.replaceAll('\\\\', '/');\n while (p.startsWith('./')) p = p.slice(2);\n return p;\n}\n\nexport function isWritable(policy: WritePolicy, relPath: string): boolean {\n if (policy.readOnly) return false;\n if (!policy.writeGlobs) return true;\n // dot:true so notes under dot-folders still match `**` patterns.\n return picomatch(policy.writeGlobs, { dot: true })(normalizeRel(relPath));\n}\n\n/**\n * Throw with an actionable message when the policy forbids writing `relPath`.\n * Call from write handlers with the final vault-relative path — after any\n * internal path computation (e.g. periodic-note date resolution).\n */\nexport function assertWritable(policy: WritePolicy, relPath: string): void {\n if (policy.readOnly) {\n throw new Error('Server is read-only (SEEKSTONE_READ_ONLY=1); write tools are disabled.');\n }\n if (!isWritable(policy, relPath)) {\n throw new Error(\n `Write not permitted: \"${relPath}\" is outside SEEKSTONE_WRITE_PATHS (${policy.writeGlobs?.join(', ')}).`,\n );\n }\n}\n","import { resolve, sep } from 'node:path';\n\n/**\n * Resolve a vault-relative path to an absolute path, guaranteeing the result\n * stays inside the vault. This is the single containment guard for every tool\n * handler — the previous inline `join(...).startsWith(vaultRoot)` checks had\n * no separator boundary, so with a vault at `/home/u/vault` the input\n * `../vault-backup/x.md` resolved to `/home/u/vault-backup/x.md` and passed.\n *\n * `resolve` (not `join`) also normalizes an absolute `input` instead of\n * concatenating it, so absolute-path inputs are contained too.\n *\n * @throws Error `Path outside vault: <input>` when the resolved path escapes.\n */\nexport function resolveVaultPath(vaultRoot: string, inputPath: string): string {\n const abs = resolve(vaultRoot, inputPath);\n if (abs !== vaultRoot && !abs.startsWith(vaultRoot + sep)) {\n throw new Error(`Path outside vault: ${inputPath}`);\n }\n return abs;\n}\n","/**\n * Link / URL / tag extractors. These run on note *bodies* (post-frontmatter)\n * so frontmatter values don't double-count as inline content.\n *\n * Heuristics here intentionally lean toward \"what Obsidian sees\" rather than\n * full CommonMark parsing — we're profiling shape, not rendering. The regexes\n * are deliberately tolerant of code-fence content; counts are reported with\n * that caveat in the README.\n */\n\nexport interface Wikilink {\n /** Raw target before `#` and `|`. May include path segments. */\n target: string;\n /** Optional heading/block reference (`#heading` or `#^block`). */\n fragment: string | null;\n /** Optional display alias. */\n alias: string | null;\n}\n\n// Quantifiers are bounded at 512 chars — well above any real Obsidian note name\n// or heading (~255 byte OS limit) — to eliminate O(n²) backtracking on\n// pathological inputs where many [[... sequences appear with no closing ]].\nconst WIKILINK_RE = /\\[\\[([^\\]|#\\n]{1,512})(#[^\\]|\\n]{1,512})?(\\|[^\\]\\n]{1,512})?\\]\\]/g;\n\n// Matches both embeds (![[...]]) and plain wikilinks ([[...]]).\nconst LINK_WITH_LINES_RE =\n /(!?\\[\\[)([^\\]|#\\n]{1,512})(#[^\\]|\\n]{1,512})?(\\|[^\\]\\n]{1,512})?(\\]\\])/g;\n\nexport type LinkType = 'wikilink' | 'embed';\n\nexport interface LinkRecord {\n /** Full raw match, e.g. \"[[Note Name|alias]]\" or \"![[embed.png]]\". */\n raw: string;\n /** Raw target before `#` and `|`. */\n target: string;\n fragment: string | null;\n alias: string | null;\n linkType: LinkType;\n /** 1-indexed line number in the source file. */\n line: number;\n}\n\n/**\n * Extract all wikilinks and embeds from a raw note (including frontmatter),\n * annotating each with its 1-indexed line number.\n */\nexport function extractLinksWithLines(raw: string): LinkRecord[] {\n const out: LinkRecord[] = [];\n const lines = raw.split('\\n');\n for (const [lineIdx, lineText] of lines.entries()) {\n for (const m of lineText.matchAll(LINK_WITH_LINES_RE)) {\n const prefix = m[1] ?? '';\n out.push({\n raw: m[0] ?? '',\n target: (m[2] ?? '').trim(),\n fragment: m[3] ? m[3].slice(1).trim() : null,\n alias: m[4] ? m[4].slice(1).trim() : null,\n linkType: prefix.startsWith('!') ? 'embed' : 'wikilink',\n line: lineIdx + 1,\n });\n }\n }\n return out;\n}\nconst URL_RE = /https?:\\/\\/[^\\s<>\"')]+/g;\n// Obsidian inline tag: must start with letter or underscore, then word chars, /, -.\n// Must not be immediately preceded by `]` (avoids matching `]#` from links) or be\n// inside a numeric-only token like `#123` (Obsidian rejects pure-digit tags).\nconst INLINE_TAG_RE = /(?:^|[^\\w\\]/])#([A-Za-z_][\\w/-]*)/g;\n\nexport function extractWikilinks(body: string): Wikilink[] {\n const out: Wikilink[] = [];\n for (const m of body.matchAll(WIKILINK_RE)) {\n out.push({\n target: (m[1] ?? '').trim(),\n fragment: m[2] ? m[2].slice(1).trim() : null,\n alias: m[3] ? m[3].slice(1).trim() : null,\n });\n }\n return out;\n}\n\nexport function extractUrls(body: string): string[] {\n const TRAILING_PUNCT = '.,;:!?)';\n const out: string[] = [];\n for (const m of body.matchAll(URL_RE)) {\n // Strip trailing punctuation with a backward walk — avoids the O(n²)\n // backtracking that /[.,;:!?)]+$/ exhibits on strings with many punct chars.\n const raw = m[0] ?? '';\n let end = raw.length;\n while (end > 0 && TRAILING_PUNCT.includes(raw.charAt(end - 1))) end--;\n out.push(raw.slice(0, end));\n }\n return out;\n}\n\nexport function extractInlineTags(body: string): string[] {\n const out: string[] = [];\n for (const m of body.matchAll(INLINE_TAG_RE)) {\n if (m[1]) out.push(m[1]);\n }\n return out;\n}\n\n/**\n * Pull tags out of a parsed frontmatter object. Obsidian accepts `tags:` or\n * `tag:`, as either a string (space- or comma-separated) or a list.\n */\nexport function frontmatterTags(fm: Record<string, unknown> | null): string[] {\n if (!fm) return [];\n const raw = fm.tags ?? fm.tag;\n if (raw == null) return [];\n if (Array.isArray(raw)) {\n return raw.filter((t): t is string => typeof t === 'string').map((t) => t.replace(/^#/, ''));\n }\n if (typeof raw === 'string') {\n return raw\n .split(/[\\s,]+/)\n .filter(Boolean)\n .map((t) => t.replace(/^#/, ''));\n }\n return [];\n}\n","import { extractLinksWithLines } from '@seekstone/core/extract';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.js';\nimport { extractExcerpt } from '../index/excerpt.js';\nimport { resolveLink } from '../index/resolve.js';\nimport { basenameNoExt } from './search.js';\n\nexport const ContextPackInput = z.object({\n query: z.string().min(1).describe('Natural-language question or topic to assemble context for.'),\n budgetBytes: z\n .number()\n .int()\n .min(256)\n .max(16384)\n .default(2048)\n .describe('Hard cap on the response JSON size in bytes. The pack never exceeds it.'),\n});\nexport type ContextPackInput = z.infer<typeof ContextPackInput>;\n\nconst MAX_CANDIDATES = 10;\nconst NEIGHBOR_HITS = 3;\nconst MAX_NEIGHBORS = 5;\nconst NEIGHBOR_SUMMARY_LEN = 100;\nconst MIN_EXCERPT_LEN = 60;\nconst FM_MAX_BYTES = 120;\nconst SOURCES_MAX = 8;\n\nexport interface ContextPackExcerpt {\n path: string;\n title?: string;\n score: number;\n excerpt: string;\n tags?: string[];\n /** Scalar frontmatter values, in key order, capped at FM_MAX_BYTES serialized. */\n fm?: Record<string, unknown>;\n}\n\nexport interface ContextPackNeighbor {\n path: string;\n rel: 'backlink' | 'outlink' | 'both';\n /** Line in the neighbor where it links to a hit (backlinks only). */\n line?: number;\n summary: string;\n}\n\nexport interface ContextPackSource {\n path: string;\n line?: number;\n}\n\nexport interface ContextPackResult {\n excerpts: ContextPackExcerpt[];\n neighborhood: ContextPackNeighbor[];\n /** Overflow refs only — matches and neighbors that did not fit the budget. */\n sources: ContextPackSource[];\n totalMatches: number;\n confidence: 'high' | 'low' | 'none';\n truncated?: true;\n}\n\n/** Serialized cost of one array entry, +1 for the separating comma (conservative). */\nfunction entryCost(entry: unknown): number {\n return Buffer.byteLength(JSON.stringify(entry), 'utf8') + 1;\n}\n\n/** Pick scalar frontmatter values in key order until the serialized subset exceeds the cap. */\nfunction pickFm(fm: Record<string, unknown> | null): Record<string, unknown> | undefined {\n if (!fm) return undefined;\n const out: Record<string, unknown> = {};\n let picked = 0;\n for (const key of Object.keys(fm)) {\n const v = fm[key];\n const t = typeof v;\n if (t !== 'string' && t !== 'number' && t !== 'boolean') continue;\n const candidate = { ...out, [key]: v };\n if (Buffer.byteLength(JSON.stringify(candidate), 'utf8') > FM_MAX_BYTES) break;\n out[key] = v;\n picked += 1;\n }\n return picked > 0 ? out : undefined;\n}\n\nfunction clamp(n: number, lo: number, hi: number): number {\n return Math.min(hi, Math.max(lo, n));\n}\n\nexport function contextPack(ctx: ServerContext, input: ContextPackInput): ContextPackResult {\n const results = ctx.index.search(input.query, {\n boost: { title: 3, tags: 2, body: 1 },\n fuzzy: 0.2,\n prefix: true,\n });\n const totalMatches = results.length;\n if (totalMatches === 0) {\n return { excerpts: [], neighborhood: [], sources: [], totalMatches: 0, confidence: 'none' };\n }\n\n const terms = input.query\n .toLowerCase()\n .split(/\\s+/)\n .filter((t) => t.length > 1);\n\n const scoreByPath = new Map<string, number>();\n for (const r of results) scoreByPath.set(r.id, r.score);\n\n // Meter the fixed envelope with worst-case flags so emitting them later can\n // never break the cap. Entries are metered against the same minified\n // serialization dispatch ships, so the budget is exact, not an estimate.\n const budget = input.budgetBytes;\n let used = Buffer.byteLength(\n JSON.stringify({\n excerpts: [],\n neighborhood: [],\n sources: [],\n totalMatches,\n confidence: 'high',\n truncated: true,\n }),\n 'utf8',\n );\n\n // Phase 1 — excerpts: greedy fill in rank order, holding back a soft reserve\n // for the neighborhood. On the first non-fit, shrink the excerpt (half, then\n // the floor); a shrunk entry ends the phase so the pack isn't all fragments.\n const excerptLen = clamp(Math.floor(budget / 10), 80, 400);\n const reserve = Math.min(Math.floor(budget / 4), 600);\n const candidates = results.slice(0, MAX_CANDIDATES);\n const excerpts: ContextPackExcerpt[] = [];\n const overflowHits: ContextPackSource[] = [];\n let droppedForBudget = false;\n\n const buildEntry = (r: (typeof results)[number], len: number): ContextPackExcerpt => {\n const note = ctx.notes.get(r.id);\n const body = note?.body ?? '';\n const noteTags = note?.tags ? note.tags.split(' ').filter(Boolean) : [];\n const entry: ContextPackExcerpt = {\n path: r.id,\n score: Math.round(r.score * 100) / 100,\n excerpt: extractExcerpt(body, terms, len),\n };\n const title = r.title as string;\n if (title && title !== basenameNoExt(r.id)) entry.title = title;\n if (noteTags.length > 0) entry.tags = noteTags;\n const fm = pickFm(note?.fm ?? null);\n if (fm) entry.fm = fm;\n return entry;\n };\n\n const excerptCap = budget - reserve;\n let phaseOpen = true;\n for (const r of candidates) {\n if (!phaseOpen) {\n overflowHits.push({ path: r.id });\n continue;\n }\n let placed = false;\n const shrinkLens = [\n ...new Set([\n excerptLen,\n Math.max(Math.floor(excerptLen / 2), MIN_EXCERPT_LEN),\n MIN_EXCERPT_LEN,\n ]),\n ];\n for (const len of shrinkLens) {\n const entry = buildEntry(r, len);\n const cost = entryCost(entry);\n if (used + cost <= excerptCap) {\n excerpts.push(entry);\n used += cost;\n placed = true;\n // A shrunk excerpt means the budget is nearly spent — stop before\n // packing many useless fragments.\n if (len !== excerptLen) phaseOpen = false;\n break;\n }\n }\n if (!placed) {\n phaseOpen = false;\n droppedForBudget = true;\n overflowHits.push({ path: r.id });\n }\n }\n\n // Phase 2 — neighborhood: backlinks + outlinks of the top included hits.\n // The reserve is released; entries fill whatever budget remains.\n const includedPaths = new Set(excerpts.map((e) => e.path));\n const seeds = excerpts.slice(0, NEIGHBOR_HITS).map((e) => e.path);\n const agg = new Map<\n string,\n { rels: Set<'backlink' | 'outlink'>; seedPaths: Set<string>; line?: number }\n >();\n const connect = (\n neighborPath: string,\n seed: string,\n rel: 'backlink' | 'outlink',\n line?: number,\n ) => {\n if (neighborPath === seed || includedPaths.has(neighborPath)) return; // excerpt wins dedup\n if (!ctx.notes.has(neighborPath)) return;\n let entry = agg.get(neighborPath);\n if (!entry) {\n entry = { rels: new Set(), seedPaths: new Set() };\n agg.set(neighborPath, entry);\n }\n entry.rels.add(rel);\n entry.seedPaths.add(seed);\n if (rel === 'backlink' && entry.line === undefined && line !== undefined) entry.line = line;\n };\n for (const seed of seeds) {\n for (const ref of ctx.backlinks.get(seed) ?? []) {\n connect(ref.path, seed, 'backlink', ref.line);\n }\n const note = ctx.notes.get(seed);\n if (!note) continue;\n for (const link of extractLinksWithLines(note.raw)) {\n const target = resolveLink(link.target, ctx.notes);\n if (target !== undefined) connect(target, seed, 'outlink');\n }\n }\n\n const ranked = [...agg.entries()].sort((a, b) => {\n const conn = b[1].seedPaths.size - a[1].seedPaths.size;\n if (conn !== 0) return conn;\n const score = (scoreByPath.get(b[0]) ?? 0) - (scoreByPath.get(a[0]) ?? 0);\n if (score !== 0) return score;\n return a[0] < b[0] ? -1 : 1;\n });\n\n const neighborhood: ContextPackNeighbor[] = [];\n const overflowNeighbors: ContextPackSource[] = [];\n for (const [path, info] of ranked) {\n if (neighborhood.length >= MAX_NEIGHBORS) {\n overflowNeighbors.push(info.line !== undefined ? { path, line: info.line } : { path });\n continue;\n }\n const note = ctx.notes.get(path);\n const entry: ContextPackNeighbor = {\n path,\n rel: info.rels.size === 2 ? 'both' : info.rels.has('backlink') ? 'backlink' : 'outlink',\n summary: extractExcerpt(note?.body ?? '', terms, NEIGHBOR_SUMMARY_LEN),\n };\n if (info.line !== undefined) entry.line = info.line;\n const cost = entryCost(entry);\n if (used + cost > budget) {\n droppedForBudget = true;\n overflowNeighbors.push(info.line !== undefined ? { path, line: info.line } : { path });\n // First non-fit ends the phase — later entries rank lower anyway.\n for (const [p, i] of ranked.slice(ranked.findIndex(([rp]) => rp === path) + 1)) {\n overflowNeighbors.push(i.line !== undefined ? { path: p, line: i.line } : { path: p });\n }\n break;\n }\n neighborhood.push(entry);\n used += cost;\n }\n\n // Phase 3 — sources: overflow refs (\"read these next\"), deduped, best-first.\n const sources: ContextPackSource[] = [];\n const sourcePaths = new Set<string>();\n for (const ref of [...overflowHits, ...overflowNeighbors]) {\n if (sources.length >= SOURCES_MAX) break;\n if (includedPaths.has(ref.path) || sourcePaths.has(ref.path)) continue;\n const cost = entryCost(ref);\n if (used + cost > budget) break;\n sources.push(ref);\n sourcePaths.add(ref.path);\n used += cost;\n }\n\n const hasTermInExcerpt = excerpts.some((e) =>\n terms.some((t) => e.excerpt.toLowerCase().includes(t)),\n );\n const confidence: ContextPackResult['confidence'] =\n excerpts.length === 0 || (terms.length > 0 && !hasTermInExcerpt) ? 'low' : 'high';\n\n const result: ContextPackResult = { excerpts, neighborhood, sources, totalMatches, confidence };\n if (droppedForBudget) result.truncated = true;\n return result;\n}\n","/**\n * Extract a short excerpt centred on the first occurrence of any query term.\n * Returns at most `maxLen` characters with leading/trailing ellipsis as needed.\n *\n * This is the core context-tax reduction: instead of returning the full note\n * (~7 KB mean, ~70 KB p99), we return ~200 chars of relevant context.\n */\nexport function extractExcerpt(text: string, terms: string[], maxLen = 200): string {\n const lower = text.toLowerCase();\n let bestIdx = -1;\n\n for (const term of terms) {\n const idx = lower.indexOf(term.toLowerCase());\n if (idx !== -1 && (bestIdx === -1 || idx < bestIdx)) {\n bestIdx = idx;\n }\n }\n\n if (bestIdx === -1) {\n // No term found in body (hit may have been in title/tags) — return the start.\n return truncate(text.trimStart(), maxLen);\n }\n\n const half = Math.floor(maxLen / 2);\n const start = Math.max(0, bestIdx - half);\n const end = Math.min(text.length, bestIdx + half + (terms[0]?.length ?? 0));\n const slice = text.slice(start, end).trim();\n\n return `${start > 0 ? '…' : ''}${slice}${end < text.length ? '…' : ''}`;\n}\n\nfunction truncate(s: string, max: number): string {\n if (s.length <= max) return s;\n return `${s.slice(0, max).trimEnd()}…`;\n}\n","/**\n * Resolve a wikilink target string to a vault-relative note path.\n *\n * Uses Obsidian's loose resolution: exact match → add .md → basename match\n * → path-without-extension match. Returns undefined if no note matches.\n */\nexport function resolveLink(target: string, notes: Map<string, unknown>): string | undefined {\n if (notes.has(target)) return target;\n const withMd = `${target}.md`;\n if (notes.has(withMd)) return withMd;\n\n const targetLower = target.toLowerCase();\n const targetBase = (target.split('/').pop() ?? target).toLowerCase();\n\n for (const path of notes.keys()) {\n const pathNoExt = path.replace(/\\.md$/i, '');\n if (pathNoExt.toLowerCase() === targetLower) return path;\n const pathBase = (pathNoExt.split('/').pop() ?? pathNoExt).toLowerCase();\n if (pathBase === targetBase) return path;\n }\n\n return undefined;\n}\n","import { z } from 'zod';\nimport type { ServerContext } from '../context.js';\nimport { extractExcerpt } from '../index/excerpt.js';\nimport type { SearchHit } from '../index/types.js';\n\nexport const SearchInput = z.object({\n query: z.string().min(1).describe('Search query. Supports fuzzy matching and prefix search.'),\n limit: z\n .number()\n .int()\n .min(1)\n .max(50)\n .default(10)\n .describe('Maximum number of results to return.'),\n folder: z\n .string()\n .optional()\n .describe('Restrict results to notes under this vault-relative folder prefix.'),\n tag: z.string().optional().describe('Restrict results to notes containing this tag.'),\n excerptLength: z\n .number()\n .int()\n .min(20)\n .max(2000)\n .optional()\n .describe(\n 'Max characters of match context per hit (default 120). Lower trims payload; higher gives more context.',\n ),\n});\nexport type SearchInput = z.infer<typeof SearchInput>;\n\n/** Basename without extension — the title is omitted from a hit when it matches this. */\nexport function basenameNoExt(path: string): string {\n const base = path.slice(path.lastIndexOf('/') + 1);\n const dot = base.lastIndexOf('.');\n return dot > 0 ? base.slice(0, dot) : base;\n}\n\nexport function search(ctx: ServerContext, input: SearchInput): SearchHit[] {\n const results = ctx.index.search(input.query, {\n boost: { title: 3, tags: 2, body: 1 },\n fuzzy: 0.2,\n prefix: true,\n });\n\n const terms = input.query\n .toLowerCase()\n .split(/\\s+/)\n .filter((t) => t.length > 1);\n\n const hits: SearchHit[] = [];\n for (const r of results) {\n if (input.folder && !r.id.startsWith(input.folder)) continue;\n if (input.tag) {\n const note = ctx.notes.get(r.id);\n if (!note) continue;\n const noteTags = note.tags.split(' ');\n if (!noteTags.some((t) => t === input.tag || t === input.tag?.replace(/^#/, ''))) continue;\n }\n\n const note = ctx.notes.get(r.id);\n const body = note?.body ?? '';\n const noteTags = note?.tags ? note.tags.split(' ').filter(Boolean) : [];\n\n const title = r.title as string;\n const hit: SearchHit = {\n path: r.id,\n // Round the raw MiniSearch score: 2 decimals preserve relevance gaps without 17-digit float tax.\n score: Math.round(r.score * 100) / 100,\n excerpt: extractExcerpt(body, terms, input.excerptLength ?? 120),\n };\n // Omit redundant/empty metadata to keep the payload lean (both are recoverable).\n if (title && title !== basenameNoExt(r.id)) hit.title = title;\n if (noteTags.length > 0) hit.tags = noteTags;\n hits.push(hit);\n\n if (hits.length >= input.limit) break;\n }\n\n return hits;\n}\n","import { access, mkdir, readFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { stringify as stringifyYaml } from 'yaml';\nimport { z } from 'zod';\nimport { atomicWrite } from '../atomic-write.js';\nimport { assertHashMatch, contentHash } from '../content-hash.js';\nimport type { ServerContext } from '../context.js';\nimport { buildDoc, upsertDoc } from '../index/doc.js';\nimport { assertWritable } from '../policy.js';\nimport { resolveVaultPath } from '../vault-path.js';\n\nexport const CreateNoteInput = z\n .object({\n path: z\n .string()\n .describe(\n 'Vault-relative path for the new note, e.g. \"Daily Notes/2026-06-01.md\". Parent directories are created automatically.',\n ),\n content: z.string().default('').describe('Body content for the note.'),\n frontmatter: z\n .record(z.string(), z.unknown())\n .optional()\n .describe('Frontmatter key-value pairs to write as a YAML block at the top of the note.'),\n overwrite: z\n .boolean()\n .default(false)\n .describe(\n 'Overwrite an existing note. Defaults to false — throws if the note already exists.',\n ),\n prevHash: z\n .string()\n .optional()\n .describe(\n 'Optional compare-and-swap guard for overwrite: true — the contentHash of the note being replaced. Fails with hash_conflict if it changed since it was read.',\n ),\n })\n .refine((v) => v.prevHash === undefined || v.overwrite === true, {\n message:\n 'prevHash only makes sense with overwrite: true (a fresh create has no prior version).',\n });\nexport type CreateNoteInput = z.input<typeof CreateNoteInput>;\n\nexport interface CreateNoteResult {\n path: string;\n bytesWritten: number;\n /** sha-256 (hex) of the written content — usable as prevHash for a chained edit. */\n contentHash: string;\n}\n\nexport async function createNote(\n ctx: ServerContext,\n rawInput: CreateNoteInput,\n): Promise<CreateNoteResult> {\n const input = CreateNoteInput.parse(rawInput);\n const absPath = resolveVaultPath(ctx.vaultRoot, input.path);\n assertWritable(ctx.policy, input.path);\n\n if (!input.overwrite) {\n try {\n await access(absPath);\n throw new Error(`Note already exists: ${input.path}. Pass overwrite: true to replace it.`);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n } else if (input.prevHash !== undefined) {\n // CAS on the note being replaced; a missing file is a conflict too (the\n // version the caller read no longer exists).\n let existing: string;\n try {\n existing = await readFile(absPath, 'utf8');\n } catch {\n throw new Error(\n JSON.stringify({\n error: 'hash_conflict',\n path: input.path,\n expected: input.prevHash,\n actual: null,\n hint: 'The note no longer exists on disk. Re-check before overwriting.',\n }),\n );\n }\n assertHashMatch(existing, input.prevHash, input.path);\n }\n\n let raw = '';\n if (input.frontmatter && Object.keys(input.frontmatter).length > 0) {\n raw = `---\\n${stringifyYaml(input.frontmatter, { lineWidth: 0 })}---\\n`;\n }\n raw += input.content ?? '';\n\n await mkdir(dirname(absPath), { recursive: true });\n await atomicWrite(absPath, raw);\n\n // Sync in-memory index so the new note is immediately searchable.\n upsertDoc(ctx, buildDoc(input.path, raw));\n\n return {\n path: input.path,\n bytesWritten: Buffer.byteLength(raw, 'utf8'),\n contentHash: contentHash(raw),\n };\n}\n","import { basename, extname } from 'node:path';\nimport { extractInlineTags, frontmatterTags } from '@seekstone/core/extract';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport type { IndexedNote } from './types.js';\n\n/** Build an IndexedNote from a vault-relative path and raw file content. */\nexport function buildDoc(relPath: string, raw: string): IndexedNote {\n const fm = parseFrontmatter(raw);\n const allTags = [...new Set([...extractInlineTags(fm.body), ...frontmatterTags(fm.data)])];\n const title =\n typeof fm.data?.title === 'string' && fm.data.title\n ? fm.data.title\n : basename(relPath, extname(relPath));\n return {\n id: relPath,\n title,\n body: fm.body,\n tags: allTags.join(' '),\n fmKeys: fm.keys.join(' '),\n fm: fm.data,\n raw,\n sizeBytes: Buffer.byteLength(raw, 'utf8'),\n mtimeMs: Date.now(),\n };\n}\n\n/** Upsert a document into the context index. Discards any stale entry first. */\nexport function upsertDoc(\n ctx: {\n index: { discard(id: string): void; add(doc: IndexedNote): void };\n notes: Map<string, IndexedNote>;\n },\n doc: IndexedNote,\n): void {\n if (ctx.notes.has(doc.id)) ctx.index.discard(doc.id);\n ctx.notes.set(doc.id, doc);\n ctx.index.add(doc);\n}\n","import { access, mkdir, rename, rm } from 'node:fs/promises';\nimport { basename, extname, join } from 'node:path';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.js';\nimport { removeNoteBacklinks } from '../index/backlinks.js';\nimport { assertWritable } from '../policy.js';\nimport { resolveVaultPath } from '../vault-path.js';\n\nexport const DeleteNoteInput = z.object({\n path: z.string().describe('Vault-relative path of the note to delete.'),\n permanent: z\n .boolean()\n .default(false)\n .describe(\n 'Permanently remove the note instead of moving it to the vault .trash/ folder. Defaults to false — deletes are recoverable.',\n ),\n});\nexport type DeleteNoteInput = z.input<typeof DeleteNoteInput>;\n\nexport interface DeleteNoteResult {\n path: string;\n /** Vault-relative path the note was moved to, when not permanent. */\n trashedTo?: string;\n permanent: boolean;\n}\n\n/** Find a free destination name inside .trash/, suffixing before the extension on collision. */\nasync function freeTrashPath(vaultRoot: string, noteBase: string): Promise<string> {\n const ext = extname(noteBase);\n const stem = noteBase.slice(0, noteBase.length - ext.length);\n let candidate = `.trash/${noteBase}`;\n for (;;) {\n try {\n await access(join(vaultRoot, candidate));\n } catch {\n return candidate;\n }\n // Epoch-ms suffix; loop guards the (test-speed) case of two collisions in one ms.\n candidate = `.trash/${stem}.${Date.now()}${ext}`;\n }\n}\n\nexport async function deleteNote(\n ctx: ServerContext,\n rawInput: DeleteNoteInput,\n): Promise<DeleteNoteResult> {\n const input = DeleteNoteInput.parse(rawInput);\n const absPath = resolveVaultPath(ctx.vaultRoot, input.path);\n // Policy applies to the note being deleted; the .trash/ destination is the\n // safety mechanism itself, not a user write.\n assertWritable(ctx.policy, input.path);\n\n let trashedTo: string | undefined;\n if (input.permanent) {\n // rm throws ENOENT if the file doesn't exist — let it propagate.\n await rm(absPath);\n } else {\n // access first so a missing note throws ENOENT like rm does, not a\n // half-made .trash/ dir plus a rename error.\n await access(absPath);\n await mkdir(join(ctx.vaultRoot, '.trash'), { recursive: true });\n trashedTo = await freeTrashPath(ctx.vaultRoot, basename(input.path));\n // Same-volume rename: atomic, byte-identical, and invisible to the index —\n // the walker and watcher both exclude .trash/.\n await rename(absPath, join(ctx.vaultRoot, trashedTo));\n }\n\n // Clear the note's own outgoing refs while its entry is still readable.\n removeNoteBacklinks(ctx, input.path);\n if (ctx.notes.has(input.path)) {\n ctx.index.discard(input.path);\n ctx.notes.delete(input.path);\n }\n\n const result: DeleteNoteResult = { path: input.path, permanent: input.permanent };\n if (trashedTo !== undefined) result.trashedTo = trashedTo;\n return result;\n}\n","import { extractLinksWithLines } from '@seekstone/core/extract';\nimport type { BacklinkRef, ServerContext } from '../context.js';\nimport { resolveLink } from './resolve.js';\n\n/**\n * Incremental backlink-index maintenance, shared by the watcher and the\n * link-aware move path. Remove BEFORE mutating the note's entry in ctx.notes\n * (it reads the old raw); add AFTER the entry is current.\n */\n\nexport function removeNoteBacklinks(ctx: ServerContext, relPath: string): void {\n const oldDoc = ctx.notes.get(relPath);\n if (oldDoc === undefined) return;\n for (const link of extractLinksWithLines(oldDoc.raw)) {\n const resolved = resolveLink(link.target, ctx.notes);\n if (resolved === undefined) continue;\n const arr = ctx.backlinks.get(resolved);\n if (arr === undefined) continue;\n const filtered = arr.filter((r) => r.path !== relPath);\n ctx.backlinks.set(resolved, filtered);\n }\n}\n\nexport function addNoteBacklinks(ctx: ServerContext, relPath: string, raw: string): void {\n const seen = new Set<string>();\n for (const link of extractLinksWithLines(raw)) {\n const resolved = resolveLink(link.target, ctx.notes);\n if (resolved === undefined) continue;\n const dedupeKey = `${relPath}\\0${resolved}`;\n if (seen.has(dedupeKey)) continue;\n seen.add(dedupeKey);\n let arr = ctx.backlinks.get(resolved);\n if (arr === undefined) {\n arr = [];\n ctx.backlinks.set(resolved, arr);\n }\n const ref: BacklinkRef = { path: relPath, line: link.line, linkType: link.linkType };\n arr.push(ref);\n arr.sort((a, b) => a.path.localeCompare(b.path));\n }\n}\n","import { z } from 'zod';\nimport type { ServerContext } from '../context.js';\nimport { resolveVaultPath } from '../vault-path.js';\n\nexport const GetBacklinksInput = z.object({\n path: z.string().describe('Vault-relative path to the target note.'),\n includeContext: z\n .boolean()\n .optional()\n .describe('Include a short excerpt (~200 chars) from the linking line. Default true.'),\n limit: z\n .number()\n .int()\n .min(1)\n .max(500)\n .optional()\n .describe('Max backlinks to return. Default 50.'),\n});\nexport type GetBacklinksInput = z.infer<typeof GetBacklinksInput>;\n\nexport interface BacklinkEntry {\n path: string;\n line: number;\n excerpt?: string;\n linkType: 'wikilink' | 'embed';\n}\n\nexport interface GetBacklinksResult {\n target: string;\n backlinks: BacklinkEntry[];\n total: number;\n}\n\nconst EXCERPT_MAX = 200;\n\nexport function getBacklinks(ctx: ServerContext, input: GetBacklinksInput): GetBacklinksResult {\n resolveVaultPath(ctx.vaultRoot, input.path); // containment validation only\n\n const includeContext = input.includeContext ?? true;\n const limit = input.limit ?? 50;\n\n const refs = ctx.backlinks.get(input.path) ?? [];\n const total = refs.length;\n const limited = refs.slice(0, limit);\n\n const backlinks: BacklinkEntry[] = [];\n for (const ref of limited) {\n const entry: BacklinkEntry = { path: ref.path, line: ref.line, linkType: ref.linkType };\n if (includeContext) {\n const srcNote = ctx.notes.get(ref.path);\n if (srcNote !== undefined) {\n const rawLines = srcNote.raw.split('\\n');\n const lineText = rawLines[ref.line - 1];\n if (lineText !== undefined) {\n entry.excerpt =\n lineText.length > EXCERPT_MAX ? `${lineText.slice(0, EXCERPT_MAX)}…` : lineText;\n }\n }\n }\n backlinks.push(entry);\n }\n\n return { target: input.path, backlinks, total };\n}\n","import type { LinkType } from '@seekstone/core/extract';\nimport { extractLinksWithLines } from '@seekstone/core/extract';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.js';\nimport { resolveLink } from '../index/resolve.js';\nimport { resolveVaultPath } from '../vault-path.js';\n\nexport const GetLinksInput = z.object({\n path: z.string().describe('Vault-relative path to the note.'),\n});\nexport type GetLinksInput = z.infer<typeof GetLinksInput>;\n\nexport interface GetLinksEntry {\n raw: string;\n /** Resolved vault-relative path. Absent when unresolved. */\n target?: string;\n resolved: boolean;\n linkType: LinkType;\n line: number;\n}\n\nexport interface GetLinksResult {\n path: string;\n links: GetLinksEntry[];\n}\n\nexport function getLinks(ctx: ServerContext, input: GetLinksInput): GetLinksResult {\n resolveVaultPath(ctx.vaultRoot, input.path); // containment validation only\n const note = ctx.notes.get(input.path);\n if (note === undefined) throw new Error(`Note not found: ${input.path}`);\n\n const rawLinks = extractLinksWithLines(note.raw);\n\n // De-duplicate: one entry per (target string) — keep first occurrence by line\n const seen = new Set<string>();\n const links: GetLinksEntry[] = [];\n for (const link of rawLinks) {\n if (seen.has(link.target)) continue;\n seen.add(link.target);\n const resolvedPath = resolveLink(link.target, ctx.notes);\n const entry: GetLinksEntry = {\n raw: link.raw,\n resolved: resolvedPath !== undefined,\n linkType: link.linkType,\n line: link.line,\n };\n if (resolvedPath !== undefined) entry.target = resolvedPath;\n links.push(entry);\n }\n\n links.sort((a, b) => a.line - b.line);\n\n return { path: input.path, links };\n}\n","import { z } from 'zod';\nimport type { ServerContext } from '../context.js';\n\nexport const ListNotesInput = z.object({\n folder: z\n .string()\n .optional()\n .describe(\n 'Vault-relative folder prefix to list. Omit for all notes. Example: \"Daily Notes/2026\".',\n ),\n tag: z.string().optional().describe('Filter by tag. Include or omit the leading #.'),\n limit: z.number().int().min(1).max(500).default(100).describe('Maximum number of results.'),\n});\nexport type ListNotesInput = z.infer<typeof ListNotesInput>;\n\nexport interface NoteEntry {\n path: string;\n title: string;\n tags: string[];\n sizeBytes: number;\n}\n\nexport function listNotes(ctx: ServerContext, input: ListNotesInput): NoteEntry[] {\n const tag = input.tag?.replace(/^#/, '');\n const results: NoteEntry[] = [];\n\n for (const [path, note] of ctx.notes) {\n if (input.folder && !path.startsWith(input.folder)) continue;\n if (tag) {\n const noteTags = note.tags.split(' ').filter(Boolean);\n if (!noteTags.includes(tag)) continue;\n }\n results.push({\n path,\n title: note.title,\n tags: note.tags.split(' ').filter(Boolean),\n sizeBytes: note.sizeBytes,\n });\n if (results.length >= input.limit) break;\n }\n\n return results.sort((a, b) => a.path.localeCompare(b.path));\n}\n","import { z } from 'zod';\nimport type { ServerContext } from '../context.js';\n\nexport const ListTagsInput = z.object({\n pattern: z\n .string()\n .optional()\n .describe('Substring filter on tag name. Example: \"work\" matches \"work\" and \"area/work\".'),\n minCount: z\n .number()\n .int()\n .min(1)\n .optional()\n .describe('Only return tags used in at least this many notes.'),\n sort: z\n .enum(['count', 'name'])\n .default('count')\n .describe('Sort by usage count descending (default) or alphabetically.'),\n});\nexport type ListTagsInput = z.infer<typeof ListTagsInput>;\n\nexport interface TagEntry {\n tag: string;\n count: number;\n parent?: string;\n}\n\nexport interface ListTagsResult {\n tags: TagEntry[];\n total: number;\n}\n\nexport function listTags(ctx: ServerContext, input: ListTagsInput): ListTagsResult {\n const counts = new Map<string, number>();\n\n for (const note of ctx.notes.values()) {\n for (const tag of note.tags.split(' ').filter(Boolean)) {\n counts.set(tag, (counts.get(tag) ?? 0) + 1);\n }\n }\n\n const tags: TagEntry[] = [];\n for (const [tag, count] of counts) {\n if (input.pattern !== undefined && !tag.includes(input.pattern)) continue;\n if (input.minCount !== undefined && count < input.minCount) continue;\n const slashIdx = tag.lastIndexOf('/');\n const entry: TagEntry = { tag, count };\n if (slashIdx !== -1) entry.parent = tag.slice(0, slashIdx);\n tags.push(entry);\n }\n\n if (input.sort === 'name') {\n tags.sort((a, b) => a.tag.localeCompare(b.tag));\n } else {\n tags.sort((a, b) => b.count - a.count || a.tag.localeCompare(b.tag));\n }\n\n return { tags, total: tags.length };\n}\n","import { access, mkdir, rename } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { z } from 'zod';\nimport { atomicWrite } from '../atomic-write.js';\nimport type { ServerContext } from '../context.js';\nimport { addNoteBacklinks, removeNoteBacklinks } from '../index/backlinks.js';\nimport { buildDoc, upsertDoc } from '../index/doc.js';\nimport { assertWritable, isWritable } from '../policy.js';\nimport { resolveVaultPath } from '../vault-path.js';\nimport { hasMarkdownLinkTo, rewriteNoteLinks } from './rewrite_links.js';\n\nexport const MoveNoteInput = z.object({\n from: z.string().describe('Vault-relative path of the note to move.'),\n to: z.string().describe('Vault-relative destination path.'),\n overwrite: z\n .boolean()\n .default(false)\n .describe('Overwrite the destination if it exists. Defaults to false.'),\n rewriteLinks: z\n .boolean()\n .default(true)\n .describe(\n 'Rewrite wikilinks and markdown links in other notes that point at the moved note, so nothing breaks. Defaults to true; pass false to move the file only.',\n ),\n});\nexport type MoveNoteInput = z.input<typeof MoveNoteInput>;\n\nexport interface MoveNoteResult {\n from: string;\n to: string;\n /** Referencing notes whose content was rewritten. */\n notesRewritten: number;\n /** Individual links rewritten across those notes. */\n linksRewritten: number;\n /** Referencing notes left untouched because SEEKSTONE_WRITE_PATHS excludes them. */\n skipped?: string[];\n}\n\nexport async function moveNote(\n ctx: ServerContext,\n rawInput: MoveNoteInput,\n): Promise<MoveNoteResult> {\n // Parse here (not only in dispatch) so defaults like rewriteLinks apply to\n // direct callers too — same pattern as periodic_note.\n const input = MoveNoteInput.parse(rawInput);\n const absFrom = resolveVaultPath(ctx.vaultRoot, input.from);\n const absTo = resolveVaultPath(ctx.vaultRoot, input.to);\n assertWritable(ctx.policy, input.from);\n assertWritable(ctx.policy, input.to);\n\n try {\n await access(absFrom);\n } catch {\n throw new Error(`Note not found: ${input.from}`);\n }\n\n if (!input.overwrite) {\n try {\n await access(absTo);\n throw new Error(\n `Destination already exists: ${input.to}. Pass overwrite: true to replace it.`,\n );\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n }\n\n // Snapshot pre-move state: link resolution before/after the move differs,\n // and the rewriter needs both views to rewrite only what breaks.\n const preNotes = new Map(ctx.notes);\n\n // Candidate referencing notes: wikilink sources from the backlink index,\n // plus a scan for markdown links (which the backlink index doesn't cover).\n // The moved note's own outgoing links are untouched by design — wikilinks\n // are path-independent, and rewriting its relative markdown links is a\n // separate concern from \"don't orphan the note in the graph\".\n const candidates = new Set<string>();\n if (input.rewriteLinks) {\n for (const ref of ctx.backlinks.get(input.from) ?? []) candidates.add(ref.path);\n for (const [path, doc] of preNotes) {\n if (path === input.from || candidates.has(path)) continue;\n if (hasMarkdownLinkTo(doc.raw, path, input.from)) candidates.add(path);\n }\n candidates.delete(input.from);\n }\n\n await mkdir(dirname(absTo), { recursive: true });\n await rename(absFrom, absTo);\n\n // Grab raw before removing the old entry, and clear the moved note's own\n // outgoing backlink refs while its old entry is still readable — otherwise\n // they stay registered under the stale source path forever.\n const raw = ctx.notes.get(input.from)?.raw ?? '';\n removeNoteBacklinks(ctx, input.from);\n\n if (ctx.notes.has(input.from)) {\n ctx.index.discard(input.from);\n ctx.notes.delete(input.from);\n }\n upsertDoc(ctx, buildDoc(input.to, raw));\n addNoteBacklinks(ctx, input.to, raw);\n\n let notesRewritten = 0;\n let linksRewritten = 0;\n const skipped: string[] = [];\n\n for (const path of [...candidates].sort()) {\n const doc = ctx.notes.get(path);\n if (doc === undefined) continue;\n if (!isWritable(ctx.policy, path)) {\n // Out of write scope: leave the note untouched but report it, so the\n // caller knows which references were NOT updated.\n skipped.push(path);\n continue;\n }\n const { content, count } = rewriteNoteLinks(\n doc.raw,\n path,\n input.from,\n input.to,\n preNotes,\n ctx.notes,\n );\n // Re-register this note's backlinks against post-move resolution even\n // when nothing was rewritten (basename links now resolve to the new\n // path); remove reads the old raw, so call it before updating the entry.\n removeNoteBacklinks(ctx, path);\n if (count > 0) {\n await atomicWrite(join(ctx.vaultRoot, path), content);\n upsertDoc(ctx, buildDoc(path, content));\n notesRewritten++;\n linksRewritten += count;\n addNoteBacklinks(ctx, path, content);\n } else {\n addNoteBacklinks(ctx, path, doc.raw);\n }\n }\n\n // Nothing resolves to the old path anymore — drop the stale key.\n ctx.backlinks.delete(input.from);\n\n const result: MoveNoteResult = { from: input.from, to: input.to, notesRewritten, linksRewritten };\n if (skipped.length > 0) result.skipped = skipped;\n return result;\n}\n","import { posix } from 'node:path';\nimport { resolveLink } from '../index/resolve.js';\n\n/**\n * Pure link-rewriting for link-aware moves: given a referencing note's raw\n * content, rewrite every wikilink, embed, and relative/vault-absolute markdown\n * link that pointed at `oldPath` so it points at `newPath`.\n *\n * Principles:\n * - **Rewrite only what breaks.** A link is left byte-identical when it still\n * resolves to the moved note after the move (Obsidian's shortest-unique-\n * basename semantics — e.g. `[[Italy]]` survives a folder move unchanged).\n * - **Preserve everything around the target**: embed `!`, `#fragment`,\n * `|alias`, `%20`-encoding and `<...>` wrapping in markdown links.\n * - **Skip fenced code blocks** (``` / ~~~). Inline backtick spans are NOT\n * skipped — consistent with the repo's deliberately loose link parsing.\n *\n * `preNotes` is the notes map as it was before the move (oldPath present),\n * `postNotes` after (newPath present) — both are needed to decide whether a\n * link used to point at the moved note and whether it still does.\n */\n\n// Mirrors LINK_WITH_LINES_RE in @seekstone/core/extract (same bounded\n// quantifiers), split into groups for reconstruction.\nconst WIKI_RE = /(!?)\\[\\[([^\\]|#\\n]{1,512})(#[^\\]|\\n]{1,512})?(\\|[^\\]\\n]{1,512})?\\]\\]/g;\n\n// Markdown links/embeds: target is everything to the first `)` (titles like\n// `[t](x \"title\")` are out of scope, matching the loose-parsing ethos).\nconst MD_RE = /(!?)\\[([^\\]\\n]*)\\]\\(([^)\\n]+)\\)/g;\n\nconst FENCE_RE = /^\\s*(```|~~~)/;\n\nexport interface RewriteNoteLinksResult {\n content: string;\n /** Number of individual links rewritten. */\n count: number;\n}\n\n/** Shortest target that still uniquely resolves to newPath: basename if unambiguous, else full path. */\nfunction wikiTargetFor(newPath: string, postNotes: Map<string, unknown>): string {\n const noExt = newPath.replace(/\\.md$/i, '');\n const base = noExt.split('/').pop() ?? noExt;\n // Count basename collisions directly — resolveLink picks the first match by\n // map iteration order, which is not a stable notion of \"unambiguous\".\n const baseLower = base.toLowerCase();\n let matches = 0;\n for (const p of postNotes.keys()) {\n const pBase = (p.replace(/\\.md$/i, '').split('/').pop() ?? '').toLowerCase();\n if (pBase === baseLower && ++matches > 1) return noExt;\n }\n return base;\n}\n\n/** Interpret a markdown-link target as a vault-relative note path (or undefined). */\nfunction mdTargetToVaultPath(rawTarget: string, sourceDir: string): string | undefined {\n let t = rawTarget.trim();\n if (t.startsWith('<') && t.endsWith('>')) t = t.slice(1, -1);\n if (/^[a-z][a-z0-9+.-]*:/i.test(t)) return undefined; // http:, mailto:, obsidian:, …\n if (t.startsWith('#') || t === '') return undefined; // intra-note anchor\n const hashIdx = t.indexOf('#');\n if (hashIdx !== -1) t = t.slice(0, hashIdx);\n try {\n t = decodeURIComponent(t);\n } catch {\n /* malformed escape — use as-is */\n }\n const joined = posix.normalize(posix.join(sourceDir, t));\n if (joined.startsWith('..')) return undefined; // escapes the vault\n return joined;\n}\n\nexport function rewriteNoteLinks(\n raw: string,\n sourcePath: string,\n oldPath: string,\n newPath: string,\n preNotes: Map<string, unknown>,\n postNotes: Map<string, unknown>,\n): RewriteNoteLinksResult {\n const sourceDir = posix.dirname(sourcePath);\n const oldNoExt = oldPath.replace(/\\.md$/i, '');\n let count = 0;\n\n const rewriteWikiLine = (line: string): string =>\n line.replace(WIKI_RE, (full, bang: string, target: string, frag?: string, alias?: string) => {\n const t = target.trim();\n // Still resolves to the moved note → Obsidian keeps working; don't touch.\n if (resolveLink(t, postNotes) === newPath) return full;\n // Only rewrite links that used to point at the moved note.\n if (resolveLink(t, preNotes) !== oldPath) return full;\n count++;\n const next = wikiTargetFor(newPath, postNotes);\n return `${bang}[[${next}${frag ?? ''}${alias ?? ''}]]`;\n });\n\n const rewriteMdLine = (line: string): string =>\n line.replace(MD_RE, (full, bang: string, text: string, rawTarget: string) => {\n // Relative-to-source interpretation first, then vault-absolute.\n const asRelative = mdTargetToVaultPath(rawTarget, sourceDir);\n const asAbsolute = mdTargetToVaultPath(rawTarget, '.');\n let vaultAbsoluteStyle: boolean;\n if (asRelative === oldPath || asRelative === oldNoExt) vaultAbsoluteStyle = false;\n else if (asAbsolute === oldPath || asAbsolute === oldNoExt) vaultAbsoluteStyle = true;\n else return full;\n count++;\n\n const trimmed = rawTarget.trim();\n const wrapped = trimmed.startsWith('<') && trimmed.endsWith('>');\n const inner = wrapped ? trimmed.slice(1, -1) : trimmed;\n const hashIdx = inner.indexOf('#');\n const fragment = hashIdx === -1 ? '' : inner.slice(hashIdx);\n const hadExt = /\\.md(#|$)/i.test(inner);\n\n let next = vaultAbsoluteStyle ? newPath : posix.relative(sourceDir, newPath);\n if (!hadExt) next = next.replace(/\\.md$/i, '');\n // Preserve the original encoding style for spaces; wrap in <> when the\n // new path has spaces and the original used neither style.\n if (inner.includes('%20')) next = next.replaceAll(' ', '%20');\n const needsWrap = wrapped || next.includes(' ');\n const target = needsWrap ? `<${next}${fragment}>` : `${next}${fragment}`;\n return `${bang}[${text}](${target})`;\n });\n\n let inFence = false;\n const lines = raw.split('\\n').map((line) => {\n if (FENCE_RE.test(line)) {\n inFence = !inFence;\n return line;\n }\n if (inFence) return line;\n return rewriteMdLine(rewriteWikiLine(line));\n });\n\n return { content: lines.join('\\n'), count };\n}\n\n/**\n * Scan a note's raw content for markdown links resolving to `targetPath` —\n * the candidate-discovery pass for referencing notes the backlink index can't\n * see (it only indexes wikilinks).\n */\nexport function hasMarkdownLinkTo(raw: string, sourcePath: string, targetPath: string): boolean {\n if (!raw.includes('](')) return false;\n const sourceDir = posix.dirname(sourcePath);\n const noExt = targetPath.replace(/\\.md$/i, '');\n for (const m of raw.matchAll(MD_RE)) {\n const rawTarget = m[3] ?? '';\n const asRelative = mdTargetToVaultPath(rawTarget, sourceDir);\n const asAbsolute = mdTargetToVaultPath(rawTarget, '.');\n if (\n asRelative === targetPath ||\n asRelative === noExt ||\n asAbsolute === targetPath ||\n asAbsolute === noExt\n ) {\n return true;\n }\n }\n return false;\n}\n","import { readFile } from 'node:fs/promises';\nimport { buildOutline } from '@seekstone/core/outline';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.js';\nimport { resolveVaultPath } from '../vault-path.js';\n\nexport const OutlineNoteInput = z.object({\n path: z.string().describe('Vault-relative path to the note.'),\n includeBlocks: z\n .boolean()\n .default(true)\n .describe('Include block-reference anchors (^id). Default true.'),\n includeSizes: z\n .boolean()\n .default(false)\n .describe('Include per-section character length in each heading entry. Default false.'),\n});\nexport type OutlineNoteInput = z.infer<typeof OutlineNoteInput>;\n\nexport async function outlineNote(ctx: ServerContext, input: OutlineNoteInput) {\n const absPath = resolveVaultPath(ctx.vaultRoot, input.path);\n\n const raw = await readFile(absPath, 'utf8');\n return buildOutline(raw, {\n includeBlocks: input.includeBlocks,\n includeSizes: input.includeSizes,\n });\n}\n","import { parseFrontmatter } from './frontmatter.js';\n\nexport interface HeadingEntry {\n text: string;\n level: number;\n /** 1-based line number in the file. */\n line: number;\n /** Character offset of the leading `#` in the raw file string. */\n byteOffset: number;\n /** Characters from this heading to the next (or end of file). Only present when includeSizes is true. */\n byteLength?: number;\n}\n\nexport interface BlockEntry {\n id: string;\n /** 1-based line number in the file. */\n line: number;\n /** Character offset of the `^` in the raw file string. */\n byteOffset: number;\n}\n\nexport interface NoteOutline {\n headings: HeadingEntry[];\n blocks: BlockEntry[];\n /** Top-level frontmatter keys in original order (empty if no frontmatter). */\n frontmatterKeys: string[];\n /** True UTF-8 byte length of the file. */\n totalBytes: number;\n}\n\nexport interface OutlineOptions {\n /** Include block-reference anchors (^id). Default true. */\n includeBlocks?: boolean;\n /** Include per-section character length in each heading entry. Default false. */\n includeSizes?: boolean;\n}\n\nconst HEADING_RE = /^(#{1,6})\\s+(.+)/;\nconst BLOCK_REF_RE = /\\^([a-zA-Z0-9-]+)$/;\n\n/**\n * Parse a note's structure — heading tree, block anchors, frontmatter keys —\n * without touching the prose. Shared by outline_note and patch_note.\n *\n * Offsets are character positions in the raw string (identical to UTF-8 byte\n * offsets for ASCII content, which covers the vast majority of vault notes).\n */\nexport function buildOutline(raw: string, options: OutlineOptions = {}): NoteOutline {\n const { includeBlocks = true, includeSizes = false } = options;\n\n const fm = parseFrontmatter(raw);\n const totalBytes = Buffer.byteLength(raw, 'utf8');\n const headings: HeadingEntry[] = [];\n const blocks: BlockEntry[] = [];\n\n let charOffset = 0;\n\n for (const [i, line] of raw.split('\\n').entries()) {\n const lineCharOffset = charOffset;\n const lineNum = i + 1;\n\n if (charOffset >= fm.bodyStart) {\n const hm = HEADING_RE.exec(line);\n if (hm) {\n headings.push({\n text: (hm[2] ?? '').trim(),\n level: (hm[1] ?? '').length,\n line: lineNum,\n byteOffset: lineCharOffset,\n });\n }\n\n if (includeBlocks) {\n const trimmed = line.trimEnd();\n const bm = BLOCK_REF_RE.exec(trimmed);\n if (bm) {\n const caretPos = trimmed.lastIndexOf('^');\n blocks.push({\n id: bm[1] ?? '',\n line: lineNum,\n byteOffset: lineCharOffset + caretPos,\n });\n }\n }\n }\n\n charOffset += line.length + 1; // +1 for the '\\n' consumed by split\n }\n\n if (includeSizes) {\n for (const [i, heading] of headings.entries()) {\n const next = headings[i + 1];\n heading.byteLength = (next?.byteOffset ?? raw.length) - heading.byteOffset;\n }\n }\n\n return { headings, blocks, frontmatterKeys: fm.keys, totalBytes };\n}\n","import { readFile } from 'node:fs/promises';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport { parseDocument } from 'yaml';\nimport { z } from 'zod';\nimport { atomicWrite } from '../atomic-write.js';\nimport { assertHashMatch, contentHash } from '../content-hash.js';\nimport type { ServerContext } from '../context.js';\nimport { assertWritable } from '../policy.js';\nimport { resolveVaultPath } from '../vault-path.js';\n\nexport const PatchFrontmatterInput = z.object({\n path: z.string().describe('Vault-relative path to the note.'),\n patch: z\n .record(z.string(), z.unknown())\n .describe(\n 'Key-value pairs to set in the frontmatter. Existing keys not in the patch are unchanged. Pass null as a value to remove a key.',\n ),\n prevHash: z\n .string()\n .optional()\n .describe(\n 'Optional compare-and-swap guard: the contentHash from a prior read. Fails with hash_conflict if the note changed since.',\n ),\n});\nexport type PatchFrontmatterInput = z.infer<typeof PatchFrontmatterInput>;\n\nexport interface PatchFrontmatterResult {\n path: string;\n keysChanged: string[];\n keysAdded: string[];\n keysRemoved: string[];\n /** sha-256 (hex) of the new content — usable as prevHash for a chained edit. */\n contentHash: string;\n}\n\n/**\n * Edit frontmatter values without reordering existing keys or changing\n * quote style. Uses yaml.parseDocument which preserves the CST, so round-trips\n * are byte-faithful for keys not touched by the patch.\n *\n * Patch semantics:\n * - Key present in patch, value non-null → set/update\n * - Key present in patch, value null → delete\n * - Key absent from patch → untouched\n *\n * If the note has no frontmatter, a new `---` block is created at the top.\n */\nexport async function patchFrontmatter(\n ctx: ServerContext,\n input: PatchFrontmatterInput,\n): Promise<PatchFrontmatterResult> {\n const absPath = resolveVaultPath(ctx.vaultRoot, input.path);\n assertWritable(ctx.policy, input.path);\n\n const original = await readFile(absPath, 'utf8');\n if (input.prevHash !== undefined) assertHashMatch(original, input.prevHash, input.path);\n const fm = parseFrontmatter(original);\n\n const keysChanged: string[] = [];\n const keysAdded: string[] = [];\n const keysRemoved: string[] = [];\n\n let newContent: string;\n\n if (!fm.present) {\n // No existing FM — create a minimal block with just the patch keys.\n const doc = parseDocument('');\n for (const [k, v] of Object.entries(input.patch)) {\n if (v !== null) {\n doc.set(k, v);\n keysAdded.push(k);\n }\n }\n newContent = `---\\n${doc.toString({ lineWidth: 0 })}---\\n${original}`;\n } else {\n // Parse the existing FM block preserving source tokens.\n const opensWithCRLF = original.startsWith('---\\r\\n');\n const openLen = opensWithCRLF ? 5 : 4;\n const closeLen = opensWithCRLF ? 7 : 5;\n const yamlText = original.slice(openLen, fm.bodyStart - closeLen);\n\n const doc = parseDocument(yamlText, { keepSourceTokens: true });\n\n for (const [k, v] of Object.entries(input.patch)) {\n const existing = doc.has(k);\n if (v === null) {\n if (existing) {\n doc.delete(k);\n keysRemoved.push(k);\n }\n } else if (existing) {\n doc.set(k, v);\n keysChanged.push(k);\n } else {\n doc.set(k, v);\n keysAdded.push(k);\n }\n }\n\n const head = opensWithCRLF ? '---\\r\\n' : '---\\n';\n const tail = opensWithCRLF ? '---\\r\\n' : '---\\n';\n newContent = `${head}${doc.toString({ lineWidth: 0 })}${tail}${fm.body}`;\n }\n\n await atomicWrite(absPath, newContent);\n\n // Update in-memory cache.\n const cached = ctx.notes.get(input.path);\n if (cached) {\n const updated = parseFrontmatter(newContent);\n cached.raw = newContent;\n cached.fmKeys = updated.keys.join(' ');\n }\n\n return {\n path: input.path,\n keysChanged,\n keysAdded,\n keysRemoved,\n contentHash: contentHash(newContent),\n };\n}\n","import { readFile } from 'node:fs/promises';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport { buildOutline } from '@seekstone/core/outline';\nimport { z } from 'zod';\nimport { atomicWrite } from '../atomic-write.js';\nimport { assertHashMatch, contentHash } from '../content-hash.js';\nimport type { ServerContext } from '../context.js';\nimport { assertWritable } from '../policy.js';\nimport { resolveVaultPath } from '../vault-path.js';\n\nexport const PatchNoteInput = z.object({\n path: z.string().describe('Vault-relative path to the note.'),\n target: z\n .union([\n z.object({ heading: z.string().describe('Heading text (without # markers).') }),\n z.object({ block: z.string().describe('Block reference ID (without the ^ prefix).') }),\n ])\n .describe('Target section. Provide exactly one of heading or block.'),\n operation: z\n .enum(['append', 'prepend', 'replace'])\n .describe(\n 'append: insert after section content. prepend: insert after heading line. replace: swap section content.',\n ),\n content: z.string().min(1).describe('Content to insert or replace with.'),\n createIfMissing: z\n .boolean()\n .default(false)\n .describe(\n 'If the heading target is not found, append a new heading (level 2) + content. Only valid for heading targets.',\n ),\n prevHash: z\n .string()\n .optional()\n .describe(\n 'Optional compare-and-swap guard: the contentHash from a prior read. Fails with hash_conflict if the note changed since.',\n ),\n});\nexport type PatchNoteInput = z.infer<typeof PatchNoteInput>;\n\nexport interface PatchNoteResult {\n path: string;\n bytesWritten: number;\n targetResolvedAt: { line: number; byteOffset: number };\n frontmatterUnchanged: true;\n /** sha-256 (hex) of the new content — usable as prevHash for a chained edit. */\n contentHash: string;\n}\n\n/** Character offset of the start of the next line after `offset`. */\nfunction lineEnd(raw: string, offset: number): number {\n const nl = raw.indexOf('\\n', offset);\n return nl === -1 ? raw.length : nl + 1;\n}\n\n/** Character offset of the start of the line that contains `offset`. */\nfunction lineStart(raw: string, offset: number): number {\n if (offset === 0) return 0;\n const prev = raw.lastIndexOf('\\n', offset - 1);\n return prev === -1 ? 0 : prev + 1;\n}\n\nexport async function patchNote(\n ctx: ServerContext,\n input: PatchNoteInput,\n): Promise<PatchNoteResult> {\n const absPath = resolveVaultPath(ctx.vaultRoot, input.path);\n assertWritable(ctx.policy, input.path);\n\n const raw = await readFile(absPath, 'utf8');\n if (input.prevHash !== undefined) assertHashMatch(raw, input.prevHash, input.path);\n const fm = parseFrontmatter(raw);\n const originalFmRegion = raw.slice(0, fm.bodyStart);\n const outline = buildOutline(raw, { includeBlocks: true });\n\n let contentStart: number;\n let contentEnd: number;\n let resolvedAt: { line: number; byteOffset: number };\n\n if ('heading' in input.target) {\n const targetText = input.target.heading;\n const heading = outline.headings.find((h) => h.text.toLowerCase() === targetText.toLowerCase());\n\n if (!heading) {\n if (input.createIfMissing) {\n const sep = raw.endsWith('\\n\\n') ? '' : raw.endsWith('\\n') ? '\\n' : '\\n\\n';\n const contentNl = input.content.endsWith('\\n') ? '' : '\\n';\n const newContent = `${raw}${sep}## ${targetText}\\n\\n${input.content}${contentNl}`;\n await atomicWrite(absPath, newContent);\n await verifyFrontmatter(absPath, originalFmRegion);\n updateCache(ctx, input.path, newContent, fm.bodyStart);\n const headingOffset = raw.length + sep.length;\n return {\n path: input.path,\n bytesWritten: Buffer.byteLength(newContent, 'utf8'),\n targetResolvedAt: {\n line: newContent.slice(0, headingOffset).split('\\n').length,\n byteOffset: headingOffset,\n },\n frontmatterUnchanged: true,\n contentHash: contentHash(newContent),\n };\n }\n throw new Error(\n JSON.stringify({\n error: 'heading_not_found',\n target: targetText,\n available: outline.headings.map((h) => h.text),\n }),\n );\n }\n\n const headingLineEnd = lineEnd(raw, heading.byteOffset);\n const nextSibling = outline.headings.find(\n (h) => h.byteOffset > heading.byteOffset && h.level <= heading.level,\n );\n contentStart = headingLineEnd;\n contentEnd = nextSibling?.byteOffset ?? raw.length;\n resolvedAt = { line: heading.line, byteOffset: heading.byteOffset };\n } else {\n const targetId = input.target.block;\n const block = outline.blocks.find((b) => b.id === targetId);\n if (!block) {\n throw new Error(\n JSON.stringify({\n error: 'block_not_found',\n target: targetId,\n available: outline.blocks.map((b) => b.id),\n }),\n );\n }\n contentStart = lineStart(raw, block.byteOffset);\n contentEnd = lineEnd(raw, block.byteOffset);\n resolvedAt = { line: block.line, byteOffset: block.byteOffset };\n }\n\n const before = raw.slice(0, contentStart);\n const section = raw.slice(contentStart, contentEnd);\n const after = raw.slice(contentEnd);\n\n let newContent: string;\n switch (input.operation) {\n case 'prepend': {\n const sep = input.content.endsWith('\\n') ? '' : '\\n';\n newContent = `${before}${input.content}${sep}${section}${after}`;\n break;\n }\n case 'append': {\n const sectionNl = section.endsWith('\\n') ? '' : '\\n';\n const contentNl = input.content.endsWith('\\n') ? '' : '\\n';\n newContent = `${before}${section}${sectionNl}${input.content}${contentNl}${after}`;\n break;\n }\n case 'replace': {\n const contentNl = input.content.endsWith('\\n') ? '' : '\\n';\n newContent = `${before}${input.content}${contentNl}${after}`;\n break;\n }\n default:\n throw new Error(`Unknown operation: ${String(input.operation)}`);\n }\n\n await atomicWrite(absPath, newContent);\n await verifyFrontmatter(absPath, originalFmRegion);\n updateCache(ctx, input.path, newContent, fm.bodyStart);\n\n return {\n path: input.path,\n bytesWritten: Buffer.byteLength(newContent, 'utf8'),\n targetResolvedAt: resolvedAt,\n frontmatterUnchanged: true,\n contentHash: contentHash(newContent),\n };\n}\n\nasync function verifyFrontmatter(absPath: string, originalFmRegion: string): Promise<void> {\n const written = await readFile(absPath, 'utf8');\n if (written.slice(0, originalFmRegion.length) !== originalFmRegion) {\n throw new Error('Write-safety violation: frontmatter region changed unexpectedly');\n }\n}\n\nfunction updateCache(\n ctx: ServerContext,\n path: string,\n newContent: string,\n bodyStart: number,\n): void {\n const cached = ctx.notes.get(path);\n if (cached) {\n cached.body = newContent.slice(bodyStart);\n cached.raw = newContent;\n cached.sizeBytes = Buffer.byteLength(newContent, 'utf8');\n }\n}\n","import { access, mkdir, readFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport { z } from 'zod';\nimport { atomicWrite } from '../atomic-write.js';\nimport { assertHashMatch, contentHash } from '../content-hash.js';\nimport type { ServerContext } from '../context.js';\nimport { buildDoc, upsertDoc } from '../index/doc.js';\nimport { assertWritable } from '../policy.js';\nimport { resolveVaultPath } from '../vault-path.js';\n\n// ── Period type ───────────────────────────────────────────────────────────────\n\nconst PERIOD = z.enum(['daily', 'weekly', 'monthly', 'quarterly', 'yearly']);\ntype Period = z.infer<typeof PERIOD>;\n\n// ── Config shapes (read from .obsidian on disk) ───────────────────────────────\n\ninterface PeriodicEntry {\n folder?: string;\n format?: string;\n template?: string;\n}\n\ninterface DailyNotesJson extends PeriodicEntry {}\n\ninterface PeriodicNotesJson {\n daily?: PeriodicEntry;\n weekly?: PeriodicEntry;\n monthly?: PeriodicEntry;\n quarterly?: PeriodicEntry;\n yearly?: PeriodicEntry;\n}\n\nconst DEFAULTS: Record<Period, Required<PeriodicEntry>> = {\n daily: { folder: '', format: 'YYYY-MM-DD', template: '' },\n weekly: { folder: '', format: 'gggg-[W]ww', template: '' },\n monthly: { folder: '', format: 'YYYY-MM', template: '' },\n quarterly: { folder: '', format: 'YYYY-[Q]Q', template: '' },\n yearly: { folder: '', format: 'YYYY', template: '' },\n};\n\nasync function readJson<T>(absPath: string): Promise<T | null> {\n try {\n return JSON.parse(await readFile(absPath, 'utf8')) as T;\n } catch {\n return null;\n }\n}\n\nasync function resolveConfig(vaultRoot: string, period: Period): Promise<Required<PeriodicEntry>> {\n const defaults = DEFAULTS[period];\n\n if (period === 'daily') {\n const cfg = await readJson<DailyNotesJson>(join(vaultRoot, '.obsidian', 'daily-notes.json'));\n return {\n folder: cfg?.folder ?? defaults.folder,\n format: cfg?.format ?? defaults.format,\n template: cfg?.template ?? defaults.template,\n };\n }\n\n const cfg = await readJson<PeriodicNotesJson>(\n join(vaultRoot, '.obsidian', 'plugins', 'periodic-notes', 'data.json'),\n );\n const entry = cfg?.[period];\n return {\n folder: entry?.folder ?? defaults.folder,\n format: entry?.format ?? defaults.format,\n template: entry?.template ?? defaults.template,\n };\n}\n\n// ── Moment-style date formatting (no external dep) ───────────────────────────\n\nfunction isoWeekAndYear(date: Date): { week: number; year: number } {\n const d = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));\n const day = d.getUTCDay() || 7; // Mon=1 … Sun=7\n d.setUTCDate(d.getUTCDate() + 4 - day); // nearest Thursday\n const jan1 = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));\n const week = Math.ceil(((d.getTime() - jan1.getTime()) / 86_400_000 + 1) / 7);\n return { week, year: d.getUTCFullYear() };\n}\n\nexport function formatMomentDate(date: Date, format: string): string {\n // Use UTC accessors: ISO date strings (YYYY-MM-DD) parse as UTC midnight,\n // so local-time accessors would return the previous day in negative-offset\n // timezones (EST, PST, etc.).\n const year = date.getUTCFullYear();\n const month = date.getUTCMonth() + 1;\n const day = date.getUTCDate();\n const quarter = Math.ceil(month / 3);\n const { week: isoWeek, year: isoYear } = isoWeekAndYear(date);\n\n // Protect literal text in square brackets from token substitution.\n const PLACEHOLDER = '\\x00';\n const literals: string[] = [];\n let s = format.replace(/\\[([^\\]]*)\\]/g, (_, lit) => {\n literals.push(lit);\n return `${PLACEHOLDER}${literals.length - 1}${PLACEHOLDER}`;\n });\n\n // Substitute longest tokens first to avoid partial matches (e.g. YYYY before YY).\n s = s\n .replace(/gggg/g, String(isoYear).padStart(4, '0'))\n .replace(/YYYY/g, String(year).padStart(4, '0'))\n .replace(/YY/g, String(year).slice(-2))\n .replace(/MM/g, String(month).padStart(2, '0'))\n .replace(/M/g, String(month))\n .replace(/DD/g, String(day).padStart(2, '0'))\n .replace(/D/g, String(day))\n .replace(/ww/g, String(isoWeek).padStart(2, '0'))\n .replace(/W/g, String(isoWeek))\n .replace(/Q/g, String(quarter));\n\n // Restore literals. Built via RegExp constructor because the placeholder is a\n // control char (\\x00) that Biome disallows in a regex literal; Codacy's\n // non-literal-RegExp pattern is disabled repo-wide for this safe internal use.\n return s.replace(\n new RegExp(`${PLACEHOLDER}(\\\\d+)${PLACEHOLDER}`, 'g'),\n (_, i) => literals[+i] ?? '',\n );\n}\n\n// ── Resolve a note path from config + date ────────────────────────────────────\n\nexport function resolveNotePath(cfg: Required<PeriodicEntry>, date: Date): string {\n const filename = `${formatMomentDate(date, cfg.format)}.md`;\n return cfg.folder ? `${cfg.folder}/${filename}` : filename;\n}\n\n// ── Tool: get_periodic_note ───────────────────────────────────────────────────\n\nexport const GetPeriodicNoteInput = z.object({\n period: PERIOD.default('daily').describe(\n 'Period type: daily (default), weekly, monthly, quarterly, or yearly.',\n ),\n date: z\n .string()\n .optional()\n .describe('ISO date string (YYYY-MM-DD). Defaults to today when omitted.'),\n createIfMissing: z\n .boolean()\n .default(false)\n .describe(\n 'Create the note from the configured template if it does not exist. Default false — reports missing without creating.',\n ),\n});\nexport type GetPeriodicNoteInput = z.input<typeof GetPeriodicNoteInput>;\n\nexport interface GetPeriodicNoteResult {\n path: string;\n existed: boolean;\n created: boolean;\n /** sha-256 (hex) of the created content; only set when created is true. */\n contentHash?: string;\n}\n\nexport async function getPeriodicNote(\n ctx: ServerContext,\n rawInput: GetPeriodicNoteInput,\n): Promise<GetPeriodicNoteResult> {\n const input = GetPeriodicNoteInput.parse(rawInput);\n const date = input.date ? new Date(input.date) : new Date();\n const cfg = await resolveConfig(ctx.vaultRoot, input.period);\n const path = resolveNotePath(cfg, date);\n const abs = resolveVaultPath(ctx.vaultRoot, path);\n\n let existed = false;\n try {\n await access(abs);\n existed = true;\n } catch {}\n\n if (existed) return { path, existed: true, created: false };\n\n if (!input.createIfMissing) return { path, existed: false, created: false };\n\n // About to write: this \"read\" tool creates the note here, so the write\n // policy applies (dispatch also strips createIfMissing in read-only mode).\n assertWritable(ctx.policy, path);\n\n // Read template if configured.\n let body = '';\n if (cfg.template) {\n try {\n const templateAbs = resolveVaultPath(\n ctx.vaultRoot,\n cfg.template.endsWith('.md') ? cfg.template : `${cfg.template}.md`,\n );\n body = await readFile(templateAbs, 'utf8');\n } catch {}\n }\n\n await mkdir(dirname(abs), { recursive: true });\n await atomicWrite(abs, body);\n upsertDoc(ctx, buildDoc(path, body));\n\n return { path, existed: false, created: true, contentHash: contentHash(body) };\n}\n\n// ── Tool: append_periodic_note ────────────────────────────────────────────────\n\nexport const AppendPeriodicNoteInput = z.object({\n period: PERIOD.default('daily').describe('Period type. Default: daily.'),\n date: z.string().optional().describe('ISO date string (YYYY-MM-DD). Defaults to today.'),\n content: z\n .string()\n .min(1)\n .describe('Text to append to the note body. Separated from existing content by a blank line.'),\n createIfMissing: z\n .boolean()\n .default(true)\n .describe('Create the note if it does not exist before appending. Default true.'),\n prevHash: z\n .string()\n .optional()\n .describe(\n 'Optional compare-and-swap guard: the contentHash from a prior read of the resolved periodic note. Fails with hash_conflict if it changed since.',\n ),\n});\nexport type AppendPeriodicNoteInput = z.input<typeof AppendPeriodicNoteInput>;\n\nexport interface AppendPeriodicNoteResult {\n path: string;\n bytesWritten: number;\n /** sha-256 (hex) of the new content — usable as prevHash for a chained edit. */\n contentHash: string;\n}\n\nexport async function appendPeriodicNote(\n ctx: ServerContext,\n rawInput: AppendPeriodicNoteInput,\n): Promise<AppendPeriodicNoteResult> {\n const input = AppendPeriodicNoteInput.parse(rawInput);\n const date = input.date ? new Date(input.date) : new Date();\n const cfg = await resolveConfig(ctx.vaultRoot, input.period);\n const path = resolveNotePath(cfg, date);\n const abs = resolveVaultPath(ctx.vaultRoot, path);\n assertWritable(ctx.policy, path);\n\n let original = '';\n try {\n original = await readFile(abs, 'utf8');\n if (input.prevHash !== undefined) assertHashMatch(original, input.prevHash, path);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n if (input.prevHash !== undefined) {\n // The caller pinned a version that no longer exists — conflict, not create.\n throw new Error(\n JSON.stringify({\n error: 'hash_conflict',\n path,\n expected: input.prevHash,\n actual: null,\n hint: 'The periodic note no longer exists on disk. Re-read before retrying.',\n }),\n );\n }\n if (!input.createIfMissing) throw new Error(`Periodic note not found: ${path}`);\n await mkdir(dirname(abs), { recursive: true });\n await atomicWrite(abs, input.content);\n upsertDoc(ctx, buildDoc(path, input.content));\n return {\n path,\n bytesWritten: Buffer.byteLength(input.content, 'utf8'),\n contentHash: contentHash(input.content),\n };\n }\n\n // Preserve frontmatter, append to body.\n const fm = parseFrontmatter(original);\n const body = fm.body.endsWith('\\n') ? fm.body : `${fm.body}\\n`;\n const separator = body.endsWith('\\n\\n') ? '' : '\\n';\n const newBody = `${body}${separator}${input.content}`;\n const header = original.slice(0, fm.bodyStart);\n const newRaw = `${header}${newBody}`;\n\n await atomicWrite(abs, newRaw);\n\n const cached = ctx.notes.get(path);\n if (cached) {\n cached.body = newBody;\n cached.raw = newRaw;\n }\n\n return {\n path,\n bytesWritten: Buffer.byteLength(newRaw, 'utf8'),\n contentHash: contentHash(newRaw),\n };\n}\n","import { z } from 'zod';\nimport type { ServerContext } from '../context.js';\nimport { basenameNoExt } from './search.js';\n\nconst OPS_WITHOUT_VALUE = new Set(['exists', 'missing']);\n\nconst Predicate = z\n .object({\n key: z.string().min(1).describe('Frontmatter key to test (top-level).'),\n op: z\n .enum(['eq', 'ne', 'contains', 'exists', 'missing', 'gt', 'gte', 'lt', 'lte'])\n .describe(\n 'eq/ne compare scalars; contains matches array membership or substring; ' +\n 'exists/missing test key presence; gt/gte/lt/lte compare numbers or strings (ISO dates sort correctly).',\n ),\n value: z\n .union([z.string(), z.number(), z.boolean()])\n .optional()\n .describe('Comparison value. Required for every op except exists/missing.'),\n })\n .refine((p) => OPS_WITHOUT_VALUE.has(p.op) || p.value !== undefined, {\n message: 'value is required for this op',\n });\n\nconst ISO_DATE = z\n .string()\n .refine((s) => !Number.isNaN(Date.parse(s)), { message: 'not a parseable date' });\n\nexport const QueryNotesInput = z.object({\n where: z\n .array(Predicate)\n .max(20)\n .default([])\n .describe('Frontmatter predicates. All must match (AND).'),\n folder: z\n .string()\n .optional()\n .describe('Restrict to notes under this vault-relative folder prefix.'),\n tag: z.string().optional().describe('Restrict to notes with this tag (# prefix optional).'),\n modifiedAfter: ISO_DATE.optional().describe(\n 'Only notes modified at or after this ISO 8601 date/time.',\n ),\n modifiedBefore: ISO_DATE.optional().describe(\n 'Only notes modified before this ISO 8601 date/time.',\n ),\n minSizeBytes: z.number().int().min(0).optional().describe('Only notes at least this many bytes.'),\n maxSizeBytes: z.number().int().min(0).optional().describe('Only notes at most this many bytes.'),\n select: z\n .array(z.string())\n .max(20)\n .default([])\n .describe(\n 'Extra fields to return per hit: frontmatter keys, or the specials \"mtime\", \"size\", \"tags\". ' +\n 'Default returns only path + title, keeping the payload minimal.',\n ),\n sort: z.enum(['path', 'title', 'mtime', 'size']).default('path').describe('Sort field.'),\n order: z.enum(['asc', 'desc']).default('asc').describe('Sort order.'),\n limit: z.number().int().min(1).max(500).default(100).describe('Maximum number of results.'),\n});\nexport type QueryNotesInput = z.infer<typeof QueryNotesInput>;\n\nexport interface QueryHit {\n path: string;\n /** Omitted when it equals the path's basename (recoverable from `path`). */\n title?: string;\n /** ISO 8601 mtime — present only when \"mtime\" is selected. */\n mtime?: string;\n /** Present only when \"size\" is selected. */\n sizeBytes?: number;\n /** Present only when \"tags\" is selected and the note has tags. */\n tags?: string[];\n /** Selected frontmatter keys — present only when the note has at least one of them. */\n fm?: Record<string, unknown>;\n}\n\ntype Scalar = string | number | boolean;\n\n/** Loose scalar equality: strict first, then string-form — so `5` matches \"5\" and dates match. */\nfunction scalarEq(a: unknown, b: Scalar): boolean {\n if (a === b) return true;\n if (a === null || a === undefined || typeof a === 'object') return false;\n return String(a) === String(b);\n}\n\n/** Numeric when both sides coerce to finite numbers, else lexicographic (ISO dates sort correctly). */\nfunction compare(a: unknown, b: Scalar): number | undefined {\n if (a === null || a === undefined || typeof a === 'object' || typeof a === 'boolean') {\n return undefined;\n }\n const an = Number(a);\n const bn = Number(b);\n if (Number.isFinite(an) && Number.isFinite(bn)) return an - bn;\n const as = String(a);\n const bs = String(b);\n return as < bs ? -1 : as > bs ? 1 : 0;\n}\n\nfunction matches(fm: Record<string, unknown> | null, p: z.infer<typeof Predicate>): boolean {\n const val = fm?.[p.key];\n switch (p.op) {\n case 'exists':\n return val !== undefined;\n case 'missing':\n return val === undefined;\n case 'eq':\n return scalarEq(val, p.value as Scalar);\n // `ne` requires the key to exist — notes without the key don't match.\n case 'ne':\n return val !== undefined && !scalarEq(val, p.value as Scalar);\n case 'contains': {\n if (Array.isArray(val)) return val.some((v) => scalarEq(v, p.value as Scalar));\n if (typeof val === 'string') {\n return val.toLowerCase().includes(String(p.value).toLowerCase());\n }\n return false;\n }\n case 'gt':\n case 'gte':\n case 'lt':\n case 'lte': {\n const c = compare(val, p.value as Scalar);\n if (c === undefined) return false;\n if (p.op === 'gt') return c > 0;\n if (p.op === 'gte') return c >= 0;\n if (p.op === 'lt') return c < 0;\n return c <= 0;\n }\n }\n}\n\nexport function queryNotes(ctx: ServerContext, input: QueryNotesInput): QueryHit[] {\n const tag = input.tag?.replace(/^#/, '');\n const after = input.modifiedAfter ? Date.parse(input.modifiedAfter) : undefined;\n const before = input.modifiedBefore ? Date.parse(input.modifiedBefore) : undefined;\n const fmKeys = input.select.filter((s) => s !== 'mtime' && s !== 'size' && s !== 'tags');\n\n const hits: { hit: QueryHit; note: { title: string; mtimeMs: number; sizeBytes: number } }[] = [];\n\n for (const [path, note] of ctx.notes) {\n if (input.folder && !path.startsWith(input.folder)) continue;\n if (tag && !note.tags.split(' ').filter(Boolean).includes(tag)) continue;\n if (after !== undefined && note.mtimeMs < after) continue;\n if (before !== undefined && note.mtimeMs >= before) continue;\n if (input.minSizeBytes !== undefined && note.sizeBytes < input.minSizeBytes) continue;\n if (input.maxSizeBytes !== undefined && note.sizeBytes > input.maxSizeBytes) continue;\n if (!input.where.every((p) => matches(note.fm, p))) continue;\n\n const hit: QueryHit = { path };\n // Omit redundant/empty metadata to keep the payload lean (recoverable from `path`).\n if (note.title && note.title !== basenameNoExt(path)) hit.title = note.title;\n if (input.select.includes('mtime')) hit.mtime = new Date(note.mtimeMs).toISOString();\n if (input.select.includes('size')) hit.sizeBytes = note.sizeBytes;\n if (input.select.includes('tags')) {\n const noteTags = note.tags.split(' ').filter(Boolean);\n if (noteTags.length > 0) hit.tags = noteTags;\n }\n if (fmKeys.length > 0 && note.fm) {\n const fm: Record<string, unknown> = {};\n for (const k of fmKeys) {\n if (note.fm[k] !== undefined) fm[k] = note.fm[k];\n }\n if (Object.keys(fm).length > 0) hit.fm = fm;\n }\n hits.push({ hit, note });\n }\n\n const dir = input.order === 'desc' ? -1 : 1;\n hits.sort((a, b) => {\n switch (input.sort) {\n case 'mtime':\n return (a.note.mtimeMs - b.note.mtimeMs) * dir;\n case 'size':\n return (a.note.sizeBytes - b.note.sizeBytes) * dir;\n case 'title':\n return a.note.title.localeCompare(b.note.title) * dir;\n default:\n return a.hit.path.localeCompare(b.hit.path) * dir;\n }\n });\n\n return hits.slice(0, input.limit).map((h) => h.hit);\n}\n","import { readFile } from 'node:fs/promises';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport { buildOutline } from '@seekstone/core/outline';\nimport { z } from 'zod';\nimport { contentHash } from '../content-hash.js';\nimport type { ServerContext } from '../context.js';\nimport { resolveVaultPath } from '../vault-path.js';\n\nexport const ReadNoteInput = z\n .object({\n path: z.string().describe('Vault-relative path to the note, e.g. \"Daily Notes/2026-05-29.md\".'),\n section: z\n .string()\n .optional()\n .describe('Return only this heading section (heading text, # prefix optional).'),\n block: z\n .string()\n .optional()\n .describe('Return only the block anchored by this id (^ prefix optional).'),\n lines: z\n .object({\n from: z.number().int().min(1).describe('First line (1-indexed, inclusive).'),\n to: z\n .number()\n .int()\n .min(1)\n .optional()\n .describe('Last line (1-indexed, inclusive). Defaults to end of file.'),\n })\n .optional()\n .describe('Return only this line range (1-indexed, inclusive).'),\n includeFrontmatter: z\n .boolean()\n .optional()\n .describe(\n 'Include frontmatter in span results. Default false for section/block spans. No effect on lines or whole-note reads.',\n ),\n })\n .refine((v) => [v.section, v.block, v.lines].filter((x) => x !== undefined).length <= 1, {\n message: 'At most one of section, block, or lines may be specified.',\n });\nexport type ReadNoteInput = z.infer<typeof ReadNoteInput>;\n\nexport interface ReadNoteResult {\n path: string;\n content: string;\n /** Bytes in the returned content (UTF-8). */\n bytesReturned: number;\n /** Total UTF-8 byte size of the source file. */\n noteBytes: number;\n /** sha-256 (hex) of the whole file — pass as prevHash to edit tools for compare-and-swap. */\n contentHash: string;\n /** Character offsets of the returned span in the raw file. Absent for whole-note reads. */\n span?: { charStart: number; charEnd: number };\n}\n\nexport async function readNote(ctx: ServerContext, input: ReadNoteInput): Promise<ReadNoteResult> {\n const absPath = resolveVaultPath(ctx.vaultRoot, input.path);\n\n const raw = await readFile(absPath, 'utf8');\n const noteBytes = Buffer.byteLength(raw, 'utf8');\n // Always the whole file's hash, even for span reads — it identifies the\n // on-disk version an edit's prevHash is compared against.\n const hash = contentHash(raw);\n\n // Whole-note read — no selector\n if (input.section === undefined && input.block === undefined && input.lines === undefined) {\n return {\n path: input.path,\n content: raw,\n bytesReturned: noteBytes,\n noteBytes,\n contentHash: hash,\n };\n }\n\n const fm = parseFrontmatter(raw);\n const outline = buildOutline(raw, { includeBlocks: true });\n\n let charStart: number;\n let charEnd: number;\n\n if (input.section !== undefined) {\n const target = input.section.replace(/^#+\\s*/, '').trim();\n const heading = outline.headings.find((h) => h.text === target);\n if (!heading) {\n throw new Error(\n JSON.stringify({\n error: 'section_not_found',\n target,\n available: outline.headings.map((h) => h.text),\n }),\n );\n }\n const nextSibling = outline.headings.find(\n (h) => h.byteOffset > heading.byteOffset && h.level <= heading.level,\n );\n charStart = heading.byteOffset;\n charEnd = nextSibling?.byteOffset ?? raw.length;\n } else if (input.block !== undefined) {\n const targetId = input.block.replace(/^\\^/, '');\n const block = outline.blocks.find((b) => b.id === targetId);\n if (!block) {\n throw new Error(\n JSON.stringify({\n error: 'block_not_found',\n target: targetId,\n available: outline.blocks.map((b) => b.id),\n }),\n );\n }\n const lineS = block.byteOffset === 0 ? 0 : raw.lastIndexOf('\\n', block.byteOffset - 1) + 1;\n const nlAfter = raw.indexOf('\\n', block.byteOffset);\n const lineE = nlAfter === -1 ? raw.length : nlAfter + 1;\n charStart = lineS;\n charEnd = lineE;\n } else {\n // lines selector\n const linesRange = input.lines;\n if (linesRange === undefined) throw new Error('internal: unreachable');\n const { from, to } = linesRange;\n const rawLines = raw.split('\\n');\n const fromIdx = from - 1; // 0-indexed\n const toIdxInclusive =\n to !== undefined ? Math.min(to - 1, rawLines.length - 1) : rawLines.length - 1;\n\n let offset = 0;\n charStart = 0;\n charEnd = raw.length;\n\n for (const [i, line] of rawLines.entries()) {\n if (i === fromIdx) charStart = offset;\n offset += line.length + 1; // +1 for the \\n consumed by split\n if (i === toIdxInclusive) {\n charEnd = Math.min(offset, raw.length);\n break;\n }\n }\n }\n\n // Build content — prepend FM if requested (section/block only)\n let content: string;\n if (input.includeFrontmatter === true && input.lines === undefined) {\n const fmRegion = raw.slice(0, fm.bodyStart);\n const spanContent = raw.slice(charStart, charEnd);\n // Avoid duplicating FM if the span already covers it (e.g. section at bodyStart)\n content = charStart < fm.bodyStart ? spanContent : `${fmRegion}${spanContent}`;\n } else {\n content = raw.slice(charStart, charEnd);\n }\n\n return {\n path: input.path,\n content,\n bytesReturned: Buffer.byteLength(content, 'utf8'),\n noteBytes,\n contentHash: hash,\n span: { charStart, charEnd },\n };\n}\n","import { readFile } from 'node:fs/promises';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport { z } from 'zod';\nimport { atomicWrite } from '../atomic-write.js';\nimport { assertHashMatch, contentHash } from '../content-hash.js';\nimport type { ServerContext } from '../context.js';\nimport { assertWritable } from '../policy.js';\nimport { resolveVaultPath } from '../vault-path.js';\n\nexport const ReplaceInNoteInput = z.object({\n path: z.string().describe('Vault-relative path to the note.'),\n find: z\n .string()\n .min(1)\n .max(1000)\n .describe('Text or pattern to find (max 1000 chars; bounds regex-mode pattern size).'),\n replace: z\n .string()\n .describe('Replacement text. Supports $1, $2, … backreferences in regex mode.'),\n regex: z.boolean().default(false).describe('Treat find as a regular expression.'),\n caseSensitive: z.boolean().default(false).describe('Case-sensitive matching.'),\n wholeWord: z.boolean().default(false).describe('Match whole words only (\\\\b word-boundary).'),\n limit: z\n .number()\n .int()\n .min(1)\n .optional()\n .describe('Maximum number of replacements. Omit to replace all.'),\n dryRun: z.boolean().default(false).describe('If true, report matches without writing anything.'),\n prevHash: z\n .string()\n .optional()\n .describe(\n 'Optional compare-and-swap guard: the contentHash from a prior read. Fails with hash_conflict if the note changed since.',\n ),\n});\nexport type ReplaceInNoteInput = z.infer<typeof ReplaceInNoteInput>;\n\nexport interface MatchPosition {\n line: number;\n byteOffset: number;\n}\n\nexport interface ReplaceInNoteResult {\n path: string;\n replacements: number;\n matches: MatchPosition[];\n bytesWritten?: number;\n frontmatterUnchanged: true;\n /** sha-256 (hex) of the new content after a write; absent on dry runs and zero-match calls. */\n contentHash?: string;\n}\n\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction buildRegex(\n find: string,\n opts: { regex: boolean; caseSensitive: boolean; wholeWord: boolean },\n): RegExp {\n let pattern = opts.regex ? find : escapeRegex(find);\n if (opts.wholeWord) pattern = `\\\\b(?:${pattern})\\\\b`;\n const flags = `g${opts.caseSensitive ? '' : 'i'}`;\n return new RegExp(pattern, flags);\n}\n\nexport async function replaceInNote(\n ctx: ServerContext,\n input: ReplaceInNoteInput,\n): Promise<ReplaceInNoteResult> {\n const absPath = resolveVaultPath(ctx.vaultRoot, input.path);\n assertWritable(ctx.policy, input.path);\n\n // Validate regex before any IO.\n let re: RegExp;\n try {\n re = buildRegex(input.find, {\n regex: input.regex,\n caseSensitive: input.caseSensitive,\n wholeWord: input.wholeWord,\n });\n } catch (err) {\n throw new Error(`Invalid regex: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n const raw = await readFile(absPath, 'utf8');\n if (input.prevHash !== undefined) assertHashMatch(raw, input.prevHash, input.path);\n const fm = parseFrontmatter(raw);\n const originalFmRegion = raw.slice(0, fm.bodyStart);\n const body = raw.slice(fm.bodyStart);\n\n const allMatches = [...body.matchAll(re)];\n const toReplace = input.limit !== undefined ? allMatches.slice(0, input.limit) : allMatches;\n\n const matches: MatchPosition[] = toReplace.map((m) => {\n const fileOffset = fm.bodyStart + (m.index ?? 0);\n const line = raw.slice(0, fileOffset).split('\\n').length;\n return { line, byteOffset: fileOffset };\n });\n\n if (input.dryRun || toReplace.length === 0) {\n return {\n path: input.path,\n replacements: toReplace.length,\n matches,\n frontmatterUnchanged: true,\n };\n }\n\n // Apply replacements left-to-right, resolving backreferences per match.\n const singleRe = new RegExp(re.source, re.flags.replace('g', ''));\n let newBody = '';\n let lastIndex = 0;\n for (const m of toReplace) {\n const matchIndex = m.index ?? 0;\n newBody += body.slice(lastIndex, matchIndex);\n newBody += input.regex ? m[0].replace(singleRe, input.replace) : input.replace;\n lastIndex = matchIndex + m[0].length;\n }\n newBody += body.slice(lastIndex);\n\n const newContent = `${originalFmRegion}${newBody}`;\n await atomicWrite(absPath, newContent);\n\n const written = await readFile(absPath, 'utf8');\n if (written.slice(0, originalFmRegion.length) !== originalFmRegion) {\n throw new Error('Write-safety violation: frontmatter region changed unexpectedly');\n }\n\n const cached = ctx.notes.get(input.path);\n if (cached) {\n cached.body = newBody;\n cached.raw = newContent;\n cached.sizeBytes = Buffer.byteLength(newContent, 'utf8');\n }\n\n return {\n path: input.path,\n replacements: toReplace.length,\n matches,\n bytesWritten: Buffer.byteLength(newContent, 'utf8'),\n frontmatterUnchanged: true,\n contentHash: contentHash(newContent),\n };\n}\n","import type { ServerContext } from './context.js';\nimport type { Logger } from './log.js';\nimport { AppendNoteInput, appendNote } from './tools/append_note.js';\nimport { ContextPackInput, contextPack } from './tools/context_pack.js';\nimport { CreateNoteInput, createNote } from './tools/create_note.js';\nimport { DeleteNoteInput, deleteNote } from './tools/delete_note.js';\nimport { GetBacklinksInput, getBacklinks } from './tools/get_backlinks.js';\nimport { GetLinksInput, getLinks } from './tools/get_links.js';\nimport { ListNotesInput, listNotes } from './tools/list_notes.js';\nimport { ListTagsInput, listTags } from './tools/list_tags.js';\nimport { MoveNoteInput, moveNote } from './tools/move_note.js';\nimport { OutlineNoteInput, outlineNote } from './tools/outline_note.js';\nimport { PatchFrontmatterInput, patchFrontmatter } from './tools/patch_frontmatter.js';\nimport { PatchNoteInput, patchNote } from './tools/patch_note.js';\nimport {\n AppendPeriodicNoteInput,\n appendPeriodicNote,\n GetPeriodicNoteInput,\n getPeriodicNote,\n} from './tools/periodic_note.js';\nimport { QueryNotesInput, queryNotes } from './tools/query_notes.js';\nimport { ReadNoteInput, readNote } from './tools/read_note.js';\nimport { ReplaceInNoteInput, replaceInNote } from './tools/replace_in_note.js';\nimport { SearchInput, search } from './tools/search.js';\n\nexport type ToolResult = {\n content: { type: 'text'; text: string }[];\n isError?: boolean;\n};\n\n/** Tool names this dispatcher handles — kept in sync with the server's tool list. */\nexport const HANDLED_TOOLS = [\n 'search',\n 'query_notes',\n 'context_pack',\n 'read_note',\n 'list_notes',\n 'list_tags',\n 'create_note',\n 'delete_note',\n 'move_note',\n 'append_note',\n 'patch_frontmatter',\n 'outline_note',\n 'patch_note',\n 'get_backlinks',\n 'get_links',\n 'replace_in_note',\n 'get_periodic_note',\n 'append_periodic_note',\n] as const;\n\n/**\n * Tools that mutate the vault. Enforced here at dispatch (read-only mode\n * rejects them) and mirrored by tool-list.ts, which unregisters them from\n * tools/list — a new write tool must be added to this set to ship.\n */\nexport const WRITE_TOOLS: ReadonlySet<string> = new Set([\n 'create_note',\n 'delete_note',\n 'move_note',\n 'append_note',\n 'patch_frontmatter',\n 'patch_note',\n 'replace_in_note',\n 'append_periodic_note',\n]);\n\n// Metadata-safe keys: logged at info. Note content (`content`, `frontmatter`,\n// `patch`) and the raw `query` string are intentionally excluded — they only\n// reach logs via the debug-level `args` dump.\nconst META_KEYS = [\n 'path',\n 'from',\n 'to',\n 'folder',\n 'tag',\n 'limit',\n 'overwrite',\n 'pattern',\n 'minCount',\n 'sort',\n 'order',\n 'select',\n 'modifiedAfter',\n 'modifiedBefore',\n 'minSizeBytes',\n 'maxSizeBytes',\n 'period',\n 'date',\n 'permanent',\n 'prevHash',\n 'budgetBytes',\n] as const;\n\nfunction safeMeta(args: unknown): Record<string, unknown> {\n if (typeof args !== 'object' || args === null) return {};\n const a = args as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const k of META_KEYS) {\n if (a[k] !== undefined) out[k] = a[k];\n }\n if (typeof a.query === 'string') out.queryLen = a.query.length; // not the query itself\n if (Array.isArray(a.where)) out.whereCount = a.where.length; // predicate values may be personal\n return out;\n}\n\nfunction resultBytes(r: ToolResult): number {\n let n = 0;\n for (const c of r.content) n += Buffer.byteLength(c.text);\n return n;\n}\n\n/**\n * Route a tool call to its handler, logging the outcome. Returns an MCP tool\n * result; failures are converted to `{ isError: true }` (never thrown) so the\n * transport stays alive, and are logged at `error` with a stack.\n */\nexport async function dispatch(\n ctx: ServerContext,\n name: string,\n args: unknown,\n log: Logger,\n): Promise<ToolResult> {\n const start = performance.now();\n const meta = safeMeta(args);\n log.debug('tool start', { tool: name, args }); // full args (may include content) — debug only\n if (ctx.policy.readOnly && WRITE_TOOLS.has(name)) {\n // Unregistered from tools/list, but a client can still send the call.\n log.info('tool rejected (read-only)', { tool: name, ...meta });\n return {\n content: [\n {\n type: 'text',\n text: 'Error: Server is read-only (SEEKSTONE_READ_ONLY=1); write tools are disabled.',\n },\n ],\n isError: true,\n };\n }\n if (ctx.policy.readOnly && name === 'get_periodic_note') {\n // The one read tool with a write side-effect: force createIfMissing off.\n if (typeof args === 'object' && args !== null) {\n args = { ...(args as Record<string, unknown>), createIfMissing: false };\n }\n }\n try {\n const result = await run(ctx, name, args);\n const durationMs = Math.round((performance.now() - start) * 100) / 100;\n log.info('tool ok', { tool: name, durationMs, resultBytes: resultBytes(result), ...meta });\n return result;\n } catch (err) {\n const durationMs = Math.round((performance.now() - start) * 100) / 100;\n const message = err instanceof Error ? err.message : String(err);\n log.error('tool error', {\n tool: name,\n durationMs,\n error: message,\n stack: err instanceof Error ? err.stack : undefined,\n ...meta,\n });\n return { content: [{ type: 'text', text: `Error: ${message}` }], isError: true };\n }\n}\n\nasync function run(ctx: ServerContext, name: string, args: unknown): Promise<ToolResult> {\n switch (name) {\n case 'search': {\n const input = SearchInput.parse(args);\n const hits = search(ctx, input);\n // Minified: search is the headline context-tax metric — indentation is pure tax.\n return { content: [{ type: 'text', text: JSON.stringify(hits) }] };\n }\n case 'query_notes': {\n const input = QueryNotesInput.parse(args);\n const hits = queryNotes(ctx, input);\n // Minified like search — a second search mode, same context-tax discipline.\n return { content: [{ type: 'text', text: JSON.stringify(hits) }] };\n }\n case 'context_pack': {\n const input = ContextPackInput.parse(args);\n const pack = contextPack(ctx, input);\n // Minified: the assembler meters its byte budget against exactly this serialization.\n return { content: [{ type: 'text', text: JSON.stringify(pack) }] };\n }\n case 'read_note': {\n const input = ReadNoteInput.parse(args);\n const result = await readNote(ctx, input);\n return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };\n }\n case 'list_notes': {\n const input = ListNotesInput.parse(args);\n const entries = listNotes(ctx, input);\n return { content: [{ type: 'text', text: JSON.stringify(entries, null, 2) }] };\n }\n case 'list_tags': {\n const input = ListTagsInput.parse(args);\n const result = listTags(ctx, input);\n return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };\n }\n case 'create_note': {\n const input = CreateNoteInput.parse(args);\n const result = await createNote(ctx, input);\n return {\n content: [\n {\n type: 'text',\n text: `Created ${result.path} (${result.bytesWritten} bytes). contentHash: ${result.contentHash}`,\n },\n ],\n };\n }\n case 'delete_note': {\n const input = DeleteNoteInput.parse(args);\n const result = await deleteNote(ctx, input);\n const text = result.trashedTo\n ? `Moved ${result.path} to ${result.trashedTo} (recoverable — restore by moving it back).`\n : `Permanently deleted ${result.path}.`;\n return { content: [{ type: 'text', text }] };\n }\n case 'move_note': {\n const input = MoveNoteInput.parse(args);\n const result = await moveNote(ctx, input);\n let text = `Moved ${result.from} → ${result.to}. Rewrote ${result.linksRewritten} link${\n result.linksRewritten === 1 ? '' : 's'\n } in ${result.notesRewritten} note${result.notesRewritten === 1 ? '' : 's'}.`;\n if (result.skipped?.length) {\n text += ` Skipped (outside SEEKSTONE_WRITE_PATHS): ${result.skipped.join(', ')}.`;\n }\n return { content: [{ type: 'text', text }] };\n }\n case 'append_note': {\n const input = AppendNoteInput.parse(args);\n const result = await appendNote(ctx, input);\n return {\n content: [\n {\n type: 'text',\n text: `Appended ${result.bytesWritten} bytes to ${result.path}. contentHash: ${result.contentHash}`,\n },\n ],\n };\n }\n case 'patch_frontmatter': {\n const input = PatchFrontmatterInput.parse(args);\n const result = await patchFrontmatter(ctx, input);\n return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };\n }\n case 'outline_note': {\n const input = OutlineNoteInput.parse(args);\n const result = await outlineNote(ctx, input);\n return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };\n }\n case 'patch_note': {\n const input = PatchNoteInput.parse(args);\n const result = await patchNote(ctx, input);\n return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };\n }\n case 'get_backlinks': {\n const input = GetBacklinksInput.parse(args);\n const result = getBacklinks(ctx, input);\n return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };\n }\n case 'get_links': {\n const input = GetLinksInput.parse(args);\n const result = getLinks(ctx, input);\n return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };\n }\n case 'replace_in_note': {\n const input = ReplaceInNoteInput.parse(args);\n const result = await replaceInNote(ctx, input);\n return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };\n }\n case 'get_periodic_note': {\n const input = GetPeriodicNoteInput.parse(args);\n const result = await getPeriodicNote(ctx, input);\n return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };\n }\n case 'append_periodic_note': {\n const input = AppendPeriodicNoteInput.parse(args);\n const result = await appendPeriodicNote(ctx, input);\n return {\n content: [\n {\n type: 'text',\n text: `Appended ${result.bytesWritten} bytes to ${result.path}. contentHash: ${result.contentHash}`,\n },\n ],\n };\n }\n default:\n throw new Error(`Unknown tool: ${name}`);\n }\n}\n","import { readFile } from 'node:fs/promises';\nimport { basename, extname } from 'node:path';\nimport { extractInlineTags, extractLinksWithLines, frontmatterTags } from '@seekstone/core/extract';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport { walkVault } from '@seekstone/core/walk';\nimport MiniSearch from 'minisearch';\nimport type { BacklinkRef } from '../context.js';\nimport { resolveLink } from './resolve.js';\nimport type { IndexedNote } from './types.js';\n\nexport type VaultIndex = MiniSearch<IndexedNote>;\n\nexport interface BuildResult {\n index: VaultIndex;\n notes: Map<string, IndexedNote>;\n backlinks: Map<string, BacklinkRef[]>;\n buildMs: number;\n}\n\nexport async function buildIndex(vaultRoot: string): Promise<BuildResult> {\n const t0 = Date.now();\n\n const index = new MiniSearch<IndexedNote>({\n idField: 'id',\n fields: ['title', 'body', 'tags', 'fmKeys'],\n storeFields: ['id', 'title', 'tags', 'sizeBytes', 'mtimeMs'],\n searchOptions: {\n boost: { title: 3, tags: 2, body: 1 },\n fuzzy: 0.2,\n prefix: true,\n },\n });\n\n const entries = await walkVault(vaultRoot);\n const noteEntries = entries.filter((e) => e.kind === 'note');\n\n const docs: IndexedNote[] = [];\n const notes = new Map<string, IndexedNote>();\n\n await Promise.all(\n noteEntries.map(async (entry) => {\n try {\n const raw = await readFile(entry.absPath, 'utf8');\n const fm = parseFrontmatter(raw);\n const inlineTags = extractInlineTags(fm.body);\n const fmTags = frontmatterTags(fm.data);\n const allTags = [...new Set([...inlineTags, ...fmTags])];\n\n const title =\n typeof fm.data?.title === 'string' && fm.data.title\n ? fm.data.title\n : basename(entry.relPath, extname(entry.relPath));\n\n const doc: IndexedNote = {\n id: entry.relPath,\n title,\n body: fm.body,\n tags: allTags.join(' '),\n fmKeys: fm.keys.join(' '),\n fm: fm.data,\n raw,\n sizeBytes: entry.sizeBytes,\n mtimeMs: entry.mtimeMs,\n };\n docs.push(doc);\n notes.set(entry.relPath, doc);\n } catch {\n // Unreadable note — skip silently; index what we can.\n }\n }),\n );\n\n index.addAll(docs);\n\n const backlinks = buildBacklinks(notes);\n\n return { index, notes, backlinks, buildMs: Date.now() - t0 };\n}\n\nfunction buildBacklinks(notes: Map<string, IndexedNote>): Map<string, BacklinkRef[]> {\n const result = new Map<string, BacklinkRef[]>();\n for (const [srcPath, doc] of notes) {\n const seen = new Set<string>();\n for (const link of extractLinksWithLines(doc.raw)) {\n const resolved = resolveLink(link.target, notes);\n if (resolved === undefined) continue;\n // De-duplicate: one entry per (source, resolved-target) pair\n const dedupeKey = `${srcPath}\\0${resolved}`;\n if (seen.has(dedupeKey)) continue;\n seen.add(dedupeKey);\n let arr = result.get(resolved);\n if (arr === undefined) {\n arr = [];\n result.set(resolved, arr);\n }\n arr.push({ path: srcPath, line: link.line, linkType: link.linkType });\n }\n }\n for (const arr of result.values()) {\n arr.sort((a, b) => a.path.localeCompare(b.path));\n }\n return result;\n}\n","import { stat } from 'node:fs/promises';\nimport { extname, join, relative, sep } from 'node:path';\nimport fg from 'fast-glob';\n\n/**\n * Categories we care about. Anything else falls into `other`.\n * Excalidraw saves as `.excalidraw` (sometimes `.excalidraw.md` — handled in classify()).\n */\nexport type FileKind =\n | 'note'\n | 'image'\n | 'pdf'\n | 'excalidraw'\n | 'canvas'\n | 'video'\n | 'audio'\n | 'other';\n\nexport interface FileEntry {\n absPath: string;\n relPath: string;\n topDir: string;\n ext: string;\n kind: FileKind;\n sizeBytes: number;\n mtimeMs: number;\n}\n\nconst IMAGE_EXTS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.bmp', '.heic']);\nconst VIDEO_EXTS = new Set(['.mp4', '.mov', '.webm', '.mkv', '.avi']);\nconst AUDIO_EXTS = new Set(['.mp3', '.m4a', '.wav', '.flac', '.ogg']);\n\n/**\n * Obsidian's own dot-directories plus VCS noise. We deliberately don't ignore\n * dotfiles at the *file* level — users sometimes commit `.gitkeep` etc. — but\n * any top-level dotted directory is treated as non-vault content.\n */\nconst IGNORE_GLOBS = [\n '**/.obsidian/**',\n '**/.trash/**',\n '**/.git/**',\n '**/.DS_Store',\n '**/node_modules/**',\n];\n\nexport function classify(relPath: string): FileKind {\n // Excalidraw plugin sometimes writes `.excalidraw.md` — keep those as notes\n // (they still have YAML frontmatter and links), but pure `.excalidraw` is canvas-like.\n if (relPath.endsWith('.excalidraw.md')) return 'note';\n if (relPath.endsWith('.excalidraw')) return 'excalidraw';\n const ext = extname(relPath).toLowerCase();\n if (ext === '.md') return 'note';\n if (ext === '.canvas') return 'canvas';\n if (ext === '.pdf') return 'pdf';\n if (IMAGE_EXTS.has(ext)) return 'image';\n if (VIDEO_EXTS.has(ext)) return 'video';\n if (AUDIO_EXTS.has(ext)) return 'audio';\n return 'other';\n}\n\nexport async function walkVault(vaultRoot: string): Promise<FileEntry[]> {\n // We use fast-glob with `stats: true` so we can avoid an extra stat() round per file.\n const matches = await fg('**/*', {\n cwd: vaultRoot,\n absolute: true,\n onlyFiles: true,\n followSymbolicLinks: false,\n dot: true, // include dotfiles so the ignore globs are the only gate\n ignore: IGNORE_GLOBS,\n stats: true,\n suppressErrors: true,\n });\n\n const entries: FileEntry[] = [];\n for (const m of matches) {\n // fast-glob with stats: true returns objects; without, strings. Narrow here.\n const absPath = typeof m === 'string' ? m : m.path;\n const stats = typeof m === 'string' ? await stat(absPath) : m.stats;\n if (!stats) continue;\n const relPath = relative(vaultRoot, absPath);\n const topDir = relPath.split(sep)[0] ?? '';\n entries.push({\n absPath,\n relPath,\n topDir,\n ext: extname(relPath).toLowerCase(),\n kind: classify(relPath),\n sizeBytes: stats.size,\n mtimeMs: stats.mtimeMs,\n });\n }\n return entries;\n}\n\nexport function _testJoin(...parts: string[]): string {\n return join(...parts);\n}\n","// execFileSync is used (not exec/execSync) because it bypasses the shell\n// entirely — it exec's the named binary directly. There is no shell injection\n// risk. Socket.dev flags the child_process import as \"shellAccess\" regardless\n// of which function is used; that score penalty is accepted. See SHA-139.\nimport { execFileSync } from 'node:child_process';\nimport type { Dirent } from 'node:fs';\nimport { access, copyFile, mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';\nimport path, { dirname, join } from 'node:path';\n\n/**\n * `seekstone init` — a guided setup helper. Validates an Obsidian vault and\n * prints (or, with --write, patches) the MCP config for the chosen client\n * (Claude Desktop, Claude Code, Cursor) so a user doesn't have to hand-edit\n * JSON or guess paths.\n *\n * The pure helpers (arg parsing, config-path resolution, additive merge,\n * command string) are exported and unit-tested. `runInit` wires them to the\n * filesystem and console.\n */\n\nexport interface InitOptions {\n vault?: string;\n write: boolean;\n client: 'desktop' | 'code' | 'cursor' | 'vscode';\n /** Set when --client was given an unrecognized value; runInit errors on it. */\n invalidClient?: string;\n}\n\nexport function parseInitArgs(argv: readonly string[]): InitOptions {\n const opts: InitOptions = { write: false, client: 'desktop' };\n // argv is everything after the `init` subcommand.\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i];\n if (a === '--write') opts.write = true;\n else if (a === '--vault') opts.vault = argv[++i];\n else if (a === '--client') {\n const v = argv[++i];\n if (v === 'desktop' || v === 'code' || v === 'cursor' || v === 'vscode') opts.client = v;\n // Record the bad value instead of silently falling back to desktop —\n // agents run init non-interactively and would otherwise configure the\n // wrong client without anyone noticing.\n else opts.invalidClient = v ?? '(missing)';\n }\n }\n return opts;\n}\n\n/** The server config object that goes under `mcpServers.seekstone`. */\nexport function seekstoneServerConfig(vaultPath: string): {\n command: string;\n args: string[];\n env: Record<string, string>;\n} {\n return {\n command: 'npx',\n args: ['-y', 'seekstone'],\n env: { SEEKSTONE_VAULT: vaultPath },\n };\n}\n\n/** The `claude mcp add` one-liner for Claude Code. */\nexport function mcpAddCommand(vaultPath: string): string {\n return `claude mcp add seekstone --env SEEKSTONE_VAULT=${vaultPath} -- npx -y seekstone`;\n}\n\n/**\n * Resolve the Claude Desktop config path for a platform. Pure — takes the\n * platform and a home/APPDATA lookup so it can be tested without touching the\n * real environment.\n */\nexport function claudeDesktopConfigPath(\n platform: NodeJS.Platform,\n env: { home?: string; appData?: string },\n): string {\n // Use the target platform's path semantics, not the host's, so the result is\n // correct in production AND deterministic in tests on any CI OS.\n if (platform === 'win32') {\n const j = path.win32.join;\n const base = env.appData ?? j(env.home ?? '', 'AppData', 'Roaming');\n return j(base, 'Claude', 'claude_desktop_config.json');\n }\n const j = path.posix.join;\n const home = env.home ?? '';\n if (platform === 'darwin') {\n return j(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');\n }\n // Linux / other\n return j(home, '.config', 'Claude', 'claude_desktop_config.json');\n}\n\n/**\n * Resolve Cursor's global MCP config path for a platform. Cursor reads\n * `~/.cursor/mcp.json` on every OS (home-relative, no APPDATA) and uses the\n * same `mcpServers` shape as Claude Desktop. Pure, like claudeDesktopConfigPath.\n */\nexport function cursorConfigPath(platform: NodeJS.Platform, env: { home?: string }): string {\n const j = platform === 'win32' ? path.win32.join : path.posix.join;\n return j(env.home ?? '', '.cursor', 'mcp.json');\n}\n\n/**\n * VS Code (GitHub Copilot) workspace MCP config: `.vscode/mcp.json` under the\n * given working directory. Workspace-level on purpose — the user-global config\n * has no stable documented path (VS Code's \"MCP: Open User Configuration\"\n * command owns it), and workspace config takes precedence anyway. Host path\n * semantics (unlike the other path helpers) because it resolves the *real*\n * cwd of the running process, not a target-platform location.\n */\nexport function vscodeMcpConfigPath(cwd: string): string {\n return join(cwd, '.vscode', 'mcp.json');\n}\n\n/**\n * Resolve the Obsidian vault registry path for a platform. Pure — mirrors the\n * claudeDesktopConfigPath pattern so it can be tested without real filesystem.\n */\nexport function obsidianRegistryPath(\n platform: NodeJS.Platform,\n env: { home?: string; appData?: string },\n): string {\n if (platform === 'win32') {\n const j = path.win32.join;\n const base = env.appData ?? j(env.home ?? '', 'AppData', 'Roaming');\n return j(base, 'obsidian', 'obsidian.json');\n }\n const j = path.posix.join;\n const home = env.home ?? '';\n if (platform === 'darwin') {\n return j(home, 'Library', 'Application Support', 'obsidian', 'obsidian.json');\n }\n return j(home, '.config', 'obsidian', 'obsidian.json');\n}\n\nexport type ObsidianVaultEntry = { path: string; open?: boolean };\n\n/**\n * Parse Obsidian's obsidian.json vault registry. Tolerant of missing/extra\n * keys and malformed JSON — always returns an array. Sorted: open vault first,\n * then lexically by path for determinism across runs.\n */\nexport function parseObsidianVaults(json: string): ObsidianVaultEntry[] {\n let data: unknown;\n try {\n data = JSON.parse(json);\n } catch {\n return [];\n }\n if (!data || typeof data !== 'object') return [];\n const raw = (data as Record<string, unknown>).vaults;\n if (!raw || typeof raw !== 'object') return [];\n const entries: ObsidianVaultEntry[] = [];\n for (const v of Object.values(raw as Record<string, unknown>)) {\n if (v && typeof v === 'object') {\n const entry = v as Record<string, unknown>;\n if (typeof entry.path === 'string') {\n entries.push({ path: entry.path, open: entry.open === true });\n }\n }\n }\n return entries.sort((a, b) => {\n if (a.open !== b.open) return a.open ? -1 : 1;\n return a.path.localeCompare(b.path);\n });\n}\n\ntype McpConfig = { mcpServers?: Record<string, unknown> } & Record<string, unknown>;\n\n/**\n * Additively merge the seekstone server into an existing Claude config object.\n * Never removes or alters other servers or top-level keys. Idempotent: running\n * it again with the same vault produces an identical object. Returns a new\n * object (does not mutate the input).\n */\nexport function mergeSeekstoneConfig(existing: McpConfig | null, vaultPath: string): McpConfig {\n const base: McpConfig = existing ? structuredClone(existing) : {};\n const servers = { ...(base.mcpServers ?? {}) };\n servers.seekstone = seekstoneServerConfig(vaultPath);\n return { ...base, mcpServers: servers };\n}\n\ntype VsCodeMcpConfig = { servers?: Record<string, unknown> } & Record<string, unknown>;\n\n/** The server entry for VS Code, which requires an explicit `\"type\": \"stdio\"`. */\nexport function vscodeServerConfig(vaultPath: string): {\n type: 'stdio';\n command: string;\n args: string[];\n env: Record<string, string>;\n} {\n return { type: 'stdio', ...seekstoneServerConfig(vaultPath) };\n}\n\n/**\n * VS Code variant of mergeSeekstoneConfig. Same additive/idempotent contract,\n * but VS Code's mcp.json is the one client config keyed `\"servers\"` (not\n * `\"mcpServers\"`) and requires `\"type\": \"stdio\"` on each entry.\n */\nexport function mergeSeekstoneConfigVsCode(\n existing: VsCodeMcpConfig | null,\n vaultPath: string,\n): VsCodeMcpConfig {\n const base: VsCodeMcpConfig = existing ? structuredClone(existing) : {};\n const servers = { ...(base.servers ?? {}) };\n servers.seekstone = vscodeServerConfig(vaultPath);\n return { ...base, servers };\n}\n\nexport interface VaultCheck {\n ok: boolean;\n noteCount?: number;\n error?: string;\n}\n\n/** Validate that `vaultPath` looks like an Obsidian vault and count .md notes. */\nexport async function validateVault(vaultPath: string): Promise<VaultCheck> {\n try {\n const st = await stat(vaultPath);\n if (!st.isDirectory()) return { ok: false, error: `Not a directory: ${vaultPath}` };\n } catch {\n return { ok: false, error: `Path does not exist: ${vaultPath}` };\n }\n // An Obsidian vault has a .obsidian/ config folder.\n try {\n await access(join(vaultPath, '.obsidian'));\n } catch {\n return {\n ok: false,\n error: `No \".obsidian\" folder found in ${vaultPath} — this doesn't look like an Obsidian vault. Open the folder in Obsidian once to initialize it, or pass the correct --vault path.`,\n };\n }\n return { ok: true, noteCount: await countMarkdown(vaultPath) };\n}\n\nasync function countMarkdown(root: string): Promise<number> {\n let count = 0;\n async function walk(dir: string): Promise<void> {\n let entries: Dirent[];\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const e of entries) {\n if (e.name.startsWith('.')) continue; // skip .obsidian, .git, .trash\n const full = join(dir, e.name);\n if (e.isDirectory()) await walk(full);\n else if (e.name.endsWith('.md')) count++;\n }\n }\n await walk(root);\n return count;\n}\n\nexport interface InitResult {\n ok: boolean;\n /** Lines to print to stdout. */\n output: string[];\n /** Process exit code. */\n exitCode: number;\n /** Set when --write patched Claude Desktop config. */\n wrotePath?: string;\n backupPath?: string;\n /** Set when --write --client code successfully ran `claude mcp add`. */\n ranClaudeMcpAdd?: boolean;\n}\n\n/**\n * Run the init flow. `deps` are injectable for testing (env, platform, clock).\n * Performs filesystem reads always; only writes when `opts.write` is true.\n */\nexport async function runInit(\n opts: InitOptions,\n deps: {\n env: Record<string, string | undefined>;\n platform: NodeJS.Platform;\n /** ISO timestamp for the backup filename (injected for determinism in tests). */\n timestamp: string;\n /** Working directory for workspace-level configs (vscode); defaults to process.cwd(). */\n cwd?: string;\n /** Injectable for testing; defaults to fs/promises readFile. */\n readFile?: (p: string, enc: 'utf8') => Promise<string>;\n /**\n * Injectable for testing; defaults to execFileSync('claude', args).\n * Returns ok:true on success, ok:false with an error string on failure.\n */\n spawnClaudeMcp?: (args: string[]) => { ok: boolean; error?: string };\n },\n): Promise<InitResult> {\n if (opts.invalidClient !== undefined) {\n return {\n ok: false,\n exitCode: 1,\n output: [\n `✗ Unknown --client: ${opts.invalidClient}. Known clients: desktop, code, cursor, vscode.`,\n ],\n };\n }\n\n const rf = deps.readFile ?? readFile;\n let vaultPath = opts.vault ?? deps.env.SEEKSTONE_VAULT;\n\n if (!vaultPath) {\n // Auto-detect from Obsidian's vault registry.\n const registryPath = obsidianRegistryPath(deps.platform, {\n home: deps.env.HOME,\n appData: deps.env.APPDATA,\n });\n let vaults: ObsidianVaultEntry[] = [];\n try {\n vaults = parseObsidianVaults(await rf(registryPath, 'utf8'));\n } catch {\n // Registry missing or unreadable — fall through to error.\n }\n\n if (vaults.length === 0) {\n return {\n ok: false,\n exitCode: 1,\n output: [\n 'No vault specified. Pass --vault <path> or set SEEKSTONE_VAULT.',\n 'Example: seekstone init --vault \"/Users/you/Obsidian/My Vault\"',\n ],\n };\n }\n\n if (vaults.length === 1) {\n const [first] = vaults;\n vaultPath = first?.path ?? '';\n } else {\n const list = vaults\n .map((v, i) => ` ${i + 1}. ${v.path}${v.open ? ' (currently open)' : ''}`)\n .join('\\n');\n return {\n ok: false,\n exitCode: 1,\n output: [\n 'Multiple Obsidian vaults found. Re-run with --vault to pick one:',\n '',\n list,\n '',\n 'Example: seekstone init --vault \"/path/to/vault\"',\n ],\n };\n }\n }\n\n // Resolve to absolute before validating or writing config: clients spawn the\n // server from their own working directory, so a relative SEEKSTONE_VAULT in\n // the written config would silently point at the wrong place.\n vaultPath = path.resolve(deps.cwd ?? process.cwd(), vaultPath);\n\n const check = await validateVault(vaultPath);\n if (!check.ok) {\n return { ok: false, exitCode: 1, output: [`✗ ${check.error}`] };\n }\n\n const out: string[] = [\n `✓ Vault looks good: ${vaultPath}`,\n ` ${check.noteCount} markdown note${check.noteCount === 1 ? '' : 's'} found.`,\n '',\n ];\n\n if (opts.client === 'code') {\n if (opts.write) {\n const spawn =\n deps.spawnClaudeMcp ??\n ((args: string[]) => {\n try {\n // Runs `claude mcp add …` — the canonical way to register an MCP\n // server with Claude Code. execFileSync (no shell) is intentional;\n // writing ~/.claude.json directly would couple us to an internal\n // format Anthropic can change without notice.\n execFileSync('claude', args, { stdio: 'inherit' });\n return { ok: true };\n } catch (err) {\n return { ok: false, error: err instanceof Error ? err.message : String(err) };\n }\n });\n\n const mcpArgs = [\n 'mcp',\n 'add',\n 'seekstone',\n '--env',\n `SEEKSTONE_VAULT=${vaultPath}`,\n '--',\n 'npx',\n '-y',\n 'seekstone',\n ];\n const result = spawn(mcpArgs);\n\n if (result.ok) {\n out.push('✓ seekstone added to Claude Code.', '', 'Restart Claude Code to load seekstone.');\n return { ok: true, exitCode: 0, output: out, ranClaudeMcpAdd: true };\n }\n\n // claude not on PATH or command failed — fall back to manual instructions.\n out.push(\n '⚠ Could not run `claude mcp add` automatically.',\n result.error ? ` ${result.error}` : '',\n '',\n 'Run this command manually instead:',\n '',\n ` ${mcpAddCommand(vaultPath)}`,\n '',\n );\n return { ok: false, exitCode: 1, output: out };\n }\n\n out.push('Add to Claude Code by running:', '', ` ${mcpAddCommand(vaultPath)}`, '');\n return { ok: true, exitCode: 0, output: out };\n }\n\n // File-config clients: Claude Desktop and Cursor share the same\n // `mcpServers` JSON shape; VS Code uses `servers` + explicit `type: stdio`.\n // Only the config path, JSON shape, and restart hint differ.\n const clientLabel =\n opts.client === 'cursor'\n ? 'Cursor'\n : opts.client === 'vscode'\n ? 'VS Code (GitHub Copilot)'\n : 'Claude Desktop';\n const configPath =\n opts.client === 'vscode'\n ? vscodeMcpConfigPath(deps.cwd ?? process.cwd())\n : opts.client === 'cursor'\n ? cursorConfigPath(deps.platform, { home: deps.env.HOME })\n : claudeDesktopConfigPath(deps.platform, {\n home: deps.env.HOME,\n appData: deps.env.APPDATA,\n });\n const block =\n opts.client === 'vscode'\n ? JSON.stringify({ servers: { seekstone: vscodeServerConfig(vaultPath) } }, null, 2)\n : JSON.stringify({ mcpServers: { seekstone: seekstoneServerConfig(vaultPath) } }, null, 2);\n const restartHint =\n opts.client === 'vscode'\n ? 'Reload VS Code, then open Copilot Chat in Agent mode — seekstone appears in the tools picker.'\n : `Restart ${clientLabel} to load seekstone.`;\n\n if (!opts.write) {\n out.push(\n `Add this to your ${clientLabel} config:`,\n ` ${configPath}`,\n ...(opts.client === 'vscode'\n ? [\n ' (workspace config — run from your project root, or use the Command Palette\\'s \"MCP: Open User Configuration\" for user-global)',\n ]\n : []),\n '',\n block,\n '',\n `${restartHint} Or re-run with --write to patch it automatically.`,\n );\n return { ok: true, exitCode: 0, output: out };\n }\n\n // --write: additive, backed-up patch.\n let existing: McpConfig | null = null;\n let existingRaw: string | null = null;\n try {\n existingRaw = await rf(configPath, 'utf8');\n existing = JSON.parse(existingRaw) as McpConfig;\n } catch (err) {\n if (existingRaw !== null) {\n // File exists but isn't valid JSON — refuse rather than clobber.\n return {\n ok: false,\n exitCode: 1,\n output: [\n `✗ ${configPath} exists but is not valid JSON; not modifying it.`,\n ` ${err instanceof Error ? err.message : String(err)}`,\n ],\n };\n }\n existing = null; // file simply doesn't exist yet\n }\n\n const merged =\n opts.client === 'vscode'\n ? mergeSeekstoneConfigVsCode(existing, vaultPath)\n : mergeSeekstoneConfig(existing, vaultPath);\n let backupPath: string | undefined;\n await mkdir(dirname(configPath), { recursive: true });\n if (existingRaw !== null) {\n backupPath = `${configPath}.backup-${deps.timestamp}`;\n await copyFile(configPath, backupPath);\n }\n await writeFile(configPath, `${JSON.stringify(merged, null, 2)}\\n`, 'utf8');\n\n out.push(\n `✓ Patched ${configPath}`,\n backupPath ? ` Backup saved to ${backupPath}` : ' (created a new config file)',\n '',\n restartHint,\n );\n return { ok: true, exitCode: 0, output: out, wrotePath: configPath, backupPath };\n}\n","import { appendFileSync, renameSync, statSync } from 'node:fs';\n\n/**\n * A tiny zero-dependency leveled logger for the MCP server.\n *\n * Two sinks, never stdout (stdout carries the JSON-RPC stdio transport):\n * - stderr: concise human-readable lines, always on (Claude Desktop captures it).\n * - file: JSON-lines, opt-in via SEEKSTONE_LOG_FILE, size-rotated.\n *\n * Privacy: callers pass metadata fields at info/warn/error and only attach note\n * content (bodies, frontmatter values, raw queries) at `debug`. The logger does\n * not inspect or redact fields — that contract lives at the call sites.\n *\n * Note: file writes use appendFileSync. File logging is opt-in and the per-line\n * cost is sub-millisecond, comfortably inside tool latency budgets; the default\n * (stderr-only) path does no disk I/O. An async write queue is a future option.\n */\n\nexport type LogLevel = 'error' | 'warn' | 'info' | 'debug';\n\nconst ORDER: readonly LogLevel[] = ['error', 'warn', 'info', 'debug'] as const;\nconst DEFAULT_MAX_SIZE = 5 * 1024 * 1024;\n\nexport interface Logger {\n readonly level: LogLevel;\n error(msg: string, fields?: Record<string, unknown>): void;\n warn(msg: string, fields?: Record<string, unknown>): void;\n info(msg: string, fields?: Record<string, unknown>): void;\n debug(msg: string, fields?: Record<string, unknown>): void;\n}\n\nexport interface LoggerOptions {\n /** Env to read SEEKSTONE_LOG_* from. Defaults to process.env. */\n env?: Record<string, string | undefined>;\n /** stderr sink — injectable for tests. Defaults to process.stderr. Never stdout. */\n stderr?: (line: string) => void;\n /** Clock — injectable for deterministic tests. */\n now?: () => Date;\n}\n\nfunction parseLevel(raw: string | undefined, onBad: (m: string) => void): LogLevel {\n if (raw === undefined || raw === '') return 'info';\n const v = raw.trim().toLowerCase();\n for (const lvl of ORDER) {\n if (lvl === v) return lvl;\n }\n onBad(`invalid SEEKSTONE_LOG_LEVEL=${JSON.stringify(raw)}; using \"info\"`);\n return 'info';\n}\n\nfunction parseSize(raw: string | undefined): number {\n if (raw === undefined) return DEFAULT_MAX_SIZE;\n const m = /^(\\d+(?:\\.\\d+)?)(b|kb|mb|gb)?$/i.exec(raw.trim());\n if (m === null) return DEFAULT_MAX_SIZE;\n const num = Number(m[1]);\n const unit = (m[2] ?? 'b').toLowerCase();\n const mult = unit === 'gb' ? 1e9 : unit === 'mb' ? 1e6 : unit === 'kb' ? 1e3 : 1;\n return Math.max(1, Math.floor(num * mult));\n}\n\nfunction fmtFields(fields: Record<string, unknown>): string {\n const parts: string[] = [];\n for (const [k, v] of Object.entries(fields)) {\n if (v === undefined) continue;\n parts.push(`${k}=${typeof v === 'string' ? v : JSON.stringify(v)}`);\n }\n return parts.length > 0 ? ` ${parts.join(' ')}` : '';\n}\n\nexport function createLogger(opts: LoggerOptions = {}): Logger {\n const env = opts.env ?? process.env;\n // Default sink is stderr — deliberately never process.stdout.\n const stderr = opts.stderr ?? ((line: string): void => void process.stderr.write(line));\n const now = opts.now ?? ((): Date => new Date());\n\n const level = parseLevel(env.SEEKSTONE_LOG_LEVEL, (m) => {\n stderr(`WARN ${now().toISOString()} ${m}\\n`);\n });\n const threshold = ORDER.indexOf(level);\n\n const filePath = env.SEEKSTONE_LOG_FILE;\n const maxSize = parseSize(env.SEEKSTONE_LOG_MAX_SIZE);\n let fileOk = false;\n let bytes = 0;\n if (filePath !== undefined && filePath !== '') {\n try {\n try {\n bytes = statSync(filePath).size;\n } catch {\n bytes = 0;\n }\n appendFileSync(filePath, ''); // verify writable / create\n fileOk = true;\n } catch (err) {\n const m = err instanceof Error ? err.message : String(err);\n stderr(\n `WARN ${now().toISOString()} cannot write log file ${filePath}: ${m}; file logging disabled\\n`,\n );\n }\n }\n\n function writeFile(record: Record<string, unknown>): void {\n if (!fileOk || filePath === undefined) return;\n const line = `${JSON.stringify(record)}\\n`;\n const len = Buffer.byteLength(line);\n try {\n if (bytes > 0 && bytes + len > maxSize) {\n renameSync(filePath, `${filePath}.1`);\n bytes = 0;\n }\n appendFileSync(filePath, line);\n bytes += len;\n } catch {\n fileOk = false; // stop trying; logging must never crash the request path\n }\n }\n\n function emit(lvl: LogLevel, msg: string, fields?: Record<string, unknown>): void {\n if (ORDER.indexOf(lvl) > threshold) return; // below threshold: do no work\n const ts = now().toISOString();\n const f = fields ?? {};\n stderr(`${lvl.toUpperCase().padEnd(5)} ${ts} ${msg}${fmtFields(f)}\\n`);\n writeFile({ t: ts, level: lvl, msg, ...f });\n }\n\n return {\n level,\n error: (m, f) => {\n emit('error', m, f);\n },\n warn: (m, f) => {\n emit('warn', m, f);\n },\n info: (m, f) => {\n emit('info', m, f);\n },\n debug: (m, f) => {\n emit('debug', m, f);\n },\n };\n}\n","import type { Logger } from './log.js';\n\n/**\n * Install a process-level safety net for the long-lived stdio server session.\n *\n * The server's known async paths (dispatch, watcher) already guard themselves,\n * so a stray `unhandledRejection` is unexpected — but crashing the whole MCP\n * session (and discarding the in-memory index, which is expensive to rebuild)\n * is a worse outcome than logging it and staying up. So we log via stderr and\n * keep running. `uncaughtException` is deliberately left to Node's default\n * crash behaviour: continuing after a truly uncaught throw risks corrupt state.\n *\n * Returns a disposer that removes the listener (used by tests to avoid leaking\n * handlers across cases).\n */\nexport function installProcessGuards(log: Logger): () => void {\n const onUnhandledRejection = (reason: unknown): void => {\n const error = reason instanceof Error ? reason.message : String(reason);\n log.error('unhandled promise rejection (server kept alive)', { error });\n };\n\n process.on('unhandledRejection', onUnhandledRejection);\n\n return () => {\n process.off('unhandledRejection', onUnhandledRejection);\n };\n}\n","import { WRITE_TOOLS } from './dispatch.js';\nimport type { WritePolicy } from './policy.js';\n\n/**\n * The full tool list served to MCP clients, extracted verbatim from the\n * ListTools handler so it can be filtered by policy and unit-tested.\n * Keep in sync with HANDLED_TOOLS in dispatch.ts.\n */\nexport const ALL_TOOLS = [\n {\n name: 'search',\n description:\n 'Full-text search across the vault. Returns ranked excerpts (~200 chars) — not full notes — to minimise context usage. Supports fuzzy matching and prefix search.',\n inputSchema: {\n type: 'object',\n properties: {\n query: { type: 'string', description: 'Search query.' },\n limit: { type: 'number', description: 'Max results (1–50, default 10).' },\n folder: { type: 'string', description: 'Restrict to a vault-relative folder prefix.' },\n tag: { type: 'string', description: 'Restrict to notes with this tag.' },\n },\n required: ['query'],\n },\n },\n {\n name: 'query_notes',\n description:\n 'Structured metadata query — filter notes by frontmatter key/value predicates, tag, folder, modified time, and size. Returns compact rows (path + title by default; opt into more via select), not note content. Use this instead of search when filtering by properties rather than text.',\n inputSchema: {\n type: 'object',\n properties: {\n where: {\n type: 'array',\n description: 'Frontmatter predicates — all must match (AND).',\n items: {\n type: 'object',\n properties: {\n key: { type: 'string', description: 'Frontmatter key to test.' },\n op: {\n type: 'string',\n enum: ['eq', 'ne', 'contains', 'exists', 'missing', 'gt', 'gte', 'lt', 'lte'],\n description:\n 'eq/ne compare scalars; contains matches array membership or substring; exists/missing test key presence; gt/gte/lt/lte compare numbers or strings (ISO dates sort correctly).',\n },\n value: {\n description: 'Comparison value. Required for every op except exists/missing.',\n },\n },\n required: ['key', 'op'],\n },\n },\n folder: { type: 'string', description: 'Restrict to a vault-relative folder prefix.' },\n tag: { type: 'string', description: 'Restrict to notes with this tag (# optional).' },\n modifiedAfter: {\n type: 'string',\n description: 'Only notes modified at or after this ISO 8601 date/time.',\n },\n modifiedBefore: {\n type: 'string',\n description: 'Only notes modified before this ISO 8601 date/time.',\n },\n minSizeBytes: { type: 'number', description: 'Only notes at least this many bytes.' },\n maxSizeBytes: { type: 'number', description: 'Only notes at most this many bytes.' },\n select: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Extra fields per hit: frontmatter keys, or \"mtime\", \"size\", \"tags\". Default returns only path + title.',\n },\n sort: {\n type: 'string',\n enum: ['path', 'title', 'mtime', 'size'],\n description: 'Sort field (default path).',\n },\n order: {\n type: 'string',\n enum: ['asc', 'desc'],\n description: 'Sort order (default asc).',\n },\n limit: { type: 'number', description: 'Max results (1–500, default 100).' },\n },\n required: [],\n },\n },\n {\n name: 'context_pack',\n description:\n 'Assemble everything needed to ANSWER a natural-language question in one call, under a strict byte budget (default 2048): ranked excerpts, linked neighbor notes (backlinks/outlinks) with one-line summaries, and follow-up source paths. Use search to locate notes and query_notes for metadata filters; use context_pack when you want answer-ready context without multiple round-trips. Empty excerpts with confidence \"none\" or \"low\" means the vault lacks coverage — do not infer content.',\n inputSchema: {\n type: 'object',\n properties: {\n query: { type: 'string', description: 'Natural-language question or topic.' },\n budgetBytes: {\n type: 'number',\n description: 'Hard cap on response JSON bytes (256–16384, default 2048).',\n },\n },\n required: ['query'],\n },\n },\n {\n name: 'read_note',\n description:\n 'Read a note or a span of it — by heading section, block reference, or line range. Returns structured JSON with the content, bytes returned, total note size, and a contentHash to pass as prevHash to edit tools for compare-and-swap. Use search or outline_note first to find the right path and section names.',\n inputSchema: {\n type: 'object',\n properties: {\n path: {\n type: 'string',\n description: 'Vault-relative path, e.g. \"Daily Notes/2026-05-29.md\".',\n },\n section: {\n type: 'string',\n description:\n 'Return only this heading section (heading text, # prefix optional). First match wins.',\n },\n block: {\n type: 'string',\n description: 'Return only the line anchored by this block id (^ prefix optional).',\n },\n lines: {\n type: 'object',\n description: 'Return only this line range (1-indexed, inclusive).',\n properties: {\n from: { type: 'number', description: 'First line (1-indexed).' },\n to: {\n type: 'number',\n description: 'Last line (1-indexed, inclusive). Defaults to EOF.',\n },\n },\n required: ['from'],\n },\n includeFrontmatter: {\n type: 'boolean',\n description:\n 'Prepend frontmatter to section/block span results. Default false for spans.',\n },\n },\n required: ['path'],\n },\n },\n {\n name: 'list_notes',\n description: 'List notes, optionally filtered by folder prefix or tag.',\n inputSchema: {\n type: 'object',\n properties: {\n folder: { type: 'string', description: 'Vault-relative folder prefix.' },\n tag: { type: 'string', description: 'Filter by tag (# prefix optional).' },\n limit: { type: 'number', description: 'Max results (1–500, default 100).' },\n },\n required: [],\n },\n },\n {\n name: 'list_tags',\n description:\n 'List all tags in the vault with usage counts. Supports substring filtering, minimum count threshold, and sort order. Nested tags (e.g. area/work) include a parent field.',\n inputSchema: {\n type: 'object',\n properties: {\n pattern: {\n type: 'string',\n description:\n 'Substring filter on tag name. Example: \"work\" matches \"work\" and \"area/work\".',\n },\n minCount: {\n type: 'number',\n description: 'Only return tags used in at least this many notes.',\n },\n sort: {\n type: 'string',\n enum: ['count', 'name'],\n description: 'Sort by usage count descending (default) or alphabetically.',\n },\n },\n required: [],\n },\n },\n {\n name: 'create_note',\n description:\n 'Create a new note at a vault-relative path. Optionally sets frontmatter and body content. Parent directories are created automatically. Fails if the note already exists unless overwrite is true (prevHash may guard the overwrite).',\n inputSchema: {\n type: 'object',\n properties: {\n path: {\n type: 'string',\n description: 'Vault-relative path, e.g. \"Daily Notes/2026-06-01.md\".',\n },\n content: { type: 'string', description: 'Body content for the note.' },\n frontmatter: {\n type: 'object',\n description: 'Frontmatter key-value pairs.',\n additionalProperties: true,\n },\n overwrite: {\n type: 'boolean',\n description: 'Overwrite an existing note. Defaults to false.',\n },\n prevHash: {\n type: 'string',\n description:\n 'Optional compare-and-swap guard: the contentHash from a prior read. Fails with hash_conflict if the note changed since.',\n },\n },\n required: ['path'],\n },\n },\n {\n name: 'delete_note',\n description:\n 'Delete a note. By default it is moved to the vault .trash/ folder (recoverable by moving it back); pass permanent: true to remove it outright.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Vault-relative path of the note to delete.' },\n permanent: {\n type: 'boolean',\n description: 'Permanently remove instead of moving to .trash/. Defaults to false.',\n },\n },\n required: ['path'],\n },\n },\n {\n name: 'move_note',\n description:\n 'Move or rename a note to a new vault-relative path, rewriting wikilinks and markdown links in other notes that point at it so nothing breaks (links inside fenced code blocks are left alone). Parent directories at the destination are created automatically. Fails if the destination already exists unless overwrite is true.',\n inputSchema: {\n type: 'object',\n properties: {\n from: { type: 'string', description: 'Vault-relative source path.' },\n to: { type: 'string', description: 'Vault-relative destination path.' },\n overwrite: {\n type: 'boolean',\n description: 'Overwrite destination if it exists. Defaults to false.',\n },\n rewriteLinks: {\n type: 'boolean',\n description:\n 'Rewrite references in other notes to follow the move. Defaults to true; pass false to move the file only.',\n },\n },\n required: ['from', 'to'],\n },\n },\n {\n name: 'append_note',\n description:\n 'Append text to a note body without touching the frontmatter. Safe for meeting notes, daily logs, and append-only workflows.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Vault-relative path to the note.' },\n content: { type: 'string', description: 'Text to append.' },\n prevHash: {\n type: 'string',\n description:\n 'Optional compare-and-swap guard: the contentHash from a prior read. Fails with hash_conflict if the note changed since.',\n },\n },\n required: ['path', 'content'],\n },\n },\n {\n name: 'patch_frontmatter',\n description:\n 'Set, update, or delete frontmatter keys without reordering existing keys or changing quote style. Pass null as a value to delete a key.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Vault-relative path to the note.' },\n patch: {\n type: 'object',\n description: 'Key-value pairs to set. Null value removes the key.',\n additionalProperties: true,\n },\n prevHash: {\n type: 'string',\n description:\n 'Optional compare-and-swap guard: the contentHash from a prior read. Fails with hash_conflict if the note changed since.',\n },\n },\n required: ['path', 'patch'],\n },\n },\n {\n name: 'outline_note',\n description:\n \"Return a note's structure — heading tree with offsets, block-reference anchors, and frontmatter key list — without returning any prose. Use this before section reads or patches to discover what sections exist at a fraction of the cost of reading the full note.\",\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Vault-relative path to the note.' },\n includeBlocks: {\n type: 'boolean',\n description: 'Include block-reference anchors (^id). Default true.',\n },\n includeSizes: {\n type: 'boolean',\n description: 'Include per-section character length in each heading entry. Default false.',\n },\n },\n required: ['path'],\n },\n },\n {\n name: 'patch_note',\n description:\n 'Surgically edit a section of a note — targeted by heading or block reference — without rewriting the whole file. Operations: append (add after section), prepend (add after heading line), replace (swap section content). Frontmatter is never touched.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Vault-relative path to the note.' },\n target: {\n type: 'object',\n description:\n 'Exactly one of: { heading: \"Section Title\" } or { block: \"block-id\" } (without the ^ prefix).',\n },\n operation: {\n type: 'string',\n enum: ['append', 'prepend', 'replace'],\n description:\n 'append: insert after section content. prepend: insert after heading line. replace: swap section content.',\n },\n content: { type: 'string', description: 'Content to insert or replace with.' },\n createIfMissing: {\n type: 'boolean',\n description:\n 'If the heading target is not found, append a new heading (level 2) + content. Only valid for heading targets. Default false.',\n },\n prevHash: {\n type: 'string',\n description:\n 'Optional compare-and-swap guard: the contentHash from a prior read. Fails with hash_conflict if the note changed since.',\n },\n },\n required: ['path', 'target', 'operation', 'content'],\n },\n },\n {\n name: 'get_backlinks',\n description:\n 'Return every note that links to the target note, with the source line and an optional excerpt. Results come from the pre-built reverse-link index so this is a fast, pure index lookup. Sort order: source path ascending.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Vault-relative path to the target note.' },\n includeContext: {\n type: 'boolean',\n description: 'Include a short excerpt (~200 chars) from the linking line. Default true.',\n },\n limit: {\n type: 'number',\n description: 'Max backlinks to return (1–500). Default 50.',\n },\n },\n required: ['path'],\n },\n },\n {\n name: 'get_links',\n description:\n 'Return all outgoing wikilinks and embeds from a note. Each link is marked resolved (with target path) or unresolved. Duplicate targets are de-duplicated; results sorted by line number.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Vault-relative path to the note.' },\n },\n required: ['path'],\n },\n },\n {\n name: 'replace_in_note',\n description:\n 'Find and replace text within a note body. Supports literal and regex search, case sensitivity, whole-word matching, and a replacement limit. Frontmatter is never touched. Use dryRun to preview matches before writing.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Vault-relative path to the note.' },\n find: { type: 'string', description: 'Text or pattern to find.' },\n replace: {\n type: 'string',\n description: 'Replacement text. Supports $1, $2, … backreferences in regex mode.',\n },\n regex: {\n type: 'boolean',\n description: 'Treat find as a regular expression. Default false.',\n },\n caseSensitive: {\n type: 'boolean',\n description: 'Case-sensitive matching. Default false.',\n },\n wholeWord: {\n type: 'boolean',\n description: 'Match whole words only (\\\\b boundary). Default false.',\n },\n limit: {\n type: 'number',\n description: 'Maximum number of replacements. Omit to replace all.',\n },\n dryRun: {\n type: 'boolean',\n description: 'If true, report matches without writing. Default false.',\n },\n prevHash: {\n type: 'string',\n description:\n 'Optional compare-and-swap guard: the contentHash from a prior read. Fails with hash_conflict if the note changed since.',\n },\n },\n required: ['path', 'find', 'replace'],\n },\n },\n {\n name: 'get_periodic_note',\n description:\n 'Get the path and existence status of a periodic note (daily, weekly, monthly, quarterly, or yearly) for a given date. Reads folder/format config from .obsidian/daily-notes.json (daily) or the periodic-notes plugin data.json. Optionally creates the note from the configured template if it is missing.',\n inputSchema: {\n type: 'object',\n properties: {\n period: {\n type: 'string',\n enum: ['daily', 'weekly', 'monthly', 'quarterly', 'yearly'],\n description: 'Period type. Default: daily.',\n },\n date: {\n type: 'string',\n description: 'ISO date string (YYYY-MM-DD). Defaults to today when omitted.',\n },\n createIfMissing: {\n type: 'boolean',\n description:\n 'Create the note from the configured template if it does not exist. Default false.',\n },\n },\n required: [],\n },\n },\n {\n name: 'append_periodic_note',\n description:\n 'Append text to a periodic note (daily, weekly, monthly, quarterly, or yearly). Preserves existing frontmatter exactly. Creates the note first (from template if configured) when createIfMissing is true (default).',\n inputSchema: {\n type: 'object',\n properties: {\n period: {\n type: 'string',\n enum: ['daily', 'weekly', 'monthly', 'quarterly', 'yearly'],\n description: 'Period type. Default: daily.',\n },\n date: {\n type: 'string',\n description: 'ISO date string (YYYY-MM-DD). Defaults to today when omitted.',\n },\n content: {\n type: 'string',\n description: 'Text to append to the note body.',\n },\n createIfMissing: {\n type: 'boolean',\n description: 'Create the note if it does not exist before appending. Default true.',\n },\n prevHash: {\n type: 'string',\n description:\n 'Optional compare-and-swap guard: the contentHash from a prior read. Fails with hash_conflict if the note changed since.',\n },\n },\n required: ['content'],\n },\n },\n];\n\n/**\n * The tools visible to the client under a policy. In read-only mode write\n * tools are unregistered entirely (not just failing) — better for context\n * economy and honest about what the session can do. Dispatch still rejects\n * write calls independently; unregistering is presentation, not enforcement.\n */\nexport function visibleTools(policy: WritePolicy) {\n if (!policy.readOnly) return ALL_TOOLS;\n return ALL_TOOLS.filter((t) => !WRITE_TOOLS.has(t.name));\n}\n","import { realpathSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { join, relative, sep } from 'node:path';\nimport chokidar from 'chokidar';\nimport type { ServerContext } from './context.js';\nimport { addNoteBacklinks, removeNoteBacklinks } from './index/backlinks.js';\nimport { buildDoc, upsertDoc } from './index/doc.js';\nimport type { Logger } from './log.js';\n\n/**\n * Watch the vault for .md changes and keep the in-memory index in sync.\n *\n * Uses chokidar rather than `fs.watch(..., { recursive: true })`: recursive\n * native watching is unsupported on Linux, where fs.watch silently misses\n * nested changes. chokidar gives reliable, recursive, cross-platform events.\n *\n * Returns `stop()` to tear down the watcher and a `ready` promise that\n * resolves once chokidar has finished its initial scan and is live.\n */\nexport interface WatcherHandle {\n /**\n * Close the watcher. Returns chokidar's close promise so callers that need\n * deterministic teardown (tests, especially under usePolling where every\n * watched path holds a live stat-poll timer) can await full shutdown;\n * fire-and-forget callers may ignore the result.\n */\n stop: () => Promise<void>;\n ready: Promise<void>;\n}\n\nexport interface WatcherOptions {\n /**\n * Stat-poll instead of using native OS file events. Slower but reliable on\n * filesystems where native events are unreliable (network mounts, WSL, some\n * containers) and deterministic under load. Defaults to false, or true when\n * SEEKSTONE_WATCH_POLL=1.\n */\n usePolling?: boolean;\n /**\n * Diagnostic hook fired for every raw chokidar event, before .md filtering\n * and before the index is updated. Receives the chokidar event name, the\n * absolute path as chokidar reported it, and the vault-relative key as\n * computed by toRel(). Use in tests only to capture what chokidar actually\n * emits when a failure needs investigation.\n */\n onRawEvent?: (event: 'add' | 'change' | 'unlink', absPath: string, relPath: string) => void;\n}\n\nexport function startWatcher(\n ctx: ServerContext,\n log?: Logger,\n opts?: WatcherOptions,\n): WatcherHandle {\n const usePolling = opts?.usePolling ?? process.env.SEEKSTONE_WATCH_POLL === '1';\n\n // chokidar's followSymlinks=true (the default) calls fs.realpath() on the\n // watched path, so events arrive with the real (resolved) path. On Windows,\n // os.tmpdir() can return 8.3 short paths (e.g. RUNNER~1) while realpath()\n // expands them to the long form. Without pre-resolving here, relative()\n // inside the ignored predicate and toRel() would produce \"..\" segments;\n // \"..\" starts with \".\" and trips the dot-directory filter, silencing every\n // event — the root cause of the flaky \"vault path containing spaces\" test.\n let vaultRoot = ctx.vaultRoot;\n try {\n vaultRoot = realpathSync(ctx.vaultRoot);\n } catch {\n // Vault doesn't exist yet or can't be resolved — keep original path.\n }\n\n // Index keys are forward-slash vault-relative paths on every platform.\n const toRel = (abs: string): string => relative(vaultRoot, abs).split(sep).join('/');\n\n async function upsert(relPath: string, op: 'add' | 'change'): Promise<void> {\n try {\n const raw = await readFile(join(vaultRoot, relPath), 'utf8');\n removeNoteBacklinks(ctx, relPath);\n upsertDoc(ctx, buildDoc(relPath, raw));\n addNoteBacklinks(ctx, relPath, raw);\n log?.debug('index updated', { path: relPath, op });\n } catch {\n // File vanished between event and read — ignore; an unlink will follow.\n }\n }\n\n function remove(relPath: string): void {\n if (ctx.notes.has(relPath)) {\n removeNoteBacklinks(ctx, relPath);\n ctx.index.discard(relPath);\n ctx.notes.delete(relPath);\n log?.debug('index removed', { path: relPath, op: 'delete' });\n }\n }\n\n const onUpsert = (op: 'add' | 'change') => (abs: string) => {\n const relPath = toRel(abs);\n opts?.onRawEvent?.(op, abs, relPath);\n if (relPath.endsWith('.md')) void upsert(relPath, op);\n };\n\n const watcher = chokidar.watch(vaultRoot, {\n // The startup index build already covers existing files.\n ignoreInitial: true,\n usePolling,\n interval: 50,\n binaryInterval: 100,\n // Skip dot-dirs (.obsidian, .git, .trash, …) regardless of where the vault\n // lives — matched on the path relative to the vault root.\n ignored: (p: string) => {\n const rel = relative(vaultRoot, p);\n if (!rel || rel === '.') return false;\n // Exclude \"..\" — it means \"outside the vault root\" (a path normalisation\n // mismatch), not a dotfile. Treating \"..\" as a dot-segment would silence\n // all events if the ignored predicate ever receives an out-of-root path.\n return rel\n .split(sep)\n .filter(Boolean)\n .some((seg) => seg.startsWith('.') && seg !== '..');\n },\n });\n\n watcher\n .on('add', onUpsert('add'))\n .on('change', onUpsert('change'))\n .on('unlink', (abs: string) => {\n const relPath = toRel(abs);\n opts?.onRawEvent?.('unlink', abs, relPath);\n if (relPath.endsWith('.md')) remove(relPath);\n });\n\n const ready = new Promise<void>((resolve) => {\n watcher.once('ready', () => {\n resolve();\n });\n });\n\n return {\n stop: () => watcher.close(),\n ready,\n };\n}\n"],"mappings":"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","names":["resolve","path","policy","vaultRoot","ctx","z","notes","path","z","z","path","ctx","note","noteTags","z","ctx","path","readFile","z","ctx","z","ctx","readFile","access","mkdir","rename","basename","extname","z","ctx","z","vaultRoot","extname","access","ctx","mkdir","basename","rename","z","z","ctx","backlinks","z","z","ctx","z","ctx","path","z","ctx","access","mkdir","rename","dirname","join","z","matches","z","ctx","access","path","mkdir","dirname","rename","join","readFile","z","z","ctx","readFile","readFile","z","z","ctx","readFile","readFile","z","z","ctx","readFile","sep","newContent","path","access","mkdir","readFile","dirname","join","z","z","readFile","vaultRoot","cfg","join","ctx","path","access","mkdir","dirname","z","z","ctx","path","readFile","z","z","ctx","readFile","readFile","z","z","ctx","readFile","matches","ctx","log","readFile","basename","extname","extname","join","sep","vaultRoot","matches","vaultRoot","index","notes","readFile","basename","extname","backlinks","access","mkdir","readFile","stat","writeFile","dirname","join","j","writeFile","log","policy","readFile","join","relative","sep","ctx","log","vaultRoot","relative","sep","readFile","join","watcher","resolve","resolve"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/cli-args.ts","../src/tools/append_note.ts","../../core/src/frontmatter.ts","../src/atomic-write.ts","../src/content-hash.ts","../src/policy.ts","../src/vault-path.ts","../../core/src/extract.ts","../src/tools/context_pack.ts","../src/index/excerpt.ts","../src/index/resolve.ts","../src/tools/search.ts","../src/tools/create_note.ts","../src/index/doc.ts","../src/tools/delete_note.ts","../src/index/backlinks.ts","../src/tools/get_backlinks.ts","../src/tools/get_links.ts","../src/tools/list_notes.ts","../src/tools/list_tags.ts","../src/tools/move_note.ts","../src/tools/rewrite_links.ts","../src/tools/outline_note.ts","../../core/src/outline.ts","../src/tools/patch_frontmatter.ts","../src/tools/patch_note.ts","../src/tools/periodic_note.ts","../src/tools/query_notes.ts","../src/tools/read_note.ts","../src/tools/rename_heading.ts","../src/tools/replace_in_note.ts","../src/dispatch.ts","../src/index/build.ts","../../core/src/walk.ts","../src/init.ts","../src/log.ts","../src/process-guards.ts","../src/tool-list.ts","../src/watcher.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { resolve } from 'node:path';\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n type CallToolResult,\n ListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { helpText, initHelpText, parseCliIntent } from './cli-args.js';\nimport type { ServerContext } from './context.js';\nimport { dispatch } from './dispatch.js';\nimport { buildIndex } from './index/build.js';\nimport { parseInitArgs, runInit } from './init.js';\nimport { createLogger } from './log.js';\nimport { parseWritePolicy } from './policy.js';\nimport { installProcessGuards } from './process-guards.js';\nimport { visibleTools } from './tool-list.js';\nimport { startWatcher } from './watcher.js';\n\n// Inlined at build time by tsup (see tsup.config.ts); falls back in tsx dev.\ndeclare const __SEEKSTONE_VERSION__: string;\nconst VERSION = typeof __SEEKSTONE_VERSION__ === 'string' ? __SEEKSTONE_VERSION__ : '0.0.0-dev';\n\n// CLI subcommands / flags exit before any server setup, printing to stdout\n// (these are explicit invocations, not the MCP stdio session).\nconst intent = parseCliIntent(process.argv.slice(2));\nif (intent === 'version') {\n process.stdout.write(`${VERSION}\\n`);\n process.exit(0);\n}\nif (intent === 'help') {\n process.stdout.write(`${helpText(VERSION)}\\n`);\n process.exit(0);\n}\nif (intent === 'init-help') {\n process.stdout.write(`${initHelpText(VERSION)}\\n`);\n process.exit(0);\n}\nif (intent === 'init') {\n const result = await runInit(parseInitArgs(process.argv.slice(3)), {\n env: process.env,\n platform: process.platform,\n timestamp: new Date().toISOString().replace(/[:.]/g, '-'),\n cwd: process.cwd(),\n });\n process.stdout.write(`${result.output.join('\\n')}\\n`);\n process.exit(result.exitCode);\n}\n\nconst log = createLogger();\n\n// Long-lived stdio session: keep the server alive (and its in-memory index)\n// on a stray unhandled rejection rather than crashing the user's session.\ninstallProcessGuards(log);\n\nconst rawVaultRoot = process.env.SEEKSTONE_VAULT;\nif (!rawVaultRoot) {\n log.error('SEEKSTONE_VAULT env var is required');\n process.exit(1);\n}\n// Normalize once so the containment guard (vault-path.ts) compares against a\n// canonical absolute path — a relative or trailing-slash SEEKSTONE_VAULT must\n// not weaken the prefix boundary.\nconst vaultRoot = resolve(rawVaultRoot);\n\nconst policy = parseWritePolicy(process.env);\nif (policy.readOnly) log.info('read-only mode', { env: 'SEEKSTONE_READ_ONLY' });\nif (policy.writeGlobs) log.info('write paths scoped', { globs: policy.writeGlobs });\n\nlog.info('building index', { vault: vaultRoot });\nconst { index, notes, backlinks, buildMs } = await buildIndex(vaultRoot);\nlog.info('index ready', { notes: notes.size, buildMs });\n\nconst ctx: ServerContext = { vaultRoot, index, notes, backlinks, policy };\n\nconst watcher = startWatcher(ctx, log);\n// Exit handlers must stay synchronous — kick off the close; nothing awaits it.\nprocess.on('exit', () => void watcher.stop());\n\nconst server = new Server({ name: 'seekstone', version: VERSION }, { capabilities: { tools: {} } });\n\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: visibleTools(ctx.policy),\n}));\n\nserver.setRequestHandler(CallToolRequestSchema, async (req): Promise<CallToolResult> => {\n const { name, arguments: args } = req.params;\n return dispatch(ctx, name, args, log);\n});\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\nlog.info('ready', { tools: visibleTools(ctx.policy).length, transport: 'stdio' });\n\nprocess.stderr.write(\n `seekstone: add to Claude Desktop:\\n${JSON.stringify(\n {\n mcpServers: {\n seekstone: {\n command: 'npx',\n args: ['-y', 'seekstone'],\n env: { SEEKSTONE_VAULT: vaultRoot },\n },\n },\n },\n null,\n 2,\n )}\\n`,\n);\n","/**\n * Tiny argument handling for the `seekstone` bin. Kept pure (no process exit,\n * no IO) so it can be unit-tested. The MCP server itself takes no arguments —\n * these are just the conventional `--version` / `--help` niceties expected of a\n * CLI installed via `npx`.\n */\n\nexport type CliIntent = 'version' | 'help' | 'init' | 'init-help' | 'run';\n\nexport function parseCliIntent(argv: readonly string[]): CliIntent {\n // A subcommand, if present, is the first non-flag token. A help flag after\n // the subcommand asks for the subcommand's help, so it must be recognised\n // here — before the init run path — not swallowed by init's own arg parsing.\n if (argv[0] === 'init') {\n for (const arg of argv.slice(1)) {\n if (arg === '--help' || arg === '-h') return 'init-help';\n }\n return 'init';\n }\n for (const arg of argv) {\n if (arg === '--version' || arg === '-v') return 'version';\n if (arg === '--help' || arg === '-h') return 'help';\n }\n return 'run';\n}\n\n// Shared between the top-level help and `seekstone init --help` so the two\n// can't drift apart.\nconst initOptionLines = ` --vault <path> Vault to use (auto-detected from Obsidian if omitted; fallback: $SEEKSTONE_VAULT)\n --client <name> desktop (default) | code | cursor | vscode\n --write Patch the client config in place (backs it up first)`;\n\nexport function helpText(version: string): string {\n return `seekstone ${version} — an Obsidian MCP server (filesystem-direct, low context-tax)\n\nSeekstone runs as a Model Context Protocol stdio server. It is normally\nlaunched by an MCP client (Claude Desktop, Claude Code, Cursor, …), not run by hand.\n\nUsage:\n seekstone Start the MCP server (reads from stdin/stdout)\n seekstone init Validate a vault and print/patch the client MCP config\n seekstone --version Print the version and exit\n seekstone --help Print this help and exit\n\n\"seekstone init\" options:\n${initOptionLines}\n\nRequired environment:\n SEEKSTONE_VAULT Absolute path to your Obsidian vault\n\nOptional environment:\n SEEKSTONE_LOG_LEVEL error | warn | info (default) | debug\n SEEKSTONE_LOG_FILE Absolute path; append JSON-line logs here\n SEEKSTONE_WATCH_POLL Set to 1 to poll for changes (network drives, WSL)\n\nAdd to Claude Code:\n claude mcp add seekstone --env SEEKSTONE_VAULT=/path/to/vault -- npx -y seekstone\n\nDocs: https://github.com/shaqmughal/seekstone`;\n}\n\nexport function initHelpText(version: string): string {\n return `seekstone ${version} — seekstone init\n\nValidate an Obsidian vault and print (or, with --write, patch) the MCP config\nfor the chosen client (Claude Desktop, Claude Code, Cursor, VS Code).\n\nUsage:\n seekstone init [options]\n\nOptions:\n${initOptionLines}\n --help, -h Print this help and exit\n\nExamples:\n seekstone init\n seekstone init --vault \"/Users/you/Obsidian/My Vault\" --client code --write\n\nDocs: https://github.com/shaqmughal/seekstone`;\n}\n","import { readFile } from 'node:fs/promises';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport { z } from 'zod';\nimport { atomicWrite } from '../atomic-write.js';\nimport { assertHashMatch, contentHash } from '../content-hash.js';\nimport type { ServerContext } from '../context.js';\nimport { assertWritable } from '../policy.js';\nimport { resolveVaultPath } from '../vault-path.js';\n\nexport const AppendNoteInput = z.object({\n path: z.string().describe('Vault-relative path to the note.'),\n content: z\n .string()\n .min(1)\n .describe(\n 'Text to append to the note body. Will be separated from existing content by a blank line.',\n ),\n prevHash: z\n .string()\n .optional()\n .describe(\n 'Optional compare-and-swap guard: the contentHash from a prior read. Fails with hash_conflict if the note changed since.',\n ),\n});\nexport type AppendNoteInput = z.infer<typeof AppendNoteInput>;\n\nexport interface AppendNoteResult {\n path: string;\n bytesWritten: number;\n /** sha-256 (hex) of the new content — usable as prevHash for a chained edit. */\n contentHash: string;\n}\n\n/**\n * Append text to a note body without touching the frontmatter.\n *\n * The write contract:\n * - The frontmatter block (if any) is preserved byte-for-byte.\n * - A blank line is inserted before the appended content if the body\n * does not already end with one (keeps Obsidian's visual spacing).\n * - The file is written atomically (temp file + rename).\n */\nexport async function appendNote(\n ctx: ServerContext,\n input: AppendNoteInput,\n): Promise<AppendNoteResult> {\n const absPath = resolveVaultPath(ctx.vaultRoot, input.path);\n assertWritable(ctx.policy, input.path);\n\n const original = await readFile(absPath, 'utf8');\n if (input.prevHash !== undefined) assertHashMatch(original, input.prevHash, input.path);\n const fm = parseFrontmatter(original);\n\n // Ensure body ends with exactly one trailing newline before appending.\n const body = fm.body.endsWith('\\n') ? fm.body : `${fm.body}\\n`;\n const separator = body.endsWith('\\n\\n') ? '' : '\\n';\n const newBody = `${body}${separator}${input.content}`;\n\n // Reconstruct: original header (FM + delimiters) + new body.\n const header = original.slice(0, fm.bodyStart);\n const newContent = `${header}${newBody}`;\n\n await atomicWrite(absPath, newContent);\n\n // Update the in-memory index entry so subsequent searches reflect the change.\n const cached = ctx.notes.get(input.path);\n if (cached) {\n cached.body = newBody;\n cached.raw = newContent;\n }\n\n return {\n path: input.path,\n bytesWritten: Buffer.byteLength(newContent, 'utf8'),\n contentHash: contentHash(newContent),\n };\n}\n","import { parse as parseYaml } from 'yaml';\n\nexport interface FrontmatterResult {\n /** true if the note starts with a YAML block delimited by `---` lines. */\n present: boolean;\n /** Parsed object (null if not present or parse error or block empty). */\n data: Record<string, unknown> | null;\n /** Distinct top-level keys (empty if absent or malformed). */\n keys: string[];\n /** True if a `---` block exists but yaml.parse threw. */\n malformed: boolean;\n /** Byte offset where the body (post-frontmatter) starts. */\n bodyStart: number;\n /** Body text, frontmatter stripped. */\n body: string;\n}\n\n/**\n * Split a note into frontmatter + body without round-tripping through any\n * YAML stringifier. We need to know the *bytes* of the FM block so writers\n * can be byte-faithful — that's the write-safety contract.\n *\n * Obsidian's rule: a YAML block is present iff the file starts with `---`\n * on its own line (no leading whitespace, no BOM stripped silently — though\n * we tolerate a leading BOM since Windows users exist).\n */\nexport function parseFrontmatter(text: string): FrontmatterResult {\n let offset = 0;\n if (text.charCodeAt(0) === 0xfeff) offset = 1; // BOM\n\n // Must start with `---\\n` or `---\\r\\n`\n const head = text.slice(offset, offset + 4);\n if (head !== '---\\n' && text.slice(offset, offset + 5) !== '---\\r\\n') {\n return {\n present: false,\n data: null,\n keys: [],\n malformed: false,\n bodyStart: 0,\n body: text,\n };\n }\n const fmStart = offset + (head === '---\\n' ? 4 : 5);\n\n // Find closing `\\n---\\n` (or CRLF variant) — Obsidian requires `---` on its own line.\n // We search line-by-line from fmStart.\n const closeLF = text.indexOf('\\n---\\n', fmStart - 1);\n const closeCRLF = text.indexOf('\\r\\n---\\r\\n', fmStart - 1);\n let closeIdx = -1;\n let closeLen = 0;\n if (closeLF !== -1 && (closeCRLF === -1 || closeLF < closeCRLF)) {\n closeIdx = closeLF;\n closeLen = 5;\n } else if (closeCRLF !== -1) {\n closeIdx = closeCRLF;\n closeLen = 7;\n }\n if (closeIdx === -1) {\n // Unterminated frontmatter — count as malformed, treat whole file as body.\n return {\n present: true,\n data: null,\n keys: [],\n malformed: true,\n bodyStart: 0,\n body: text,\n };\n }\n\n const yamlText = text.slice(fmStart, closeIdx + 1); // include trailing \\n before ---\n const bodyStart = closeIdx + closeLen;\n const body = text.slice(bodyStart);\n\n let data: Record<string, unknown> | null = null;\n let malformed = false;\n try {\n const parsed = parseYaml(yamlText);\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n data = parsed as Record<string, unknown>;\n } else {\n // YAML parsed but isn't a mapping — Obsidian also treats this as malformed-for-our-purposes.\n malformed = parsed !== null && parsed !== undefined;\n }\n } catch {\n malformed = true;\n }\n\n return {\n present: true,\n data,\n keys: data ? Object.keys(data) : [],\n malformed,\n bodyStart,\n body,\n };\n}\n","import { rename, writeFile } from 'node:fs/promises';\n\n/**\n * Crash-safe file write: write to a same-directory temp file, then rename\n * over the destination. A crash mid-write leaves either the old content or\n * the new content on disk — never a torn file. rename-over-existing works on\n * the full CI OS matrix (proven by patch_note's original implementation).\n *\n * Not a lock: two seekstone processes writing the same note share this temp\n * suffix and can race each other — same exposure as any two writers on one\n * file. CAS (content-hash.ts) is the conflict-detection layer above this.\n */\nexport async function atomicWrite(absPath: string, content: string): Promise<void> {\n const tmpPath = `${absPath}.seekstone-tmp`;\n await writeFile(tmpPath, content, 'utf8');\n await rename(tmpPath, absPath);\n}\n","import { createHash } from 'node:crypto';\n\n/**\n * Content hashing for optional compare-and-swap edits.\n *\n * The hash is sha-256 (hex) of the exact raw string a tool read from disk —\n * disk bytes are the version identity. `read_note` and every mutating tool\n * return `contentHash`; edit tools accept `prevHash` and refuse to write when\n * the note changed since it was read. This is conflict DETECTION, not\n * locking: the window between check and rename is the same one the OS gives\n * any two writers. node:crypto is fully offline — the zero-network guarantee\n * is unaffected.\n */\nexport function contentHash(raw: string | Buffer): string {\n return createHash('sha256').update(raw).digest('hex');\n}\n\n/**\n * Throw the structured hash_conflict error when `prevHash` no longer matches\n * the freshly-read content. Call immediately after the disk read, before any\n * edit is computed.\n */\nexport function assertHashMatch(raw: string, prevHash: string, path: string): void {\n const actual = contentHash(raw);\n if (actual !== prevHash) {\n throw new Error(\n JSON.stringify({\n error: 'hash_conflict',\n path,\n expected: prevHash,\n actual,\n hint: 'Note changed on disk since it was read. Re-read it and retry with the new contentHash.',\n }),\n );\n }\n}\n","import picomatch from 'picomatch';\n\n/**\n * Write policy — read-only mode and write-path scoping, parsed once at boot\n * and enforced at the dispatch layer (tool visibility + rejection) and inside\n * every write handler at the point the final vault-relative path is known\n * (`assertWritable`). Two env vars:\n *\n * SEEKSTONE_READ_ONLY=1 — write tools are unregistered from\n * tools/list AND rejected at dispatch.\n * SEEKSTONE_WRITE_PATHS=<glob,>— comma-separated vault-relative globs;\n * writes are permitted only under matching\n * paths (e.g. \"journal/**,inbox/*.md\").\n *\n * Read-only wins over write paths. Unset write paths = everything writable.\n */\nexport interface WritePolicy {\n readOnly: boolean;\n /** Vault-relative glob allowlist; undefined = all paths writable. */\n writeGlobs?: string[];\n}\n\n/** Default policy for contexts built without env config (tests, benches). */\nexport const PERMISSIVE_POLICY: WritePolicy = { readOnly: false };\n\nexport function parseWritePolicy(env: Record<string, string | undefined>): WritePolicy {\n const ro = (env.SEEKSTONE_READ_ONLY ?? '').toLowerCase();\n const readOnly = ro === '1' || ro === 'true';\n const globs = (env.SEEKSTONE_WRITE_PATHS ?? '')\n .split(',')\n .map((g) => g.trim())\n .filter((g) => g.length > 0);\n return globs.length > 0 ? { readOnly, writeGlobs: globs } : { readOnly };\n}\n\n/** Normalize a vault-relative path for glob matching (Windows separators, ./). */\nfunction normalizeRel(relPath: string): string {\n let p = relPath.replaceAll('\\\\', '/');\n while (p.startsWith('./')) p = p.slice(2);\n return p;\n}\n\nexport function isWritable(policy: WritePolicy, relPath: string): boolean {\n if (policy.readOnly) return false;\n if (!policy.writeGlobs) return true;\n // dot:true so notes under dot-folders still match `**` patterns.\n return picomatch(policy.writeGlobs, { dot: true })(normalizeRel(relPath));\n}\n\n/**\n * Throw with an actionable message when the policy forbids writing `relPath`.\n * Call from write handlers with the final vault-relative path — after any\n * internal path computation (e.g. periodic-note date resolution).\n */\nexport function assertWritable(policy: WritePolicy, relPath: string): void {\n if (policy.readOnly) {\n throw new Error('Server is read-only (SEEKSTONE_READ_ONLY=1); write tools are disabled.');\n }\n if (!isWritable(policy, relPath)) {\n throw new Error(\n `Write not permitted: \"${relPath}\" is outside SEEKSTONE_WRITE_PATHS (${policy.writeGlobs?.join(', ')}).`,\n );\n }\n}\n","import { resolve, sep } from 'node:path';\n\n/**\n * Resolve a vault-relative path to an absolute path, guaranteeing the result\n * stays inside the vault. This is the single containment guard for every tool\n * handler — the previous inline `join(...).startsWith(vaultRoot)` checks had\n * no separator boundary, so with a vault at `/home/u/vault` the input\n * `../vault-backup/x.md` resolved to `/home/u/vault-backup/x.md` and passed.\n *\n * `resolve` (not `join`) also normalizes an absolute `input` instead of\n * concatenating it, so absolute-path inputs are contained too.\n *\n * @throws Error `Path outside vault: <input>` when the resolved path escapes.\n */\nexport function resolveVaultPath(vaultRoot: string, inputPath: string): string {\n const abs = resolve(vaultRoot, inputPath);\n if (abs !== vaultRoot && !abs.startsWith(vaultRoot + sep)) {\n throw new Error(`Path outside vault: ${inputPath}`);\n }\n return abs;\n}\n","/**\n * Link / URL / tag extractors. Most run on note *bodies* (post-frontmatter)\n * so frontmatter values don't double-count as inline content;\n * `extractLinksWithLines` takes the full raw note and handles the\n * frontmatter block YAML-aware.\n *\n * Heuristics here intentionally lean toward \"what Obsidian sees\" rather than\n * full CommonMark parsing — we're profiling shape, not rendering. The regexes\n * are deliberately tolerant of code-fence content; counts are reported with\n * that caveat in the README.\n */\nimport { parseDocument, visit } from 'yaml';\nimport { parseFrontmatter } from './frontmatter.js';\n\nexport interface Wikilink {\n /** Raw target before `#` and `|`. May include path segments. */\n target: string;\n /** Optional heading/block reference (`#heading` or `#^block`). */\n fragment: string | null;\n /** Optional display alias. */\n alias: string | null;\n}\n\n// Quantifiers are bounded at 512 chars — well above any real Obsidian note name\n// or heading (~255 byte OS limit) — to eliminate O(n²) backtracking on\n// pathological inputs where many [[... sequences appear with no closing ]].\nconst WIKILINK_RE = /\\[\\[([^\\]|#\\n]{1,512})(#[^\\]|\\n]{1,512})?(\\|[^\\]\\n]{1,512})?\\]\\]/g;\n\n// Matches both embeds (![[...]]) and plain wikilinks ([[...]]).\nconst LINK_WITH_LINES_RE =\n /(!?\\[\\[)([^\\]|#\\n]{1,512})(#[^\\]|\\n]{1,512})?(\\|[^\\]\\n]{1,512})?(\\]\\])/g;\n\nexport type LinkType = 'wikilink' | 'embed';\n\nexport interface LinkRecord {\n /**\n * Full raw match, e.g. \"[[Note Name|alias]]\" or \"![[embed.png]]\". For links\n * found inside folded frontmatter scalars this is the *parsed* (unfolded)\n * text, which may differ from the source bytes.\n */\n raw: string;\n /** Raw target before `#` and `|`. */\n target: string;\n fragment: string | null;\n alias: string | null;\n linkType: LinkType;\n /**\n * 1-indexed line number in the source file. Links inside multi-line\n * frontmatter scalars are attributed to the line where the scalar begins.\n */\n line: number;\n}\n\n/**\n * Extract all wikilinks and embeds from a raw note (including frontmatter),\n * annotating each with its 1-indexed line number.\n *\n * The frontmatter block is parsed as YAML and its string values walked, so\n * links split across lines by scalar folding (yaml's default 80-col wrap —\n * hand edits, other tools, or pre-0.12.1 seekstone) still extract: folding\n * turns the newline back into a space on parse. Malformed frontmatter falls\n * back to per-line regex over the block, matching the body treatment.\n */\nexport function extractLinksWithLines(raw: string): LinkRecord[] {\n const fm = parseFrontmatter(raw);\n // bodyStart 0 covers both \"no frontmatter\" and \"unterminated frontmatter\"\n // (where the whole file is treated as body).\n if (!fm.present || fm.bodyStart === 0) return linksFromLines(raw, 1);\n\n const fmRegion = raw.slice(0, fm.bodyStart);\n const out = frontmatterLinks(fmRegion, fm.malformed);\n const bodyFirstLine = countNewlines(fmRegion) + 1;\n linksFromLinesInto(out, fm.body, bodyFirstLine);\n return out;\n}\n\nfunction linksFromLines(text: string, firstLine: number): LinkRecord[] {\n const out: LinkRecord[] = [];\n linksFromLinesInto(out, text, firstLine);\n return out;\n}\n\nfunction linksFromLinesInto(out: LinkRecord[], text: string, firstLine: number): void {\n const lines = text.split('\\n');\n for (const [lineIdx, lineText] of lines.entries()) {\n matchLinksInto(out, lineText, firstLine + lineIdx);\n }\n}\n\nfunction matchLinksInto(out: LinkRecord[], text: string, line: number): void {\n for (const m of text.matchAll(LINK_WITH_LINES_RE)) {\n const prefix = m[1] ?? '';\n out.push({\n raw: m[0] ?? '',\n target: (m[2] ?? '').trim(),\n fragment: m[3] ? m[3].slice(1).trim() : null,\n alias: m[4] ? m[4].slice(1).trim() : null,\n linkType: prefix.startsWith('!') ? 'embed' : 'wikilink',\n line,\n });\n }\n}\n\n/**\n * Walk the frontmatter block's string values via the YAML AST. `fmRegion` is\n * the full block including both `---` delimiter lines. Keys are skipped —\n * Obsidian doesn't treat property names as links.\n */\nfunction frontmatterLinks(fmRegion: string, malformed: boolean): LinkRecord[] {\n if (malformed) return linksFromLines(fmRegion, 1);\n\n // Strip the delimiters: yaml content starts after the opening `---` line and\n // ends before the closing `---` line (parseDocument would read a bare `---`\n // as a document marker).\n const yamlStart = fmRegion.indexOf('\\n') + 1;\n const closeLen = fmRegion.endsWith('---\\r\\n') ? 5 : 4;\n const yamlText = fmRegion.slice(yamlStart, fmRegion.length - closeLen);\n\n const doc = parseDocument(yamlText);\n if (doc.errors.length > 0) return linksFromLines(fmRegion, 1);\n\n const out: LinkRecord[] = [];\n visit(doc, {\n Scalar(key, node) {\n if (key === 'key') return;\n if (typeof node.value !== 'string' || node.range == null) return;\n // Opening `---` is line 1, so yamlText's first line is file line 2.\n const line = 2 + countNewlines(yamlText.slice(0, node.range[0]));\n matchLinksInto(out, node.value, line);\n },\n });\n return out;\n}\n\nfunction countNewlines(text: string): number {\n let n = 0;\n for (let i = text.indexOf('\\n'); i !== -1; i = text.indexOf('\\n', i + 1)) n++;\n return n;\n}\nconst URL_RE = /https?:\\/\\/[^\\s<>\"')]+/g;\n// Obsidian inline tag: must start with letter or underscore, then word chars, /, -.\n// Must not be immediately preceded by `]` (avoids matching `]#` from links) or be\n// inside a numeric-only token like `#123` (Obsidian rejects pure-digit tags).\nconst INLINE_TAG_RE = /(?:^|[^\\w\\]/])#([A-Za-z_][\\w/-]*)/g;\n\nexport function extractWikilinks(body: string): Wikilink[] {\n const out: Wikilink[] = [];\n for (const m of body.matchAll(WIKILINK_RE)) {\n out.push({\n target: (m[1] ?? '').trim(),\n fragment: m[2] ? m[2].slice(1).trim() : null,\n alias: m[3] ? m[3].slice(1).trim() : null,\n });\n }\n return out;\n}\n\nexport function extractUrls(body: string): string[] {\n const TRAILING_PUNCT = '.,;:!?)';\n const out: string[] = [];\n for (const m of body.matchAll(URL_RE)) {\n // Strip trailing punctuation with a backward walk — avoids the O(n²)\n // backtracking that /[.,;:!?)]+$/ exhibits on strings with many punct chars.\n const raw = m[0] ?? '';\n let end = raw.length;\n while (end > 0 && TRAILING_PUNCT.includes(raw.charAt(end - 1))) end--;\n out.push(raw.slice(0, end));\n }\n return out;\n}\n\nexport function extractInlineTags(body: string): string[] {\n const out: string[] = [];\n for (const m of body.matchAll(INLINE_TAG_RE)) {\n if (m[1]) out.push(m[1]);\n }\n return out;\n}\n\n/**\n * Pull tags out of a parsed frontmatter object. Obsidian accepts `tags:` or\n * `tag:`, as either a string (space- or comma-separated) or a list.\n */\nexport function frontmatterTags(fm: Record<string, unknown> | null): string[] {\n if (!fm) return [];\n const raw = fm.tags ?? fm.tag;\n if (raw == null) return [];\n if (Array.isArray(raw)) {\n return raw.filter((t): t is string => typeof t === 'string').map((t) => t.replace(/^#/, ''));\n }\n if (typeof raw === 'string') {\n return raw\n .split(/[\\s,]+/)\n .filter(Boolean)\n .map((t) => t.replace(/^#/, ''));\n }\n return [];\n}\n","import { extractLinksWithLines } from '@seekstone/core/extract';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.js';\nimport { extractExcerpt } from '../index/excerpt.js';\nimport { resolveLink } from '../index/resolve.js';\nimport { basenameNoExt } from './search.js';\n\nexport const ContextPackInput = z.object({\n query: z.string().min(1).describe('Natural-language question or topic to assemble context for.'),\n budgetBytes: z\n .number()\n .int()\n .min(256)\n .max(16384)\n .default(2048)\n .describe('Hard cap on the response JSON size in bytes. The pack never exceeds it.'),\n});\nexport type ContextPackInput = z.infer<typeof ContextPackInput>;\n\nconst MAX_CANDIDATES = 10;\nconst NEIGHBOR_HITS = 3;\nconst MAX_NEIGHBORS = 5;\nconst NEIGHBOR_SUMMARY_LEN = 100;\nconst MIN_EXCERPT_LEN = 60;\nconst FM_MAX_BYTES = 120;\nconst SOURCES_MAX = 8;\n\nexport interface ContextPackExcerpt {\n path: string;\n title?: string;\n score: number;\n excerpt: string;\n tags?: string[];\n /** Scalar frontmatter values, in key order, capped at FM_MAX_BYTES serialized. */\n fm?: Record<string, unknown>;\n}\n\nexport interface ContextPackNeighbor {\n path: string;\n rel: 'backlink' | 'outlink' | 'both';\n /** Line in the neighbor where it links to a hit (backlinks only). */\n line?: number;\n summary: string;\n}\n\nexport interface ContextPackSource {\n path: string;\n line?: number;\n}\n\nexport interface ContextPackResult {\n excerpts: ContextPackExcerpt[];\n neighborhood: ContextPackNeighbor[];\n /** Overflow refs only — matches and neighbors that did not fit the budget. */\n sources: ContextPackSource[];\n totalMatches: number;\n confidence: 'high' | 'low' | 'none';\n truncated?: true;\n}\n\n/** Serialized cost of one array entry, +1 for the separating comma (conservative). */\nfunction entryCost(entry: unknown): number {\n return Buffer.byteLength(JSON.stringify(entry), 'utf8') + 1;\n}\n\n/** Pick scalar frontmatter values in key order until the serialized subset exceeds the cap. */\nfunction pickFm(fm: Record<string, unknown> | null): Record<string, unknown> | undefined {\n if (!fm) return undefined;\n const out: Record<string, unknown> = {};\n let picked = 0;\n for (const key of Object.keys(fm)) {\n const v = fm[key];\n const t = typeof v;\n if (t !== 'string' && t !== 'number' && t !== 'boolean') continue;\n const candidate = { ...out, [key]: v };\n if (Buffer.byteLength(JSON.stringify(candidate), 'utf8') > FM_MAX_BYTES) break;\n out[key] = v;\n picked += 1;\n }\n return picked > 0 ? out : undefined;\n}\n\nfunction clamp(n: number, lo: number, hi: number): number {\n return Math.min(hi, Math.max(lo, n));\n}\n\nexport function contextPack(ctx: ServerContext, input: ContextPackInput): ContextPackResult {\n const results = ctx.index.search(input.query, {\n boost: { title: 3, tags: 2, body: 1 },\n fuzzy: 0.2,\n prefix: true,\n });\n const totalMatches = results.length;\n if (totalMatches === 0) {\n return { excerpts: [], neighborhood: [], sources: [], totalMatches: 0, confidence: 'none' };\n }\n\n const terms = input.query\n .toLowerCase()\n .split(/\\s+/)\n .filter((t) => t.length > 1);\n\n const scoreByPath = new Map<string, number>();\n for (const r of results) scoreByPath.set(r.id, r.score);\n\n // Meter the fixed envelope with worst-case flags so emitting them later can\n // never break the cap. Entries are metered against the same minified\n // serialization dispatch ships, so the budget is exact, not an estimate.\n const budget = input.budgetBytes;\n let used = Buffer.byteLength(\n JSON.stringify({\n excerpts: [],\n neighborhood: [],\n sources: [],\n totalMatches,\n confidence: 'high',\n truncated: true,\n }),\n 'utf8',\n );\n\n // Phase 1 — excerpts: greedy fill in rank order, holding back a soft reserve\n // for the neighborhood. On the first non-fit, shrink the excerpt (half, then\n // the floor); a shrunk entry ends the phase so the pack isn't all fragments.\n const excerptLen = clamp(Math.floor(budget / 10), 80, 400);\n const reserve = Math.min(Math.floor(budget / 4), 600);\n const candidates = results.slice(0, MAX_CANDIDATES);\n const excerpts: ContextPackExcerpt[] = [];\n const overflowHits: ContextPackSource[] = [];\n let droppedForBudget = false;\n\n const buildEntry = (r: (typeof results)[number], len: number): ContextPackExcerpt => {\n const note = ctx.notes.get(r.id);\n const body = note?.body ?? '';\n const noteTags = note?.tags ? note.tags.split(' ').filter(Boolean) : [];\n const entry: ContextPackExcerpt = {\n path: r.id,\n score: Math.round(r.score * 100) / 100,\n excerpt: extractExcerpt(body, terms, len),\n };\n const title = r.title as string;\n if (title && title !== basenameNoExt(r.id)) entry.title = title;\n if (noteTags.length > 0) entry.tags = noteTags;\n const fm = pickFm(note?.fm ?? null);\n if (fm) entry.fm = fm;\n return entry;\n };\n\n const excerptCap = budget - reserve;\n let phaseOpen = true;\n for (const r of candidates) {\n if (!phaseOpen) {\n overflowHits.push({ path: r.id });\n continue;\n }\n let placed = false;\n const shrinkLens = [\n ...new Set([\n excerptLen,\n Math.max(Math.floor(excerptLen / 2), MIN_EXCERPT_LEN),\n MIN_EXCERPT_LEN,\n ]),\n ];\n for (const len of shrinkLens) {\n const entry = buildEntry(r, len);\n const cost = entryCost(entry);\n if (used + cost <= excerptCap) {\n excerpts.push(entry);\n used += cost;\n placed = true;\n // A shrunk excerpt means the budget is nearly spent — stop before\n // packing many useless fragments.\n if (len !== excerptLen) phaseOpen = false;\n break;\n }\n }\n if (!placed) {\n phaseOpen = false;\n droppedForBudget = true;\n overflowHits.push({ path: r.id });\n }\n }\n\n // Phase 2 — neighborhood: backlinks + outlinks of the top included hits.\n // The reserve is released; entries fill whatever budget remains.\n const includedPaths = new Set(excerpts.map((e) => e.path));\n const seeds = excerpts.slice(0, NEIGHBOR_HITS).map((e) => e.path);\n const agg = new Map<\n string,\n { rels: Set<'backlink' | 'outlink'>; seedPaths: Set<string>; line?: number }\n >();\n const connect = (\n neighborPath: string,\n seed: string,\n rel: 'backlink' | 'outlink',\n line?: number,\n ) => {\n if (neighborPath === seed || includedPaths.has(neighborPath)) return; // excerpt wins dedup\n if (!ctx.notes.has(neighborPath)) return;\n let entry = agg.get(neighborPath);\n if (!entry) {\n entry = { rels: new Set(), seedPaths: new Set() };\n agg.set(neighborPath, entry);\n }\n entry.rels.add(rel);\n entry.seedPaths.add(seed);\n if (rel === 'backlink' && entry.line === undefined && line !== undefined) entry.line = line;\n };\n for (const seed of seeds) {\n for (const ref of ctx.backlinks.get(seed) ?? []) {\n connect(ref.path, seed, 'backlink', ref.line);\n }\n const note = ctx.notes.get(seed);\n if (!note) continue;\n for (const link of extractLinksWithLines(note.raw)) {\n const target = resolveLink(link.target, ctx.notes);\n if (target !== undefined) connect(target, seed, 'outlink');\n }\n }\n\n const ranked = [...agg.entries()].sort((a, b) => {\n const conn = b[1].seedPaths.size - a[1].seedPaths.size;\n if (conn !== 0) return conn;\n const score = (scoreByPath.get(b[0]) ?? 0) - (scoreByPath.get(a[0]) ?? 0);\n if (score !== 0) return score;\n return a[0] < b[0] ? -1 : 1;\n });\n\n const neighborhood: ContextPackNeighbor[] = [];\n const overflowNeighbors: ContextPackSource[] = [];\n for (const [path, info] of ranked) {\n if (neighborhood.length >= MAX_NEIGHBORS) {\n overflowNeighbors.push(info.line !== undefined ? { path, line: info.line } : { path });\n continue;\n }\n const note = ctx.notes.get(path);\n const entry: ContextPackNeighbor = {\n path,\n rel: info.rels.size === 2 ? 'both' : info.rels.has('backlink') ? 'backlink' : 'outlink',\n summary: extractExcerpt(note?.body ?? '', terms, NEIGHBOR_SUMMARY_LEN),\n };\n if (info.line !== undefined) entry.line = info.line;\n const cost = entryCost(entry);\n if (used + cost > budget) {\n droppedForBudget = true;\n overflowNeighbors.push(info.line !== undefined ? { path, line: info.line } : { path });\n // First non-fit ends the phase — later entries rank lower anyway.\n for (const [p, i] of ranked.slice(ranked.findIndex(([rp]) => rp === path) + 1)) {\n overflowNeighbors.push(i.line !== undefined ? { path: p, line: i.line } : { path: p });\n }\n break;\n }\n neighborhood.push(entry);\n used += cost;\n }\n\n // Phase 3 — sources: overflow refs (\"read these next\"), deduped, best-first.\n const sources: ContextPackSource[] = [];\n const sourcePaths = new Set<string>();\n for (const ref of [...overflowHits, ...overflowNeighbors]) {\n if (sources.length >= SOURCES_MAX) break;\n if (includedPaths.has(ref.path) || sourcePaths.has(ref.path)) continue;\n const cost = entryCost(ref);\n if (used + cost > budget) break;\n sources.push(ref);\n sourcePaths.add(ref.path);\n used += cost;\n }\n\n const hasTermInExcerpt = excerpts.some((e) =>\n terms.some((t) => e.excerpt.toLowerCase().includes(t)),\n );\n const confidence: ContextPackResult['confidence'] =\n excerpts.length === 0 || (terms.length > 0 && !hasTermInExcerpt) ? 'low' : 'high';\n\n const result: ContextPackResult = { excerpts, neighborhood, sources, totalMatches, confidence };\n if (droppedForBudget) result.truncated = true;\n return result;\n}\n","/**\n * Extract a short excerpt centred on the first occurrence of any query term.\n * Returns at most `maxLen` characters with leading/trailing ellipsis as needed.\n *\n * This is the core context-tax reduction: instead of returning the full note\n * (~7 KB mean, ~70 KB p99), we return ~200 chars of relevant context.\n */\nexport function extractExcerpt(text: string, terms: string[], maxLen = 200): string {\n const lower = text.toLowerCase();\n let bestIdx = -1;\n\n for (const term of terms) {\n const idx = lower.indexOf(term.toLowerCase());\n if (idx !== -1 && (bestIdx === -1 || idx < bestIdx)) {\n bestIdx = idx;\n }\n }\n\n if (bestIdx === -1) {\n // No term found in body (hit may have been in title/tags) — return the start.\n return truncate(text.trimStart(), maxLen);\n }\n\n const half = Math.floor(maxLen / 2);\n const start = Math.max(0, bestIdx - half);\n const end = Math.min(text.length, bestIdx + half + (terms[0]?.length ?? 0));\n const slice = text.slice(start, end).trim();\n\n return `${start > 0 ? '…' : ''}${slice}${end < text.length ? '…' : ''}`;\n}\n\nfunction truncate(s: string, max: number): string {\n if (s.length <= max) return s;\n return `${s.slice(0, max).trimEnd()}…`;\n}\n","/**\n * Resolve a wikilink target string to a vault-relative note path.\n *\n * Uses Obsidian's loose resolution: exact match → add .md → basename match\n * → path-without-extension match. Returns undefined if no note matches.\n */\nexport function resolveLink(target: string, notes: Map<string, unknown>): string | undefined {\n if (notes.has(target)) return target;\n const withMd = `${target}.md`;\n if (notes.has(withMd)) return withMd;\n\n const targetLower = target.toLowerCase();\n const targetBase = (target.split('/').pop() ?? target).toLowerCase();\n\n for (const path of notes.keys()) {\n const pathNoExt = path.replace(/\\.md$/i, '');\n if (pathNoExt.toLowerCase() === targetLower) return path;\n const pathBase = (pathNoExt.split('/').pop() ?? pathNoExt).toLowerCase();\n if (pathBase === targetBase) return path;\n }\n\n return undefined;\n}\n","import { z } from 'zod';\nimport type { ServerContext } from '../context.js';\nimport { extractExcerpt } from '../index/excerpt.js';\nimport type { SearchHit } from '../index/types.js';\n\nexport const SearchInput = z.object({\n query: z.string().min(1).describe('Search query. Supports fuzzy matching and prefix search.'),\n limit: z\n .number()\n .int()\n .min(1)\n .max(50)\n .default(10)\n .describe('Maximum number of results to return.'),\n folder: z\n .string()\n .optional()\n .describe('Restrict results to notes under this vault-relative folder prefix.'),\n tag: z.string().optional().describe('Restrict results to notes containing this tag.'),\n excerptLength: z\n .number()\n .int()\n .min(20)\n .max(2000)\n .optional()\n .describe(\n 'Max characters of match context per hit (default 120). Lower trims payload; higher gives more context.',\n ),\n});\nexport type SearchInput = z.infer<typeof SearchInput>;\n\n/** Basename without extension — the title is omitted from a hit when it matches this. */\nexport function basenameNoExt(path: string): string {\n const base = path.slice(path.lastIndexOf('/') + 1);\n const dot = base.lastIndexOf('.');\n return dot > 0 ? base.slice(0, dot) : base;\n}\n\nexport function search(ctx: ServerContext, input: SearchInput): SearchHit[] {\n const results = ctx.index.search(input.query, {\n boost: { title: 3, tags: 2, body: 1 },\n fuzzy: 0.2,\n prefix: true,\n });\n\n const terms = input.query\n .toLowerCase()\n .split(/\\s+/)\n .filter((t) => t.length > 1);\n\n const hits: SearchHit[] = [];\n for (const r of results) {\n if (input.folder && !r.id.startsWith(input.folder)) continue;\n if (input.tag) {\n const note = ctx.notes.get(r.id);\n if (!note) continue;\n const noteTags = note.tags.split(' ');\n if (!noteTags.some((t) => t === input.tag || t === input.tag?.replace(/^#/, ''))) continue;\n }\n\n const note = ctx.notes.get(r.id);\n const body = note?.body ?? '';\n const noteTags = note?.tags ? note.tags.split(' ').filter(Boolean) : [];\n\n const title = r.title as string;\n const hit: SearchHit = {\n path: r.id,\n // Round the raw MiniSearch score: 2 decimals preserve relevance gaps without 17-digit float tax.\n score: Math.round(r.score * 100) / 100,\n excerpt: extractExcerpt(body, terms, input.excerptLength ?? 120),\n };\n // Omit redundant/empty metadata to keep the payload lean (both are recoverable).\n if (title && title !== basenameNoExt(r.id)) hit.title = title;\n if (noteTags.length > 0) hit.tags = noteTags;\n hits.push(hit);\n\n if (hits.length >= input.limit) break;\n }\n\n return hits;\n}\n","import { access, mkdir, readFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { stringify as stringifyYaml } from 'yaml';\nimport { z } from 'zod';\nimport { atomicWrite } from '../atomic-write.js';\nimport { assertHashMatch, contentHash } from '../content-hash.js';\nimport type { ServerContext } from '../context.js';\nimport { buildDoc, upsertDoc } from '../index/doc.js';\nimport { assertWritable } from '../policy.js';\nimport { resolveVaultPath } from '../vault-path.js';\n\nexport const CreateNoteInput = z\n .object({\n path: z\n .string()\n .describe(\n 'Vault-relative path for the new note, e.g. \"Daily Notes/2026-06-01.md\". Parent directories are created automatically.',\n ),\n content: z.string().default('').describe('Body content for the note.'),\n frontmatter: z\n .record(z.string(), z.unknown())\n .optional()\n .describe('Frontmatter key-value pairs to write as a YAML block at the top of the note.'),\n overwrite: z\n .boolean()\n .default(false)\n .describe(\n 'Overwrite an existing note. Defaults to false — throws if the note already exists.',\n ),\n prevHash: z\n .string()\n .optional()\n .describe(\n 'Optional compare-and-swap guard for overwrite: true — the contentHash of the note being replaced. Fails with hash_conflict if it changed since it was read.',\n ),\n })\n .refine((v) => v.prevHash === undefined || v.overwrite === true, {\n message:\n 'prevHash only makes sense with overwrite: true (a fresh create has no prior version).',\n });\nexport type CreateNoteInput = z.input<typeof CreateNoteInput>;\n\nexport interface CreateNoteResult {\n path: string;\n bytesWritten: number;\n /** sha-256 (hex) of the written content — usable as prevHash for a chained edit. */\n contentHash: string;\n}\n\nexport async function createNote(\n ctx: ServerContext,\n rawInput: CreateNoteInput,\n): Promise<CreateNoteResult> {\n const input = CreateNoteInput.parse(rawInput);\n const absPath = resolveVaultPath(ctx.vaultRoot, input.path);\n assertWritable(ctx.policy, input.path);\n\n if (!input.overwrite) {\n try {\n await access(absPath);\n throw new Error(`Note already exists: ${input.path}. Pass overwrite: true to replace it.`);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n } else if (input.prevHash !== undefined) {\n // CAS on the note being replaced; a missing file is a conflict too (the\n // version the caller read no longer exists).\n let existing: string;\n try {\n existing = await readFile(absPath, 'utf8');\n } catch {\n throw new Error(\n JSON.stringify({\n error: 'hash_conflict',\n path: input.path,\n expected: input.prevHash,\n actual: null,\n hint: 'The note no longer exists on disk. Re-check before overwriting.',\n }),\n );\n }\n assertHashMatch(existing, input.prevHash, input.path);\n }\n\n let raw = '';\n if (input.frontmatter && Object.keys(input.frontmatter).length > 0) {\n raw = `---\\n${stringifyYaml(input.frontmatter, { lineWidth: 0 })}---\\n`;\n }\n raw += input.content ?? '';\n\n await mkdir(dirname(absPath), { recursive: true });\n await atomicWrite(absPath, raw);\n\n // Sync in-memory index so the new note is immediately searchable.\n upsertDoc(ctx, buildDoc(input.path, raw));\n\n return {\n path: input.path,\n bytesWritten: Buffer.byteLength(raw, 'utf8'),\n contentHash: contentHash(raw),\n };\n}\n","import { basename, extname } from 'node:path';\nimport { extractInlineTags, frontmatterTags } from '@seekstone/core/extract';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport type { IndexedNote } from './types.js';\n\n/** Build an IndexedNote from a vault-relative path and raw file content. */\nexport function buildDoc(relPath: string, raw: string): IndexedNote {\n const fm = parseFrontmatter(raw);\n const allTags = [...new Set([...extractInlineTags(fm.body), ...frontmatterTags(fm.data)])];\n const title =\n typeof fm.data?.title === 'string' && fm.data.title\n ? fm.data.title\n : basename(relPath, extname(relPath));\n return {\n id: relPath,\n title,\n body: fm.body,\n tags: allTags.join(' '),\n fmKeys: fm.keys.join(' '),\n fm: fm.data,\n raw,\n sizeBytes: Buffer.byteLength(raw, 'utf8'),\n mtimeMs: Date.now(),\n };\n}\n\n/** Upsert a document into the context index. Discards any stale entry first. */\nexport function upsertDoc(\n ctx: {\n index: { discard(id: string): void; add(doc: IndexedNote): void };\n notes: Map<string, IndexedNote>;\n },\n doc: IndexedNote,\n): void {\n if (ctx.notes.has(doc.id)) ctx.index.discard(doc.id);\n ctx.notes.set(doc.id, doc);\n ctx.index.add(doc);\n}\n","import { access, mkdir, rename, rm } from 'node:fs/promises';\nimport { basename, extname, join } from 'node:path';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.js';\nimport { removeNoteBacklinks } from '../index/backlinks.js';\nimport { assertWritable } from '../policy.js';\nimport { resolveVaultPath } from '../vault-path.js';\n\nexport const DeleteNoteInput = z.object({\n path: z.string().describe('Vault-relative path of the note to delete.'),\n permanent: z\n .boolean()\n .default(false)\n .describe(\n 'Permanently remove the note instead of moving it to the vault .trash/ folder. Defaults to false — deletes are recoverable.',\n ),\n});\nexport type DeleteNoteInput = z.input<typeof DeleteNoteInput>;\n\nexport interface DeleteNoteResult {\n path: string;\n /** Vault-relative path the note was moved to, when not permanent. */\n trashedTo?: string;\n permanent: boolean;\n}\n\n/** Find a free destination name inside .trash/, suffixing before the extension on collision. */\nasync function freeTrashPath(vaultRoot: string, noteBase: string): Promise<string> {\n const ext = extname(noteBase);\n const stem = noteBase.slice(0, noteBase.length - ext.length);\n let candidate = `.trash/${noteBase}`;\n for (;;) {\n try {\n await access(join(vaultRoot, candidate));\n } catch {\n return candidate;\n }\n // Epoch-ms suffix; loop guards the (test-speed) case of two collisions in one ms.\n candidate = `.trash/${stem}.${Date.now()}${ext}`;\n }\n}\n\nexport async function deleteNote(\n ctx: ServerContext,\n rawInput: DeleteNoteInput,\n): Promise<DeleteNoteResult> {\n const input = DeleteNoteInput.parse(rawInput);\n const absPath = resolveVaultPath(ctx.vaultRoot, input.path);\n // Policy applies to the note being deleted; the .trash/ destination is the\n // safety mechanism itself, not a user write.\n assertWritable(ctx.policy, input.path);\n\n let trashedTo: string | undefined;\n if (input.permanent) {\n // rm throws ENOENT if the file doesn't exist — let it propagate.\n await rm(absPath);\n } else {\n // access first so a missing note throws ENOENT like rm does, not a\n // half-made .trash/ dir plus a rename error.\n await access(absPath);\n await mkdir(join(ctx.vaultRoot, '.trash'), { recursive: true });\n trashedTo = await freeTrashPath(ctx.vaultRoot, basename(input.path));\n // Same-volume rename: atomic, byte-identical, and invisible to the index —\n // the walker and watcher both exclude .trash/.\n await rename(absPath, join(ctx.vaultRoot, trashedTo));\n }\n\n // Clear the note's own outgoing refs while its entry is still readable.\n removeNoteBacklinks(ctx, input.path);\n if (ctx.notes.has(input.path)) {\n ctx.index.discard(input.path);\n ctx.notes.delete(input.path);\n }\n\n const result: DeleteNoteResult = { path: input.path, permanent: input.permanent };\n if (trashedTo !== undefined) result.trashedTo = trashedTo;\n return result;\n}\n","import { extractLinksWithLines } from '@seekstone/core/extract';\nimport type { BacklinkRef, ServerContext } from '../context.js';\nimport { resolveLink } from './resolve.js';\n\n/**\n * Incremental backlink-index maintenance, shared by the watcher and the\n * link-aware move path. Remove BEFORE mutating the note's entry in ctx.notes\n * (it reads the old raw); add AFTER the entry is current.\n */\n\nexport function removeNoteBacklinks(ctx: ServerContext, relPath: string): void {\n const oldDoc = ctx.notes.get(relPath);\n if (oldDoc === undefined) return;\n for (const link of extractLinksWithLines(oldDoc.raw)) {\n const resolved = resolveLink(link.target, ctx.notes);\n if (resolved === undefined) continue;\n const arr = ctx.backlinks.get(resolved);\n if (arr === undefined) continue;\n const filtered = arr.filter((r) => r.path !== relPath);\n ctx.backlinks.set(resolved, filtered);\n }\n}\n\nexport function addNoteBacklinks(ctx: ServerContext, relPath: string, raw: string): void {\n const seen = new Set<string>();\n for (const link of extractLinksWithLines(raw)) {\n const resolved = resolveLink(link.target, ctx.notes);\n if (resolved === undefined) continue;\n const dedupeKey = `${relPath}\\0${resolved}`;\n if (seen.has(dedupeKey)) continue;\n seen.add(dedupeKey);\n let arr = ctx.backlinks.get(resolved);\n if (arr === undefined) {\n arr = [];\n ctx.backlinks.set(resolved, arr);\n }\n const ref: BacklinkRef = { path: relPath, line: link.line, linkType: link.linkType };\n arr.push(ref);\n arr.sort((a, b) => a.path.localeCompare(b.path));\n }\n}\n","import { z } from 'zod';\nimport type { ServerContext } from '../context.js';\nimport { resolveVaultPath } from '../vault-path.js';\n\nexport const GetBacklinksInput = z.object({\n path: z.string().describe('Vault-relative path to the target note.'),\n includeContext: z\n .boolean()\n .optional()\n .describe('Include a short excerpt (~200 chars) from the linking line. Default true.'),\n limit: z\n .number()\n .int()\n .min(1)\n .max(500)\n .optional()\n .describe('Max backlinks to return. Default 50.'),\n});\nexport type GetBacklinksInput = z.infer<typeof GetBacklinksInput>;\n\nexport interface BacklinkEntry {\n path: string;\n line: number;\n excerpt?: string;\n linkType: 'wikilink' | 'embed';\n}\n\nexport interface GetBacklinksResult {\n target: string;\n backlinks: BacklinkEntry[];\n total: number;\n}\n\nconst EXCERPT_MAX = 200;\n\nexport function getBacklinks(ctx: ServerContext, input: GetBacklinksInput): GetBacklinksResult {\n resolveVaultPath(ctx.vaultRoot, input.path); // containment validation only\n\n const includeContext = input.includeContext ?? true;\n const limit = input.limit ?? 50;\n\n const refs = ctx.backlinks.get(input.path) ?? [];\n const total = refs.length;\n const limited = refs.slice(0, limit);\n\n const backlinks: BacklinkEntry[] = [];\n for (const ref of limited) {\n const entry: BacklinkEntry = { path: ref.path, line: ref.line, linkType: ref.linkType };\n if (includeContext) {\n const srcNote = ctx.notes.get(ref.path);\n if (srcNote !== undefined) {\n const rawLines = srcNote.raw.split('\\n');\n const lineText = rawLines[ref.line - 1];\n if (lineText !== undefined) {\n entry.excerpt =\n lineText.length > EXCERPT_MAX ? `${lineText.slice(0, EXCERPT_MAX)}…` : lineText;\n }\n }\n }\n backlinks.push(entry);\n }\n\n return { target: input.path, backlinks, total };\n}\n","import type { LinkType } from '@seekstone/core/extract';\nimport { extractLinksWithLines } from '@seekstone/core/extract';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.js';\nimport { resolveLink } from '../index/resolve.js';\nimport { resolveVaultPath } from '../vault-path.js';\n\nexport const GetLinksInput = z.object({\n path: z.string().describe('Vault-relative path to the note.'),\n});\nexport type GetLinksInput = z.infer<typeof GetLinksInput>;\n\nexport interface GetLinksEntry {\n raw: string;\n /** Resolved vault-relative path. Absent when unresolved. */\n target?: string;\n resolved: boolean;\n linkType: LinkType;\n line: number;\n}\n\nexport interface GetLinksResult {\n path: string;\n links: GetLinksEntry[];\n}\n\nexport function getLinks(ctx: ServerContext, input: GetLinksInput): GetLinksResult {\n resolveVaultPath(ctx.vaultRoot, input.path); // containment validation only\n const note = ctx.notes.get(input.path);\n if (note === undefined) throw new Error(`Note not found: ${input.path}`);\n\n const rawLinks = extractLinksWithLines(note.raw);\n\n // De-duplicate: one entry per (target string) — keep first occurrence by line\n const seen = new Set<string>();\n const links: GetLinksEntry[] = [];\n for (const link of rawLinks) {\n if (seen.has(link.target)) continue;\n seen.add(link.target);\n const resolvedPath = resolveLink(link.target, ctx.notes);\n const entry: GetLinksEntry = {\n raw: link.raw,\n resolved: resolvedPath !== undefined,\n linkType: link.linkType,\n line: link.line,\n };\n if (resolvedPath !== undefined) entry.target = resolvedPath;\n links.push(entry);\n }\n\n links.sort((a, b) => a.line - b.line);\n\n return { path: input.path, links };\n}\n","import { z } from 'zod';\nimport type { ServerContext } from '../context.js';\n\nexport const ListNotesInput = z.object({\n folder: z\n .string()\n .optional()\n .describe(\n 'Vault-relative folder prefix to list. Omit for all notes. Example: \"Daily Notes/2026\".',\n ),\n tag: z.string().optional().describe('Filter by tag. Include or omit the leading #.'),\n limit: z.number().int().min(1).max(500).default(100).describe('Maximum number of results.'),\n});\nexport type ListNotesInput = z.infer<typeof ListNotesInput>;\n\nexport interface NoteEntry {\n path: string;\n title: string;\n tags: string[];\n sizeBytes: number;\n}\n\nexport function listNotes(ctx: ServerContext, input: ListNotesInput): NoteEntry[] {\n const tag = input.tag?.replace(/^#/, '');\n const results: NoteEntry[] = [];\n\n for (const [path, note] of ctx.notes) {\n if (input.folder && !path.startsWith(input.folder)) continue;\n if (tag) {\n const noteTags = note.tags.split(' ').filter(Boolean);\n if (!noteTags.includes(tag)) continue;\n }\n results.push({\n path,\n title: note.title,\n tags: note.tags.split(' ').filter(Boolean),\n sizeBytes: note.sizeBytes,\n });\n if (results.length >= input.limit) break;\n }\n\n return results.sort((a, b) => a.path.localeCompare(b.path));\n}\n","import { z } from 'zod';\nimport type { ServerContext } from '../context.js';\n\nexport const ListTagsInput = z.object({\n pattern: z\n .string()\n .optional()\n .describe('Substring filter on tag name. Example: \"work\" matches \"work\" and \"area/work\".'),\n minCount: z\n .number()\n .int()\n .min(1)\n .optional()\n .describe('Only return tags used in at least this many notes.'),\n sort: z\n .enum(['count', 'name'])\n .default('count')\n .describe('Sort by usage count descending (default) or alphabetically.'),\n});\nexport type ListTagsInput = z.infer<typeof ListTagsInput>;\n\nexport interface TagEntry {\n tag: string;\n count: number;\n parent?: string;\n}\n\nexport interface ListTagsResult {\n tags: TagEntry[];\n total: number;\n}\n\nexport function listTags(ctx: ServerContext, input: ListTagsInput): ListTagsResult {\n const counts = new Map<string, number>();\n\n for (const note of ctx.notes.values()) {\n for (const tag of note.tags.split(' ').filter(Boolean)) {\n counts.set(tag, (counts.get(tag) ?? 0) + 1);\n }\n }\n\n const tags: TagEntry[] = [];\n for (const [tag, count] of counts) {\n if (input.pattern !== undefined && !tag.includes(input.pattern)) continue;\n if (input.minCount !== undefined && count < input.minCount) continue;\n const slashIdx = tag.lastIndexOf('/');\n const entry: TagEntry = { tag, count };\n if (slashIdx !== -1) entry.parent = tag.slice(0, slashIdx);\n tags.push(entry);\n }\n\n if (input.sort === 'name') {\n tags.sort((a, b) => a.tag.localeCompare(b.tag));\n } else {\n tags.sort((a, b) => b.count - a.count || a.tag.localeCompare(b.tag));\n }\n\n return { tags, total: tags.length };\n}\n","import { access, mkdir, rename } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { z } from 'zod';\nimport { atomicWrite } from '../atomic-write.js';\nimport type { ServerContext } from '../context.js';\nimport { addNoteBacklinks, removeNoteBacklinks } from '../index/backlinks.js';\nimport { buildDoc, upsertDoc } from '../index/doc.js';\nimport { assertWritable, isWritable } from '../policy.js';\nimport { resolveVaultPath } from '../vault-path.js';\nimport { hasMarkdownLinkTo, rewriteNoteLinks } from './rewrite_links.js';\n\nexport const MoveNoteInput = z.object({\n from: z.string().describe('Vault-relative path of the note to move.'),\n to: z.string().describe('Vault-relative destination path.'),\n overwrite: z\n .boolean()\n .default(false)\n .describe('Overwrite the destination if it exists. Defaults to false.'),\n rewriteLinks: z\n .boolean()\n .default(true)\n .describe(\n 'Rewrite wikilinks and markdown links in other notes that point at the moved note, so nothing breaks. Defaults to true; pass false to move the file only.',\n ),\n});\nexport type MoveNoteInput = z.input<typeof MoveNoteInput>;\n\nexport interface MoveNoteResult {\n from: string;\n to: string;\n /** Referencing notes whose content was rewritten. */\n notesRewritten: number;\n /** Individual links rewritten across those notes. */\n linksRewritten: number;\n /** Referencing notes left untouched because SEEKSTONE_WRITE_PATHS excludes them. */\n skipped?: string[];\n}\n\nexport async function moveNote(\n ctx: ServerContext,\n rawInput: MoveNoteInput,\n): Promise<MoveNoteResult> {\n // Parse here (not only in dispatch) so defaults like rewriteLinks apply to\n // direct callers too — same pattern as periodic_note.\n const input = MoveNoteInput.parse(rawInput);\n const absFrom = resolveVaultPath(ctx.vaultRoot, input.from);\n const absTo = resolveVaultPath(ctx.vaultRoot, input.to);\n assertWritable(ctx.policy, input.from);\n assertWritable(ctx.policy, input.to);\n\n try {\n await access(absFrom);\n } catch {\n throw new Error(`Note not found: ${input.from}`);\n }\n\n if (!input.overwrite) {\n try {\n await access(absTo);\n throw new Error(\n `Destination already exists: ${input.to}. Pass overwrite: true to replace it.`,\n );\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n }\n\n // Snapshot pre-move state: link resolution before/after the move differs,\n // and the rewriter needs both views to rewrite only what breaks.\n const preNotes = new Map(ctx.notes);\n\n // Candidate referencing notes: wikilink sources from the backlink index,\n // plus a scan for markdown links (which the backlink index doesn't cover).\n // The moved note's own outgoing links are untouched by design — wikilinks\n // are path-independent, and rewriting its relative markdown links is a\n // separate concern from \"don't orphan the note in the graph\".\n const candidates = new Set<string>();\n if (input.rewriteLinks) {\n for (const ref of ctx.backlinks.get(input.from) ?? []) candidates.add(ref.path);\n for (const [path, doc] of preNotes) {\n if (path === input.from || candidates.has(path)) continue;\n if (hasMarkdownLinkTo(doc.raw, path, input.from)) candidates.add(path);\n }\n candidates.delete(input.from);\n }\n\n await mkdir(dirname(absTo), { recursive: true });\n await rename(absFrom, absTo);\n\n // Grab raw before removing the old entry, and clear the moved note's own\n // outgoing backlink refs while its old entry is still readable — otherwise\n // they stay registered under the stale source path forever.\n const raw = ctx.notes.get(input.from)?.raw ?? '';\n removeNoteBacklinks(ctx, input.from);\n\n if (ctx.notes.has(input.from)) {\n ctx.index.discard(input.from);\n ctx.notes.delete(input.from);\n }\n upsertDoc(ctx, buildDoc(input.to, raw));\n addNoteBacklinks(ctx, input.to, raw);\n\n let notesRewritten = 0;\n let linksRewritten = 0;\n const skipped: string[] = [];\n\n for (const path of [...candidates].sort()) {\n const doc = ctx.notes.get(path);\n if (doc === undefined) continue;\n if (!isWritable(ctx.policy, path)) {\n // Out of write scope: leave the note untouched but report it, so the\n // caller knows which references were NOT updated.\n skipped.push(path);\n continue;\n }\n const { content, count } = rewriteNoteLinks(\n doc.raw,\n path,\n input.from,\n input.to,\n preNotes,\n ctx.notes,\n );\n // Re-register this note's backlinks against post-move resolution even\n // when nothing was rewritten (basename links now resolve to the new\n // path); remove reads the old raw, so call it before updating the entry.\n removeNoteBacklinks(ctx, path);\n if (count > 0) {\n await atomicWrite(join(ctx.vaultRoot, path), content);\n upsertDoc(ctx, buildDoc(path, content));\n notesRewritten++;\n linksRewritten += count;\n addNoteBacklinks(ctx, path, content);\n } else {\n addNoteBacklinks(ctx, path, doc.raw);\n }\n }\n\n // Nothing resolves to the old path anymore — drop the stale key.\n ctx.backlinks.delete(input.from);\n\n const result: MoveNoteResult = { from: input.from, to: input.to, notesRewritten, linksRewritten };\n if (skipped.length > 0) result.skipped = skipped;\n return result;\n}\n","import { posix } from 'node:path';\nimport { resolveLink } from '../index/resolve.js';\n\n/**\n * Pure link-rewriting for link-aware moves: given a referencing note's raw\n * content, rewrite every wikilink, embed, and relative/vault-absolute markdown\n * link that pointed at `oldPath` so it points at `newPath`.\n *\n * Principles:\n * - **Rewrite only what breaks.** A link is left byte-identical when it still\n * resolves to the moved note after the move (Obsidian's shortest-unique-\n * basename semantics — e.g. `[[Italy]]` survives a folder move unchanged).\n * - **Preserve everything around the target**: embed `!`, `#fragment`,\n * `|alias`, `%20`-encoding and `<...>` wrapping in markdown links.\n * - **Skip fenced code blocks** (``` / ~~~). Inline backtick spans are NOT\n * skipped — consistent with the repo's deliberately loose link parsing.\n *\n * `preNotes` is the notes map as it was before the move (oldPath present),\n * `postNotes` after (newPath present) — both are needed to decide whether a\n * link used to point at the moved note and whether it still does.\n */\n\n// Mirrors LINK_WITH_LINES_RE in @seekstone/core/extract (same bounded\n// quantifiers), split into groups for reconstruction.\nconst WIKI_RE = /(!?)\\[\\[([^\\]|#\\n]{1,512})(#[^\\]|\\n]{1,512})?(\\|[^\\]\\n]{1,512})?\\]\\]/g;\n\n// Markdown links/embeds: target is everything to the first `)` (titles like\n// `[t](x \"title\")` are out of scope, matching the loose-parsing ethos).\nconst MD_RE = /(!?)\\[([^\\]\\n]*)\\]\\(([^)\\n]+)\\)/g;\n\nconst FENCE_RE = /^\\s*(```|~~~)/;\n\n/**\n * Map `fn` over every line outside fenced code blocks (``` / ~~~), leaving\n * fence delimiters and fenced content byte-identical. Shared by the move and\n * rename-heading rewriters.\n */\nexport function mapUnfencedLines(raw: string, fn: (line: string) => string): string {\n let inFence = false;\n return raw\n .split('\\n')\n .map((line) => {\n if (FENCE_RE.test(line)) {\n inFence = !inFence;\n return line;\n }\n return inFence ? line : fn(line);\n })\n .join('\\n');\n}\n\nexport interface RewriteNoteLinksResult {\n content: string;\n /** Number of individual links rewritten. */\n count: number;\n}\n\n/** Shortest target that still uniquely resolves to newPath: basename if unambiguous, else full path. */\nfunction wikiTargetFor(newPath: string, postNotes: Map<string, unknown>): string {\n const noExt = newPath.replace(/\\.md$/i, '');\n const base = noExt.split('/').pop() ?? noExt;\n // Count basename collisions directly — resolveLink picks the first match by\n // map iteration order, which is not a stable notion of \"unambiguous\".\n const baseLower = base.toLowerCase();\n let matches = 0;\n for (const p of postNotes.keys()) {\n const pBase = (p.replace(/\\.md$/i, '').split('/').pop() ?? '').toLowerCase();\n if (pBase === baseLower && ++matches > 1) return noExt;\n }\n return base;\n}\n\n/** Interpret a markdown-link target as a vault-relative note path (or undefined). */\nfunction mdTargetToVaultPath(rawTarget: string, sourceDir: string): string | undefined {\n let t = rawTarget.trim();\n if (t.startsWith('<') && t.endsWith('>')) t = t.slice(1, -1);\n if (/^[a-z][a-z0-9+.-]*:/i.test(t)) return undefined; // http:, mailto:, obsidian:, …\n if (t.startsWith('#') || t === '') return undefined; // intra-note anchor\n const hashIdx = t.indexOf('#');\n if (hashIdx !== -1) t = t.slice(0, hashIdx);\n try {\n t = decodeURIComponent(t);\n } catch {\n /* malformed escape — use as-is */\n }\n const joined = posix.normalize(posix.join(sourceDir, t));\n if (joined.startsWith('..')) return undefined; // escapes the vault\n return joined;\n}\n\nexport function rewriteNoteLinks(\n raw: string,\n sourcePath: string,\n oldPath: string,\n newPath: string,\n preNotes: Map<string, unknown>,\n postNotes: Map<string, unknown>,\n): RewriteNoteLinksResult {\n const sourceDir = posix.dirname(sourcePath);\n const oldNoExt = oldPath.replace(/\\.md$/i, '');\n let count = 0;\n\n const rewriteWikiLine = (line: string): string =>\n line.replace(WIKI_RE, (full, bang: string, target: string, frag?: string, alias?: string) => {\n const t = target.trim();\n // Still resolves to the moved note → Obsidian keeps working; don't touch.\n if (resolveLink(t, postNotes) === newPath) return full;\n // Only rewrite links that used to point at the moved note.\n if (resolveLink(t, preNotes) !== oldPath) return full;\n count++;\n const next = wikiTargetFor(newPath, postNotes);\n return `${bang}[[${next}${frag ?? ''}${alias ?? ''}]]`;\n });\n\n const rewriteMdLine = (line: string): string =>\n line.replace(MD_RE, (full, bang: string, text: string, rawTarget: string) => {\n // Relative-to-source interpretation first, then vault-absolute.\n const asRelative = mdTargetToVaultPath(rawTarget, sourceDir);\n const asAbsolute = mdTargetToVaultPath(rawTarget, '.');\n let vaultAbsoluteStyle: boolean;\n if (asRelative === oldPath || asRelative === oldNoExt) vaultAbsoluteStyle = false;\n else if (asAbsolute === oldPath || asAbsolute === oldNoExt) vaultAbsoluteStyle = true;\n else return full;\n count++;\n\n const trimmed = rawTarget.trim();\n const wrapped = trimmed.startsWith('<') && trimmed.endsWith('>');\n const inner = wrapped ? trimmed.slice(1, -1) : trimmed;\n const hashIdx = inner.indexOf('#');\n const fragment = hashIdx === -1 ? '' : inner.slice(hashIdx);\n const hadExt = /\\.md(#|$)/i.test(inner);\n\n let next = vaultAbsoluteStyle ? newPath : posix.relative(sourceDir, newPath);\n if (!hadExt) next = next.replace(/\\.md$/i, '');\n // Preserve the original encoding style for spaces; wrap in <> when the\n // new path has spaces and the original used neither style.\n if (inner.includes('%20')) next = next.replaceAll(' ', '%20');\n const needsWrap = wrapped || next.includes(' ');\n const target = needsWrap ? `<${next}${fragment}>` : `${next}${fragment}`;\n return `${bang}[${text}](${target})`;\n });\n\n const content = mapUnfencedLines(raw, (line) => rewriteMdLine(rewriteWikiLine(line)));\n\n return { content, count };\n}\n\n/**\n * Scan a note's raw content for markdown links resolving to `targetPath` —\n * the candidate-discovery pass for referencing notes the backlink index can't\n * see (it only indexes wikilinks).\n */\nexport function hasMarkdownLinkTo(raw: string, sourcePath: string, targetPath: string): boolean {\n if (!raw.includes('](')) return false;\n const sourceDir = posix.dirname(sourcePath);\n const noExt = targetPath.replace(/\\.md$/i, '');\n for (const m of raw.matchAll(MD_RE)) {\n const rawTarget = m[3] ?? '';\n const asRelative = mdTargetToVaultPath(rawTarget, sourceDir);\n const asAbsolute = mdTargetToVaultPath(rawTarget, '.');\n if (\n asRelative === targetPath ||\n asRelative === noExt ||\n asAbsolute === targetPath ||\n asAbsolute === noExt\n ) {\n return true;\n }\n }\n return false;\n}\n","import { readFile } from 'node:fs/promises';\nimport { buildOutline } from '@seekstone/core/outline';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.js';\nimport { resolveVaultPath } from '../vault-path.js';\n\nexport const OutlineNoteInput = z.object({\n path: z.string().describe('Vault-relative path to the note.'),\n includeBlocks: z\n .boolean()\n .default(true)\n .describe('Include block-reference anchors (^id). Default true.'),\n includeSizes: z\n .boolean()\n .default(false)\n .describe('Include per-section character length in each heading entry. Default false.'),\n});\nexport type OutlineNoteInput = z.infer<typeof OutlineNoteInput>;\n\nexport async function outlineNote(ctx: ServerContext, input: OutlineNoteInput) {\n const absPath = resolveVaultPath(ctx.vaultRoot, input.path);\n\n const raw = await readFile(absPath, 'utf8');\n return buildOutline(raw, {\n includeBlocks: input.includeBlocks,\n includeSizes: input.includeSizes,\n });\n}\n","import { parseFrontmatter } from './frontmatter.js';\n\nexport interface HeadingEntry {\n text: string;\n level: number;\n /** 1-based line number in the file. */\n line: number;\n /** Character offset of the leading `#` in the raw file string. */\n byteOffset: number;\n /** Characters from this heading to the next (or end of file). Only present when includeSizes is true. */\n byteLength?: number;\n}\n\nexport interface BlockEntry {\n id: string;\n /** 1-based line number in the file. */\n line: number;\n /** Character offset of the `^` in the raw file string. */\n byteOffset: number;\n}\n\nexport interface NoteOutline {\n headings: HeadingEntry[];\n blocks: BlockEntry[];\n /** Top-level frontmatter keys in original order (empty if no frontmatter). */\n frontmatterKeys: string[];\n /** True UTF-8 byte length of the file. */\n totalBytes: number;\n}\n\nexport interface OutlineOptions {\n /** Include block-reference anchors (^id). Default true. */\n includeBlocks?: boolean;\n /** Include per-section character length in each heading entry. Default false. */\n includeSizes?: boolean;\n}\n\nconst HEADING_RE = /^(#{1,6})\\s+(.+)/;\nconst BLOCK_REF_RE = /\\^([a-zA-Z0-9-]+)$/;\n\n/**\n * Parse a note's structure — heading tree, block anchors, frontmatter keys —\n * without touching the prose. Shared by outline_note and patch_note.\n *\n * Offsets are character positions in the raw string (identical to UTF-8 byte\n * offsets for ASCII content, which covers the vast majority of vault notes).\n */\nexport function buildOutline(raw: string, options: OutlineOptions = {}): NoteOutline {\n const { includeBlocks = true, includeSizes = false } = options;\n\n const fm = parseFrontmatter(raw);\n const totalBytes = Buffer.byteLength(raw, 'utf8');\n const headings: HeadingEntry[] = [];\n const blocks: BlockEntry[] = [];\n\n let charOffset = 0;\n\n for (const [i, line] of raw.split('\\n').entries()) {\n const lineCharOffset = charOffset;\n const lineNum = i + 1;\n\n if (charOffset >= fm.bodyStart) {\n const hm = HEADING_RE.exec(line);\n if (hm) {\n headings.push({\n text: (hm[2] ?? '').trim(),\n level: (hm[1] ?? '').length,\n line: lineNum,\n byteOffset: lineCharOffset,\n });\n }\n\n if (includeBlocks) {\n const trimmed = line.trimEnd();\n const bm = BLOCK_REF_RE.exec(trimmed);\n if (bm) {\n const caretPos = trimmed.lastIndexOf('^');\n blocks.push({\n id: bm[1] ?? '',\n line: lineNum,\n byteOffset: lineCharOffset + caretPos,\n });\n }\n }\n }\n\n charOffset += line.length + 1; // +1 for the '\\n' consumed by split\n }\n\n if (includeSizes) {\n for (const [i, heading] of headings.entries()) {\n const next = headings[i + 1];\n heading.byteLength = (next?.byteOffset ?? raw.length) - heading.byteOffset;\n }\n }\n\n return { headings, blocks, frontmatterKeys: fm.keys, totalBytes };\n}\n","import { readFile } from 'node:fs/promises';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport { parseDocument } from 'yaml';\nimport { z } from 'zod';\nimport { atomicWrite } from '../atomic-write.js';\nimport { assertHashMatch, contentHash } from '../content-hash.js';\nimport type { ServerContext } from '../context.js';\nimport { assertWritable } from '../policy.js';\nimport { resolveVaultPath } from '../vault-path.js';\n\nexport const PatchFrontmatterInput = z.object({\n path: z.string().describe('Vault-relative path to the note.'),\n patch: z\n .record(z.string(), z.unknown())\n .describe(\n 'Key-value pairs to set in the frontmatter. Existing keys not in the patch are unchanged. Pass null as a value to remove a key.',\n ),\n prevHash: z\n .string()\n .optional()\n .describe(\n 'Optional compare-and-swap guard: the contentHash from a prior read. Fails with hash_conflict if the note changed since.',\n ),\n});\nexport type PatchFrontmatterInput = z.infer<typeof PatchFrontmatterInput>;\n\nexport interface PatchFrontmatterResult {\n path: string;\n keysChanged: string[];\n keysAdded: string[];\n keysRemoved: string[];\n /** sha-256 (hex) of the new content — usable as prevHash for a chained edit. */\n contentHash: string;\n}\n\n/**\n * Edit frontmatter values without reordering existing keys or changing\n * quote style. Uses yaml.parseDocument which preserves the CST, so round-trips\n * are byte-faithful for keys not touched by the patch.\n *\n * Patch semantics:\n * - Key present in patch, value non-null → set/update\n * - Key present in patch, value null → delete\n * - Key absent from patch → untouched\n *\n * If the note has no frontmatter, a new `---` block is created at the top.\n */\nexport async function patchFrontmatter(\n ctx: ServerContext,\n input: PatchFrontmatterInput,\n): Promise<PatchFrontmatterResult> {\n const absPath = resolveVaultPath(ctx.vaultRoot, input.path);\n assertWritable(ctx.policy, input.path);\n\n const original = await readFile(absPath, 'utf8');\n if (input.prevHash !== undefined) assertHashMatch(original, input.prevHash, input.path);\n const fm = parseFrontmatter(original);\n\n const keysChanged: string[] = [];\n const keysAdded: string[] = [];\n const keysRemoved: string[] = [];\n\n let newContent: string;\n\n if (!fm.present) {\n // No existing FM — create a minimal block with just the patch keys.\n const doc = parseDocument('');\n for (const [k, v] of Object.entries(input.patch)) {\n if (v !== null) {\n doc.set(k, v);\n keysAdded.push(k);\n }\n }\n newContent = `---\\n${doc.toString({ lineWidth: 0 })}---\\n${original}`;\n } else {\n // Parse the existing FM block preserving source tokens.\n const opensWithCRLF = original.startsWith('---\\r\\n');\n const openLen = opensWithCRLF ? 5 : 4;\n const closeLen = opensWithCRLF ? 7 : 5;\n const yamlText = original.slice(openLen, fm.bodyStart - closeLen);\n\n const doc = parseDocument(yamlText, { keepSourceTokens: true });\n\n for (const [k, v] of Object.entries(input.patch)) {\n const existing = doc.has(k);\n if (v === null) {\n if (existing) {\n doc.delete(k);\n keysRemoved.push(k);\n }\n } else if (existing) {\n doc.set(k, v);\n keysChanged.push(k);\n } else {\n doc.set(k, v);\n keysAdded.push(k);\n }\n }\n\n const head = opensWithCRLF ? '---\\r\\n' : '---\\n';\n const tail = opensWithCRLF ? '---\\r\\n' : '---\\n';\n newContent = `${head}${doc.toString({ lineWidth: 0 })}${tail}${fm.body}`;\n }\n\n await atomicWrite(absPath, newContent);\n\n // Update in-memory cache.\n const cached = ctx.notes.get(input.path);\n if (cached) {\n const updated = parseFrontmatter(newContent);\n cached.raw = newContent;\n cached.fmKeys = updated.keys.join(' ');\n }\n\n return {\n path: input.path,\n keysChanged,\n keysAdded,\n keysRemoved,\n contentHash: contentHash(newContent),\n };\n}\n","import { readFile } from 'node:fs/promises';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport { buildOutline } from '@seekstone/core/outline';\nimport { z } from 'zod';\nimport { atomicWrite } from '../atomic-write.js';\nimport { assertHashMatch, contentHash } from '../content-hash.js';\nimport type { ServerContext } from '../context.js';\nimport { assertWritable } from '../policy.js';\nimport { resolveVaultPath } from '../vault-path.js';\n\nexport const PatchNoteInput = z.object({\n path: z.string().describe('Vault-relative path to the note.'),\n target: z\n .union([\n z.object({ heading: z.string().describe('Heading text (without # markers).') }),\n z.object({ block: z.string().describe('Block reference ID (without the ^ prefix).') }),\n ])\n .describe('Target section. Provide exactly one of heading or block.'),\n operation: z\n .enum(['append', 'prepend', 'replace'])\n .describe(\n 'append: insert after section content. prepend: insert after heading line. replace: swap section content.',\n ),\n content: z.string().min(1).describe('Content to insert or replace with.'),\n createIfMissing: z\n .boolean()\n .default(false)\n .describe(\n 'If the heading target is not found, append a new heading (level 2) + content. Only valid for heading targets.',\n ),\n prevHash: z\n .string()\n .optional()\n .describe(\n 'Optional compare-and-swap guard: the contentHash from a prior read. Fails with hash_conflict if the note changed since.',\n ),\n});\nexport type PatchNoteInput = z.infer<typeof PatchNoteInput>;\n\nexport interface PatchNoteResult {\n path: string;\n bytesWritten: number;\n targetResolvedAt: { line: number; byteOffset: number };\n frontmatterUnchanged: true;\n /** sha-256 (hex) of the new content — usable as prevHash for a chained edit. */\n contentHash: string;\n}\n\n/** Character offset of the start of the next line after `offset`. */\nfunction lineEnd(raw: string, offset: number): number {\n const nl = raw.indexOf('\\n', offset);\n return nl === -1 ? raw.length : nl + 1;\n}\n\n/** Character offset of the start of the line that contains `offset`. */\nfunction lineStart(raw: string, offset: number): number {\n if (offset === 0) return 0;\n const prev = raw.lastIndexOf('\\n', offset - 1);\n return prev === -1 ? 0 : prev + 1;\n}\n\nexport async function patchNote(\n ctx: ServerContext,\n input: PatchNoteInput,\n): Promise<PatchNoteResult> {\n const absPath = resolveVaultPath(ctx.vaultRoot, input.path);\n assertWritable(ctx.policy, input.path);\n\n const raw = await readFile(absPath, 'utf8');\n if (input.prevHash !== undefined) assertHashMatch(raw, input.prevHash, input.path);\n const fm = parseFrontmatter(raw);\n const originalFmRegion = raw.slice(0, fm.bodyStart);\n const outline = buildOutline(raw, { includeBlocks: true });\n\n let contentStart: number;\n let contentEnd: number;\n let resolvedAt: { line: number; byteOffset: number };\n\n if ('heading' in input.target) {\n const targetText = input.target.heading;\n const heading = outline.headings.find((h) => h.text.toLowerCase() === targetText.toLowerCase());\n\n if (!heading) {\n if (input.createIfMissing) {\n const sep = raw.endsWith('\\n\\n') ? '' : raw.endsWith('\\n') ? '\\n' : '\\n\\n';\n const contentNl = input.content.endsWith('\\n') ? '' : '\\n';\n const newContent = `${raw}${sep}## ${targetText}\\n\\n${input.content}${contentNl}`;\n await atomicWrite(absPath, newContent);\n await verifyFrontmatter(absPath, originalFmRegion);\n updateCache(ctx, input.path, newContent, fm.bodyStart);\n const headingOffset = raw.length + sep.length;\n return {\n path: input.path,\n bytesWritten: Buffer.byteLength(newContent, 'utf8'),\n targetResolvedAt: {\n line: newContent.slice(0, headingOffset).split('\\n').length,\n byteOffset: headingOffset,\n },\n frontmatterUnchanged: true,\n contentHash: contentHash(newContent),\n };\n }\n throw new Error(\n JSON.stringify({\n error: 'heading_not_found',\n target: targetText,\n available: outline.headings.map((h) => h.text),\n }),\n );\n }\n\n const headingLineEnd = lineEnd(raw, heading.byteOffset);\n const nextSibling = outline.headings.find(\n (h) => h.byteOffset > heading.byteOffset && h.level <= heading.level,\n );\n contentStart = headingLineEnd;\n contentEnd = nextSibling?.byteOffset ?? raw.length;\n resolvedAt = { line: heading.line, byteOffset: heading.byteOffset };\n } else {\n const targetId = input.target.block;\n const block = outline.blocks.find((b) => b.id === targetId);\n if (!block) {\n throw new Error(\n JSON.stringify({\n error: 'block_not_found',\n target: targetId,\n available: outline.blocks.map((b) => b.id),\n }),\n );\n }\n contentStart = lineStart(raw, block.byteOffset);\n contentEnd = lineEnd(raw, block.byteOffset);\n resolvedAt = { line: block.line, byteOffset: block.byteOffset };\n }\n\n const before = raw.slice(0, contentStart);\n const section = raw.slice(contentStart, contentEnd);\n const after = raw.slice(contentEnd);\n\n let newContent: string;\n switch (input.operation) {\n case 'prepend': {\n const sep = input.content.endsWith('\\n') ? '' : '\\n';\n newContent = `${before}${input.content}${sep}${section}${after}`;\n break;\n }\n case 'append': {\n const sectionNl = section.endsWith('\\n') ? '' : '\\n';\n const contentNl = input.content.endsWith('\\n') ? '' : '\\n';\n newContent = `${before}${section}${sectionNl}${input.content}${contentNl}${after}`;\n break;\n }\n case 'replace': {\n const contentNl = input.content.endsWith('\\n') ? '' : '\\n';\n newContent = `${before}${input.content}${contentNl}${after}`;\n break;\n }\n default:\n throw new Error(`Unknown operation: ${String(input.operation)}`);\n }\n\n await atomicWrite(absPath, newContent);\n await verifyFrontmatter(absPath, originalFmRegion);\n updateCache(ctx, input.path, newContent, fm.bodyStart);\n\n return {\n path: input.path,\n bytesWritten: Buffer.byteLength(newContent, 'utf8'),\n targetResolvedAt: resolvedAt,\n frontmatterUnchanged: true,\n contentHash: contentHash(newContent),\n };\n}\n\nasync function verifyFrontmatter(absPath: string, originalFmRegion: string): Promise<void> {\n const written = await readFile(absPath, 'utf8');\n if (written.slice(0, originalFmRegion.length) !== originalFmRegion) {\n throw new Error('Write-safety violation: frontmatter region changed unexpectedly');\n }\n}\n\nfunction updateCache(\n ctx: ServerContext,\n path: string,\n newContent: string,\n bodyStart: number,\n): void {\n const cached = ctx.notes.get(path);\n if (cached) {\n cached.body = newContent.slice(bodyStart);\n cached.raw = newContent;\n cached.sizeBytes = Buffer.byteLength(newContent, 'utf8');\n }\n}\n","import { access, mkdir, readFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport { z } from 'zod';\nimport { atomicWrite } from '../atomic-write.js';\nimport { assertHashMatch, contentHash } from '../content-hash.js';\nimport type { ServerContext } from '../context.js';\nimport { buildDoc, upsertDoc } from '../index/doc.js';\nimport { assertWritable } from '../policy.js';\nimport { resolveVaultPath } from '../vault-path.js';\n\n// ── Period type ───────────────────────────────────────────────────────────────\n\nconst PERIOD = z.enum(['daily', 'weekly', 'monthly', 'quarterly', 'yearly']);\ntype Period = z.infer<typeof PERIOD>;\n\n// ── Config shapes (read from .obsidian on disk) ───────────────────────────────\n\ninterface PeriodicEntry {\n folder?: string;\n format?: string;\n template?: string;\n}\n\ninterface DailyNotesJson extends PeriodicEntry {}\n\ninterface PeriodicNotesJson {\n daily?: PeriodicEntry;\n weekly?: PeriodicEntry;\n monthly?: PeriodicEntry;\n quarterly?: PeriodicEntry;\n yearly?: PeriodicEntry;\n}\n\nconst DEFAULTS: Record<Period, Required<PeriodicEntry>> = {\n daily: { folder: '', format: 'YYYY-MM-DD', template: '' },\n weekly: { folder: '', format: 'gggg-[W]ww', template: '' },\n monthly: { folder: '', format: 'YYYY-MM', template: '' },\n quarterly: { folder: '', format: 'YYYY-[Q]Q', template: '' },\n yearly: { folder: '', format: 'YYYY', template: '' },\n};\n\nasync function readJson<T>(absPath: string): Promise<T | null> {\n try {\n return JSON.parse(await readFile(absPath, 'utf8')) as T;\n } catch {\n return null;\n }\n}\n\nasync function resolveConfig(vaultRoot: string, period: Period): Promise<Required<PeriodicEntry>> {\n const defaults = DEFAULTS[period];\n\n if (period === 'daily') {\n const cfg = await readJson<DailyNotesJson>(join(vaultRoot, '.obsidian', 'daily-notes.json'));\n return {\n folder: cfg?.folder ?? defaults.folder,\n format: cfg?.format ?? defaults.format,\n template: cfg?.template ?? defaults.template,\n };\n }\n\n const cfg = await readJson<PeriodicNotesJson>(\n join(vaultRoot, '.obsidian', 'plugins', 'periodic-notes', 'data.json'),\n );\n const entry = cfg?.[period];\n return {\n folder: entry?.folder ?? defaults.folder,\n format: entry?.format ?? defaults.format,\n template: entry?.template ?? defaults.template,\n };\n}\n\n// ── Moment-style date formatting (no external dep) ───────────────────────────\n\nfunction isoWeekAndYear(date: Date): { week: number; year: number } {\n const d = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));\n const day = d.getUTCDay() || 7; // Mon=1 … Sun=7\n d.setUTCDate(d.getUTCDate() + 4 - day); // nearest Thursday\n const jan1 = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));\n const week = Math.ceil(((d.getTime() - jan1.getTime()) / 86_400_000 + 1) / 7);\n return { week, year: d.getUTCFullYear() };\n}\n\nexport function formatMomentDate(date: Date, format: string): string {\n // Use UTC accessors: ISO date strings (YYYY-MM-DD) parse as UTC midnight,\n // so local-time accessors would return the previous day in negative-offset\n // timezones (EST, PST, etc.).\n const year = date.getUTCFullYear();\n const month = date.getUTCMonth() + 1;\n const day = date.getUTCDate();\n const quarter = Math.ceil(month / 3);\n const { week: isoWeek, year: isoYear } = isoWeekAndYear(date);\n\n // Protect literal text in square brackets from token substitution.\n const PLACEHOLDER = '\\x00';\n const literals: string[] = [];\n let s = format.replace(/\\[([^\\]]*)\\]/g, (_, lit) => {\n literals.push(lit);\n return `${PLACEHOLDER}${literals.length - 1}${PLACEHOLDER}`;\n });\n\n // Substitute longest tokens first to avoid partial matches (e.g. YYYY before YY).\n s = s\n .replace(/gggg/g, String(isoYear).padStart(4, '0'))\n .replace(/YYYY/g, String(year).padStart(4, '0'))\n .replace(/YY/g, String(year).slice(-2))\n .replace(/MM/g, String(month).padStart(2, '0'))\n .replace(/M/g, String(month))\n .replace(/DD/g, String(day).padStart(2, '0'))\n .replace(/D/g, String(day))\n .replace(/ww/g, String(isoWeek).padStart(2, '0'))\n .replace(/W/g, String(isoWeek))\n .replace(/Q/g, String(quarter));\n\n // Restore literals. Built via RegExp constructor because the placeholder is a\n // control char (\\x00) that Biome disallows in a regex literal; Codacy's\n // non-literal-RegExp pattern is disabled repo-wide for this safe internal use.\n return s.replace(\n new RegExp(`${PLACEHOLDER}(\\\\d+)${PLACEHOLDER}`, 'g'),\n (_, i) => literals[+i] ?? '',\n );\n}\n\n// ── Resolve a note path from config + date ────────────────────────────────────\n\nexport function resolveNotePath(cfg: Required<PeriodicEntry>, date: Date): string {\n const filename = `${formatMomentDate(date, cfg.format)}.md`;\n return cfg.folder ? `${cfg.folder}/${filename}` : filename;\n}\n\n// ── Tool: get_periodic_note ───────────────────────────────────────────────────\n\nexport const GetPeriodicNoteInput = z.object({\n period: PERIOD.default('daily').describe(\n 'Period type: daily (default), weekly, monthly, quarterly, or yearly.',\n ),\n date: z\n .string()\n .optional()\n .describe('ISO date string (YYYY-MM-DD). Defaults to today when omitted.'),\n createIfMissing: z\n .boolean()\n .default(false)\n .describe(\n 'Create the note from the configured template if it does not exist. Default false — reports missing without creating.',\n ),\n});\nexport type GetPeriodicNoteInput = z.input<typeof GetPeriodicNoteInput>;\n\nexport interface GetPeriodicNoteResult {\n path: string;\n existed: boolean;\n created: boolean;\n /** sha-256 (hex) of the created content; only set when created is true. */\n contentHash?: string;\n}\n\nexport async function getPeriodicNote(\n ctx: ServerContext,\n rawInput: GetPeriodicNoteInput,\n): Promise<GetPeriodicNoteResult> {\n const input = GetPeriodicNoteInput.parse(rawInput);\n const date = input.date ? new Date(input.date) : new Date();\n const cfg = await resolveConfig(ctx.vaultRoot, input.period);\n const path = resolveNotePath(cfg, date);\n const abs = resolveVaultPath(ctx.vaultRoot, path);\n\n let existed = false;\n try {\n await access(abs);\n existed = true;\n } catch {}\n\n if (existed) return { path, existed: true, created: false };\n\n if (!input.createIfMissing) return { path, existed: false, created: false };\n\n // About to write: this \"read\" tool creates the note here, so the write\n // policy applies (dispatch also strips createIfMissing in read-only mode).\n assertWritable(ctx.policy, path);\n\n // Read template if configured.\n let body = '';\n if (cfg.template) {\n try {\n const templateAbs = resolveVaultPath(\n ctx.vaultRoot,\n cfg.template.endsWith('.md') ? cfg.template : `${cfg.template}.md`,\n );\n body = await readFile(templateAbs, 'utf8');\n } catch {}\n }\n\n await mkdir(dirname(abs), { recursive: true });\n await atomicWrite(abs, body);\n upsertDoc(ctx, buildDoc(path, body));\n\n return { path, existed: false, created: true, contentHash: contentHash(body) };\n}\n\n// ── Tool: append_periodic_note ────────────────────────────────────────────────\n\nexport const AppendPeriodicNoteInput = z.object({\n period: PERIOD.default('daily').describe('Period type. Default: daily.'),\n date: z.string().optional().describe('ISO date string (YYYY-MM-DD). Defaults to today.'),\n content: z\n .string()\n .min(1)\n .describe('Text to append to the note body. Separated from existing content by a blank line.'),\n createIfMissing: z\n .boolean()\n .default(true)\n .describe('Create the note if it does not exist before appending. Default true.'),\n prevHash: z\n .string()\n .optional()\n .describe(\n 'Optional compare-and-swap guard: the contentHash from a prior read of the resolved periodic note. Fails with hash_conflict if it changed since.',\n ),\n});\nexport type AppendPeriodicNoteInput = z.input<typeof AppendPeriodicNoteInput>;\n\nexport interface AppendPeriodicNoteResult {\n path: string;\n bytesWritten: number;\n /** sha-256 (hex) of the new content — usable as prevHash for a chained edit. */\n contentHash: string;\n}\n\nexport async function appendPeriodicNote(\n ctx: ServerContext,\n rawInput: AppendPeriodicNoteInput,\n): Promise<AppendPeriodicNoteResult> {\n const input = AppendPeriodicNoteInput.parse(rawInput);\n const date = input.date ? new Date(input.date) : new Date();\n const cfg = await resolveConfig(ctx.vaultRoot, input.period);\n const path = resolveNotePath(cfg, date);\n const abs = resolveVaultPath(ctx.vaultRoot, path);\n assertWritable(ctx.policy, path);\n\n let original = '';\n try {\n original = await readFile(abs, 'utf8');\n if (input.prevHash !== undefined) assertHashMatch(original, input.prevHash, path);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n if (input.prevHash !== undefined) {\n // The caller pinned a version that no longer exists — conflict, not create.\n throw new Error(\n JSON.stringify({\n error: 'hash_conflict',\n path,\n expected: input.prevHash,\n actual: null,\n hint: 'The periodic note no longer exists on disk. Re-read before retrying.',\n }),\n );\n }\n if (!input.createIfMissing) throw new Error(`Periodic note not found: ${path}`);\n await mkdir(dirname(abs), { recursive: true });\n await atomicWrite(abs, input.content);\n upsertDoc(ctx, buildDoc(path, input.content));\n return {\n path,\n bytesWritten: Buffer.byteLength(input.content, 'utf8'),\n contentHash: contentHash(input.content),\n };\n }\n\n // Preserve frontmatter, append to body.\n const fm = parseFrontmatter(original);\n const body = fm.body.endsWith('\\n') ? fm.body : `${fm.body}\\n`;\n const separator = body.endsWith('\\n\\n') ? '' : '\\n';\n const newBody = `${body}${separator}${input.content}`;\n const header = original.slice(0, fm.bodyStart);\n const newRaw = `${header}${newBody}`;\n\n await atomicWrite(abs, newRaw);\n\n const cached = ctx.notes.get(path);\n if (cached) {\n cached.body = newBody;\n cached.raw = newRaw;\n }\n\n return {\n path,\n bytesWritten: Buffer.byteLength(newRaw, 'utf8'),\n contentHash: contentHash(newRaw),\n };\n}\n","import { z } from 'zod';\nimport type { ServerContext } from '../context.js';\nimport { basenameNoExt } from './search.js';\n\nconst OPS_WITHOUT_VALUE = new Set(['exists', 'missing']);\n\nconst Predicate = z\n .object({\n key: z.string().min(1).describe('Frontmatter key to test (top-level).'),\n op: z\n .enum(['eq', 'ne', 'contains', 'exists', 'missing', 'gt', 'gte', 'lt', 'lte'])\n .describe(\n 'eq/ne compare scalars; contains matches array membership or substring; ' +\n 'exists/missing test key presence; gt/gte/lt/lte compare numbers or strings (ISO dates sort correctly).',\n ),\n value: z\n .union([z.string(), z.number(), z.boolean()])\n .optional()\n .describe('Comparison value. Required for every op except exists/missing.'),\n })\n .refine((p) => OPS_WITHOUT_VALUE.has(p.op) || p.value !== undefined, {\n message: 'value is required for this op',\n });\n\nconst ISO_DATE = z\n .string()\n .refine((s) => !Number.isNaN(Date.parse(s)), { message: 'not a parseable date' });\n\nexport const QueryNotesInput = z.object({\n where: z\n .array(Predicate)\n .max(20)\n .default([])\n .describe('Frontmatter predicates. All must match (AND).'),\n folder: z\n .string()\n .optional()\n .describe('Restrict to notes under this vault-relative folder prefix.'),\n tag: z.string().optional().describe('Restrict to notes with this tag (# prefix optional).'),\n modifiedAfter: ISO_DATE.optional().describe(\n 'Only notes modified at or after this ISO 8601 date/time.',\n ),\n modifiedBefore: ISO_DATE.optional().describe(\n 'Only notes modified before this ISO 8601 date/time.',\n ),\n minSizeBytes: z.number().int().min(0).optional().describe('Only notes at least this many bytes.'),\n maxSizeBytes: z.number().int().min(0).optional().describe('Only notes at most this many bytes.'),\n select: z\n .array(z.string())\n .max(20)\n .default([])\n .describe(\n 'Extra fields to return per hit: frontmatter keys, or the specials \"mtime\", \"size\", \"tags\". ' +\n 'Default returns only path + title, keeping the payload minimal.',\n ),\n sort: z.enum(['path', 'title', 'mtime', 'size']).default('path').describe('Sort field.'),\n order: z.enum(['asc', 'desc']).default('asc').describe('Sort order.'),\n limit: z.number().int().min(1).max(500).default(100).describe('Maximum number of results.'),\n});\nexport type QueryNotesInput = z.infer<typeof QueryNotesInput>;\n\nexport interface QueryHit {\n path: string;\n /** Omitted when it equals the path's basename (recoverable from `path`). */\n title?: string;\n /** ISO 8601 mtime — present only when \"mtime\" is selected. */\n mtime?: string;\n /** Present only when \"size\" is selected. */\n sizeBytes?: number;\n /** Present only when \"tags\" is selected and the note has tags. */\n tags?: string[];\n /** Selected frontmatter keys — present only when the note has at least one of them. */\n fm?: Record<string, unknown>;\n}\n\ntype Scalar = string | number | boolean;\n\n/** Loose scalar equality: strict first, then string-form — so `5` matches \"5\" and dates match. */\nfunction scalarEq(a: unknown, b: Scalar): boolean {\n if (a === b) return true;\n if (a === null || a === undefined || typeof a === 'object') return false;\n return String(a) === String(b);\n}\n\n/** Numeric when both sides coerce to finite numbers, else lexicographic (ISO dates sort correctly). */\nfunction compare(a: unknown, b: Scalar): number | undefined {\n if (a === null || a === undefined || typeof a === 'object' || typeof a === 'boolean') {\n return undefined;\n }\n const an = Number(a);\n const bn = Number(b);\n if (Number.isFinite(an) && Number.isFinite(bn)) return an - bn;\n const as = String(a);\n const bs = String(b);\n return as < bs ? -1 : as > bs ? 1 : 0;\n}\n\nfunction matches(fm: Record<string, unknown> | null, p: z.infer<typeof Predicate>): boolean {\n const val = fm?.[p.key];\n switch (p.op) {\n case 'exists':\n return val !== undefined;\n case 'missing':\n return val === undefined;\n case 'eq':\n return scalarEq(val, p.value as Scalar);\n // `ne` requires the key to exist — notes without the key don't match.\n case 'ne':\n return val !== undefined && !scalarEq(val, p.value as Scalar);\n case 'contains': {\n if (Array.isArray(val)) return val.some((v) => scalarEq(v, p.value as Scalar));\n if (typeof val === 'string') {\n return val.toLowerCase().includes(String(p.value).toLowerCase());\n }\n return false;\n }\n case 'gt':\n case 'gte':\n case 'lt':\n case 'lte': {\n const c = compare(val, p.value as Scalar);\n if (c === undefined) return false;\n if (p.op === 'gt') return c > 0;\n if (p.op === 'gte') return c >= 0;\n if (p.op === 'lt') return c < 0;\n return c <= 0;\n }\n }\n}\n\nexport function queryNotes(ctx: ServerContext, input: QueryNotesInput): QueryHit[] {\n const tag = input.tag?.replace(/^#/, '');\n const after = input.modifiedAfter ? Date.parse(input.modifiedAfter) : undefined;\n const before = input.modifiedBefore ? Date.parse(input.modifiedBefore) : undefined;\n const fmKeys = input.select.filter((s) => s !== 'mtime' && s !== 'size' && s !== 'tags');\n\n const hits: { hit: QueryHit; note: { title: string; mtimeMs: number; sizeBytes: number } }[] = [];\n\n for (const [path, note] of ctx.notes) {\n if (input.folder && !path.startsWith(input.folder)) continue;\n if (tag && !note.tags.split(' ').filter(Boolean).includes(tag)) continue;\n if (after !== undefined && note.mtimeMs < after) continue;\n if (before !== undefined && note.mtimeMs >= before) continue;\n if (input.minSizeBytes !== undefined && note.sizeBytes < input.minSizeBytes) continue;\n if (input.maxSizeBytes !== undefined && note.sizeBytes > input.maxSizeBytes) continue;\n if (!input.where.every((p) => matches(note.fm, p))) continue;\n\n const hit: QueryHit = { path };\n // Omit redundant/empty metadata to keep the payload lean (recoverable from `path`).\n if (note.title && note.title !== basenameNoExt(path)) hit.title = note.title;\n if (input.select.includes('mtime')) hit.mtime = new Date(note.mtimeMs).toISOString();\n if (input.select.includes('size')) hit.sizeBytes = note.sizeBytes;\n if (input.select.includes('tags')) {\n const noteTags = note.tags.split(' ').filter(Boolean);\n if (noteTags.length > 0) hit.tags = noteTags;\n }\n if (fmKeys.length > 0 && note.fm) {\n const fm: Record<string, unknown> = {};\n for (const k of fmKeys) {\n if (note.fm[k] !== undefined) fm[k] = note.fm[k];\n }\n if (Object.keys(fm).length > 0) hit.fm = fm;\n }\n hits.push({ hit, note });\n }\n\n const dir = input.order === 'desc' ? -1 : 1;\n hits.sort((a, b) => {\n switch (input.sort) {\n case 'mtime':\n return (a.note.mtimeMs - b.note.mtimeMs) * dir;\n case 'size':\n return (a.note.sizeBytes - b.note.sizeBytes) * dir;\n case 'title':\n return a.note.title.localeCompare(b.note.title) * dir;\n default:\n return a.hit.path.localeCompare(b.hit.path) * dir;\n }\n });\n\n return hits.slice(0, input.limit).map((h) => h.hit);\n}\n","import { readFile } from 'node:fs/promises';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport { buildOutline } from '@seekstone/core/outline';\nimport { z } from 'zod';\nimport { contentHash } from '../content-hash.js';\nimport type { ServerContext } from '../context.js';\nimport { resolveVaultPath } from '../vault-path.js';\n\nexport const ReadNoteInput = z\n .object({\n path: z.string().describe('Vault-relative path to the note, e.g. \"Daily Notes/2026-05-29.md\".'),\n section: z\n .string()\n .optional()\n .describe('Return only this heading section (heading text, # prefix optional).'),\n block: z\n .string()\n .optional()\n .describe('Return only the block anchored by this id (^ prefix optional).'),\n lines: z\n .object({\n from: z.number().int().min(1).describe('First line (1-indexed, inclusive).'),\n to: z\n .number()\n .int()\n .min(1)\n .optional()\n .describe('Last line (1-indexed, inclusive). Defaults to end of file.'),\n })\n .optional()\n .describe('Return only this line range (1-indexed, inclusive).'),\n includeFrontmatter: z\n .boolean()\n .optional()\n .describe(\n 'Include frontmatter in span results. Default false for section/block spans. No effect on lines or whole-note reads.',\n ),\n })\n .refine((v) => [v.section, v.block, v.lines].filter((x) => x !== undefined).length <= 1, {\n message: 'At most one of section, block, or lines may be specified.',\n });\nexport type ReadNoteInput = z.infer<typeof ReadNoteInput>;\n\nexport interface ReadNoteResult {\n path: string;\n content: string;\n /** Bytes in the returned content (UTF-8). */\n bytesReturned: number;\n /** Total UTF-8 byte size of the source file. */\n noteBytes: number;\n /** sha-256 (hex) of the whole file — pass as prevHash to edit tools for compare-and-swap. */\n contentHash: string;\n /** Character offsets of the returned span in the raw file. Absent for whole-note reads. */\n span?: { charStart: number; charEnd: number };\n}\n\nexport async function readNote(ctx: ServerContext, input: ReadNoteInput): Promise<ReadNoteResult> {\n const absPath = resolveVaultPath(ctx.vaultRoot, input.path);\n\n const raw = await readFile(absPath, 'utf8');\n const noteBytes = Buffer.byteLength(raw, 'utf8');\n // Always the whole file's hash, even for span reads — it identifies the\n // on-disk version an edit's prevHash is compared against.\n const hash = contentHash(raw);\n\n // Whole-note read — no selector\n if (input.section === undefined && input.block === undefined && input.lines === undefined) {\n return {\n path: input.path,\n content: raw,\n bytesReturned: noteBytes,\n noteBytes,\n contentHash: hash,\n };\n }\n\n const fm = parseFrontmatter(raw);\n const outline = buildOutline(raw, { includeBlocks: true });\n\n let charStart: number;\n let charEnd: number;\n\n if (input.section !== undefined) {\n const target = input.section.replace(/^#+\\s*/, '').trim();\n const heading = outline.headings.find((h) => h.text === target);\n if (!heading) {\n throw new Error(\n JSON.stringify({\n error: 'section_not_found',\n target,\n available: outline.headings.map((h) => h.text),\n }),\n );\n }\n const nextSibling = outline.headings.find(\n (h) => h.byteOffset > heading.byteOffset && h.level <= heading.level,\n );\n charStart = heading.byteOffset;\n charEnd = nextSibling?.byteOffset ?? raw.length;\n } else if (input.block !== undefined) {\n const targetId = input.block.replace(/^\\^/, '');\n const block = outline.blocks.find((b) => b.id === targetId);\n if (!block) {\n throw new Error(\n JSON.stringify({\n error: 'block_not_found',\n target: targetId,\n available: outline.blocks.map((b) => b.id),\n }),\n );\n }\n const lineS = block.byteOffset === 0 ? 0 : raw.lastIndexOf('\\n', block.byteOffset - 1) + 1;\n const nlAfter = raw.indexOf('\\n', block.byteOffset);\n const lineE = nlAfter === -1 ? raw.length : nlAfter + 1;\n charStart = lineS;\n charEnd = lineE;\n } else {\n // lines selector\n const linesRange = input.lines;\n if (linesRange === undefined) throw new Error('internal: unreachable');\n const { from, to } = linesRange;\n const rawLines = raw.split('\\n');\n const fromIdx = from - 1; // 0-indexed\n const toIdxInclusive =\n to !== undefined ? Math.min(to - 1, rawLines.length - 1) : rawLines.length - 1;\n\n let offset = 0;\n charStart = 0;\n charEnd = raw.length;\n\n for (const [i, line] of rawLines.entries()) {\n if (i === fromIdx) charStart = offset;\n offset += line.length + 1; // +1 for the \\n consumed by split\n if (i === toIdxInclusive) {\n charEnd = Math.min(offset, raw.length);\n break;\n }\n }\n }\n\n // Build content — prepend FM if requested (section/block only)\n let content: string;\n if (input.includeFrontmatter === true && input.lines === undefined) {\n const fmRegion = raw.slice(0, fm.bodyStart);\n const spanContent = raw.slice(charStart, charEnd);\n // Avoid duplicating FM if the span already covers it (e.g. section at bodyStart)\n content = charStart < fm.bodyStart ? spanContent : `${fmRegion}${spanContent}`;\n } else {\n content = raw.slice(charStart, charEnd);\n }\n\n return {\n path: input.path,\n content,\n bytesReturned: Buffer.byteLength(content, 'utf8'),\n noteBytes,\n contentHash: hash,\n span: { charStart, charEnd },\n };\n}\n","import { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport { buildOutline } from '@seekstone/core/outline';\nimport { z } from 'zod';\nimport { atomicWrite } from '../atomic-write.js';\nimport { assertHashMatch, contentHash } from '../content-hash.js';\nimport type { ServerContext } from '../context.js';\nimport { addNoteBacklinks, removeNoteBacklinks } from '../index/backlinks.js';\nimport { buildDoc, upsertDoc } from '../index/doc.js';\nimport { resolveLink } from '../index/resolve.js';\nimport { assertWritable, isWritable } from '../policy.js';\nimport { resolveVaultPath } from '../vault-path.js';\nimport { mapUnfencedLines } from './rewrite_links.js';\n\nexport const RenameHeadingInput = z.object({\n path: z.string().describe('Vault-relative path of the note containing the heading.'),\n oldHeading: z\n .string()\n .min(1)\n .describe(\n 'Current heading text, without # markers. Matched case-insensitively; if the note has duplicate headings the first one wins, mirroring Obsidian link resolution.',\n ),\n newHeading: z\n .string()\n .min(1)\n .refine((s) => !/[\\n[\\]|#]/.test(s), {\n message:\n 'newHeading must not contain newlines, \"[\", \"]\", \"|\", or \"#\" — those break [[note#heading]] link syntax.',\n })\n .describe('New heading text, without # markers.'),\n prevHash: z\n .string()\n .optional()\n .describe(\n 'Optional compare-and-swap guard: the contentHash from a prior read of this note. Fails with hash_conflict if the note changed since.',\n ),\n});\nexport type RenameHeadingInput = z.input<typeof RenameHeadingInput>;\n\nexport interface RenameHeadingResult {\n path: string;\n oldHeading: string;\n newHeading: string;\n /** 1-based line of the renamed heading. */\n line: number;\n /**\n * Notes with at least one reference rewritten — includes the renamed note\n * itself when it linked to its own heading. The heading rename alone does\n * not count.\n */\n notesRewritten: number;\n /** Individual [[…#heading]] links and embeds rewritten across the vault. */\n linksRewritten: number;\n /** Referencing notes left untouched because SEEKSTONE_WRITE_PATHS excludes them. */\n skipped?: string[];\n /** sha-256 (hex) of the renamed note's new content — usable as prevHash for a chained edit. */\n contentHash: string;\n}\n\n// Heading-link shape: optional embed bang, optional target (empty = same-note\n// anchor like [[#Heading]]), required #fragment, optional |alias. Bounds\n// mirror LINK_WITH_LINES_RE in @seekstone/core/extract.\nconst HEADING_LINK_RE = /(!?)\\[\\[([^\\]|#\\n]{0,512})(#[^\\]|\\n]{1,512})(\\|[^\\]\\n]{1,512})?\\]\\]/g;\n\n/**\n * Rewrite `[[target#fragment]]` links on unfenced lines. `matchesTarget`\n * decides whether a link's target refers to the renamed note; the fragment\n * must equal `oldHeading` case-insensitively (Obsidian heading links are\n * case-insensitive). Block refs (`#^id`) and nested heading paths (`#A#B`)\n * are never touched.\n */\nfunction rewriteHeadingLinks(\n raw: string,\n oldHeading: string,\n newHeading: string,\n matchesTarget: (target: string) => boolean,\n): { content: string; count: number } {\n const oldLower = oldHeading.toLowerCase();\n let count = 0;\n const content = mapUnfencedLines(raw, (line) =>\n line.replace(\n HEADING_LINK_RE,\n (full, bang: string, target: string, frag: string, alias?: string) => {\n const fragText = frag.slice(1);\n if (fragText.startsWith('^') || fragText.includes('#')) return full;\n if (fragText.trim().toLowerCase() !== oldLower) return full;\n if (!matchesTarget(target.trim())) return full;\n count++;\n return `${bang}[[${target}#${newHeading}${alias ?? ''}]]`;\n },\n ),\n );\n return { content, count };\n}\n\nexport async function renameHeading(\n ctx: ServerContext,\n rawInput: RenameHeadingInput,\n): Promise<RenameHeadingResult> {\n // Parse here (not only in dispatch) so validation applies to direct callers\n // too — same pattern as move_note.\n const input = RenameHeadingInput.parse(rawInput);\n const absPath = resolveVaultPath(ctx.vaultRoot, input.path);\n assertWritable(ctx.policy, input.path);\n\n let raw: string;\n try {\n raw = await readFile(absPath, 'utf8');\n } catch {\n throw new Error(`Note not found: ${input.path}`);\n }\n if (input.prevHash !== undefined) assertHashMatch(raw, input.prevHash, input.path);\n\n const outline = buildOutline(raw, { includeBlocks: false });\n const heading = outline.headings.find(\n (h) => h.text.toLowerCase() === input.oldHeading.toLowerCase(),\n );\n if (!heading) {\n throw new Error(\n JSON.stringify({\n error: 'heading_not_found',\n target: input.oldHeading,\n available: outline.headings.map((h) => h.text),\n }),\n );\n }\n\n // Rename the heading line surgically by offset, preserving the marker run,\n // the whitespace after it, and any trailing whitespace (incl. CR).\n const nl = raw.indexOf('\\n', heading.byteOffset);\n const lineEnd = nl === -1 ? raw.length : nl;\n const lineText = raw.slice(heading.byteOffset, lineEnd);\n const markerPrefix = /^#{1,6}\\s+/.exec(lineText)?.[0] ?? '';\n const trailing = /\\s*$/.exec(lineText)?.[0] ?? '';\n const newLineText = `${markerPrefix}${input.newHeading}${trailing}`;\n let newRaw = raw.slice(0, heading.byteOffset) + newLineText + raw.slice(lineEnd);\n\n // Rewrite the note's own references to the renamed heading — same-note\n // anchors ([[#Heading]]) and self-links by name — in the body region only,\n // so the frontmatter stays byte-identical.\n const fm = parseFrontmatter(newRaw);\n const bodyRegion = newRaw.slice(fm.bodyStart);\n const selfRewrite = rewriteHeadingLinks(\n bodyRegion,\n input.oldHeading,\n input.newHeading,\n (target) => target === '' || resolveLink(target, ctx.notes) === input.path,\n );\n newRaw = newRaw.slice(0, fm.bodyStart) + selfRewrite.content;\n\n const originalFmRegion = raw.slice(0, fm.bodyStart);\n await atomicWrite(absPath, newRaw);\n const written = await readFile(absPath, 'utf8');\n if (written.slice(0, originalFmRegion.length) !== originalFmRegion) {\n throw new Error('Write-safety violation: frontmatter region changed unexpectedly');\n }\n\n // Refresh the renamed note's index entry. removeNoteBacklinks reads the old\n // raw out of ctx.notes, so it must run before upsertDoc replaces the entry.\n removeNoteBacklinks(ctx, input.path);\n upsertDoc(ctx, buildDoc(input.path, newRaw));\n addNoteBacklinks(ctx, input.path, newRaw);\n\n let notesRewritten = selfRewrite.count > 0 ? 1 : 0;\n let linksRewritten = selfRewrite.count;\n const skipped: string[] = [];\n\n // Referencing notes come straight off the warm backlink index — no scan.\n // Heading links are wikilinks/embeds, which the index fully covers\n // (markdown anchors like [t](note.md#h) are out of scope, as documented).\n const candidates = new Set<string>();\n for (const ref of ctx.backlinks.get(input.path) ?? []) candidates.add(ref.path);\n candidates.delete(input.path);\n\n for (const path of [...candidates].sort()) {\n const doc = ctx.notes.get(path);\n if (doc === undefined) continue;\n const { content, count } = rewriteHeadingLinks(\n doc.raw,\n input.oldHeading,\n input.newHeading,\n (target) => target !== '' && resolveLink(target, ctx.notes) === input.path,\n );\n if (count === 0) continue;\n if (!isWritable(ctx.policy, path)) {\n // Out of write scope: leave the note untouched but report it, so the\n // caller knows which references were NOT updated.\n skipped.push(path);\n continue;\n }\n await atomicWrite(join(ctx.vaultRoot, path), content);\n // Fragment rewrites change neither link resolution nor line numbers, so\n // the backlink index needs no refresh — only the notes/search entries.\n upsertDoc(ctx, buildDoc(path, content));\n notesRewritten++;\n linksRewritten += count;\n }\n\n const result: RenameHeadingResult = {\n path: input.path,\n oldHeading: input.oldHeading,\n newHeading: input.newHeading,\n line: heading.line,\n notesRewritten,\n linksRewritten,\n contentHash: contentHash(newRaw),\n };\n if (skipped.length > 0) result.skipped = skipped;\n return result;\n}\n","import { readFile } from 'node:fs/promises';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport { z } from 'zod';\nimport { atomicWrite } from '../atomic-write.js';\nimport { assertHashMatch, contentHash } from '../content-hash.js';\nimport type { ServerContext } from '../context.js';\nimport { assertWritable } from '../policy.js';\nimport { resolveVaultPath } from '../vault-path.js';\n\nexport const ReplaceInNoteInput = z.object({\n path: z.string().describe('Vault-relative path to the note.'),\n find: z\n .string()\n .min(1)\n .max(1000)\n .describe('Text or pattern to find (max 1000 chars; bounds regex-mode pattern size).'),\n replace: z\n .string()\n .describe('Replacement text. Supports $1, $2, … backreferences in regex mode.'),\n regex: z.boolean().default(false).describe('Treat find as a regular expression.'),\n caseSensitive: z.boolean().default(false).describe('Case-sensitive matching.'),\n wholeWord: z.boolean().default(false).describe('Match whole words only (\\\\b word-boundary).'),\n limit: z\n .number()\n .int()\n .min(1)\n .optional()\n .describe('Maximum number of replacements. Omit to replace all.'),\n dryRun: z.boolean().default(false).describe('If true, report matches without writing anything.'),\n prevHash: z\n .string()\n .optional()\n .describe(\n 'Optional compare-and-swap guard: the contentHash from a prior read. Fails with hash_conflict if the note changed since.',\n ),\n});\nexport type ReplaceInNoteInput = z.infer<typeof ReplaceInNoteInput>;\n\nexport interface MatchPosition {\n line: number;\n byteOffset: number;\n}\n\nexport interface ReplaceInNoteResult {\n path: string;\n replacements: number;\n matches: MatchPosition[];\n bytesWritten?: number;\n frontmatterUnchanged: true;\n /** sha-256 (hex) of the new content after a write; absent on dry runs and zero-match calls. */\n contentHash?: string;\n}\n\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction buildRegex(\n find: string,\n opts: { regex: boolean; caseSensitive: boolean; wholeWord: boolean },\n): RegExp {\n let pattern = opts.regex ? find : escapeRegex(find);\n if (opts.wholeWord) pattern = `\\\\b(?:${pattern})\\\\b`;\n const flags = `g${opts.caseSensitive ? '' : 'i'}`;\n return new RegExp(pattern, flags);\n}\n\nexport async function replaceInNote(\n ctx: ServerContext,\n input: ReplaceInNoteInput,\n): Promise<ReplaceInNoteResult> {\n const absPath = resolveVaultPath(ctx.vaultRoot, input.path);\n assertWritable(ctx.policy, input.path);\n\n // Validate regex before any IO.\n let re: RegExp;\n try {\n re = buildRegex(input.find, {\n regex: input.regex,\n caseSensitive: input.caseSensitive,\n wholeWord: input.wholeWord,\n });\n } catch (err) {\n throw new Error(`Invalid regex: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n const raw = await readFile(absPath, 'utf8');\n if (input.prevHash !== undefined) assertHashMatch(raw, input.prevHash, input.path);\n const fm = parseFrontmatter(raw);\n const originalFmRegion = raw.slice(0, fm.bodyStart);\n const body = raw.slice(fm.bodyStart);\n\n const allMatches = [...body.matchAll(re)];\n const toReplace = input.limit !== undefined ? allMatches.slice(0, input.limit) : allMatches;\n\n const matches: MatchPosition[] = toReplace.map((m) => {\n const fileOffset = fm.bodyStart + (m.index ?? 0);\n const line = raw.slice(0, fileOffset).split('\\n').length;\n return { line, byteOffset: fileOffset };\n });\n\n if (input.dryRun || toReplace.length === 0) {\n return {\n path: input.path,\n replacements: toReplace.length,\n matches,\n frontmatterUnchanged: true,\n };\n }\n\n // Apply replacements left-to-right, resolving backreferences per match.\n const singleRe = new RegExp(re.source, re.flags.replace('g', ''));\n let newBody = '';\n let lastIndex = 0;\n for (const m of toReplace) {\n const matchIndex = m.index ?? 0;\n newBody += body.slice(lastIndex, matchIndex);\n newBody += input.regex ? m[0].replace(singleRe, input.replace) : input.replace;\n lastIndex = matchIndex + m[0].length;\n }\n newBody += body.slice(lastIndex);\n\n const newContent = `${originalFmRegion}${newBody}`;\n await atomicWrite(absPath, newContent);\n\n const written = await readFile(absPath, 'utf8');\n if (written.slice(0, originalFmRegion.length) !== originalFmRegion) {\n throw new Error('Write-safety violation: frontmatter region changed unexpectedly');\n }\n\n const cached = ctx.notes.get(input.path);\n if (cached) {\n cached.body = newBody;\n cached.raw = newContent;\n cached.sizeBytes = Buffer.byteLength(newContent, 'utf8');\n }\n\n return {\n path: input.path,\n replacements: toReplace.length,\n matches,\n bytesWritten: Buffer.byteLength(newContent, 'utf8'),\n frontmatterUnchanged: true,\n contentHash: contentHash(newContent),\n };\n}\n","import type { ServerContext } from './context.js';\nimport type { Logger } from './log.js';\nimport { AppendNoteInput, appendNote } from './tools/append_note.js';\nimport { ContextPackInput, contextPack } from './tools/context_pack.js';\nimport { CreateNoteInput, createNote } from './tools/create_note.js';\nimport { DeleteNoteInput, deleteNote } from './tools/delete_note.js';\nimport { GetBacklinksInput, getBacklinks } from './tools/get_backlinks.js';\nimport { GetLinksInput, getLinks } from './tools/get_links.js';\nimport { ListNotesInput, listNotes } from './tools/list_notes.js';\nimport { ListTagsInput, listTags } from './tools/list_tags.js';\nimport { MoveNoteInput, moveNote } from './tools/move_note.js';\nimport { OutlineNoteInput, outlineNote } from './tools/outline_note.js';\nimport { PatchFrontmatterInput, patchFrontmatter } from './tools/patch_frontmatter.js';\nimport { PatchNoteInput, patchNote } from './tools/patch_note.js';\nimport {\n AppendPeriodicNoteInput,\n appendPeriodicNote,\n GetPeriodicNoteInput,\n getPeriodicNote,\n} from './tools/periodic_note.js';\nimport { QueryNotesInput, queryNotes } from './tools/query_notes.js';\nimport { ReadNoteInput, readNote } from './tools/read_note.js';\nimport { RenameHeadingInput, renameHeading } from './tools/rename_heading.js';\nimport { ReplaceInNoteInput, replaceInNote } from './tools/replace_in_note.js';\nimport { SearchInput, search } from './tools/search.js';\n\nexport type ToolResult = {\n content: { type: 'text'; text: string }[];\n isError?: boolean;\n};\n\n/** Tool names this dispatcher handles — kept in sync with the server's tool list. */\nexport const HANDLED_TOOLS = [\n 'search',\n 'query_notes',\n 'context_pack',\n 'read_note',\n 'list_notes',\n 'list_tags',\n 'create_note',\n 'delete_note',\n 'move_note',\n 'rename_heading',\n 'append_note',\n 'patch_frontmatter',\n 'outline_note',\n 'patch_note',\n 'get_backlinks',\n 'get_links',\n 'replace_in_note',\n 'get_periodic_note',\n 'append_periodic_note',\n] as const;\n\n/**\n * Tools that mutate the vault. Enforced here at dispatch (read-only mode\n * rejects them) and mirrored by tool-list.ts, which unregisters them from\n * tools/list — a new write tool must be added to this set to ship.\n */\nexport const WRITE_TOOLS: ReadonlySet<string> = new Set([\n 'create_note',\n 'delete_note',\n 'move_note',\n 'rename_heading',\n 'append_note',\n 'patch_frontmatter',\n 'patch_note',\n 'replace_in_note',\n 'append_periodic_note',\n]);\n\n// Metadata-safe keys: logged at info. Note content (`content`, `frontmatter`,\n// `patch`) and the raw `query` string are intentionally excluded — they only\n// reach logs via the debug-level `args` dump.\nconst META_KEYS = [\n 'path',\n 'from',\n 'to',\n 'folder',\n 'tag',\n 'limit',\n 'overwrite',\n 'pattern',\n 'minCount',\n 'sort',\n 'order',\n 'select',\n 'modifiedAfter',\n 'modifiedBefore',\n 'minSizeBytes',\n 'maxSizeBytes',\n 'period',\n 'date',\n 'permanent',\n 'prevHash',\n 'budgetBytes',\n] as const;\n\nfunction safeMeta(args: unknown): Record<string, unknown> {\n if (typeof args !== 'object' || args === null) return {};\n const a = args as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const k of META_KEYS) {\n if (a[k] !== undefined) out[k] = a[k];\n }\n if (typeof a.query === 'string') out.queryLen = a.query.length; // not the query itself\n if (Array.isArray(a.where)) out.whereCount = a.where.length; // predicate values may be personal\n return out;\n}\n\nfunction resultBytes(r: ToolResult): number {\n let n = 0;\n for (const c of r.content) n += Buffer.byteLength(c.text);\n return n;\n}\n\n/**\n * Route a tool call to its handler, logging the outcome. Returns an MCP tool\n * result; failures are converted to `{ isError: true }` (never thrown) so the\n * transport stays alive, and are logged at `error` with a stack.\n */\nexport async function dispatch(\n ctx: ServerContext,\n name: string,\n args: unknown,\n log: Logger,\n): Promise<ToolResult> {\n const start = performance.now();\n const meta = safeMeta(args);\n log.debug('tool start', { tool: name, args }); // full args (may include content) — debug only\n if (ctx.policy.readOnly && WRITE_TOOLS.has(name)) {\n // Unregistered from tools/list, but a client can still send the call.\n log.info('tool rejected (read-only)', { tool: name, ...meta });\n return {\n content: [\n {\n type: 'text',\n text: 'Error: Server is read-only (SEEKSTONE_READ_ONLY=1); write tools are disabled.',\n },\n ],\n isError: true,\n };\n }\n if (ctx.policy.readOnly && name === 'get_periodic_note') {\n // The one read tool with a write side-effect: force createIfMissing off.\n if (typeof args === 'object' && args !== null) {\n args = { ...(args as Record<string, unknown>), createIfMissing: false };\n }\n }\n try {\n const result = await run(ctx, name, args);\n const durationMs = Math.round((performance.now() - start) * 100) / 100;\n log.info('tool ok', { tool: name, durationMs, resultBytes: resultBytes(result), ...meta });\n return result;\n } catch (err) {\n const durationMs = Math.round((performance.now() - start) * 100) / 100;\n const message = err instanceof Error ? err.message : String(err);\n log.error('tool error', {\n tool: name,\n durationMs,\n error: message,\n stack: err instanceof Error ? err.stack : undefined,\n ...meta,\n });\n return { content: [{ type: 'text', text: `Error: ${message}` }], isError: true };\n }\n}\n\nasync function run(ctx: ServerContext, name: string, args: unknown): Promise<ToolResult> {\n switch (name) {\n case 'search': {\n const input = SearchInput.parse(args);\n const hits = search(ctx, input);\n // Minified: search is the headline context-tax metric — indentation is pure tax.\n return { content: [{ type: 'text', text: JSON.stringify(hits) }] };\n }\n case 'query_notes': {\n const input = QueryNotesInput.parse(args);\n const hits = queryNotes(ctx, input);\n // Minified like search — a second search mode, same context-tax discipline.\n return { content: [{ type: 'text', text: JSON.stringify(hits) }] };\n }\n case 'context_pack': {\n const input = ContextPackInput.parse(args);\n const pack = contextPack(ctx, input);\n // Minified: the assembler meters its byte budget against exactly this serialization.\n return { content: [{ type: 'text', text: JSON.stringify(pack) }] };\n }\n case 'read_note': {\n const input = ReadNoteInput.parse(args);\n const result = await readNote(ctx, input);\n return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };\n }\n case 'list_notes': {\n const input = ListNotesInput.parse(args);\n const entries = listNotes(ctx, input);\n return { content: [{ type: 'text', text: JSON.stringify(entries, null, 2) }] };\n }\n case 'list_tags': {\n const input = ListTagsInput.parse(args);\n const result = listTags(ctx, input);\n return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };\n }\n case 'create_note': {\n const input = CreateNoteInput.parse(args);\n const result = await createNote(ctx, input);\n return {\n content: [\n {\n type: 'text',\n text: `Created ${result.path} (${result.bytesWritten} bytes). contentHash: ${result.contentHash}`,\n },\n ],\n };\n }\n case 'delete_note': {\n const input = DeleteNoteInput.parse(args);\n const result = await deleteNote(ctx, input);\n const text = result.trashedTo\n ? `Moved ${result.path} to ${result.trashedTo} (recoverable — restore by moving it back).`\n : `Permanently deleted ${result.path}.`;\n return { content: [{ type: 'text', text }] };\n }\n case 'move_note': {\n const input = MoveNoteInput.parse(args);\n const result = await moveNote(ctx, input);\n let text = `Moved ${result.from} → ${result.to}. Rewrote ${result.linksRewritten} link${\n result.linksRewritten === 1 ? '' : 's'\n } in ${result.notesRewritten} note${result.notesRewritten === 1 ? '' : 's'}.`;\n if (result.skipped?.length) {\n text += ` Skipped (outside SEEKSTONE_WRITE_PATHS): ${result.skipped.join(', ')}.`;\n }\n return { content: [{ type: 'text', text }] };\n }\n case 'rename_heading': {\n const input = RenameHeadingInput.parse(args);\n const result = await renameHeading(ctx, input);\n let text = `Renamed heading \"${result.oldHeading}\" → \"${result.newHeading}\" in ${result.path} (line ${result.line}). Rewrote ${result.linksRewritten} link${\n result.linksRewritten === 1 ? '' : 's'\n } in ${result.notesRewritten} note${result.notesRewritten === 1 ? '' : 's'}. contentHash: ${result.contentHash}`;\n if (result.skipped?.length) {\n text += ` Skipped (outside SEEKSTONE_WRITE_PATHS): ${result.skipped.join(', ')}.`;\n }\n return { content: [{ type: 'text', text }] };\n }\n case 'append_note': {\n const input = AppendNoteInput.parse(args);\n const result = await appendNote(ctx, input);\n return {\n content: [\n {\n type: 'text',\n text: `Appended ${result.bytesWritten} bytes to ${result.path}. contentHash: ${result.contentHash}`,\n },\n ],\n };\n }\n case 'patch_frontmatter': {\n const input = PatchFrontmatterInput.parse(args);\n const result = await patchFrontmatter(ctx, input);\n return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };\n }\n case 'outline_note': {\n const input = OutlineNoteInput.parse(args);\n const result = await outlineNote(ctx, input);\n return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };\n }\n case 'patch_note': {\n const input = PatchNoteInput.parse(args);\n const result = await patchNote(ctx, input);\n return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };\n }\n case 'get_backlinks': {\n const input = GetBacklinksInput.parse(args);\n const result = getBacklinks(ctx, input);\n return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };\n }\n case 'get_links': {\n const input = GetLinksInput.parse(args);\n const result = getLinks(ctx, input);\n return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };\n }\n case 'replace_in_note': {\n const input = ReplaceInNoteInput.parse(args);\n const result = await replaceInNote(ctx, input);\n return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };\n }\n case 'get_periodic_note': {\n const input = GetPeriodicNoteInput.parse(args);\n const result = await getPeriodicNote(ctx, input);\n return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };\n }\n case 'append_periodic_note': {\n const input = AppendPeriodicNoteInput.parse(args);\n const result = await appendPeriodicNote(ctx, input);\n return {\n content: [\n {\n type: 'text',\n text: `Appended ${result.bytesWritten} bytes to ${result.path}. contentHash: ${result.contentHash}`,\n },\n ],\n };\n }\n default:\n throw new Error(`Unknown tool: ${name}`);\n }\n}\n","import { readFile } from 'node:fs/promises';\nimport { basename, extname } from 'node:path';\nimport { extractInlineTags, extractLinksWithLines, frontmatterTags } from '@seekstone/core/extract';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport { walkVault } from '@seekstone/core/walk';\nimport MiniSearch from 'minisearch';\nimport type { BacklinkRef } from '../context.js';\nimport { resolveLink } from './resolve.js';\nimport type { IndexedNote } from './types.js';\n\nexport type VaultIndex = MiniSearch<IndexedNote>;\n\nexport interface BuildResult {\n index: VaultIndex;\n notes: Map<string, IndexedNote>;\n backlinks: Map<string, BacklinkRef[]>;\n buildMs: number;\n}\n\nexport async function buildIndex(vaultRoot: string): Promise<BuildResult> {\n const t0 = Date.now();\n\n const index = new MiniSearch<IndexedNote>({\n idField: 'id',\n fields: ['title', 'body', 'tags', 'fmKeys'],\n storeFields: ['id', 'title', 'tags', 'sizeBytes', 'mtimeMs'],\n searchOptions: {\n boost: { title: 3, tags: 2, body: 1 },\n fuzzy: 0.2,\n prefix: true,\n },\n });\n\n const entries = await walkVault(vaultRoot);\n const noteEntries = entries.filter((e) => e.kind === 'note');\n\n const docs: IndexedNote[] = [];\n const notes = new Map<string, IndexedNote>();\n\n await Promise.all(\n noteEntries.map(async (entry) => {\n try {\n const raw = await readFile(entry.absPath, 'utf8');\n const fm = parseFrontmatter(raw);\n const inlineTags = extractInlineTags(fm.body);\n const fmTags = frontmatterTags(fm.data);\n const allTags = [...new Set([...inlineTags, ...fmTags])];\n\n const title =\n typeof fm.data?.title === 'string' && fm.data.title\n ? fm.data.title\n : basename(entry.relPath, extname(entry.relPath));\n\n const doc: IndexedNote = {\n id: entry.relPath,\n title,\n body: fm.body,\n tags: allTags.join(' '),\n fmKeys: fm.keys.join(' '),\n fm: fm.data,\n raw,\n sizeBytes: entry.sizeBytes,\n mtimeMs: entry.mtimeMs,\n };\n docs.push(doc);\n notes.set(entry.relPath, doc);\n } catch {\n // Unreadable note — skip silently; index what we can.\n }\n }),\n );\n\n index.addAll(docs);\n\n const backlinks = buildBacklinks(notes);\n\n return { index, notes, backlinks, buildMs: Date.now() - t0 };\n}\n\nfunction buildBacklinks(notes: Map<string, IndexedNote>): Map<string, BacklinkRef[]> {\n const result = new Map<string, BacklinkRef[]>();\n for (const [srcPath, doc] of notes) {\n const seen = new Set<string>();\n for (const link of extractLinksWithLines(doc.raw)) {\n const resolved = resolveLink(link.target, notes);\n if (resolved === undefined) continue;\n // De-duplicate: one entry per (source, resolved-target) pair\n const dedupeKey = `${srcPath}\\0${resolved}`;\n if (seen.has(dedupeKey)) continue;\n seen.add(dedupeKey);\n let arr = result.get(resolved);\n if (arr === undefined) {\n arr = [];\n result.set(resolved, arr);\n }\n arr.push({ path: srcPath, line: link.line, linkType: link.linkType });\n }\n }\n for (const arr of result.values()) {\n arr.sort((a, b) => a.path.localeCompare(b.path));\n }\n return result;\n}\n","import { stat } from 'node:fs/promises';\nimport { extname, join, relative, sep } from 'node:path';\nimport fg from 'fast-glob';\n\n/**\n * Categories we care about. Anything else falls into `other`.\n * Excalidraw saves as `.excalidraw` (sometimes `.excalidraw.md` — handled in classify()).\n */\nexport type FileKind =\n | 'note'\n | 'image'\n | 'pdf'\n | 'excalidraw'\n | 'canvas'\n | 'video'\n | 'audio'\n | 'other';\n\nexport interface FileEntry {\n absPath: string;\n relPath: string;\n topDir: string;\n ext: string;\n kind: FileKind;\n sizeBytes: number;\n mtimeMs: number;\n}\n\nconst IMAGE_EXTS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.bmp', '.heic']);\nconst VIDEO_EXTS = new Set(['.mp4', '.mov', '.webm', '.mkv', '.avi']);\nconst AUDIO_EXTS = new Set(['.mp3', '.m4a', '.wav', '.flac', '.ogg']);\n\n/**\n * Obsidian's own dot-directories plus VCS noise. We deliberately don't ignore\n * dotfiles at the *file* level — users sometimes commit `.gitkeep` etc. — but\n * any top-level dotted directory is treated as non-vault content.\n */\nconst IGNORE_GLOBS = [\n '**/.obsidian/**',\n '**/.trash/**',\n '**/.git/**',\n '**/.DS_Store',\n '**/node_modules/**',\n];\n\nexport function classify(relPath: string): FileKind {\n // Excalidraw plugin sometimes writes `.excalidraw.md` — keep those as notes\n // (they still have YAML frontmatter and links), but pure `.excalidraw` is canvas-like.\n if (relPath.endsWith('.excalidraw.md')) return 'note';\n if (relPath.endsWith('.excalidraw')) return 'excalidraw';\n const ext = extname(relPath).toLowerCase();\n if (ext === '.md') return 'note';\n if (ext === '.canvas') return 'canvas';\n if (ext === '.pdf') return 'pdf';\n if (IMAGE_EXTS.has(ext)) return 'image';\n if (VIDEO_EXTS.has(ext)) return 'video';\n if (AUDIO_EXTS.has(ext)) return 'audio';\n return 'other';\n}\n\nexport async function walkVault(vaultRoot: string): Promise<FileEntry[]> {\n // We use fast-glob with `stats: true` so we can avoid an extra stat() round per file.\n const matches = await fg('**/*', {\n cwd: vaultRoot,\n absolute: true,\n onlyFiles: true,\n followSymbolicLinks: false,\n dot: true, // include dotfiles so the ignore globs are the only gate\n ignore: IGNORE_GLOBS,\n stats: true,\n suppressErrors: true,\n });\n\n const entries: FileEntry[] = [];\n for (const m of matches) {\n // fast-glob with stats: true returns objects; without, strings. Narrow here.\n const absPath = typeof m === 'string' ? m : m.path;\n const stats = typeof m === 'string' ? await stat(absPath) : m.stats;\n if (!stats) continue;\n const relPath = relative(vaultRoot, absPath);\n const topDir = relPath.split(sep)[0] ?? '';\n entries.push({\n absPath,\n relPath,\n topDir,\n ext: extname(relPath).toLowerCase(),\n kind: classify(relPath),\n sizeBytes: stats.size,\n mtimeMs: stats.mtimeMs,\n });\n }\n return entries;\n}\n\nexport function _testJoin(...parts: string[]): string {\n return join(...parts);\n}\n","// execFileSync is used (not exec/execSync) because it bypasses the shell\n// entirely — it exec's the named binary directly. There is no shell injection\n// risk. Socket.dev flags the child_process import as \"shellAccess\" regardless\n// of which function is used; that score penalty is accepted. See SHA-139.\nimport { execFileSync } from 'node:child_process';\nimport type { Dirent } from 'node:fs';\nimport { access, copyFile, mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';\nimport path, { dirname, join } from 'node:path';\n\n/**\n * `seekstone init` — a guided setup helper. Validates an Obsidian vault and\n * prints (or, with --write, patches) the MCP config for the chosen client\n * (Claude Desktop, Claude Code, Cursor) so a user doesn't have to hand-edit\n * JSON or guess paths.\n *\n * The pure helpers (arg parsing, config-path resolution, additive merge,\n * command string) are exported and unit-tested. `runInit` wires them to the\n * filesystem and console.\n */\n\nexport interface InitOptions {\n vault?: string;\n write: boolean;\n client: 'desktop' | 'code' | 'cursor' | 'vscode';\n /** Set when --client was given an unrecognized value; runInit errors on it. */\n invalidClient?: string;\n}\n\nexport function parseInitArgs(argv: readonly string[]): InitOptions {\n const opts: InitOptions = { write: false, client: 'desktop' };\n // argv is everything after the `init` subcommand.\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i];\n if (a === '--write') opts.write = true;\n else if (a === '--vault') opts.vault = argv[++i];\n else if (a === '--client') {\n const v = argv[++i];\n if (v === 'desktop' || v === 'code' || v === 'cursor' || v === 'vscode') opts.client = v;\n // Record the bad value instead of silently falling back to desktop —\n // agents run init non-interactively and would otherwise configure the\n // wrong client without anyone noticing.\n else opts.invalidClient = v ?? '(missing)';\n }\n }\n return opts;\n}\n\n/** The server config object that goes under `mcpServers.seekstone`. */\nexport function seekstoneServerConfig(vaultPath: string): {\n command: string;\n args: string[];\n env: Record<string, string>;\n} {\n return {\n command: 'npx',\n args: ['-y', 'seekstone'],\n env: { SEEKSTONE_VAULT: vaultPath },\n };\n}\n\n/** The `claude mcp add` one-liner for Claude Code. */\nexport function mcpAddCommand(vaultPath: string): string {\n return `claude mcp add seekstone --env SEEKSTONE_VAULT=${vaultPath} -- npx -y seekstone`;\n}\n\n/**\n * Resolve the Claude Desktop config path for a platform. Pure — takes the\n * platform and a home/APPDATA lookup so it can be tested without touching the\n * real environment.\n */\nexport function claudeDesktopConfigPath(\n platform: NodeJS.Platform,\n env: { home?: string; appData?: string },\n): string {\n // Use the target platform's path semantics, not the host's, so the result is\n // correct in production AND deterministic in tests on any CI OS.\n if (platform === 'win32') {\n const j = path.win32.join;\n const base = env.appData ?? j(env.home ?? '', 'AppData', 'Roaming');\n return j(base, 'Claude', 'claude_desktop_config.json');\n }\n const j = path.posix.join;\n const home = env.home ?? '';\n if (platform === 'darwin') {\n return j(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');\n }\n // Linux / other\n return j(home, '.config', 'Claude', 'claude_desktop_config.json');\n}\n\n/**\n * Resolve Cursor's global MCP config path for a platform. Cursor reads\n * `~/.cursor/mcp.json` on every OS (home-relative, no APPDATA) and uses the\n * same `mcpServers` shape as Claude Desktop. Pure, like claudeDesktopConfigPath.\n */\nexport function cursorConfigPath(platform: NodeJS.Platform, env: { home?: string }): string {\n const j = platform === 'win32' ? path.win32.join : path.posix.join;\n return j(env.home ?? '', '.cursor', 'mcp.json');\n}\n\n/**\n * VS Code (GitHub Copilot) workspace MCP config: `.vscode/mcp.json` under the\n * given working directory. Workspace-level on purpose — the user-global config\n * has no stable documented path (VS Code's \"MCP: Open User Configuration\"\n * command owns it), and workspace config takes precedence anyway. Host path\n * semantics (unlike the other path helpers) because it resolves the *real*\n * cwd of the running process, not a target-platform location.\n */\nexport function vscodeMcpConfigPath(cwd: string): string {\n return join(cwd, '.vscode', 'mcp.json');\n}\n\n/**\n * Resolve the Obsidian vault registry path for a platform. Pure — mirrors the\n * claudeDesktopConfigPath pattern so it can be tested without real filesystem.\n */\nexport function obsidianRegistryPath(\n platform: NodeJS.Platform,\n env: { home?: string; appData?: string },\n): string {\n if (platform === 'win32') {\n const j = path.win32.join;\n const base = env.appData ?? j(env.home ?? '', 'AppData', 'Roaming');\n return j(base, 'obsidian', 'obsidian.json');\n }\n const j = path.posix.join;\n const home = env.home ?? '';\n if (platform === 'darwin') {\n return j(home, 'Library', 'Application Support', 'obsidian', 'obsidian.json');\n }\n return j(home, '.config', 'obsidian', 'obsidian.json');\n}\n\nexport type ObsidianVaultEntry = { path: string; open?: boolean };\n\n/**\n * Parse Obsidian's obsidian.json vault registry. Tolerant of missing/extra\n * keys and malformed JSON — always returns an array. Sorted: open vault first,\n * then lexically by path for determinism across runs.\n */\nexport function parseObsidianVaults(json: string): ObsidianVaultEntry[] {\n let data: unknown;\n try {\n data = JSON.parse(json);\n } catch {\n return [];\n }\n if (!data || typeof data !== 'object') return [];\n const raw = (data as Record<string, unknown>).vaults;\n if (!raw || typeof raw !== 'object') return [];\n const entries: ObsidianVaultEntry[] = [];\n for (const v of Object.values(raw as Record<string, unknown>)) {\n if (v && typeof v === 'object') {\n const entry = v as Record<string, unknown>;\n if (typeof entry.path === 'string') {\n entries.push({ path: entry.path, open: entry.open === true });\n }\n }\n }\n return entries.sort((a, b) => {\n if (a.open !== b.open) return a.open ? -1 : 1;\n return a.path.localeCompare(b.path);\n });\n}\n\ntype McpConfig = { mcpServers?: Record<string, unknown> } & Record<string, unknown>;\n\n/**\n * Additively merge the seekstone server into an existing Claude config object.\n * Never removes or alters other servers or top-level keys. Idempotent: running\n * it again with the same vault produces an identical object. Returns a new\n * object (does not mutate the input).\n */\nexport function mergeSeekstoneConfig(existing: McpConfig | null, vaultPath: string): McpConfig {\n const base: McpConfig = existing ? structuredClone(existing) : {};\n const servers = { ...(base.mcpServers ?? {}) };\n servers.seekstone = seekstoneServerConfig(vaultPath);\n return { ...base, mcpServers: servers };\n}\n\ntype VsCodeMcpConfig = { servers?: Record<string, unknown> } & Record<string, unknown>;\n\n/** The server entry for VS Code, which requires an explicit `\"type\": \"stdio\"`. */\nexport function vscodeServerConfig(vaultPath: string): {\n type: 'stdio';\n command: string;\n args: string[];\n env: Record<string, string>;\n} {\n return { type: 'stdio', ...seekstoneServerConfig(vaultPath) };\n}\n\n/**\n * VS Code variant of mergeSeekstoneConfig. Same additive/idempotent contract,\n * but VS Code's mcp.json is the one client config keyed `\"servers\"` (not\n * `\"mcpServers\"`) and requires `\"type\": \"stdio\"` on each entry.\n */\nexport function mergeSeekstoneConfigVsCode(\n existing: VsCodeMcpConfig | null,\n vaultPath: string,\n): VsCodeMcpConfig {\n const base: VsCodeMcpConfig = existing ? structuredClone(existing) : {};\n const servers = { ...(base.servers ?? {}) };\n servers.seekstone = vscodeServerConfig(vaultPath);\n return { ...base, servers };\n}\n\nexport interface VaultCheck {\n ok: boolean;\n noteCount?: number;\n error?: string;\n}\n\n/** Validate that `vaultPath` looks like an Obsidian vault and count .md notes. */\nexport async function validateVault(vaultPath: string): Promise<VaultCheck> {\n try {\n const st = await stat(vaultPath);\n if (!st.isDirectory()) return { ok: false, error: `Not a directory: ${vaultPath}` };\n } catch {\n return { ok: false, error: `Path does not exist: ${vaultPath}` };\n }\n // An Obsidian vault has a .obsidian/ config folder.\n try {\n await access(join(vaultPath, '.obsidian'));\n } catch {\n return {\n ok: false,\n error: `No \".obsidian\" folder found in ${vaultPath} — this doesn't look like an Obsidian vault. Open the folder in Obsidian once to initialize it, or pass the correct --vault path.`,\n };\n }\n return { ok: true, noteCount: await countMarkdown(vaultPath) };\n}\n\nasync function countMarkdown(root: string): Promise<number> {\n let count = 0;\n async function walk(dir: string): Promise<void> {\n let entries: Dirent[];\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const e of entries) {\n if (e.name.startsWith('.')) continue; // skip .obsidian, .git, .trash\n const full = join(dir, e.name);\n if (e.isDirectory()) await walk(full);\n else if (e.name.endsWith('.md')) count++;\n }\n }\n await walk(root);\n return count;\n}\n\nexport interface InitResult {\n ok: boolean;\n /** Lines to print to stdout. */\n output: string[];\n /** Process exit code. */\n exitCode: number;\n /** Set when --write patched Claude Desktop config. */\n wrotePath?: string;\n backupPath?: string;\n /** Set when --write --client code successfully ran `claude mcp add`. */\n ranClaudeMcpAdd?: boolean;\n}\n\n/**\n * Run the init flow. `deps` are injectable for testing (env, platform, clock).\n * Performs filesystem reads always; only writes when `opts.write` is true.\n */\nexport async function runInit(\n opts: InitOptions,\n deps: {\n env: Record<string, string | undefined>;\n platform: NodeJS.Platform;\n /** ISO timestamp for the backup filename (injected for determinism in tests). */\n timestamp: string;\n /** Working directory for workspace-level configs (vscode); defaults to process.cwd(). */\n cwd?: string;\n /** Injectable for testing; defaults to fs/promises readFile. */\n readFile?: (p: string, enc: 'utf8') => Promise<string>;\n /**\n * Injectable for testing; defaults to execFileSync('claude', args).\n * Returns ok:true on success, ok:false with an error string on failure.\n */\n spawnClaudeMcp?: (args: string[]) => { ok: boolean; error?: string };\n },\n): Promise<InitResult> {\n if (opts.invalidClient !== undefined) {\n return {\n ok: false,\n exitCode: 1,\n output: [\n `✗ Unknown --client: ${opts.invalidClient}. Known clients: desktop, code, cursor, vscode.`,\n ],\n };\n }\n\n const rf = deps.readFile ?? readFile;\n let vaultPath = opts.vault ?? deps.env.SEEKSTONE_VAULT;\n\n if (!vaultPath) {\n // Auto-detect from Obsidian's vault registry.\n const registryPath = obsidianRegistryPath(deps.platform, {\n home: deps.env.HOME,\n appData: deps.env.APPDATA,\n });\n let vaults: ObsidianVaultEntry[] = [];\n try {\n vaults = parseObsidianVaults(await rf(registryPath, 'utf8'));\n } catch {\n // Registry missing or unreadable — fall through to error.\n }\n\n if (vaults.length === 0) {\n return {\n ok: false,\n exitCode: 1,\n output: [\n 'No vault specified. Pass --vault <path> or set SEEKSTONE_VAULT.',\n 'Example: seekstone init --vault \"/Users/you/Obsidian/My Vault\"',\n ],\n };\n }\n\n if (vaults.length === 1) {\n const [first] = vaults;\n vaultPath = first?.path ?? '';\n } else {\n const list = vaults\n .map((v, i) => ` ${i + 1}. ${v.path}${v.open ? ' (currently open)' : ''}`)\n .join('\\n');\n return {\n ok: false,\n exitCode: 1,\n output: [\n 'Multiple Obsidian vaults found. Re-run with --vault to pick one:',\n '',\n list,\n '',\n 'Example: seekstone init --vault \"/path/to/vault\"',\n ],\n };\n }\n }\n\n // Resolve to absolute before validating or writing config: clients spawn the\n // server from their own working directory, so a relative SEEKSTONE_VAULT in\n // the written config would silently point at the wrong place.\n vaultPath = path.resolve(deps.cwd ?? process.cwd(), vaultPath);\n\n const check = await validateVault(vaultPath);\n if (!check.ok) {\n return { ok: false, exitCode: 1, output: [`✗ ${check.error}`] };\n }\n\n const out: string[] = [\n `✓ Vault looks good: ${vaultPath}`,\n ` ${check.noteCount} markdown note${check.noteCount === 1 ? '' : 's'} found.`,\n '',\n ];\n\n if (opts.client === 'code') {\n if (opts.write) {\n const spawn =\n deps.spawnClaudeMcp ??\n ((args: string[]) => {\n try {\n // Runs `claude mcp add …` — the canonical way to register an MCP\n // server with Claude Code. execFileSync (no shell) is intentional;\n // writing ~/.claude.json directly would couple us to an internal\n // format Anthropic can change without notice.\n execFileSync('claude', args, { stdio: 'inherit' });\n return { ok: true };\n } catch (err) {\n return { ok: false, error: err instanceof Error ? err.message : String(err) };\n }\n });\n\n const mcpArgs = [\n 'mcp',\n 'add',\n 'seekstone',\n '--env',\n `SEEKSTONE_VAULT=${vaultPath}`,\n '--',\n 'npx',\n '-y',\n 'seekstone',\n ];\n const result = spawn(mcpArgs);\n\n if (result.ok) {\n out.push('✓ seekstone added to Claude Code.', '', 'Restart Claude Code to load seekstone.');\n return { ok: true, exitCode: 0, output: out, ranClaudeMcpAdd: true };\n }\n\n // claude not on PATH or command failed — fall back to manual instructions.\n out.push(\n '⚠ Could not run `claude mcp add` automatically.',\n result.error ? ` ${result.error}` : '',\n '',\n 'Run this command manually instead:',\n '',\n ` ${mcpAddCommand(vaultPath)}`,\n '',\n );\n return { ok: false, exitCode: 1, output: out };\n }\n\n out.push('Add to Claude Code by running:', '', ` ${mcpAddCommand(vaultPath)}`, '');\n return { ok: true, exitCode: 0, output: out };\n }\n\n // File-config clients: Claude Desktop and Cursor share the same\n // `mcpServers` JSON shape; VS Code uses `servers` + explicit `type: stdio`.\n // Only the config path, JSON shape, and restart hint differ.\n const clientLabel =\n opts.client === 'cursor'\n ? 'Cursor'\n : opts.client === 'vscode'\n ? 'VS Code (GitHub Copilot)'\n : 'Claude Desktop';\n const configPath =\n opts.client === 'vscode'\n ? vscodeMcpConfigPath(deps.cwd ?? process.cwd())\n : opts.client === 'cursor'\n ? cursorConfigPath(deps.platform, { home: deps.env.HOME })\n : claudeDesktopConfigPath(deps.platform, {\n home: deps.env.HOME,\n appData: deps.env.APPDATA,\n });\n const block =\n opts.client === 'vscode'\n ? JSON.stringify({ servers: { seekstone: vscodeServerConfig(vaultPath) } }, null, 2)\n : JSON.stringify({ mcpServers: { seekstone: seekstoneServerConfig(vaultPath) } }, null, 2);\n const restartHint =\n opts.client === 'vscode'\n ? 'Reload VS Code, then open Copilot Chat in Agent mode — seekstone appears in the tools picker.'\n : `Restart ${clientLabel} to load seekstone.`;\n\n if (!opts.write) {\n out.push(\n `Add this to your ${clientLabel} config:`,\n ` ${configPath}`,\n ...(opts.client === 'vscode'\n ? [\n ' (workspace config — run from your project root, or use the Command Palette\\'s \"MCP: Open User Configuration\" for user-global)',\n ]\n : []),\n '',\n block,\n '',\n `${restartHint} Or re-run with --write to patch it automatically.`,\n );\n return { ok: true, exitCode: 0, output: out };\n }\n\n // --write: additive, backed-up patch.\n let existing: McpConfig | null = null;\n let existingRaw: string | null = null;\n try {\n existingRaw = await rf(configPath, 'utf8');\n existing = JSON.parse(existingRaw) as McpConfig;\n } catch (err) {\n if (existingRaw !== null) {\n // File exists but isn't valid JSON — refuse rather than clobber.\n return {\n ok: false,\n exitCode: 1,\n output: [\n `✗ ${configPath} exists but is not valid JSON; not modifying it.`,\n ` ${err instanceof Error ? err.message : String(err)}`,\n ],\n };\n }\n existing = null; // file simply doesn't exist yet\n }\n\n const merged =\n opts.client === 'vscode'\n ? mergeSeekstoneConfigVsCode(existing, vaultPath)\n : mergeSeekstoneConfig(existing, vaultPath);\n let backupPath: string | undefined;\n await mkdir(dirname(configPath), { recursive: true });\n if (existingRaw !== null) {\n backupPath = `${configPath}.backup-${deps.timestamp}`;\n await copyFile(configPath, backupPath);\n }\n await writeFile(configPath, `${JSON.stringify(merged, null, 2)}\\n`, 'utf8');\n\n out.push(\n `✓ Patched ${configPath}`,\n backupPath ? ` Backup saved to ${backupPath}` : ' (created a new config file)',\n '',\n restartHint,\n );\n return { ok: true, exitCode: 0, output: out, wrotePath: configPath, backupPath };\n}\n","import { appendFileSync, renameSync, statSync } from 'node:fs';\n\n/**\n * A tiny zero-dependency leveled logger for the MCP server.\n *\n * Two sinks, never stdout (stdout carries the JSON-RPC stdio transport):\n * - stderr: concise human-readable lines, always on (Claude Desktop captures it).\n * - file: JSON-lines, opt-in via SEEKSTONE_LOG_FILE, size-rotated.\n *\n * Privacy: callers pass metadata fields at info/warn/error and only attach note\n * content (bodies, frontmatter values, raw queries) at `debug`. The logger does\n * not inspect or redact fields — that contract lives at the call sites.\n *\n * Note: file writes use appendFileSync. File logging is opt-in and the per-line\n * cost is sub-millisecond, comfortably inside tool latency budgets; the default\n * (stderr-only) path does no disk I/O. An async write queue is a future option.\n */\n\nexport type LogLevel = 'error' | 'warn' | 'info' | 'debug';\n\nconst ORDER: readonly LogLevel[] = ['error', 'warn', 'info', 'debug'] as const;\nconst DEFAULT_MAX_SIZE = 5 * 1024 * 1024;\n\nexport interface Logger {\n readonly level: LogLevel;\n error(msg: string, fields?: Record<string, unknown>): void;\n warn(msg: string, fields?: Record<string, unknown>): void;\n info(msg: string, fields?: Record<string, unknown>): void;\n debug(msg: string, fields?: Record<string, unknown>): void;\n}\n\nexport interface LoggerOptions {\n /** Env to read SEEKSTONE_LOG_* from. Defaults to process.env. */\n env?: Record<string, string | undefined>;\n /** stderr sink — injectable for tests. Defaults to process.stderr. Never stdout. */\n stderr?: (line: string) => void;\n /** Clock — injectable for deterministic tests. */\n now?: () => Date;\n}\n\nfunction parseLevel(raw: string | undefined, onBad: (m: string) => void): LogLevel {\n if (raw === undefined || raw === '') return 'info';\n const v = raw.trim().toLowerCase();\n for (const lvl of ORDER) {\n if (lvl === v) return lvl;\n }\n onBad(`invalid SEEKSTONE_LOG_LEVEL=${JSON.stringify(raw)}; using \"info\"`);\n return 'info';\n}\n\nfunction parseSize(raw: string | undefined): number {\n if (raw === undefined) return DEFAULT_MAX_SIZE;\n const m = /^(\\d+(?:\\.\\d+)?)(b|kb|mb|gb)?$/i.exec(raw.trim());\n if (m === null) return DEFAULT_MAX_SIZE;\n const num = Number(m[1]);\n const unit = (m[2] ?? 'b').toLowerCase();\n const mult = unit === 'gb' ? 1e9 : unit === 'mb' ? 1e6 : unit === 'kb' ? 1e3 : 1;\n return Math.max(1, Math.floor(num * mult));\n}\n\nfunction fmtFields(fields: Record<string, unknown>): string {\n const parts: string[] = [];\n for (const [k, v] of Object.entries(fields)) {\n if (v === undefined) continue;\n parts.push(`${k}=${typeof v === 'string' ? v : JSON.stringify(v)}`);\n }\n return parts.length > 0 ? ` ${parts.join(' ')}` : '';\n}\n\nexport function createLogger(opts: LoggerOptions = {}): Logger {\n const env = opts.env ?? process.env;\n // Default sink is stderr — deliberately never process.stdout.\n const stderr = opts.stderr ?? ((line: string): void => void process.stderr.write(line));\n const now = opts.now ?? ((): Date => new Date());\n\n const level = parseLevel(env.SEEKSTONE_LOG_LEVEL, (m) => {\n stderr(`WARN ${now().toISOString()} ${m}\\n`);\n });\n const threshold = ORDER.indexOf(level);\n\n const filePath = env.SEEKSTONE_LOG_FILE;\n const maxSize = parseSize(env.SEEKSTONE_LOG_MAX_SIZE);\n let fileOk = false;\n let bytes = 0;\n if (filePath !== undefined && filePath !== '') {\n try {\n try {\n bytes = statSync(filePath).size;\n } catch {\n bytes = 0;\n }\n appendFileSync(filePath, ''); // verify writable / create\n fileOk = true;\n } catch (err) {\n const m = err instanceof Error ? err.message : String(err);\n stderr(\n `WARN ${now().toISOString()} cannot write log file ${filePath}: ${m}; file logging disabled\\n`,\n );\n }\n }\n\n function writeFile(record: Record<string, unknown>): void {\n if (!fileOk || filePath === undefined) return;\n const line = `${JSON.stringify(record)}\\n`;\n const len = Buffer.byteLength(line);\n try {\n if (bytes > 0 && bytes + len > maxSize) {\n renameSync(filePath, `${filePath}.1`);\n bytes = 0;\n }\n appendFileSync(filePath, line);\n bytes += len;\n } catch {\n fileOk = false; // stop trying; logging must never crash the request path\n }\n }\n\n function emit(lvl: LogLevel, msg: string, fields?: Record<string, unknown>): void {\n if (ORDER.indexOf(lvl) > threshold) return; // below threshold: do no work\n const ts = now().toISOString();\n const f = fields ?? {};\n stderr(`${lvl.toUpperCase().padEnd(5)} ${ts} ${msg}${fmtFields(f)}\\n`);\n writeFile({ t: ts, level: lvl, msg, ...f });\n }\n\n return {\n level,\n error: (m, f) => {\n emit('error', m, f);\n },\n warn: (m, f) => {\n emit('warn', m, f);\n },\n info: (m, f) => {\n emit('info', m, f);\n },\n debug: (m, f) => {\n emit('debug', m, f);\n },\n };\n}\n","import type { Logger } from './log.js';\n\n/**\n * Install a process-level safety net for the long-lived stdio server session.\n *\n * The server's known async paths (dispatch, watcher) already guard themselves,\n * so a stray `unhandledRejection` is unexpected — but crashing the whole MCP\n * session (and discarding the in-memory index, which is expensive to rebuild)\n * is a worse outcome than logging it and staying up. So we log via stderr and\n * keep running. `uncaughtException` is deliberately left to Node's default\n * crash behaviour: continuing after a truly uncaught throw risks corrupt state.\n *\n * Returns a disposer that removes the listener (used by tests to avoid leaking\n * handlers across cases).\n */\nexport function installProcessGuards(log: Logger): () => void {\n const onUnhandledRejection = (reason: unknown): void => {\n const error = reason instanceof Error ? reason.message : String(reason);\n log.error('unhandled promise rejection (server kept alive)', { error });\n };\n\n process.on('unhandledRejection', onUnhandledRejection);\n\n return () => {\n process.off('unhandledRejection', onUnhandledRejection);\n };\n}\n","import { WRITE_TOOLS } from './dispatch.js';\nimport type { WritePolicy } from './policy.js';\n\n/**\n * The full tool list served to MCP clients, extracted verbatim from the\n * ListTools handler so it can be filtered by policy and unit-tested.\n * Keep in sync with HANDLED_TOOLS in dispatch.ts.\n */\nexport const ALL_TOOLS = [\n {\n name: 'search',\n description:\n 'Full-text search across the vault. Returns ranked excerpts (~200 chars) — not full notes — to minimise context usage. Supports fuzzy matching and prefix search.',\n inputSchema: {\n type: 'object',\n properties: {\n query: { type: 'string', description: 'Search query.' },\n limit: { type: 'number', description: 'Max results (1–50, default 10).' },\n folder: { type: 'string', description: 'Restrict to a vault-relative folder prefix.' },\n tag: { type: 'string', description: 'Restrict to notes with this tag.' },\n },\n required: ['query'],\n },\n },\n {\n name: 'query_notes',\n description:\n 'Structured metadata query — filter notes by frontmatter key/value predicates, tag, folder, modified time, and size. Returns compact rows (path + title by default; opt into more via select), not note content. Use this instead of search when filtering by properties rather than text.',\n inputSchema: {\n type: 'object',\n properties: {\n where: {\n type: 'array',\n description: 'Frontmatter predicates — all must match (AND).',\n items: {\n type: 'object',\n properties: {\n key: { type: 'string', description: 'Frontmatter key to test.' },\n op: {\n type: 'string',\n enum: ['eq', 'ne', 'contains', 'exists', 'missing', 'gt', 'gte', 'lt', 'lte'],\n description:\n 'eq/ne compare scalars; contains matches array membership or substring; exists/missing test key presence; gt/gte/lt/lte compare numbers or strings (ISO dates sort correctly).',\n },\n value: {\n description: 'Comparison value. Required for every op except exists/missing.',\n },\n },\n required: ['key', 'op'],\n },\n },\n folder: { type: 'string', description: 'Restrict to a vault-relative folder prefix.' },\n tag: { type: 'string', description: 'Restrict to notes with this tag (# optional).' },\n modifiedAfter: {\n type: 'string',\n description: 'Only notes modified at or after this ISO 8601 date/time.',\n },\n modifiedBefore: {\n type: 'string',\n description: 'Only notes modified before this ISO 8601 date/time.',\n },\n minSizeBytes: { type: 'number', description: 'Only notes at least this many bytes.' },\n maxSizeBytes: { type: 'number', description: 'Only notes at most this many bytes.' },\n select: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Extra fields per hit: frontmatter keys, or \"mtime\", \"size\", \"tags\". Default returns only path + title.',\n },\n sort: {\n type: 'string',\n enum: ['path', 'title', 'mtime', 'size'],\n description: 'Sort field (default path).',\n },\n order: {\n type: 'string',\n enum: ['asc', 'desc'],\n description: 'Sort order (default asc).',\n },\n limit: { type: 'number', description: 'Max results (1–500, default 100).' },\n },\n required: [],\n },\n },\n {\n name: 'context_pack',\n description:\n 'Assemble everything needed to ANSWER a natural-language question in one call, under a strict byte budget (default 2048): ranked excerpts, linked neighbor notes (backlinks/outlinks) with one-line summaries, and follow-up source paths. Use search to locate notes and query_notes for metadata filters; use context_pack when you want answer-ready context without multiple round-trips. Empty excerpts with confidence \"none\" or \"low\" means the vault lacks coverage — do not infer content.',\n inputSchema: {\n type: 'object',\n properties: {\n query: { type: 'string', description: 'Natural-language question or topic.' },\n budgetBytes: {\n type: 'number',\n description: 'Hard cap on response JSON bytes (256–16384, default 2048).',\n },\n },\n required: ['query'],\n },\n },\n {\n name: 'read_note',\n description:\n 'Read a note or a span of it — by heading section, block reference, or line range. Returns structured JSON with the content, bytes returned, total note size, and a contentHash to pass as prevHash to edit tools for compare-and-swap. Use search or outline_note first to find the right path and section names.',\n inputSchema: {\n type: 'object',\n properties: {\n path: {\n type: 'string',\n description: 'Vault-relative path, e.g. \"Daily Notes/2026-05-29.md\".',\n },\n section: {\n type: 'string',\n description:\n 'Return only this heading section (heading text, # prefix optional). First match wins.',\n },\n block: {\n type: 'string',\n description: 'Return only the line anchored by this block id (^ prefix optional).',\n },\n lines: {\n type: 'object',\n description: 'Return only this line range (1-indexed, inclusive).',\n properties: {\n from: { type: 'number', description: 'First line (1-indexed).' },\n to: {\n type: 'number',\n description: 'Last line (1-indexed, inclusive). Defaults to EOF.',\n },\n },\n required: ['from'],\n },\n includeFrontmatter: {\n type: 'boolean',\n description:\n 'Prepend frontmatter to section/block span results. Default false for spans.',\n },\n },\n required: ['path'],\n },\n },\n {\n name: 'list_notes',\n description: 'List notes, optionally filtered by folder prefix or tag.',\n inputSchema: {\n type: 'object',\n properties: {\n folder: { type: 'string', description: 'Vault-relative folder prefix.' },\n tag: { type: 'string', description: 'Filter by tag (# prefix optional).' },\n limit: { type: 'number', description: 'Max results (1–500, default 100).' },\n },\n required: [],\n },\n },\n {\n name: 'list_tags',\n description:\n 'List all tags in the vault with usage counts. Supports substring filtering, minimum count threshold, and sort order. Nested tags (e.g. area/work) include a parent field.',\n inputSchema: {\n type: 'object',\n properties: {\n pattern: {\n type: 'string',\n description:\n 'Substring filter on tag name. Example: \"work\" matches \"work\" and \"area/work\".',\n },\n minCount: {\n type: 'number',\n description: 'Only return tags used in at least this many notes.',\n },\n sort: {\n type: 'string',\n enum: ['count', 'name'],\n description: 'Sort by usage count descending (default) or alphabetically.',\n },\n },\n required: [],\n },\n },\n {\n name: 'create_note',\n description:\n 'Create a new note at a vault-relative path. Optionally sets frontmatter and body content. Parent directories are created automatically. Fails if the note already exists unless overwrite is true (prevHash may guard the overwrite).',\n inputSchema: {\n type: 'object',\n properties: {\n path: {\n type: 'string',\n description: 'Vault-relative path, e.g. \"Daily Notes/2026-06-01.md\".',\n },\n content: { type: 'string', description: 'Body content for the note.' },\n frontmatter: {\n type: 'object',\n description: 'Frontmatter key-value pairs.',\n additionalProperties: true,\n },\n overwrite: {\n type: 'boolean',\n description: 'Overwrite an existing note. Defaults to false.',\n },\n prevHash: {\n type: 'string',\n description:\n 'Optional compare-and-swap guard: the contentHash from a prior read. Fails with hash_conflict if the note changed since.',\n },\n },\n required: ['path'],\n },\n },\n {\n name: 'delete_note',\n description:\n 'Delete a note. By default it is moved to the vault .trash/ folder (recoverable by moving it back); pass permanent: true to remove it outright.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Vault-relative path of the note to delete.' },\n permanent: {\n type: 'boolean',\n description: 'Permanently remove instead of moving to .trash/. Defaults to false.',\n },\n },\n required: ['path'],\n },\n },\n {\n name: 'move_note',\n description:\n 'Move or rename a note to a new vault-relative path, rewriting wikilinks and markdown links in other notes that point at it so nothing breaks (links inside fenced code blocks are left alone). Parent directories at the destination are created automatically. Fails if the destination already exists unless overwrite is true.',\n inputSchema: {\n type: 'object',\n properties: {\n from: { type: 'string', description: 'Vault-relative source path.' },\n to: { type: 'string', description: 'Vault-relative destination path.' },\n overwrite: {\n type: 'boolean',\n description: 'Overwrite destination if it exists. Defaults to false.',\n },\n rewriteLinks: {\n type: 'boolean',\n description:\n 'Rewrite references in other notes to follow the move. Defaults to true; pass false to move the file only.',\n },\n },\n required: ['from', 'to'],\n },\n },\n {\n name: 'rename_heading',\n description:\n 'Rename a heading in a note and rewrite every [[note#heading]] wikilink and embed across the vault so references keep working — aliases preserved, fenced code blocks left alone. Served from the warm backlink index, no vault scan. Heading matching is case-insensitive; with duplicate headings the first match wins, mirroring Obsidian link resolution.',\n inputSchema: {\n type: 'object',\n properties: {\n path: {\n type: 'string',\n description: 'Vault-relative path of the note containing the heading.',\n },\n oldHeading: {\n type: 'string',\n description:\n 'Current heading text, without # markers. Case-insensitive; first match wins.',\n },\n newHeading: {\n type: 'string',\n description:\n 'New heading text, without # markers. Must not contain newlines, \"[\", \"]\", \"|\", or \"#\".',\n },\n prevHash: {\n type: 'string',\n description:\n 'Optional compare-and-swap guard: the contentHash from a prior read. Fails with hash_conflict if the note changed since.',\n },\n },\n required: ['path', 'oldHeading', 'newHeading'],\n },\n },\n {\n name: 'append_note',\n description:\n 'Append text to a note body without touching the frontmatter. Safe for meeting notes, daily logs, and append-only workflows.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Vault-relative path to the note.' },\n content: { type: 'string', description: 'Text to append.' },\n prevHash: {\n type: 'string',\n description:\n 'Optional compare-and-swap guard: the contentHash from a prior read. Fails with hash_conflict if the note changed since.',\n },\n },\n required: ['path', 'content'],\n },\n },\n {\n name: 'patch_frontmatter',\n description:\n 'Set, update, or delete frontmatter keys without reordering existing keys or changing quote style. Pass null as a value to delete a key.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Vault-relative path to the note.' },\n patch: {\n type: 'object',\n description: 'Key-value pairs to set. Null value removes the key.',\n additionalProperties: true,\n },\n prevHash: {\n type: 'string',\n description:\n 'Optional compare-and-swap guard: the contentHash from a prior read. Fails with hash_conflict if the note changed since.',\n },\n },\n required: ['path', 'patch'],\n },\n },\n {\n name: 'outline_note',\n description:\n \"Return a note's structure — heading tree with offsets, block-reference anchors, and frontmatter key list — without returning any prose. Use this before section reads or patches to discover what sections exist at a fraction of the cost of reading the full note.\",\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Vault-relative path to the note.' },\n includeBlocks: {\n type: 'boolean',\n description: 'Include block-reference anchors (^id). Default true.',\n },\n includeSizes: {\n type: 'boolean',\n description: 'Include per-section character length in each heading entry. Default false.',\n },\n },\n required: ['path'],\n },\n },\n {\n name: 'patch_note',\n description:\n 'Surgically edit a section of a note — targeted by heading or block reference — without rewriting the whole file. Operations: append (add after section), prepend (add after heading line), replace (swap section content). Frontmatter is never touched.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Vault-relative path to the note.' },\n target: {\n type: 'object',\n description:\n 'Exactly one of: { heading: \"Section Title\" } or { block: \"block-id\" } (without the ^ prefix).',\n },\n operation: {\n type: 'string',\n enum: ['append', 'prepend', 'replace'],\n description:\n 'append: insert after section content. prepend: insert after heading line. replace: swap section content.',\n },\n content: { type: 'string', description: 'Content to insert or replace with.' },\n createIfMissing: {\n type: 'boolean',\n description:\n 'If the heading target is not found, append a new heading (level 2) + content. Only valid for heading targets. Default false.',\n },\n prevHash: {\n type: 'string',\n description:\n 'Optional compare-and-swap guard: the contentHash from a prior read. Fails with hash_conflict if the note changed since.',\n },\n },\n required: ['path', 'target', 'operation', 'content'],\n },\n },\n {\n name: 'get_backlinks',\n description:\n 'Return every note that links to the target note, with the source line and an optional excerpt. Results come from the pre-built reverse-link index so this is a fast, pure index lookup. Sort order: source path ascending.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Vault-relative path to the target note.' },\n includeContext: {\n type: 'boolean',\n description: 'Include a short excerpt (~200 chars) from the linking line. Default true.',\n },\n limit: {\n type: 'number',\n description: 'Max backlinks to return (1–500). Default 50.',\n },\n },\n required: ['path'],\n },\n },\n {\n name: 'get_links',\n description:\n 'Return all outgoing wikilinks and embeds from a note. Each link is marked resolved (with target path) or unresolved. Duplicate targets are de-duplicated; results sorted by line number.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Vault-relative path to the note.' },\n },\n required: ['path'],\n },\n },\n {\n name: 'replace_in_note',\n description:\n 'Find and replace text within a note body. Supports literal and regex search, case sensitivity, whole-word matching, and a replacement limit. Frontmatter is never touched. Use dryRun to preview matches before writing.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Vault-relative path to the note.' },\n find: { type: 'string', description: 'Text or pattern to find.' },\n replace: {\n type: 'string',\n description: 'Replacement text. Supports $1, $2, … backreferences in regex mode.',\n },\n regex: {\n type: 'boolean',\n description: 'Treat find as a regular expression. Default false.',\n },\n caseSensitive: {\n type: 'boolean',\n description: 'Case-sensitive matching. Default false.',\n },\n wholeWord: {\n type: 'boolean',\n description: 'Match whole words only (\\\\b boundary). Default false.',\n },\n limit: {\n type: 'number',\n description: 'Maximum number of replacements. Omit to replace all.',\n },\n dryRun: {\n type: 'boolean',\n description: 'If true, report matches without writing. Default false.',\n },\n prevHash: {\n type: 'string',\n description:\n 'Optional compare-and-swap guard: the contentHash from a prior read. Fails with hash_conflict if the note changed since.',\n },\n },\n required: ['path', 'find', 'replace'],\n },\n },\n {\n name: 'get_periodic_note',\n description:\n 'Get the path and existence status of a periodic note (daily, weekly, monthly, quarterly, or yearly) for a given date. Reads folder/format config from .obsidian/daily-notes.json (daily) or the periodic-notes plugin data.json. Optionally creates the note from the configured template if it is missing.',\n inputSchema: {\n type: 'object',\n properties: {\n period: {\n type: 'string',\n enum: ['daily', 'weekly', 'monthly', 'quarterly', 'yearly'],\n description: 'Period type. Default: daily.',\n },\n date: {\n type: 'string',\n description: 'ISO date string (YYYY-MM-DD). Defaults to today when omitted.',\n },\n createIfMissing: {\n type: 'boolean',\n description:\n 'Create the note from the configured template if it does not exist. Default false.',\n },\n },\n required: [],\n },\n },\n {\n name: 'append_periodic_note',\n description:\n 'Append text to a periodic note (daily, weekly, monthly, quarterly, or yearly). Preserves existing frontmatter exactly. Creates the note first (from template if configured) when createIfMissing is true (default).',\n inputSchema: {\n type: 'object',\n properties: {\n period: {\n type: 'string',\n enum: ['daily', 'weekly', 'monthly', 'quarterly', 'yearly'],\n description: 'Period type. Default: daily.',\n },\n date: {\n type: 'string',\n description: 'ISO date string (YYYY-MM-DD). Defaults to today when omitted.',\n },\n content: {\n type: 'string',\n description: 'Text to append to the note body.',\n },\n createIfMissing: {\n type: 'boolean',\n description: 'Create the note if it does not exist before appending. Default true.',\n },\n prevHash: {\n type: 'string',\n description:\n 'Optional compare-and-swap guard: the contentHash from a prior read. Fails with hash_conflict if the note changed since.',\n },\n },\n required: ['content'],\n },\n },\n];\n\n/**\n * The tools visible to the client under a policy. In read-only mode write\n * tools are unregistered entirely (not just failing) — better for context\n * economy and honest about what the session can do. Dispatch still rejects\n * write calls independently; unregistering is presentation, not enforcement.\n */\nexport function visibleTools(policy: WritePolicy) {\n if (!policy.readOnly) return ALL_TOOLS;\n return ALL_TOOLS.filter((t) => !WRITE_TOOLS.has(t.name));\n}\n","import { realpathSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { join, relative, sep } from 'node:path';\nimport chokidar from 'chokidar';\nimport type { ServerContext } from './context.js';\nimport { addNoteBacklinks, removeNoteBacklinks } from './index/backlinks.js';\nimport { buildDoc, upsertDoc } from './index/doc.js';\nimport type { Logger } from './log.js';\n\n/**\n * Watch the vault for .md changes and keep the in-memory index in sync.\n *\n * Uses chokidar rather than `fs.watch(..., { recursive: true })`: recursive\n * native watching is unsupported on Linux, where fs.watch silently misses\n * nested changes. chokidar gives reliable, recursive, cross-platform events.\n *\n * Returns `stop()` to tear down the watcher and a `ready` promise that\n * resolves once chokidar has finished its initial scan and is live.\n */\nexport interface WatcherHandle {\n /**\n * Close the watcher. Returns chokidar's close promise so callers that need\n * deterministic teardown (tests, especially under usePolling where every\n * watched path holds a live stat-poll timer) can await full shutdown;\n * fire-and-forget callers may ignore the result.\n */\n stop: () => Promise<void>;\n ready: Promise<void>;\n}\n\nexport interface WatcherOptions {\n /**\n * Stat-poll instead of using native OS file events. Slower but reliable on\n * filesystems where native events are unreliable (network mounts, WSL, some\n * containers) and deterministic under load. Defaults to false, or true when\n * SEEKSTONE_WATCH_POLL=1.\n */\n usePolling?: boolean;\n /**\n * Diagnostic hook fired for every raw chokidar event, before .md filtering\n * and before the index is updated. Receives the chokidar event name, the\n * absolute path as chokidar reported it, and the vault-relative key as\n * computed by toRel(). Use in tests only to capture what chokidar actually\n * emits when a failure needs investigation.\n */\n onRawEvent?: (event: 'add' | 'change' | 'unlink', absPath: string, relPath: string) => void;\n}\n\nexport function startWatcher(\n ctx: ServerContext,\n log?: Logger,\n opts?: WatcherOptions,\n): WatcherHandle {\n const usePolling = opts?.usePolling ?? process.env.SEEKSTONE_WATCH_POLL === '1';\n\n // chokidar's followSymlinks=true (the default) calls fs.realpath() on the\n // watched path, so events arrive with the real (resolved) path. On Windows,\n // os.tmpdir() can return 8.3 short paths (e.g. RUNNER~1) while realpath()\n // expands them to the long form. Without pre-resolving here, relative()\n // inside the ignored predicate and toRel() would produce \"..\" segments;\n // \"..\" starts with \".\" and trips the dot-directory filter, silencing every\n // event — the root cause of the flaky \"vault path containing spaces\" test.\n let vaultRoot = ctx.vaultRoot;\n try {\n vaultRoot = realpathSync(ctx.vaultRoot);\n } catch {\n // Vault doesn't exist yet or can't be resolved — keep original path.\n }\n\n // Index keys are forward-slash vault-relative paths on every platform.\n const toRel = (abs: string): string => relative(vaultRoot, abs).split(sep).join('/');\n\n async function upsert(relPath: string, op: 'add' | 'change'): Promise<void> {\n try {\n const raw = await readFile(join(vaultRoot, relPath), 'utf8');\n removeNoteBacklinks(ctx, relPath);\n upsertDoc(ctx, buildDoc(relPath, raw));\n addNoteBacklinks(ctx, relPath, raw);\n log?.debug('index updated', { path: relPath, op });\n } catch {\n // File vanished between event and read — ignore; an unlink will follow.\n }\n }\n\n function remove(relPath: string): void {\n if (ctx.notes.has(relPath)) {\n removeNoteBacklinks(ctx, relPath);\n ctx.index.discard(relPath);\n ctx.notes.delete(relPath);\n log?.debug('index removed', { path: relPath, op: 'delete' });\n }\n }\n\n const onUpsert = (op: 'add' | 'change') => (abs: string) => {\n const relPath = toRel(abs);\n opts?.onRawEvent?.(op, abs, relPath);\n if (relPath.endsWith('.md')) void upsert(relPath, op);\n };\n\n const watcher = chokidar.watch(vaultRoot, {\n // The startup index build already covers existing files.\n ignoreInitial: true,\n usePolling,\n interval: 50,\n binaryInterval: 100,\n // Skip dot-dirs (.obsidian, .git, .trash, …) regardless of where the vault\n // lives — matched on the path relative to the vault root.\n ignored: (p: string) => {\n const rel = relative(vaultRoot, p);\n if (!rel || rel === '.') return false;\n // Exclude \"..\" — it means \"outside the vault root\" (a path normalisation\n // mismatch), not a dotfile. Treating \"..\" as a dot-segment would silence\n // all events if the ignored predicate ever receives an out-of-root path.\n return rel\n .split(sep)\n .filter(Boolean)\n .some((seg) => seg.startsWith('.') && seg !== '..');\n },\n });\n\n watcher\n .on('add', onUpsert('add'))\n .on('change', onUpsert('change'))\n .on('unlink', (abs: string) => {\n const relPath = toRel(abs);\n opts?.onRawEvent?.('unlink', abs, relPath);\n if (relPath.endsWith('.md')) remove(relPath);\n });\n\n const ready = new Promise<void>((resolve) => {\n watcher.once('ready', () => {\n resolve();\n });\n });\n\n return {\n stop: () => watcher.close(),\n ready,\n };\n}\n"],"mappings":"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","names":["resolve","path","policy","vaultRoot","ctx","z","notes","path","z","z","path","ctx","note","noteTags","z","ctx","path","readFile","z","ctx","z","ctx","readFile","access","mkdir","rename","basename","extname","z","ctx","z","vaultRoot","extname","access","ctx","mkdir","basename","rename","z","z","ctx","backlinks","z","z","ctx","z","ctx","path","z","ctx","access","mkdir","rename","dirname","join","z","matches","z","ctx","access","path","mkdir","dirname","rename","join","readFile","z","z","ctx","readFile","readFile","parseDocument","z","z","ctx","readFile","parseDocument","readFile","z","z","ctx","readFile","sep","newContent","path","access","mkdir","readFile","dirname","join","z","z","readFile","vaultRoot","cfg","join","ctx","path","access","mkdir","dirname","z","z","ctx","path","readFile","z","z","ctx","readFile","readFile","join","z","z","ctx","readFile","lineEnd","path","join","readFile","z","z","ctx","readFile","matches","ctx","log","readFile","basename","extname","extname","join","sep","vaultRoot","matches","vaultRoot","index","notes","readFile","basename","extname","backlinks","access","mkdir","readFile","stat","writeFile","dirname","join","j","writeFile","log","policy","readFile","join","relative","sep","ctx","log","vaultRoot","relative","sep","readFile","join","watcher","resolve","resolve"]}