seekstone 0.4.0 → 0.5.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 CHANGED
@@ -848,8 +848,101 @@ async function readNote(ctx2, input) {
848
848
  };
849
849
  }
850
850
 
851
- // src/tools/search.ts
851
+ // src/tools/replace_in_note.ts
852
+ import { readFile as readFile6, rename as rename3, writeFile as writeFile5 } from "fs/promises";
853
+ import { join as join11 } from "path";
852
854
  import { z as z13 } from "zod";
855
+ var ReplaceInNoteInput = z13.object({
856
+ path: z13.string().describe("Vault-relative path to the note."),
857
+ find: z13.string().min(1).describe("Text or pattern to find."),
858
+ replace: z13.string().describe("Replacement text. Supports $1, $2, \u2026 backreferences in regex mode."),
859
+ regex: z13.boolean().default(false).describe("Treat find as a regular expression."),
860
+ caseSensitive: z13.boolean().default(false).describe("Case-sensitive matching."),
861
+ wholeWord: z13.boolean().default(false).describe("Match whole words only (\\b word-boundary)."),
862
+ limit: z13.number().int().min(1).optional().describe("Maximum number of replacements. Omit to replace all."),
863
+ dryRun: z13.boolean().default(false).describe("If true, report matches without writing anything.")
864
+ });
865
+ function escapeRegex(s) {
866
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
867
+ }
868
+ function buildRegex(find, opts) {
869
+ let pattern = opts.regex ? find : escapeRegex(find);
870
+ if (opts.wholeWord) pattern = `\\b(?:${pattern})\\b`;
871
+ const flags = `g${opts.caseSensitive ? "" : "i"}`;
872
+ return new RegExp(pattern, flags);
873
+ }
874
+ async function atomicWrite2(absPath, content) {
875
+ const tmpPath = `${absPath}.seekstone-replace-tmp`;
876
+ await writeFile5(tmpPath, content, "utf8");
877
+ await rename3(tmpPath, absPath);
878
+ }
879
+ async function replaceInNote(ctx2, input) {
880
+ const absPath = join11(ctx2.vaultRoot, input.path);
881
+ if (!absPath.startsWith(ctx2.vaultRoot)) {
882
+ throw new Error(`Path outside vault: ${input.path}`);
883
+ }
884
+ let re;
885
+ try {
886
+ re = buildRegex(input.find, {
887
+ regex: input.regex,
888
+ caseSensitive: input.caseSensitive,
889
+ wholeWord: input.wholeWord
890
+ });
891
+ } catch (err) {
892
+ throw new Error(`Invalid regex: ${err instanceof Error ? err.message : String(err)}`);
893
+ }
894
+ const raw = await readFile6(absPath, "utf8");
895
+ const fm = parseFrontmatter(raw);
896
+ const originalFmRegion = raw.slice(0, fm.bodyStart);
897
+ const body = raw.slice(fm.bodyStart);
898
+ const allMatches = [...body.matchAll(re)];
899
+ const toReplace = input.limit !== void 0 ? allMatches.slice(0, input.limit) : allMatches;
900
+ const matches = toReplace.map((m) => {
901
+ const fileOffset = fm.bodyStart + (m.index ?? 0);
902
+ const line = raw.slice(0, fileOffset).split("\n").length;
903
+ return { line, byteOffset: fileOffset };
904
+ });
905
+ if (input.dryRun || toReplace.length === 0) {
906
+ return {
907
+ path: input.path,
908
+ replacements: toReplace.length,
909
+ matches,
910
+ frontmatterUnchanged: true
911
+ };
912
+ }
913
+ const singleRe = new RegExp(re.source, re.flags.replace("g", ""));
914
+ let newBody = "";
915
+ let lastIndex = 0;
916
+ for (const m of toReplace) {
917
+ const matchIndex = m.index ?? 0;
918
+ newBody += body.slice(lastIndex, matchIndex);
919
+ newBody += input.regex ? m[0].replace(singleRe, input.replace) : input.replace;
920
+ lastIndex = matchIndex + m[0].length;
921
+ }
922
+ newBody += body.slice(lastIndex);
923
+ const newContent = `${originalFmRegion}${newBody}`;
924
+ await atomicWrite2(absPath, newContent);
925
+ const written = await readFile6(absPath, "utf8");
926
+ if (written.slice(0, originalFmRegion.length) !== originalFmRegion) {
927
+ throw new Error("Write-safety violation: frontmatter region changed unexpectedly");
928
+ }
929
+ const cached = ctx2.notes.get(input.path);
930
+ if (cached) {
931
+ cached.body = newBody;
932
+ cached.raw = newContent;
933
+ cached.sizeBytes = Buffer.byteLength(newContent, "utf8");
934
+ }
935
+ return {
936
+ path: input.path,
937
+ replacements: toReplace.length,
938
+ matches,
939
+ bytesWritten: Buffer.byteLength(newContent, "utf8"),
940
+ frontmatterUnchanged: true
941
+ };
942
+ }
943
+
944
+ // src/tools/search.ts
945
+ import { z as z14 } from "zod";
853
946
 
854
947
  // src/index/excerpt.ts
