seekstone 0.0.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/index.js +232 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,6 +29,16 @@ claude mcp add seekstone --env SEEKSTONE_VAULT=/absolute/path/to/your/vault -- n
|
|
|
29
29
|
|
|
30
30
|
Restart the client. On startup seekstone walks the vault and builds an in-memory full-text index (a couple of seconds for a few thousand notes), then keeps it in sync as notes change.
|
|
31
31
|
|
|
32
|
+
### Guided setup
|
|
33
|
+
|
|
34
|
+
`seekstone init` validates your vault and prints the config to paste, or patches the Claude Desktop config for you (with a backup, leaving other MCP servers untouched):
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx -y seekstone init --vault "/absolute/path/to/your/vault" # print config
|
|
38
|
+
npx -y seekstone init --vault "/absolute/path/to/your/vault" --write # patch Claude Desktop
|
|
39
|
+
npx -y seekstone init --vault "/absolute/path/to/your/vault" --client code # print Claude Code command
|
|
40
|
+
```
|
|
41
|
+
|
|
32
42
|
## Configuration
|
|
33
43
|
|
|
34
44
|
| Env var | Required | Description |
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,46 @@ import {
|
|
|
8
8
|
ListToolsRequestSchema
|
|
9
9
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
10
10
|
|
|
11
|
+
// src/cli-args.ts
|
|
12
|
+
function parseCliIntent(argv) {
|
|
13
|
+
if (argv[0] === "init") return "init";
|
|
14
|
+
for (const arg of argv) {
|
|
15
|
+
if (arg === "--version" || arg === "-v") return "version";
|
|
16
|
+
if (arg === "--help" || arg === "-h") return "help";
|
|
17
|
+
}
|
|
18
|
+
return "run";
|
|
19
|
+
}
|
|
20
|
+
function helpText(version) {
|
|
21
|
+
return `seekstone ${version} \u2014 an Obsidian MCP server (filesystem-direct, low context-tax)
|
|
22
|
+
|
|
23
|
+
Seekstone runs as a Model Context Protocol stdio server. It is normally
|
|
24
|
+
launched by an MCP client (Claude Desktop, Claude Code), not run by hand.
|
|
25
|
+
|
|
26
|
+
Usage:
|
|
27
|
+
seekstone Start the MCP server (reads from stdin/stdout)
|
|
28
|
+
seekstone init Validate a vault and print/patch the Claude config
|
|
29
|
+
seekstone --version Print the version and exit
|
|
30
|
+
seekstone --help Print this help and exit
|
|
31
|
+
|
|
32
|
+
"seekstone init" options:
|
|
33
|
+
--vault <path> Vault to use (default: $SEEKSTONE_VAULT)
|
|
34
|
+
--client <name> desktop (default) | code
|
|
35
|
+
--write Patch the Claude Desktop config in place (backs it up first)
|
|
36
|
+
|
|
37
|
+
Required environment:
|
|
38
|
+
SEEKSTONE_VAULT Absolute path to your Obsidian vault
|
|
39
|
+
|
|
40
|
+
Optional environment:
|
|
41
|
+
SEEKSTONE_LOG_LEVEL error | warn | info (default) | debug
|
|
42
|
+
SEEKSTONE_LOG_FILE Absolute path; append JSON-line logs here
|
|
43
|
+
SEEKSTONE_WATCH_POLL Set to 1 to poll for changes (network drives, WSL)
|
|
44
|
+
|
|
45
|
+
Add to Claude Code:
|
|
46
|
+
claude mcp add seekstone --env SEEKSTONE_VAULT=/path/to/vault -- npx -y seekstone
|
|
47
|
+
|
|
48
|
+
Docs: https://github.com/shaqmughal/seekstone`;
|
|
49
|
+
}
|
|
50
|
+
|
|
11
51
|
// src/tools/append_note.ts
|
|
12
52
|
import { readFile, writeFile } from "fs/promises";
|
|
13
53
|
import { join } from "path";
|
|
@@ -225,14 +265,14 @@ var ListNotesInput = z4.object({
|
|
|
225
265
|
function listNotes(ctx2, input) {
|
|
226
266
|
const tag = input.tag?.replace(/^#/, "");
|
|
227
267
|
const results = [];
|
|
228
|
-
for (const [
|
|
229
|
-
if (input.folder && !
|
|
268
|
+
for (const [path2, note] of ctx2.notes) {
|
|
269
|
+
if (input.folder && !path2.startsWith(input.folder)) continue;
|
|
230
270
|
if (tag) {
|
|
231
271
|
const noteTags = note.tags.split(" ").filter(Boolean);
|
|
232
272
|
if (!noteTags.includes(tag)) continue;
|
|
233
273
|
}
|
|
234
274
|
results.push({
|
|
235
|
-
path,
|
|
275
|
+
path: path2,
|
|
236
276
|
title: note.title,
|
|
237
277
|
tags: note.tags.split(" ").filter(Boolean),
|
|
238
278
|
sizeBytes: note.sizeBytes
|
|
@@ -636,6 +676,168 @@ async function buildIndex(vaultRoot2) {
|
|
|
636
676
|
return { index: index2, notes: notes2, buildMs: Date.now() - t0 };
|
|
637
677
|
}
|
|
638
678
|
|
|
679
|
+
// src/init.ts
|
|
680
|
+
import { access as access3, copyFile, mkdir as mkdir3, readFile as readFile5, readdir, stat as stat2, writeFile as writeFile4 } from "fs/promises";
|
|
681
|
+
import path, { dirname as dirname3, join as join8 } from "path";
|
|
682
|
+
function parseInitArgs(argv) {
|
|
683
|
+
const opts = { write: false, client: "desktop" };
|
|
684
|
+
for (let i = 0; i < argv.length; i++) {
|
|
685
|
+
const a = argv[i];
|
|
686
|
+
if (a === "--write") opts.write = true;
|
|
687
|
+
else if (a === "--vault") opts.vault = argv[++i];
|
|
688
|
+
else if (a === "--client") {
|
|
689
|
+
const v = argv[++i];
|
|
690
|
+
if (v === "desktop" || v === "code") opts.client = v;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
return opts;
|
|
694
|
+
}
|
|
695
|
+
function seekstoneServerConfig(vaultPath) {
|
|
696
|
+
return {
|
|
697
|
+
command: "npx",
|
|
698
|
+
args: ["-y", "seekstone"],
|
|
699
|
+
env: { SEEKSTONE_VAULT: vaultPath }
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
function mcpAddCommand(vaultPath) {
|
|
703
|
+
return `claude mcp add seekstone --env SEEKSTONE_VAULT=${vaultPath} -- npx -y seekstone`;
|
|
704
|
+
}
|
|
705
|
+
function claudeDesktopConfigPath(platform, env) {
|
|
706
|
+
if (platform === "win32") {
|
|
707
|
+
const j2 = path.win32.join;
|
|
708
|
+
const base = env.appData ?? j2(env.home ?? "", "AppData", "Roaming");
|
|
709
|
+
return j2(base, "Claude", "claude_desktop_config.json");
|
|
710
|
+
}
|
|
711
|
+
const j = path.posix.join;
|
|
712
|
+
const home = env.home ?? "";
|
|
713
|
+
if (platform === "darwin") {
|
|
714
|
+
return j(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
715
|
+
}
|
|
716
|
+
return j(home, ".config", "Claude", "claude_desktop_config.json");
|
|
717
|
+
}
|
|
718
|
+
function mergeSeekstoneConfig(existing, vaultPath) {
|
|
719
|
+
const base = existing ? structuredClone(existing) : {};
|
|
720
|
+
const servers = { ...base.mcpServers ?? {} };
|
|
721
|
+
servers.seekstone = seekstoneServerConfig(vaultPath);
|
|
722
|
+
return { ...base, mcpServers: servers };
|
|
723
|
+
}
|
|
724
|
+
async function validateVault(vaultPath) {
|
|
725
|
+
try {
|
|
726
|
+
const st = await stat2(vaultPath);
|
|
727
|
+
if (!st.isDirectory()) return { ok: false, error: `Not a directory: ${vaultPath}` };
|
|
728
|
+
} catch {
|
|
729
|
+
return { ok: false, error: `Path does not exist: ${vaultPath}` };
|
|
730
|
+
}
|
|
731
|
+
try {
|
|
732
|
+
await access3(join8(vaultPath, ".obsidian"));
|
|
733
|
+
} catch {
|
|
734
|
+
return {
|
|
735
|
+
ok: false,
|
|
736
|
+
error: `No ".obsidian" folder found in ${vaultPath} \u2014 this doesn't look like an Obsidian vault. Open the folder in Obsidian once to initialize it, or pass the correct --vault path.`
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
return { ok: true, noteCount: await countMarkdown(vaultPath) };
|
|
740
|
+
}
|
|
741
|
+
async function countMarkdown(root) {
|
|
742
|
+
let count = 0;
|
|
743
|
+
async function walk(dir) {
|
|
744
|
+
let entries;
|
|
745
|
+
try {
|
|
746
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
747
|
+
} catch {
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
for (const e of entries) {
|
|
751
|
+
if (e.name.startsWith(".")) continue;
|
|
752
|
+
const full = join8(dir, e.name);
|
|
753
|
+
if (e.isDirectory()) await walk(full);
|
|
754
|
+
else if (e.name.endsWith(".md")) count++;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
await walk(root);
|
|
758
|
+
return count;
|
|
759
|
+
}
|
|
760
|
+
async function runInit(opts, deps) {
|
|
761
|
+
const vaultPath = opts.vault ?? deps.env.SEEKSTONE_VAULT;
|
|
762
|
+
if (!vaultPath) {
|
|
763
|
+
return {
|
|
764
|
+
ok: false,
|
|
765
|
+
exitCode: 1,
|
|
766
|
+
output: [
|
|
767
|
+
"No vault specified. Pass --vault <path> or set SEEKSTONE_VAULT.",
|
|
768
|
+
'Example: seekstone init --vault "/Users/you/Obsidian/My Vault"'
|
|
769
|
+
]
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
const check = await validateVault(vaultPath);
|
|
773
|
+
if (!check.ok) {
|
|
774
|
+
return { ok: false, exitCode: 1, output: [`\u2717 ${check.error}`] };
|
|
775
|
+
}
|
|
776
|
+
const out = [
|
|
777
|
+
`\u2713 Vault looks good: ${vaultPath}`,
|
|
778
|
+
` ${check.noteCount} markdown note${check.noteCount === 1 ? "" : "s"} found.`,
|
|
779
|
+
""
|
|
780
|
+
];
|
|
781
|
+
if (opts.client === "code") {
|
|
782
|
+
out.push("Add to Claude Code by running:", "", ` ${mcpAddCommand(vaultPath)}`, "");
|
|
783
|
+
return { ok: true, exitCode: 0, output: out };
|
|
784
|
+
}
|
|
785
|
+
const configPath = claudeDesktopConfigPath(deps.platform, {
|
|
786
|
+
home: deps.env.HOME,
|
|
787
|
+
appData: deps.env.APPDATA
|
|
788
|
+
});
|
|
789
|
+
const block = JSON.stringify(
|
|
790
|
+
{ mcpServers: { seekstone: seekstoneServerConfig(vaultPath) } },
|
|
791
|
+
null,
|
|
792
|
+
2
|
|
793
|
+
);
|
|
794
|
+
if (!opts.write) {
|
|
795
|
+
out.push(
|
|
796
|
+
"Add this to your Claude Desktop config:",
|
|
797
|
+
` ${configPath}`,
|
|
798
|
+
"",
|
|
799
|
+
block,
|
|
800
|
+
"",
|
|
801
|
+
"Then restart Claude Desktop. Or re-run with --write to patch it automatically."
|
|
802
|
+
);
|
|
803
|
+
return { ok: true, exitCode: 0, output: out };
|
|
804
|
+
}
|
|
805
|
+
let existing = null;
|
|
806
|
+
let existingRaw = null;
|
|
807
|
+
try {
|
|
808
|
+
existingRaw = await readFile5(configPath, "utf8");
|
|
809
|
+
existing = JSON.parse(existingRaw);
|
|
810
|
+
} catch (err) {
|
|
811
|
+
if (existingRaw !== null) {
|
|
812
|
+
return {
|
|
813
|
+
ok: false,
|
|
814
|
+
exitCode: 1,
|
|
815
|
+
output: [
|
|
816
|
+
`\u2717 ${configPath} exists but is not valid JSON; not modifying it.`,
|
|
817
|
+
` ${err instanceof Error ? err.message : String(err)}`
|
|
818
|
+
]
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
existing = null;
|
|
822
|
+
}
|
|
823
|
+
const merged = mergeSeekstoneConfig(existing, vaultPath);
|
|
824
|
+
let backupPath;
|
|
825
|
+
await mkdir3(dirname3(configPath), { recursive: true });
|
|
826
|
+
if (existingRaw !== null) {
|
|
827
|
+
backupPath = `${configPath}.backup-${deps.timestamp}`;
|
|
828
|
+
await copyFile(configPath, backupPath);
|
|
829
|
+
}
|
|
830
|
+
await writeFile4(configPath, `${JSON.stringify(merged, null, 2)}
|
|
831
|
+
`, "utf8");
|
|
832
|
+
out.push(
|
|
833
|
+
`\u2713 Patched ${configPath}`,
|
|
834
|
+
backupPath ? ` Backup saved to ${backupPath}` : " (created a new config file)",
|
|
835
|
+
"",
|
|
836
|
+
"Restart Claude Desktop to load seekstone."
|
|
837
|
+
);
|
|
838
|
+
return { ok: true, exitCode: 0, output: out, wrotePath: configPath, backupPath };
|
|
839
|
+
}
|
|
840
|
+
|
|
639
841
|
// src/log.ts
|
|
640
842
|
import { appendFileSync, renameSync, statSync } from "fs";
|
|
641
843
|
var ORDER = ["error", "warn", "info", "debug"];
|
|
@@ -697,7 +899,7 @@ function createLogger(opts = {}) {
|
|
|
697
899
|
);
|
|
698
900
|
}
|
|
699
901
|
}
|
|
700
|
-
function
|
|
902
|
+
function writeFile5(record) {
|
|
701
903
|
if (!fileOk || filePath === void 0) return;
|
|
702
904
|
const line = `${JSON.stringify(record)}
|
|
703
905
|
`;
|
|
@@ -719,7 +921,7 @@ function createLogger(opts = {}) {
|
|
|
719
921
|
const f = fields ?? {};
|
|
720
922
|
stderr(`${lvl.toUpperCase().padEnd(5)} ${ts} ${msg}${fmtFields(f)}
|
|
721
923
|
`);
|
|
722
|
-
|
|
924
|
+
writeFile5({ t: ts, level: lvl, msg, ...f });
|
|
723
925
|
}
|
|
724
926
|
return {
|
|
725
927
|
level,
|
|
@@ -731,15 +933,15 @@ function createLogger(opts = {}) {
|
|
|
731
933
|
}
|
|
732
934
|
|
|
733
935
|
// src/watcher.ts
|
|
734
|
-
import { readFile as
|
|
735
|
-
import { join as
|
|
936
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
937
|
+
import { join as join9, relative as relative2, sep as sep2 } from "path";
|
|
736
938
|
import chokidar from "chokidar";
|
|
737
939
|
function startWatcher(ctx2, log2, opts) {
|
|
738
940
|
const usePolling = opts?.usePolling ?? process.env.SEEKSTONE_WATCH_POLL === "1";
|
|
739
941
|
const toRel = (abs) => relative2(ctx2.vaultRoot, abs).split(sep2).join("/");
|
|
740
942
|
async function upsert(relPath, op) {
|
|
741
943
|
try {
|
|
742
|
-
const raw = await
|
|
944
|
+
const raw = await readFile6(join9(ctx2.vaultRoot, relPath), "utf8");
|
|
743
945
|
upsertDoc(ctx2, buildDoc(relPath, raw));
|
|
744
946
|
log2?.debug("index updated", { path: relPath, op });
|
|
745
947
|
} catch {
|
|
@@ -780,6 +982,28 @@ function startWatcher(ctx2, log2, opts) {
|
|
|
780
982
|
}
|
|
781
983
|
|
|
782
984
|
// src/index.ts
|
|
985
|
+
var VERSION = true ? "0.2.0" : "0.0.0-dev";
|
|
986
|
+
var intent = parseCliIntent(process.argv.slice(2));
|
|
987
|
+
if (intent === "version") {
|
|
988
|
+
process.stdout.write(`${VERSION}
|
|
989
|
+
`);
|
|
990
|
+
process.exit(0);
|
|
991
|
+
}
|
|
992
|
+
if (intent === "help") {
|
|
993
|
+
process.stdout.write(`${helpText(VERSION)}
|
|
994
|
+
`);
|
|
995
|
+
process.exit(0);
|
|
996
|
+
}
|
|
997
|
+
if (intent === "init") {
|
|
998
|
+
const result = await runInit(parseInitArgs(process.argv.slice(3)), {
|
|
999
|
+
env: process.env,
|
|
1000
|
+
platform: process.platform,
|
|
1001
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")
|
|
1002
|
+
});
|
|
1003
|
+
process.stdout.write(`${result.output.join("\n")}
|
|
1004
|
+
`);
|
|
1005
|
+
process.exit(result.exitCode);
|
|
1006
|
+
}
|
|
783
1007
|
var log = createLogger();
|
|
784
1008
|
var vaultRoot = process.env.SEEKSTONE_VAULT;
|
|
785
1009
|
if (!vaultRoot) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.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/list_notes.ts","../src/tools/move_note.ts","../src/tools/patch_frontmatter.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/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 type { ServerContext } from './context.js';\nimport { dispatch } from './dispatch.js';\nimport { buildIndex } from './index/build.js';\nimport { createLogger } from './log.js';\nimport { startWatcher } from './watcher.js';\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, buildMs } = await buildIndex(vaultRoot);\nlog.info('index ready', { notes: notes.size, buildMs });\n\nconst ctx: ServerContext = { vaultRoot, index, notes };\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 the full content of a specific note by its vault-relative path. Use search first to find the right path.',\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 },\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: '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}));\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: 8, 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","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;\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 { 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 { 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, 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 } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.js';\n\nexport const ReadNoteInput = z.object({\n path: z.string().describe('Vault-relative path to the note, e.g. \"Daily Notes/2026-05-29.md\".'),\n});\nexport type ReadNoteInput = z.infer<typeof ReadNoteInput>;\n\nexport interface ReadNoteResult {\n path: string;\n content: string;\n sizeBytes: number;\n}\n\nexport async function readNote(ctx: ServerContext, input: ReadNoteInput): Promise<ReadNoteResult> {\n // Prevent path traversal — the path must not escape the vault root.\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 content = await readFile(absPath, 'utf8');\n return {\n path: input.path,\n content,\n sizeBytes: Buffer.byteLength(content, 'utf8'),\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 { ListNotesInput, listNotes } from './tools/list_notes.js';\nimport { MoveNoteInput, moveNote } from './tools/move_note.js';\nimport { PatchFrontmatterInput, patchFrontmatter } from './tools/patch_frontmatter.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 'create_note',\n 'delete_note',\n 'move_note',\n 'append_note',\n 'patch_frontmatter',\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 = ['path', 'from', 'to', 'folder', 'tag', 'limit', 'overwrite'] 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: result.content }] };\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 '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 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, frontmatterTags } from '@seekstone/core/extract';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport { walkVault } from '@seekstone/core/walk';\nimport MiniSearch from 'minisearch';\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 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 return { index, notes, buildMs: Date.now() - t0 };\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 { 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 chokidar from 'chokidar';\nimport type { ServerContext } from './context.js';\nimport { buildDoc, upsertDoc } from './index/doc.js';\nimport type { Logger } from './log.js';\n\n/**\n * Watch the vault for .md changes and keep the in-memory index in sync.\n *\n * Uses chokidar rather than `fs.watch(..., { recursive: true })`: recursive\n * native watching is unsupported on Linux, where fs.watch silently misses\n * nested changes. chokidar gives reliable, recursive, cross-platform events.\n *\n * Returns `stop()` to tear down the watcher and a `ready` promise that\n * resolves once chokidar has finished its initial scan and is live.\n */\nexport interface WatcherHandle {\n 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\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 upsertDoc(ctx, buildDoc(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 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 relative(ctx.vaultRoot, p)\n .split(sep)\n .some((seg) => seg.startsWith('.')),\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","z","ctx","access","mkdir","dirname","join","z","z","ctx","join","access","mkdir","dirname","readFile","writeFile","join","z","ctx","join","readFile","writeFile","readFile","join","z","ctx","z","z","ctx","note","noteTags","ctx","log","readFile","basename","extname","extname","join","vaultRoot","vaultRoot","index","notes","readFile","basename","extname","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/list_notes.ts","../src/tools/move_note.ts","../src/tools/patch_frontmatter.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, buildMs } = await buildIndex(vaultRoot);\nlog.info('index ready', { notes: notes.size, buildMs });\n\nconst ctx: ServerContext = { vaultRoot, index, notes };\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 the full content of a specific note by its vault-relative path. Use search first to find the right path.',\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 },\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: '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}));\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: 8, 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 (default: $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;\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 { 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 { 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, 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 } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { z } from 'zod';\nimport type { ServerContext } from '../context.js';\n\nexport const ReadNoteInput = z.object({\n path: z.string().describe('Vault-relative path to the note, e.g. \"Daily Notes/2026-05-29.md\".'),\n});\nexport type ReadNoteInput = z.infer<typeof ReadNoteInput>;\n\nexport interface ReadNoteResult {\n path: string;\n content: string;\n sizeBytes: number;\n}\n\nexport async function readNote(ctx: ServerContext, input: ReadNoteInput): Promise<ReadNoteResult> {\n // Prevent path traversal — the path must not escape the vault root.\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 content = await readFile(absPath, 'utf8');\n return {\n path: input.path,\n content,\n sizeBytes: Buffer.byteLength(content, 'utf8'),\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 { ListNotesInput, listNotes } from './tools/list_notes.js';\nimport { MoveNoteInput, moveNote } from './tools/move_note.js';\nimport { PatchFrontmatterInput, patchFrontmatter } from './tools/patch_frontmatter.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 'create_note',\n 'delete_note',\n 'move_note',\n 'append_note',\n 'patch_frontmatter',\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 = ['path', 'from', 'to', 'folder', 'tag', 'limit', 'overwrite'] 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: result.content }] };\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 '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 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, frontmatterTags } from '@seekstone/core/extract';\nimport { parseFrontmatter } from '@seekstone/core/frontmatter';\nimport { walkVault } from '@seekstone/core/walk';\nimport MiniSearch from 'minisearch';\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 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 return { index, notes, buildMs: Date.now() - t0 };\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 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\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 actually patched a file. */\n wrotePath?: string;\n backupPath?: string;\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 },\n): Promise<InitResult> {\n const vaultPath = opts.vault ?? deps.env.SEEKSTONE_VAULT;\n if (!vaultPath) {\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 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 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 readFile(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 chokidar from 'chokidar';\nimport type { ServerContext } from './context.js';\nimport { buildDoc, upsertDoc } from './index/doc.js';\nimport type { Logger } from './log.js';\n\n/**\n * Watch the vault for .md changes and keep the in-memory index in sync.\n *\n * Uses chokidar rather than `fs.watch(..., { recursive: true })`: recursive\n * native watching is unsupported on Linux, where fs.watch silently misses\n * nested changes. chokidar gives reliable, recursive, cross-platform events.\n *\n * Returns `stop()` to tear down the watcher and a `ready` promise that\n * resolves once chokidar has finished its initial scan and is live.\n */\nexport interface WatcherHandle {\n 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\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 upsertDoc(ctx, buildDoc(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 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 relative(ctx.vaultRoot, p)\n .split(sep)\n .some((seg) => seg.startsWith('.')),\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","z","ctx","path","access","mkdir","dirname","join","z","z","ctx","join","access","mkdir","dirname","readFile","writeFile","join","z","ctx","join","readFile","writeFile","readFile","join","z","ctx","z","z","ctx","note","noteTags","ctx","log","readFile","basename","extname","extname","join","vaultRoot","vaultRoot","index","notes","readFile","basename","extname","access","mkdir","readFile","stat","writeFile","dirname","join","j","writeFile","readFile","join","relative","sep","ctx","log","relative","sep","readFile","join","watcher"]}
|