855
948
  function extractExcerpt(text, terms, maxLen = 200) {
@@ -876,11 +969,11 @@ function truncate(s, max) {
876
969
  }
877
970
 
878
971
  // src/tools/search.ts
879
- var SearchInput = z13.object({
880
- query: z13.string().min(1).describe("Search query. Supports fuzzy matching and prefix search."),
881
- limit: z13.number().int().min(1).max(50).default(10).describe("Maximum number of results to return."),
882
- folder: z13.string().optional().describe("Restrict results to notes under this vault-relative folder prefix."),
883
- tag: z13.string().optional().describe("Restrict results to notes containing this tag.")
972
+ var SearchInput = z14.object({
973
+ query: z14.string().min(1).describe("Search query. Supports fuzzy matching and prefix search."),
974
+ limit: z14.number().int().min(1).max(50).default(10).describe("Maximum number of results to return."),
975
+ folder: z14.string().optional().describe("Restrict results to notes under this vault-relative folder prefix."),
976
+ tag: z14.string().optional().describe("Restrict results to notes containing this tag.")
884
977
  });
885
978
  function search(ctx2, input) {
886
979
  const results = ctx2.index.search(input.query, {
@@ -1036,18 +1129,23 @@ async function run(ctx2, name, args) {
1036
1129
  const result = getLinks(ctx2, input);
1037
1130
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1038
1131
  }
1132
+ case "replace_in_note": {
1133
+ const input = ReplaceInNoteInput.parse(args);
1134
+ const result = await replaceInNote(ctx2, input);
1135
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1136
+ }
1039
1137
  default:
1040
1138
  throw new Error(`Unknown tool: ${name}`);
1041
1139
  }
1042
1140
  }
1043
1141
 
1044
1142
  // src/index/build.ts
1045
- import { readFile as readFile6 } from "fs/promises";
1143
+ import { readFile as readFile7 } from "fs/promises";
1046
1144
  import { basename as basename2, extname as extname3 } from "path";
1047
1145
 
1048
1146
  // ../core/src/walk.ts
1049
1147
  import { stat } from "fs/promises";
1050
- import { extname as extname2, join as join11, relative, sep } from "path";
1148
+ import { extname as extname2, join as join12, relative, sep } from "path";
1051
1149
  import fg from "fast-glob";
1052
1150
  var IMAGE_EXTS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg", ".bmp", ".heic"]);
1053
1151
  var VIDEO_EXTS = /* @__PURE__ */ new Set([".mp4", ".mov", ".webm", ".mkv", ".avi"]);
@@ -1124,7 +1222,7 @@ async function buildIndex(vaultRoot2) {
1124
1222
  await Promise.all(
1125
1223
  noteEntries.map(async (entry) => {
1126
1224
  try {
1127
- const raw = await readFile6(entry.absPath, "utf8");
1225
+ const raw = await readFile7(entry.absPath, "utf8");
1128
1226
  const fm = parseFrontmatter(raw);
1129
1227
  const inlineTags = extractInlineTags(fm.body);
1130
1228
  const fmTags = frontmatterTags(fm.data);
@@ -1176,8 +1274,8 @@ function buildBacklinks(notes2) {
1176
1274
 
1177
1275
  // src/init.ts
1178
1276
  import { execFileSync } from "child_process";
1179
- import { access as access3, copyFile, mkdir as mkdir3, readFile as readFile7, readdir, stat as stat2, writeFile as writeFile5 } from "fs/promises";
1180
- import path, { dirname as dirname3, join as join12 } from "path";
1277
+ import { access as access3, copyFile, mkdir as mkdir3, readFile as readFile8, readdir, stat as stat2, writeFile as writeFile6 } from "fs/promises";
1278
+ import path, { dirname as dirname3, join as join13 } from "path";
1181
1279
  function parseInitArgs(argv) {
1182
1280
  const opts = { write: false, client: "desktop" };
1183
1281
  for (let i = 0; i < argv.length; i++) {
@@ -1265,7 +1363,7 @@ async function validateVault(vaultPath) {
1265
1363
  return { ok: false, error: `Path does not exist: ${vaultPath}` };
1266
1364
  }
1267
1365
  try {
1268
- await access3(join12(vaultPath, ".obsidian"));
1366
+ await access3(join13(vaultPath, ".obsidian"));
1269
1367
  } catch {
1270
1368
  return {
1271
1369
  ok: false,
@@ -1285,7 +1383,7 @@ async function countMarkdown(root) {
1285
1383
  }
1286
1384
  for (const e of entries) {
1287
1385
  if (e.name.startsWith(".")) continue;
1288
- const full = join12(dir, e.name);
1386
+ const full = join13(dir, e.name);
1289
1387
  if (e.isDirectory()) await walk(full);
1290
1388
  else if (e.name.endsWith(".md")) count++;
1291
1389
  }
@@ -1294,7 +1392,7 @@ async function countMarkdown(root) {
1294
1392
  return count;
1295
1393
  }
1296
1394
  async function runInit(opts, deps) {
1297
- const rf = deps.readFile ?? readFile7;
1395
+ const rf = deps.readFile ?? readFile8;
1298
1396
  let vaultPath = opts.vault ?? deps.env.SEEKSTONE_VAULT;
1299
1397
  if (!vaultPath) {
1300
1398
  const registryPath = obsidianRegistryPath(deps.platform, {
@@ -1428,7 +1526,7 @@ async function runInit(opts, deps) {
1428
1526
  backupPath = `${configPath}.backup-${deps.timestamp}`;
1429
1527
  await copyFile(configPath, backupPath);
1430
1528
  }
1431
- await writeFile5(configPath, `${JSON.stringify(merged, null, 2)}
1529
+ await writeFile6(configPath, `${JSON.stringify(merged, null, 2)}
1432
1530
  `, "utf8");
1433
1531
  out.push(
1434
1532
  `\u2713 Patched ${configPath}`,
@@ -1500,7 +1598,7 @@ function createLogger(opts = {}) {
1500
1598
  );
1501
1599
  }
1502
1600
  }
1503
- function writeFile6(record) {
1601
+ function writeFile7(record) {
1504
1602
  if (!fileOk || filePath === void 0) return;
1505
1603
  const line = `${JSON.stringify(record)}
1506
1604
  `;
@@ -1522,7 +1620,7 @@ function createLogger(opts = {}) {
1522
1620
  const f = fields ?? {};
1523
1621
  stderr(`${lvl.toUpperCase().padEnd(5)} ${ts} ${msg}${fmtFields(f)}
1524
1622
  `);
1525
- writeFile6({ t: ts, level: lvl, msg, ...f });
1623
+ writeFile7({ t: ts, level: lvl, msg, ...f });
1526
1624
  }
1527
1625
  return {
1528
1626
  level,
@@ -1534,8 +1632,8 @@ function createLogger(opts = {}) {
1534
1632
  }
1535
1633
 
1536
1634
  // src/watcher.ts
1537
- import { readFile as readFile8 } from "fs/promises";
1538
- import { join as join13, relative as relative2, sep as sep2 } from "path";
1635
+ import { readFile as readFile9 } from "fs/promises";
1636
+ import { join as join14, relative as relative2, sep as sep2 } from "path";
1539
1637
  import chokidar from "chokidar";
1540
1638
  function removeNoteBacklinks(ctx2, relPath) {
1541
1639
  const oldDoc = ctx2.notes.get(relPath);
@@ -1572,7 +1670,7 @@ function startWatcher(ctx2, log2, opts) {
1572
1670
  const toRel = (abs) => relative2(ctx2.vaultRoot, abs).split(sep2).join("/");
1573
1671
  async function upsert(relPath, op) {
1574
1672
  try {
1575
- const raw = await readFile8(join13(ctx2.vaultRoot, relPath), "utf8");
1673
+ const raw = await readFile9(join14(ctx2.vaultRoot, relPath), "utf8");
1576
1674
  removeNoteBacklinks(ctx2, relPath);
1577
1675
  upsertDoc(ctx2, buildDoc(relPath, raw));
1578
1676
  addNoteBacklinks(ctx2, relPath, raw);
@@ -1620,7 +1718,7 @@ function startWatcher(ctx2, log2, opts) {
1620
1718
  }
1621
1719
 
1622
1720
  // src/index.ts
1623
- var VERSION = true ? "0.4.0" : "0.0.0-dev";
1721
+ var VERSION = true ? "0.5.0" : "0.0.0-dev";
1624
1722
  var intent = parseCliIntent(process.argv.slice(2));
1625
1723
  if (intent === "version") {
1626
1724
  process.stdout.write(`${VERSION}
@@ -1654,7 +1752,7 @@ log.info("index ready", { notes: notes.size, buildMs });
1654
1752
  var ctx = { vaultRoot, index, notes, backlinks };
1655
1753
  var watcher = startWatcher(ctx, log);
1656
1754
  process.on("exit", watcher.stop);
1657
- var server = new Server({ name: "seekstone", version: "0.1.0" }, { capabilities: { tools: {} } });
1755
+ var server = new Server({ name: "seekstone", version: VERSION }, { capabilities: { tools: {} } });
1658
1756
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
1659
1757
  tools: [
1660
1758
  {
@@ -1897,6 +1995,42 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
1897
1995
  },
1898
1996
  required: ["path"]
1899
1997
  }
1998
+ },
1999
+ {
2000
+ name: "replace_in_note",
2001
+ description: "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.",
2002
+ inputSchema: {
2003
+ type: "object",
2004
+ properties: {
2005
+ path: { type: "string", description: "Vault-relative path to the note." },
2006
+ find: { type: "string", description: "Text or pattern to find." },
2007
+ replace: {
2008
+ type: "string",
2009
+ description: "Replacement text. Supports $1, $2, \u2026 backreferences in regex mode."
2010
+ },
2011
+ regex: {
2012
+ type: "boolean",
2013
+ description: "Treat find as a regular expression. Default false."
2014
+ },
2015
+ caseSensitive: {
2016
+ type: "boolean",
2017
+ description: "Case-sensitive matching. Default false."
2018
+ },
2019
+ wholeWord: {
2020
+ type: "boolean",
2021
+ description: "Match whole words only (\\b boundary). Default false."
2022
+ },
2023
+ limit: {
2024
+ type: "number",
2025
+ description: "Maximum number of replacements. Omit to replace all."
2026
+ },
2027
+ dryRun: {
2028
+ type: "boolean",
2029
+ description: "If true, report matches without writing. Default false."
2030
+ }
2031
+ },
2032
+ required: ["path", "find", "replace"]
2033
+ }
1900
2034
  }
1901
2035
  ]
1902
2036
  }));
@@ -1906,7 +2040,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
1906
2040
  });
1907
2041
  var transport = new StdioServerTransport();
1908
2042
  await server.connect(transport);
1909
- log.info("ready", { tools: 13, transport: "stdio" });
2043
+ log.info("ready", { tools: 14, transport: "stdio" });
1910
2044
  process.stderr.write(
1911
2045
  `seekstone: add to Claude Desktop:
1912
2046
  ${JSON.stringify(
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/tools/create_note.ts","../src/index/doc.ts","../../core/src/extract.ts","../src/tools/delete_note.ts","../src/tools/get_backlinks.ts","../src/tools/get_links.ts","../src/index/resolve.ts","../src/tools/list_notes.ts","../src/tools/list_tags.ts","../src/tools/move_note.ts","../src/tools/outline_note.ts","../../core/src/outline.ts","../src/tools/patch_frontmatter.ts","../src/tools/patch_note.ts","../src/tools/read_note.ts","../src/tools/search.ts","../src/index/excerpt.ts","../src/dispatch.ts","../src/index/build.ts","../../core/src/walk.ts","../src/init.ts","../src/log.ts","../src/watcher.ts"],"sourcesContent":["#!/usr/bin/env node\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, 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 { 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') {\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 });\n process.stdout.write(`${result.output.join('\\n')}\\n`);\n process.exit(result.exitCode);\n}\n\nconst log = createLogger();\n\nconst vaultRoot = process.env.SEEKSTONE_VAULT;\nif (!vaultRoot) {\n log.error('SEEKSTONE_VAULT env var is required');\n process.exit(1);\n}\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 };\n\nconst watcher = startWatcher(ctx, log);\nprocess.on('exit', watcher.stop);\n\nconst server = new Server({ name: 'seekstone', version: '0.1.0' }, { capabilities: { tools: {} } });\n\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({\n 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: '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, and total note size so payload savings are measurable. 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.',\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 },\n required: ['path'],\n },\n },\n {\n name: 'delete_note',\n description: 'Permanently delete a note from the vault. This cannot be undone.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Vault-relative path of the note to delete.' },\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. 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 },\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 },\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 },\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:\n '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 },\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:\n '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}));\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: 13, 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' | 'run';\n\nexport function parseCliIntent(argv: readonly string[]): CliIntent {\n // A subcommand, if present, is the first non-flag token.\n if (argv[0] === 'init') return 'init';\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\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), 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 Claude config\n seekstone --version Print the version and exit\n seekstone --help Print this help and exit\n\n\"seekstone init\" options:\n --vault <path> Vault to use (auto-detected from Obsidian if omitted; fallback: $SEEKSTONE_VAULT)\n --client <name> desktop (default) | code\n --write Patch the Claude Desktop config in place (backs it up first)\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","import { readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.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});\nexport type AppendNoteInput = z.infer<typeof AppendNoteInput>;\n\nexport interface AppendNoteResult {\n path: string;\n bytesWritten: number;\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 (overwrite in place, same path).\n */\nexport async function appendNote(\n ctx: ServerContext,\n input: AppendNoteInput,\n): Promise<AppendNoteResult> {\n const absPath = join(ctx.vaultRoot, input.path);\n if (!absPath.startsWith(ctx.vaultRoot)) {\n throw new Error(`Path outside vault: ${input.path}`);\n }\n\n const original = await readFile(absPath, 'utf8');\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 writeFile(absPath, newContent, 'utf8');\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 { path: input.path, bytesWritten: Buffer.byteLength(newContent, 'utf8') };\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 { access, mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { stringify as stringifyYaml } from 'yaml';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.js';\nimport { buildDoc, upsertDoc } from '../index/doc.js';\n\nexport const CreateNoteInput = z.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('Overwrite an existing note. Defaults to false — throws if the note already exists.'),\n});\nexport type CreateNoteInput = z.input<typeof CreateNoteInput>;\n\nexport interface CreateNoteResult {\n path: string;\n bytesWritten: number;\n}\n\nexport async function createNote(\n ctx: ServerContext,\n input: CreateNoteInput,\n): Promise<CreateNoteResult> {\n const absPath = join(ctx.vaultRoot, input.path);\n if (!absPath.startsWith(ctx.vaultRoot)) {\n throw new Error(`Path outside vault: ${input.path}`);\n }\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 }\n\n let raw = '';\n if (input.frontmatter && Object.keys(input.frontmatter).length > 0) {\n raw = `---\\n${stringifyYaml(input.frontmatter)}---\\n`;\n }\n raw += input.content ?? '';\n\n await mkdir(dirname(absPath), { recursive: true });\n await writeFile(absPath, raw, 'utf8');\n\n // Sync in-memory index so the new note is immediately searchable.\n upsertDoc(ctx, buildDoc(input.path, raw));\n\n return { path: input.path, bytesWritten: Buffer.byteLength(raw, 'utf8') };\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 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","/**\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\nconst WIKILINK_RE = /\\[\\[([^\\]|#\\n]+)(#[^\\]|\\n]+)?(\\|[^\\]\\n]+)?\\]\\]/g;\n\n// Matches both embeds (![[...]]) and plain wikilinks ([[...]]).\nconst LINK_WITH_LINES_RE_SRC = /(!?\\[\\[)([^\\]|#\\n]+)(#[^\\]|\\n]+)?(\\|[^\\]\\n]+)?(\\]\\])/.source;\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(new RegExp(LINK_WITH_LINES_RE_SRC, 'g'))) {\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 out: string[] = [];\n for (const m of body.matchAll(URL_RE)) {\n // Strip trailing punctuation that almost certainly isn't part of the URL.\n out.push((m[0] ?? '').replace(/[.,;:!?)]+$/, ''));\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 { rm } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.js';\n\nexport const DeleteNoteInput = z.object({\n path: z.string().describe('Vault-relative path of the note to delete.'),\n});\nexport type DeleteNoteInput = z.input<typeof DeleteNoteInput>;\n\nexport interface DeleteNoteResult {\n path: string;\n}\n\nexport async function deleteNote(\n ctx: ServerContext,\n input: DeleteNoteInput,\n): Promise<DeleteNoteResult> {\n const absPath = join(ctx.vaultRoot, input.path);\n if (!absPath.startsWith(ctx.vaultRoot)) {\n throw new Error(`Path outside vault: ${input.path}`);\n }\n\n // rm throws ENOENT if the file doesn't exist — let it propagate.\n await rm(absPath);\n\n if (ctx.notes.has(input.path)) {\n ctx.index.discard(input.path);\n ctx.notes.delete(input.path);\n }\n\n return { path: input.path };\n}\n","import { join } from 'node:path';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.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 const absPath = join(ctx.vaultRoot, input.path);\n if (!absPath.startsWith(ctx.vaultRoot)) {\n throw new Error(`Path outside vault: ${input.path}`);\n }\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 { join } from 'node:path';\nimport { extractLinksWithLines } from '@seekstone/core/extract';\nimport type { LinkType } from '@seekstone/core/extract';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.js';\nimport { resolveLink } from '../index/resolve.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 const absPath = join(ctx.vaultRoot, input.path);\n if (!absPath.startsWith(ctx.vaultRoot)) {\n throw new Error(`Path outside vault: ${input.path}`);\n }\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","/**\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';\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 type { ServerContext } from '../context.js';\nimport { buildDoc, upsertDoc } from '../index/doc.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});\nexport type MoveNoteInput = z.input<typeof MoveNoteInput>;\n\nexport interface MoveNoteResult {\n from: string;\n to: string;\n}\n\nexport async function moveNote(ctx: ServerContext, input: MoveNoteInput): Promise<MoveNoteResult> {\n const absFrom = join(ctx.vaultRoot, input.from);\n const absTo = join(ctx.vaultRoot, input.to);\n\n if (!absFrom.startsWith(ctx.vaultRoot)) throw new Error(`Path outside vault: ${input.from}`);\n if (!absTo.startsWith(ctx.vaultRoot)) throw new Error(`Path outside vault: ${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 await mkdir(dirname(absTo), { recursive: true });\n await rename(absFrom, absTo);\n\n // Grab raw before removing the old entry.\n const raw = ctx.notes.get(input.from)?.raw ?? '';\n\n // Remove old index entry.\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\n return { from: input.from, to: input.to };\n}\n","import { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { buildOutline } from '@seekstone/core/outline';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.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 = join(ctx.vaultRoot, input.path);\n if (!absPath.startsWith(ctx.vaultRoot)) {\n throw new Error(`Path outside vault: ${input.path}`);\n }\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, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport { parseDocument } from 'yaml';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.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});\nexport type PatchFrontmatterInput = z.infer<typeof PatchFrontmatterInput>;\n\nexport interface PatchFrontmatterResult {\n path: string;\n keysChanged: string[];\n keysAdded: string[];\n keysRemoved: 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 = join(ctx.vaultRoot, input.path);\n if (!absPath.startsWith(ctx.vaultRoot)) {\n throw new Error(`Path outside vault: ${input.path}`);\n }\n\n const original = await readFile(absPath, 'utf8');\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()}---\\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()}${tail}${fm.body}`;\n }\n\n await writeFile(absPath, newContent, 'utf8');\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 { path: input.path, keysChanged, keysAdded, keysRemoved };\n}\n","import { readFile, rename, writeFile } 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 type { ServerContext } from '../context.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});\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}\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\nasync function atomicWrite(absPath: string, content: string): Promise<void> {\n const tmpPath = `${absPath}.seekstone-patch-tmp`;\n await writeFile(tmpPath, content, 'utf8');\n await rename(tmpPath, absPath);\n}\n\nexport async function patchNote(\n ctx: ServerContext,\n input: PatchNoteInput,\n): Promise<PatchNoteResult> {\n const absPath = join(ctx.vaultRoot, input.path);\n if (!absPath.startsWith(ctx.vaultRoot)) {\n throw new Error(`Path outside vault: ${input.path}`);\n }\n\n const raw = await readFile(absPath, 'utf8');\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 };\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 };\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 { 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 type { ServerContext } from '../context.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 /** 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 = join(ctx.vaultRoot, input.path);\n if (!absPath.startsWith(ctx.vaultRoot)) {\n throw new Error(`Path outside vault: ${input.path}`);\n }\n\n const raw = await readFile(absPath, 'utf8');\n const noteBytes = Buffer.byteLength(raw, 'utf8');\n\n // Whole-note read — no selector\n if (input.section === undefined && input.block === undefined && input.lines === undefined) {\n return { path: input.path, content: raw, bytesReturned: noteBytes, noteBytes };\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 span: { charStart, charEnd },\n };\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});\nexport type SearchInput = z.infer<typeof SearchInput>;\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 hits.push({\n path: r.id,\n title: r.title as string,\n score: r.score,\n excerpt: extractExcerpt(body, terms),\n tags: noteTags,\n });\n\n if (hits.length >= input.limit) break;\n }\n\n return hits;\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","import type { ServerContext } from './context.js';\nimport type { Logger } from './log.js';\nimport { AppendNoteInput, appendNote } from './tools/append_note.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 { ReadNoteInput, readNote } from './tools/read_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 '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] as const;\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] 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 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 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 return { content: [{ type: 'text', text: JSON.stringify(hits, null, 2) }] };\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: [{ type: 'text', text: `Created ${result.path} (${result.bytesWritten} bytes).` }],\n };\n }\n case 'delete_note': {\n const input = DeleteNoteInput.parse(args);\n await deleteNote(ctx, input);\n return { content: [{ type: 'text', text: `Deleted ${input.path}.` }] };\n }\n case 'move_note': {\n const input = MoveNoteInput.parse(args);\n const result = await moveNote(ctx, input);\n return { content: [{ type: 'text', text: `Moved ${result.from} → ${result.to}.` }] };\n }\n case 'append_note': {\n const input = AppendNoteInput.parse(args);\n const result = await appendNote(ctx, input);\n return {\n content: [\n { type: 'text', text: `Appended ${result.bytesWritten} bytes to ${result.path}.` },\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 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 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","import { execFileSync } from 'node:child_process';\nimport type { Dirent } from 'node:fs';\nimport { access, copyFile, mkdir, readFile, readdir, 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 Claude MCP config so a user doesn't\n * have to hand-edit 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';\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') opts.client = v;\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 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\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 /** 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 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 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 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 // Claude Desktop\n const configPath = claudeDesktopConfigPath(deps.platform, {\n home: deps.env.HOME,\n appData: deps.env.APPDATA,\n });\n const block = JSON.stringify(\n { mcpServers: { seekstone: seekstoneServerConfig(vaultPath) } },\n null,\n 2,\n );\n\n if (!opts.write) {\n out.push(\n 'Add this to your Claude Desktop config:',\n ` ${configPath}`,\n '',\n block,\n '',\n 'Then restart Claude Desktop. 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 = 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 'Restart Claude Desktop to load seekstone.',\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) => emit('error', m, f),\n warn: (m, f) => emit('warn', m, f),\n info: (m, f) => emit('info', m, f),\n debug: (m, f) => emit('debug', m, f),\n };\n}\n","import { readFile } from 'node:fs/promises';\nimport { join, relative, sep } from 'node:path';\nimport { extractLinksWithLines } from '@seekstone/core/extract';\nimport chokidar from 'chokidar';\nimport type { BacklinkRef, ServerContext } from './context.js';\nimport { buildDoc, upsertDoc } from './index/doc.js';\nimport { resolveLink } from './index/resolve.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 stop: () => 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\nfunction 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\nfunction 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\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 // Index keys are forward-slash vault-relative paths on every platform.\n const toRel = (abs: string): string => relative(ctx.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(ctx.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 if (relPath.endsWith('.md')) void upsert(relPath, op);\n };\n\n const watcher = chokidar.watch(ctx.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(ctx.vaultRoot, p);\n if (!rel || rel === '.') return false;\n return rel\n .split(sep)\n .filter(Boolean)\n .some((seg) => seg.startsWith('.'));\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 if (relPath.endsWith('.md')) remove(relPath);\n });\n\n const ready = new Promise<void>((resolve) => {\n watcher.once('ready', () => resolve());\n });\n\n return {\n stop: () => void watcher.close(),\n ready,\n };\n}\n"],"mappings":"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","names":["ctx","writeFile","join","z","ctx","z","ctx","join","writeFile","join","z","ctx","join","z","ctx","backlinks","join","z","notes","path","z","ctx","join","z","ctx","path","z","ctx","access","mkdir","dirname","join","z","z","ctx","join","access","mkdir","dirname","readFile","join","z","ctx","join","readFile","readFile","writeFile","join","z","ctx","join","readFile","writeFile","readFile","rename","writeFile","join","z","writeFile","rename","ctx","join","readFile","sep","newContent","path","readFile","join","z","ctx","join","readFile","z","z","ctx","note","noteTags","ctx","log","readFile","basename","extname","extname","join","vaultRoot","vaultRoot","index","notes","readFile","basename","extname","backlinks","access","mkdir","readFile","stat","writeFile","dirname","join","j","writeFile","readFile","join","relative","sep","ctx","log","relative","sep","readFile","join","watcher"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/cli-args.ts","../src/tools/append_note.ts","../../core/src/frontmatter.ts","../src/tools/create_note.ts","../src/index/doc.ts","../../core/src/extract.ts","../src/tools/delete_note.ts","../src/tools/get_backlinks.ts","../src/tools/get_links.ts","../src/index/resolve.ts","../src/tools/list_notes.ts","../src/tools/list_tags.ts","../src/tools/move_note.ts","../src/tools/outline_note.ts","../../core/src/outline.ts","../src/tools/patch_frontmatter.ts","../src/tools/patch_note.ts","../src/tools/read_note.ts","../src/tools/replace_in_note.ts","../src/tools/search.ts","../src/index/excerpt.ts","../src/dispatch.ts","../src/index/build.ts","../../core/src/walk.ts","../src/init.ts","../src/log.ts","../src/watcher.ts"],"sourcesContent":["#!/usr/bin/env node\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, 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 { 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') {\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 });\n process.stdout.write(`${result.output.join('\\n')}\\n`);\n process.exit(result.exitCode);\n}\n\nconst log = createLogger();\n\nconst vaultRoot = process.env.SEEKSTONE_VAULT;\nif (!vaultRoot) {\n log.error('SEEKSTONE_VAULT env var is required');\n process.exit(1);\n}\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 };\n\nconst watcher = startWatcher(ctx, log);\nprocess.on('exit', watcher.stop);\n\nconst server = new Server({ name: 'seekstone', version: VERSION }, { capabilities: { tools: {} } });\n\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({\n 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: '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, and total note size so payload savings are measurable. 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.',\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 },\n required: ['path'],\n },\n },\n {\n name: 'delete_note',\n description: 'Permanently delete a note from the vault. This cannot be undone.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Vault-relative path of the note to delete.' },\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. 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 },\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 },\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 },\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:\n '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 },\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:\n '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 },\n required: ['path', 'find', 'replace'],\n },\n },\n ],\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: 14, 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' | 'run';\n\nexport function parseCliIntent(argv: readonly string[]): CliIntent {\n // A subcommand, if present, is the first non-flag token.\n if (argv[0] === 'init') return 'init';\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\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), 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 Claude config\n seekstone --version Print the version and exit\n seekstone --help Print this help and exit\n\n\"seekstone init\" options:\n --vault <path> Vault to use (auto-detected from Obsidian if omitted; fallback: $SEEKSTONE_VAULT)\n --client <name> desktop (default) | code\n --write Patch the Claude Desktop config in place (backs it up first)\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","import { readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.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});\nexport type AppendNoteInput = z.infer<typeof AppendNoteInput>;\n\nexport interface AppendNoteResult {\n path: string;\n bytesWritten: number;\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 (overwrite in place, same path).\n */\nexport async function appendNote(\n ctx: ServerContext,\n input: AppendNoteInput,\n): Promise<AppendNoteResult> {\n const absPath = join(ctx.vaultRoot, input.path);\n if (!absPath.startsWith(ctx.vaultRoot)) {\n throw new Error(`Path outside vault: ${input.path}`);\n }\n\n const original = await readFile(absPath, 'utf8');\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 writeFile(absPath, newContent, 'utf8');\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 { path: input.path, bytesWritten: Buffer.byteLength(newContent, 'utf8') };\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 { access, mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { stringify as stringifyYaml } from 'yaml';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.js';\nimport { buildDoc, upsertDoc } from '../index/doc.js';\n\nexport const CreateNoteInput = z.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('Overwrite an existing note. Defaults to false — throws if the note already exists.'),\n});\nexport type CreateNoteInput = z.input<typeof CreateNoteInput>;\n\nexport interface CreateNoteResult {\n path: string;\n bytesWritten: number;\n}\n\nexport async function createNote(\n ctx: ServerContext,\n input: CreateNoteInput,\n): Promise<CreateNoteResult> {\n const absPath = join(ctx.vaultRoot, input.path);\n if (!absPath.startsWith(ctx.vaultRoot)) {\n throw new Error(`Path outside vault: ${input.path}`);\n }\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 }\n\n let raw = '';\n if (input.frontmatter && Object.keys(input.frontmatter).length > 0) {\n raw = `---\\n${stringifyYaml(input.frontmatter)}---\\n`;\n }\n raw += input.content ?? '';\n\n await mkdir(dirname(absPath), { recursive: true });\n await writeFile(absPath, raw, 'utf8');\n\n // Sync in-memory index so the new note is immediately searchable.\n upsertDoc(ctx, buildDoc(input.path, raw));\n\n return { path: input.path, bytesWritten: Buffer.byteLength(raw, 'utf8') };\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 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","/**\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\nconst WIKILINK_RE = /\\[\\[([^\\]|#\\n]+)(#[^\\]|\\n]+)?(\\|[^\\]\\n]+)?\\]\\]/g;\n\n// Matches both embeds (![[...]]) and plain wikilinks ([[...]]).\nconst LINK_WITH_LINES_RE_SRC = /(!?\\[\\[)([^\\]|#\\n]+)(#[^\\]|\\n]+)?(\\|[^\\]\\n]+)?(\\]\\])/.source;\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(new RegExp(LINK_WITH_LINES_RE_SRC, 'g'))) {\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 out: string[] = [];\n for (const m of body.matchAll(URL_RE)) {\n // Strip trailing punctuation that almost certainly isn't part of the URL.\n out.push((m[0] ?? '').replace(/[.,;:!?)]+$/, ''));\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 { rm } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.js';\n\nexport const DeleteNoteInput = z.object({\n path: z.string().describe('Vault-relative path of the note to delete.'),\n});\nexport type DeleteNoteInput = z.input<typeof DeleteNoteInput>;\n\nexport interface DeleteNoteResult {\n path: string;\n}\n\nexport async function deleteNote(\n ctx: ServerContext,\n input: DeleteNoteInput,\n): Promise<DeleteNoteResult> {\n const absPath = join(ctx.vaultRoot, input.path);\n if (!absPath.startsWith(ctx.vaultRoot)) {\n throw new Error(`Path outside vault: ${input.path}`);\n }\n\n // rm throws ENOENT if the file doesn't exist — let it propagate.\n await rm(absPath);\n\n if (ctx.notes.has(input.path)) {\n ctx.index.discard(input.path);\n ctx.notes.delete(input.path);\n }\n\n return { path: input.path };\n}\n","import { join } from 'node:path';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.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 const absPath = join(ctx.vaultRoot, input.path);\n if (!absPath.startsWith(ctx.vaultRoot)) {\n throw new Error(`Path outside vault: ${input.path}`);\n }\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 { join } from 'node:path';\nimport { extractLinksWithLines } from '@seekstone/core/extract';\nimport type { LinkType } from '@seekstone/core/extract';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.js';\nimport { resolveLink } from '../index/resolve.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 const absPath = join(ctx.vaultRoot, input.path);\n if (!absPath.startsWith(ctx.vaultRoot)) {\n throw new Error(`Path outside vault: ${input.path}`);\n }\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","/**\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';\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 type { ServerContext } from '../context.js';\nimport { buildDoc, upsertDoc } from '../index/doc.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});\nexport type MoveNoteInput = z.input<typeof MoveNoteInput>;\n\nexport interface MoveNoteResult {\n from: string;\n to: string;\n}\n\nexport async function moveNote(ctx: ServerContext, input: MoveNoteInput): Promise<MoveNoteResult> {\n const absFrom = join(ctx.vaultRoot, input.from);\n const absTo = join(ctx.vaultRoot, input.to);\n\n if (!absFrom.startsWith(ctx.vaultRoot)) throw new Error(`Path outside vault: ${input.from}`);\n if (!absTo.startsWith(ctx.vaultRoot)) throw new Error(`Path outside vault: ${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 await mkdir(dirname(absTo), { recursive: true });\n await rename(absFrom, absTo);\n\n // Grab raw before removing the old entry.\n const raw = ctx.notes.get(input.from)?.raw ?? '';\n\n // Remove old index entry.\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\n return { from: input.from, to: input.to };\n}\n","import { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { buildOutline } from '@seekstone/core/outline';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.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 = join(ctx.vaultRoot, input.path);\n if (!absPath.startsWith(ctx.vaultRoot)) {\n throw new Error(`Path outside vault: ${input.path}`);\n }\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, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport { parseDocument } from 'yaml';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.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});\nexport type PatchFrontmatterInput = z.infer<typeof PatchFrontmatterInput>;\n\nexport interface PatchFrontmatterResult {\n path: string;\n keysChanged: string[];\n keysAdded: string[];\n keysRemoved: 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 = join(ctx.vaultRoot, input.path);\n if (!absPath.startsWith(ctx.vaultRoot)) {\n throw new Error(`Path outside vault: ${input.path}`);\n }\n\n const original = await readFile(absPath, 'utf8');\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()}---\\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()}${tail}${fm.body}`;\n }\n\n await writeFile(absPath, newContent, 'utf8');\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 { path: input.path, keysChanged, keysAdded, keysRemoved };\n}\n","import { readFile, rename, writeFile } 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 type { ServerContext } from '../context.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});\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}\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\nasync function atomicWrite(absPath: string, content: string): Promise<void> {\n const tmpPath = `${absPath}.seekstone-patch-tmp`;\n await writeFile(tmpPath, content, 'utf8');\n await rename(tmpPath, absPath);\n}\n\nexport async function patchNote(\n ctx: ServerContext,\n input: PatchNoteInput,\n): Promise<PatchNoteResult> {\n const absPath = join(ctx.vaultRoot, input.path);\n if (!absPath.startsWith(ctx.vaultRoot)) {\n throw new Error(`Path outside vault: ${input.path}`);\n }\n\n const raw = await readFile(absPath, 'utf8');\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 };\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 };\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 { 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 type { ServerContext } from '../context.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 /** 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 = join(ctx.vaultRoot, input.path);\n if (!absPath.startsWith(ctx.vaultRoot)) {\n throw new Error(`Path outside vault: ${input.path}`);\n }\n\n const raw = await readFile(absPath, 'utf8');\n const noteBytes = Buffer.byteLength(raw, 'utf8');\n\n // Whole-note read — no selector\n if (input.section === undefined && input.block === undefined && input.lines === undefined) {\n return { path: input.path, content: raw, bytesReturned: noteBytes, noteBytes };\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 span: { charStart, charEnd },\n };\n}\n","import { readFile, rename, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.js';\n\nexport const ReplaceInNoteInput = z.object({\n path: z.string().describe('Vault-relative path to the note.'),\n find: z.string().min(1).describe('Text or pattern to find.'),\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});\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}\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\nasync function atomicWrite(absPath: string, content: string): Promise<void> {\n const tmpPath = `${absPath}.seekstone-replace-tmp`;\n await writeFile(tmpPath, content, 'utf8');\n await rename(tmpPath, absPath);\n}\n\nexport async function replaceInNote(\n ctx: ServerContext,\n input: ReplaceInNoteInput,\n): Promise<ReplaceInNoteResult> {\n const absPath = join(ctx.vaultRoot, input.path);\n if (!absPath.startsWith(ctx.vaultRoot)) {\n throw new Error(`Path outside vault: ${input.path}`);\n }\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 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 };\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});\nexport type SearchInput = z.infer<typeof SearchInput>;\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 hits.push({\n path: r.id,\n title: r.title as string,\n score: r.score,\n excerpt: extractExcerpt(body, terms),\n tags: noteTags,\n });\n\n if (hits.length >= input.limit) break;\n }\n\n return hits;\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","import type { ServerContext } from './context.js';\nimport type { Logger } from './log.js';\nimport { AppendNoteInput, appendNote } from './tools/append_note.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 { 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 '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] as const;\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] 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 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 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 return { content: [{ type: 'text', text: JSON.stringify(hits, null, 2) }] };\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: [{ type: 'text', text: `Created ${result.path} (${result.bytesWritten} bytes).` }],\n };\n }\n case 'delete_note': {\n const input = DeleteNoteInput.parse(args);\n await deleteNote(ctx, input);\n return { content: [{ type: 'text', text: `Deleted ${input.path}.` }] };\n }\n case 'move_note': {\n const input = MoveNoteInput.parse(args);\n const result = await moveNote(ctx, input);\n return { content: [{ type: 'text', text: `Moved ${result.from} → ${result.to}.` }] };\n }\n case 'append_note': {\n const input = AppendNoteInput.parse(args);\n const result = await appendNote(ctx, input);\n return {\n content: [\n { type: 'text', text: `Appended ${result.bytesWritten} bytes to ${result.path}.` },\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 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 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","import { execFileSync } from 'node:child_process';\nimport type { Dirent } from 'node:fs';\nimport { access, copyFile, mkdir, readFile, readdir, 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 Claude MCP config so a user doesn't\n * have to hand-edit 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';\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') opts.client = v;\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 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\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 /** 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 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 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 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 // Claude Desktop\n const configPath = claudeDesktopConfigPath(deps.platform, {\n home: deps.env.HOME,\n appData: deps.env.APPDATA,\n });\n const block = JSON.stringify(\n { mcpServers: { seekstone: seekstoneServerConfig(vaultPath) } },\n null,\n 2,\n );\n\n if (!opts.write) {\n out.push(\n 'Add this to your Claude Desktop config:',\n ` ${configPath}`,\n '',\n block,\n '',\n 'Then restart Claude Desktop. 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 = 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 'Restart Claude Desktop to load seekstone.',\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) => emit('error', m, f),\n warn: (m, f) => emit('warn', m, f),\n info: (m, f) => emit('info', m, f),\n debug: (m, f) => emit('debug', m, f),\n };\n}\n","import { readFile } from 'node:fs/promises';\nimport { join, relative, sep } from 'node:path';\nimport { extractLinksWithLines } from '@seekstone/core/extract';\nimport chokidar from 'chokidar';\nimport type { BacklinkRef, ServerContext } from './context.js';\nimport { buildDoc, upsertDoc } from './index/doc.js';\nimport { resolveLink } from './index/resolve.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 stop: () => 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\nfunction 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\nfunction 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\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 // Index keys are forward-slash vault-relative paths on every platform.\n const toRel = (abs: string): string => relative(ctx.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(ctx.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 if (relPath.endsWith('.md')) void upsert(relPath, op);\n };\n\n const watcher = chokidar.watch(ctx.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(ctx.vaultRoot, p);\n if (!rel || rel === '.') return false;\n return rel\n .split(sep)\n .filter(Boolean)\n .some((seg) => seg.startsWith('.'));\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 if (relPath.endsWith('.md')) remove(relPath);\n });\n\n const ready = new Promise<void>((resolve) => {\n watcher.once('ready', () => resolve());\n });\n\n return {\n stop: () => void watcher.close(),\n ready,\n };\n}\n"],"mappings":"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","names":["ctx","writeFile","join","z","ctx","z","ctx","join","writeFile","join","z","ctx","join","z","ctx","backlinks","join","z","notes","path","z","ctx","join","z","ctx","path","z","ctx","access","mkdir","dirname","join","z","z","ctx","join","access","mkdir","dirname","readFile","join","z","ctx","join","readFile","readFile","writeFile","join","z","ctx","join","readFile","writeFile","readFile","rename","writeFile","join","z","writeFile","rename","ctx","join","readFile","sep","newContent","path","readFile","join","z","ctx","join","readFile","readFile","rename","writeFile","join","z","atomicWrite","writeFile","rename","ctx","join","readFile","z","z","ctx","note","noteTags","ctx","log","readFile","basename","extname","extname","join","vaultRoot","vaultRoot","index","notes","readFile","basename","extname","backlinks","access","mkdir","readFile","stat","writeFile","dirname","join","j","writeFile","readFile","join","relative","sep","ctx","log","relative","sep","readFile","join","watcher"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "seekstone",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "mcpName": "io.github.shaqmughal/seekstone",
5
5
  "type": "module",
6
6
  "description": "Obsidian MCP server for Claude — filesystem-direct vault access, 575× smaller payloads than the REST plugin.",