obelisk-mcp 0.1.0 → 0.1.2

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 CHANGED
@@ -4,8 +4,8 @@ Inline comments and GitHub-style suggested edits for [Obsidian](https://obsidian
4
4
 
5
5
  Select a passage, leave a comment on it, and optionally propose a replacement
6
6
  that can be applied with one click. Comments are stored in the note's own
7
- frontmatter, so they travel with the file through sync, git, export and rename.
8
- There is no sidecar database to fall out of step with the vault.
7
+ frontmatter, so they travel with the file through sync, git, export and
8
+ rename.
9
9
 
10
10
  > An *obelus* (†) was the mark ancient editors drew in the margin of a
11
11
  > manuscript to say: this passage is disputed.
@@ -13,8 +13,9 @@ There is no sidecar database to fall out of step with the vault.
13
13
  ## Status
14
14
 
15
15
  Pre-release. Anchoring, decoration, the sidebar, suggested edits and the agent
16
- integration are implemented. Not yet released or submitted to the community
17
- plugin list, and not yet exercised against a large vault.
16
+ integration are implemented. The CLI and the MCP server are on npm as
17
+ `obelisk-mcp`; the plugin has not been submitted to the community plugin list,
18
+ and none of it has been exercised against a large vault.
18
19
 
19
20
  ## Features
20
21
 
@@ -29,7 +30,7 @@ plugin list, and not yet exercised against a large vault.
29
30
  underlying text has changed since. It also resolves the comment, and *Reopen*
30
31
  is there if the comment asked something the edit did not answer.
31
32
  - **Sidebar.** All of a note's comments in document order, or newest first from
32
- the sort toggle; click one to scroll the editor to it. Chips filter to open
33
+ the sort toggle. Click one to scroll the editor to it. Chips filter to open
33
34
  comments, to comments carrying a suggestion, or to one agent's review pass.
34
35
  Resolved comments stay in the list, greyed rather than hidden, since they are
35
36
  still highlighted in the note.
@@ -51,7 +52,7 @@ plugin list, and not yet exercised against a large vault.
51
52
  the way down.
52
53
  - **Editable.** Rewrite a comment or a reply in the same composer that wrote
53
54
  it, suggestion button and all, from the card's *Edit* button or by
54
- right-clicking the passage. Edited bodies are marked as such; the anchor is
55
+ right-clicking the passage. Edited bodies are marked as such. The anchor is
55
56
  left alone, so changing what you said never changes what you said it about.
56
57
  - **Deletable.** A whole comment from the card's *Delete*, or a single reply
57
58
  from the trash icon in its header, so striking one remark out of a thread
@@ -101,57 +102,35 @@ person wrote it, so nothing already in a vault needs migrating.
101
102
  ## Agents
102
103
 
103
104
  The same comments, from outside Obsidian. A model reviews a note and its
104
- remarks appear in the sidebar of the note you already have open; or you leave
105
+ remarks appear in the sidebar of the note you already have open. Or you leave
105
106
  comments asking for things and a model reads them, makes the edits, and
106
107
  resolves them.
107
108
 
108
109
  ```bash
109
- npm run build
110
- npm link # puts `obelisk` and `obelisk-mcp` on PATH
110
+ npm install -g obelisk-mcp # puts `obelisk` and `obelisk-mcp` on PATH
111
111
 
112
112
  obelisk list note.md
113
113
  obelisk comment note.md --quote "The horse, which had been standing there, bolted." \
114
114
  --body "Two clauses fighting over one sentence." --run r7k2mq
115
115
  ```
116
116
 
117
- For an agent that speaks MCP, register the server with it. Nothing is published
118
- to npm yet, so point at the built file:
117
+ For an agent that speaks MCP, register the server with it:
119
118
 
120
119
  ```bash
121
- npm run build # from the root of this repo
122
- claude mcp add obelisk --scope user -- \
123
- node "$PWD/dist/mcp.mjs" --vault /path/to/your/vault
120
+ claude mcp add obelisk --scope user -- npx -y obelisk-mcp
124
121
  ```
125
122
 
126
- `$PWD` fills in this repo's path, so the vault is the only one you have to
127
- supply, and it has to be a real one. A path that does not exist makes `node`
128
- exit before it says anything, which reaches the agent as `CONNECTION_CLOSED`
129
- and names nothing; if you see that, the path in `claude mcp get obelisk` is the
130
- first thing to check.
131
-
132
- Two things bite here, both of them silent:
133
-
134
- - **Scope.** `claude mcp add` defaults to `local`, which files the server under
135
- the directory you happened to run it in. Run it from this repo and you get a
136
- server that exists only while you are in this repo, the one place you have no
137
- notes to comment on. `--scope user` registers it everywhere; `--scope
138
- project` writes a `.mcp.json` in the vault instead, if the vault is a repo
139
- and everyone working in it should get the server.
140
- - **`npx obelisk-mcp` only works inside this repo.** The package is not on npm
141
- yet, and inside the repo npx resolves the bin out of the local
142
- `package.json`. Anywhere else it goes to the registry and 404s. Use the
143
- absolute path above, or `npm link` first and register `obelisk-mcp`.
144
-
145
- Both paths have to be absolute. The server is spawned without a shell, so a `~`
146
- in a config file stays a literal tilde, and the process inherits the agent's
147
- working directory rather than the vault's.
123
+ One registration covers every vault: the tools work on whichever vault the
124
+ agent is running in, and an absolute path reaches a note in one it is not. The
125
+ server is listed in the official MCP registry as `io.github.zachhannum/obelisk`
126
+ for a client that installs from there.
148
127
 
149
128
  **There is nothing to start.** It speaks MCP over stdio: the agent spawns a
150
129
  process when a session opens and kills it when the session ends, so
151
130
  `obelisk-mcp` is never run by hand and there is no port and no daemon. One
152
131
  process per session is also what makes a session's comments share one run chip
153
- in the sidebar. Rebuild while a session is open and it keeps the old process;
154
- reconnect it from `/mcp`, or start a new session.
132
+ in the sidebar. A session keeps the process it spawned, so a new version of the
133
+ package arrives at the next one. To take it sooner, reconnect from `/mcp`.
155
134
 
156
135
  To check the registration, `claude mcp list`, or `/mcp` inside a session. To
157
136
  check the server itself with no agent in the way:
@@ -161,7 +140,7 @@ printf '%s\n' \
161
140
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"smoke","version":"0"}}}' \
162
141
  '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
163
142
  '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
164
- | node dist/mcp.mjs --vault /abs/path/to/vault
143
+ | npx -y obelisk-mcp
165
144
  ```
166
145
 
167
146
  That should print the handshake and then the four tools. A server that fails
@@ -178,11 +157,11 @@ plausible wrong number attaches a comment to the wrong paragraph without ever
178
157
  looking like an error, so `--near-line` only picks between identical quotes and
179
158
  takes a number copied out of `obelisk list`. `--quote` has to appear in the
180
159
  note character for character, and if it does not, or appears twice, nothing is
181
- written and the tool says so. `obelisk list` prints the body line-numbered so
160
+ written and the reason is printed. `obelisk list` prints the body numbered so
182
161
  the exact text is there to copy.
183
162
 
184
163
  Writes are frontmatter-only and leave the body byte-identical, so they are safe
185
- while the note is open in Obsidian; a write also re-reads the file first and
164
+ while the note is open in Obsidian. A write also re-reads the file first and
186
165
  refuses if it changed underneath.
187
166
 
188
167
  ## Development
package/dist/cli.mjs CHANGED
@@ -7351,9 +7351,39 @@ var require_dist = __commonJS({
7351
7351
 
7352
7352
  // src/cli/main.ts
7353
7353
  import { readFile, writeFile } from "node:fs/promises";
7354
- import { basename, isAbsolute, join, resolve as resolvePath } from "node:path";
7354
+ import { basename } from "node:path";
7355
7355
  import { parseArgs } from "node:util";
7356
7356
 
7357
+ // src/bin/vault.ts
7358
+ import { existsSync, statSync } from "node:fs";
7359
+ import { dirname, isAbsolute, join, resolve as resolvePath } from "node:path";
7360
+ function findVault(from = process.cwd()) {
7361
+ let dir = resolvePath(from);
7362
+ for (; ; ) {
7363
+ if (isDirectory(join(dir, ".obsidian"))) return dir;
7364
+ const parent = dirname(dir);
7365
+ if (parent === dir) return void 0;
7366
+ dir = parent;
7367
+ }
7368
+ }
7369
+ function notePath(arg) {
7370
+ const named = arg.endsWith(".md") ? arg : `${arg}.md`;
7371
+ if (isAbsolute(named)) return resolvePath(named);
7372
+ const fromCwd = resolvePath(named);
7373
+ if (existsSync(fromCwd)) return fromCwd;
7374
+ const vault = findVault();
7375
+ if (!vault) return fromCwd;
7376
+ const fromVault = resolvePath(join(vault, named));
7377
+ return existsSync(fromVault) ? fromVault : fromCwd;
7378
+ }
7379
+ function isDirectory(path) {
7380
+ try {
7381
+ return statSync(path).isDirectory();
7382
+ } catch {
7383
+ return false;
7384
+ }
7385
+ }
7386
+
7357
7387
  // src/core/note.ts
7358
7388
  var import_yaml = __toESM(require_dist());
7359
7389
 
@@ -8048,8 +8078,12 @@ Attribution
8048
8078
  every comment in one review; --human writes an ordinary human comment
8049
8079
  instead.
8050
8080
 
8081
+ Note paths
8082
+ <note> is resolved from the working directory, then from the root of the
8083
+ vault it is in, so a bare note name works from anywhere under the vault.
8084
+ The .md is optional.
8085
+
8051
8086
  Options
8052
- --vault DIR Resolve <note> inside this vault (or $OBELISK_VAULT).
8053
8087
  --body - Read the comment body from stdin, fences and all.
8054
8088
  --author NAME Written into \`author\`. Defaults to the model, or "agent".
8055
8089
  --model NAME Recorded on the comment as the model that wrote it.
@@ -8068,7 +8102,6 @@ var OPTIONS = {
8068
8102
  run: { type: "string" },
8069
8103
  model: { type: "string" },
8070
8104
  author: { type: "string" },
8071
- vault: { type: "string" },
8072
8105
  open: { type: "boolean", default: false },
8073
8106
  json: { type: "boolean", default: false },
8074
8107
  "no-body": { type: "boolean", default: false },
@@ -8127,7 +8160,7 @@ ${USAGE}`);
8127
8160
  }
8128
8161
  }
8129
8162
  async function listCommand(flags, args) {
8130
- const path = notePath(flags, args[0]);
8163
+ const path = notePath2(args[0]);
8131
8164
  const listing = list(await read(path), basename(path));
8132
8165
  const shown = flags.open ? listing.comments.filter((c) => !c.resolved) : listing.comments;
8133
8166
  if (flags.json) {
@@ -8148,7 +8181,7 @@ async function listCommand(flags, args) {
8148
8181
  return 0;
8149
8182
  }
8150
8183
  async function commentCommand(flags, args) {
8151
- const path = notePath(flags, args[0]);
8184
+ const path = notePath2(args[0]);
8152
8185
  const quote = flags.quote;
8153
8186
  if (!quote) throw new UsageError("comment needs --quote.");
8154
8187
  const body = await bodyArg(flags);
@@ -8169,7 +8202,7 @@ async function commentCommand(flags, args) {
8169
8202
  return 0;
8170
8203
  }
8171
8204
  async function replyCommand(flags, args) {
8172
- const path = notePath(flags, args[0]);
8205
+ const path = notePath2(args[0]);
8173
8206
  const id = args[1];
8174
8207
  if (!id) throw new UsageError("reply needs the id of the comment to reply to.");
8175
8208
  const body = await bodyArg(flags);
@@ -8183,7 +8216,7 @@ async function replyCommand(flags, args) {
8183
8216
  return 0;
8184
8217
  }
8185
8218
  async function resolveCommand(flags, args) {
8186
- const path = notePath(flags, args[0]);
8219
+ const path = notePath2(args[0]);
8187
8220
  const id = args[1];
8188
8221
  if (!id) throw new UsageError("resolve needs the id of a comment.");
8189
8222
  const result = await edit(
@@ -8294,11 +8327,9 @@ function oneLine(text, max = 120) {
8294
8327
  const flat = text.replace(/\s+/g, " ").trim();
8295
8328
  return flat.length > max ? flat.slice(0, max - 1) + "\u2026" : flat;
8296
8329
  }
8297
- function notePath(flags, arg) {
8330
+ function notePath2(arg) {
8298
8331
  if (!arg) throw new UsageError("Which note? Pass a path.");
8299
- const vault = flags.vault ?? process.env.OBELISK_VAULT;
8300
- const path = isAbsolute(arg) || !vault ? resolvePath(arg) : resolvePath(join(vault, arg));
8301
- return path.endsWith(".md") ? path : `${path}.md`;
8332
+ return notePath(arg);
8302
8333
  }
8303
8334
  async function bodyArg(flags) {
8304
8335
  if (flags.body === void 0) {
package/dist/mcp.mjs CHANGED
@@ -295,24 +295,24 @@ var require_scope = __commonJS({
295
295
  return;
296
296
  return vs.get(keyOrRef);
297
297
  }
298
- scopeRefs(scopeName, values2 = this._values) {
299
- return this._reduceValues(values2, (name) => {
298
+ scopeRefs(scopeName, values = this._values) {
299
+ return this._reduceValues(values, (name) => {
300
300
  if (name.scopePath === void 0)
301
301
  throw new Error(`CodeGen: name "${name}" has no value`);
302
302
  return (0, code_1._)`${scopeName}${name.scopePath}`;
303
303
  });
304
304
  }
305
- scopeCode(values2 = this._values, usedValues, getCode) {
306
- return this._reduceValues(values2, (name) => {
305
+ scopeCode(values = this._values, usedValues, getCode) {
306
+ return this._reduceValues(values, (name) => {
307
307
  if (name.value === void 0)
308
308
  throw new Error(`CodeGen: name "${name}" has no value`);
309
309
  return name.value.code;
310
310
  }, usedValues, getCode);
311
311
  }
312
- _reduceValues(values2, valueCode, usedValues = {}, getCode) {
312
+ _reduceValues(values, valueCode, usedValues = {}, getCode) {
313
313
  let code = code_1.nil;
314
- for (const prefix in values2) {
315
- const vs = values2[prefix];
314
+ for (const prefix in values) {
315
+ const vs = values[prefix];
316
316
  if (!vs)
317
317
  continue;
318
318
  const nameSet = usedValues[prefix] = usedValues[prefix] || /* @__PURE__ */ new Map();
@@ -14509,8 +14509,7 @@ var require_dist2 = __commonJS({
14509
14509
 
14510
14510
  // src/mcp/main.ts
14511
14511
  import { readFile, writeFile } from "node:fs/promises";
14512
- import { basename, isAbsolute, join, resolve as resolvePath } from "node:path";
14513
- import { parseArgs } from "node:util";
14512
+ import { basename } from "node:path";
14514
14513
 
14515
14514
  // node_modules/zod/v3/helpers/util.js
14516
14515
  var util;
@@ -17823,9 +17822,9 @@ ZodLiteral.create = (value, params) => {
17823
17822
  ...processCreateParams(params)
17824
17823
  });
17825
17824
  };
17826
- function createZodEnum(values2, params) {
17825
+ function createZodEnum(values, params) {
17827
17826
  return new ZodEnum({
17828
- values: values2,
17827
+ values,
17829
17828
  typeName: ZodFirstPartyTypeKind.ZodEnum,
17830
17829
  ...processCreateParams(params)
17831
17830
  });
@@ -17881,14 +17880,14 @@ var ZodEnum = class _ZodEnum extends ZodType {
17881
17880
  }
17882
17881
  return enumValues;
17883
17882
  }
17884
- extract(values2, newDef = this._def) {
17885
- return _ZodEnum.create(values2, {
17883
+ extract(values, newDef = this._def) {
17884
+ return _ZodEnum.create(values, {
17886
17885
  ...this._def,
17887
17886
  ...newDef
17888
17887
  });
17889
17888
  }
17890
- exclude(values2, newDef = this._def) {
17891
- return _ZodEnum.create(this.options.filter((opt) => !values2.includes(opt)), {
17889
+ exclude(values, newDef = this._def) {
17890
+ return _ZodEnum.create(this.options.filter((opt) => !values.includes(opt)), {
17892
17891
  ...this._def,
17893
17892
  ...newDef
17894
17893
  });
@@ -17926,9 +17925,9 @@ var ZodNativeEnum = class extends ZodType {
17926
17925
  return this._def.values;
17927
17926
  }
17928
17927
  };
17929
- ZodNativeEnum.create = (values2, params) => {
17928
+ ZodNativeEnum.create = (values, params) => {
17930
17929
  return new ZodNativeEnum({
17931
- values: values2,
17930
+ values,
17932
17931
  typeName: ZodFirstPartyTypeKind.ZodNativeEnum,
17933
17932
  ...processCreateParams(params)
17934
17933
  });
@@ -18810,8 +18809,8 @@ function assert(_) {
18810
18809
  }
18811
18810
  function getEnumValues(entries) {
18812
18811
  const numericValues = Object.values(entries).filter((v) => typeof v === "number");
18813
- const values2 = Object.entries(entries).filter(([k, _]) => numericValues.indexOf(+k) === -1).map(([_, v]) => v);
18814
- return values2;
18812
+ const values = Object.entries(entries).filter(([k, _]) => numericValues.indexOf(+k) === -1).map(([_, v]) => v);
18813
+ return values;
18815
18814
  }
18816
18815
  function joinValues(array2, separator = "|") {
18817
18816
  return array2.map((val) => stringifyPrimitive(val)).join(separator);
@@ -21967,10 +21966,10 @@ var $ZodDiscriminatedUnion = /* @__PURE__ */ $constructor("$ZodDiscriminatedUnio
21967
21966
  const opts = def.options;
21968
21967
  const map2 = /* @__PURE__ */ new Map();
21969
21968
  for (const o of opts) {
21970
- const values2 = o._zod.propValues?.[def.discriminator];
21971
- if (!values2 || values2.size === 0)
21969
+ const values = o._zod.propValues?.[def.discriminator];
21970
+ if (!values || values.size === 0)
21972
21971
  throw new Error(`Invalid discriminated union option at index "${def.options.indexOf(o)}"`);
21973
- for (const v of values2) {
21972
+ for (const v of values) {
21974
21973
  if (map2.has(v)) {
21975
21974
  throw new Error(`Duplicate discriminator value "${String(v)}"`);
21976
21975
  }
@@ -22253,11 +22252,11 @@ var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
22253
22252
  return payload;
22254
22253
  }
22255
22254
  const proms = [];
22256
- const values2 = def.keyType._zod.values;
22257
- if (values2 && !def.partial) {
22255
+ const values = def.keyType._zod.values;
22256
+ if (values && !def.partial) {
22258
22257
  payload.value = memo2 ? memo2.alloc(inst, payload, {}, ctx) : {};
22259
22258
  const recordKeys = /* @__PURE__ */ new Set();
22260
- for (const key of values2) {
22259
+ for (const key of values) {
22261
22260
  if (typeof key === "string" || typeof key === "number" || typeof key === "symbol") {
22262
22261
  recordKeys.add(typeof key === "number" ? key.toString() : key);
22263
22262
  if (key === "__proto__")
@@ -22343,7 +22342,7 @@ var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
22343
22342
  if (keyResult.issues.length) {
22344
22343
  if (def.mode === "loose") {
22345
22344
  payload.value[key] = input2[key];
22346
- } else if (values2) {
22345
+ } else if (values) {
22347
22346
  unrecognized = unrecognized ?? [];
22348
22347
  unrecognized.push(key);
22349
22348
  } else {
@@ -22492,10 +22491,10 @@ function handleSetResult(result, final) {
22492
22491
  }
22493
22492
  var $ZodEnum = /* @__PURE__ */ $constructor("$ZodEnum", (inst, def) => {
22494
22493
  $ZodType.init(inst, def);
22495
- const values2 = getEnumValues(def.entries);
22496
- const valuesSet = new Set(values2);
22494
+ const values = getEnumValues(def.entries);
22495
+ const valuesSet = new Set(values);
22497
22496
  inst._zod.values = valuesSet;
22498
- const patternValues = values2.filter((k) => propertyKeyTypes.has(typeof k));
22497
+ const patternValues = values.filter((k) => propertyKeyTypes.has(typeof k));
22499
22498
  inst._zod.pattern = new RegExp(patternValues.length ? `^(${patternValues.map((o) => escapeRegex(o.toString())).join("|")})$` : "^[^\\s\\S]$");
22500
22499
  inst._zod.parse = (payload, _ctx) => {
22501
22500
  const input2 = payload.value;
@@ -22504,7 +22503,7 @@ var $ZodEnum = /* @__PURE__ */ $constructor("$ZodEnum", (inst, def) => {
22504
22503
  }
22505
22504
  payload.issues.push({
22506
22505
  code: "invalid_value",
22507
- values: values2,
22506
+ values,
22508
22507
  input: input2,
22509
22508
  inst
22510
22509
  });
@@ -22513,12 +22512,12 @@ var $ZodEnum = /* @__PURE__ */ $constructor("$ZodEnum", (inst, def) => {
22513
22512
  });
22514
22513
  var $ZodLiteral = /* @__PURE__ */ $constructor("$ZodLiteral", (inst, def) => {
22515
22514
  $ZodType.init(inst, def);
22516
- const values2 = new Set(def.values);
22517
- inst._zod.values = values2;
22515
+ const values = new Set(def.values);
22516
+ inst._zod.values = values;
22518
22517
  inst._zod.pattern = new RegExp(def.values.length ? `^(${def.values.map((o) => typeof o === "string" ? escapeRegex(o) : o ? escapeRegex(o.toString()) : String(o)).join("|")})$` : "^[^\\s\\S]$");
22519
22518
  inst._zod.parse = (payload, _ctx) => {
22520
22519
  const input2 = payload.value;
22521
- if (values2.has(input2)) {
22520
+ if (values.has(input2)) {
22522
22521
  return payload;
22523
22522
  }
22524
22523
  payload.issues.push({
@@ -22577,8 +22576,8 @@ var $ZodOptional = /* @__PURE__ */ $constructor("$ZodOptional", (inst, def) => {
22577
22576
  defineLazyInternal(inst, "optin", (zod) => zod.def.innerType._zod.optin === "defaulted" ? "defaulted" : "optional");
22578
22577
  inst._zod.optout = "optional";
22579
22578
  defineLazyInternal(inst, "values", (zod) => {
22580
- const values2 = zod.def.innerType._zod.values;
22581
- return values2 ? /* @__PURE__ */ new Set([...values2, void 0]) : void 0;
22579
+ const values = zod.def.innerType._zod.values;
22580
+ return values ? /* @__PURE__ */ new Set([...values, void 0]) : void 0;
22582
22581
  });
22583
22582
  defineLazyInternal(inst, "pattern", (zod) => {
22584
22583
  const pattern = zod.def.innerType._zod.pattern;
@@ -31662,15 +31661,15 @@ function generateArrayCheck(doc, ctx, schema, accessor, buildsValue = true) {
31662
31661
  }
31663
31662
  function generateLiteralCheck(doc, ctx, schema, accessor) {
31664
31663
  const def = schema._zod.def;
31665
- const values2 = def.values;
31666
- if (values2.length !== 1) {
31667
- const literalSet = addConstant(ctx, new Set(values2));
31664
+ const values = def.values;
31665
+ if (values.length !== 1) {
31666
+ const literalSet = addConstant(ctx, new Set(values));
31668
31667
  doc.write(`if (!${literalSet}.has(${accessor})) return INVALID;`);
31669
31668
  return accessor;
31670
31669
  }
31671
- const value = values2[0];
31670
+ const value = values[0];
31672
31671
  if (typeof value === "number" && Number.isNaN(value)) {
31673
- const literalSet = addConstant(ctx, new Set(values2));
31672
+ const literalSet = addConstant(ctx, new Set(values));
31674
31673
  doc.write(`if (!${literalSet}.has(${accessor})) return INVALID;`);
31675
31674
  return accessor;
31676
31675
  }
@@ -31690,11 +31689,11 @@ function generateLiteralCheck(doc, ctx, schema, accessor) {
31690
31689
  return accessor;
31691
31690
  }
31692
31691
  function generateEnumCheck(doc, ctx, schema, accessor) {
31693
- const values2 = schema._zod.values;
31694
- if (!values2) {
31692
+ const values = schema._zod.values;
31693
+ if (!values) {
31695
31694
  throw new ZodCompileUnsupportedError("enum schema without enumerated values");
31696
31695
  }
31697
- const enumSet = addConstant(ctx, values2);
31696
+ const enumSet = addConstant(ctx, values);
31698
31697
  doc.write(`if (!${enumSet}.has(${accessor})) return INVALID;`);
31699
31698
  return accessor;
31700
31699
  }
@@ -31843,8 +31842,8 @@ function generateUnionCheck(doc, ctx, schema, accessor) {
31843
31842
  }
31844
31843
  const allLiterals = options.every((opt) => opt._zod.def.type === "literal" && !opt._zod.def.checks?.length);
31845
31844
  if (allLiterals) {
31846
- const values2 = new Set(options.flatMap((opt) => opt._zod.def.values));
31847
- const valuesConst = addConstant(ctx, values2);
31845
+ const values = new Set(options.flatMap((opt) => opt._zod.def.values));
31846
+ const valuesConst = addConstant(ctx, values);
31848
31847
  doc.write(`if (!${valuesConst}.has(${accessor})) return INVALID;`);
31849
31848
  return accessor;
31850
31849
  }
@@ -31881,17 +31880,17 @@ function generateDiscriminatedUnionCheck(doc, ctx, def, accessor) {
31881
31880
  let firstBranch = true;
31882
31881
  const claimed = /* @__PURE__ */ new Set();
31883
31882
  for (const option of def.options) {
31884
- const values2 = option._zod.propValues?.[def.discriminator];
31885
- if (!values2 || values2.size === 0) {
31883
+ const values = option._zod.propValues?.[def.discriminator];
31884
+ if (!values || values.size === 0) {
31886
31885
  throw new ZodCompileUnsupportedError("discriminated union option without static discriminator values");
31887
31886
  }
31888
- for (const value of values2) {
31887
+ for (const value of values) {
31889
31888
  if (claimed.has(value)) {
31890
31889
  throw new ZodCompileUnsupportedError(`duplicate discriminator value ${String(value)}`);
31891
31890
  }
31892
31891
  claimed.add(value);
31893
31892
  }
31894
- const conditions = Array.from(values2, (value) => literalEquality(ctx, discVar, value));
31893
+ const conditions = Array.from(values, (value) => literalEquality(ctx, discVar, value));
31895
31894
  const prefix = firstBranch ? "if" : "else if";
31896
31895
  doc.write(`${prefix} (${conditions.join(" || ")}) {`);
31897
31896
  doc.indented((d) => {
@@ -32970,8 +32969,8 @@ function _set(Class2, valueType, params) {
32970
32969
  });
32971
32970
  }
32972
32971
  // @__NO_SIDE_EFFECTS__
32973
- function _enum(Class2, values2, params) {
32974
- const entries = Array.isArray(values2) ? Object.fromEntries(values2.map((v) => [v, v])) : values2;
32972
+ function _enum(Class2, values, params) {
32973
+ const entries = Array.isArray(values) ? Object.fromEntries(values.map((v) => [v, v])) : values;
32975
32974
  return new Class2({
32976
32975
  type: "enum",
32977
32976
  entries,
@@ -33887,16 +33886,16 @@ var dateProcessor = (schema, ctx, json2, params) => {
33887
33886
  };
33888
33887
  var enumProcessor = (schema, _ctx, json2, _params) => {
33889
33888
  const def = schema._zod.def;
33890
- const values2 = getEnumValues(def.entries);
33891
- if (values2.length === 0) {
33889
+ const values = getEnumValues(def.entries);
33890
+ if (values.length === 0) {
33892
33891
  json2.not = {};
33893
33892
  return;
33894
33893
  }
33895
- if (values2.every((v) => typeof v === "number"))
33894
+ if (values.every((v) => typeof v === "number"))
33896
33895
  json2.type = "number";
33897
- if (values2.every((v) => typeof v === "string"))
33896
+ if (values.every((v) => typeof v === "string"))
33898
33897
  json2.type = "string";
33899
- json2.enum = values2;
33898
+ json2.enum = values;
33900
33899
  };
33901
33900
  var literalProcessor = (schema, ctx, json2, params) => {
33902
33901
  const def = schema._zod.def;
@@ -34171,8 +34170,8 @@ function stringifyKeyNames(bySchema, json2, visited) {
34171
34170
  }
34172
34171
  const types = Array.isArray(json2.type) ? json2.type : [json2.type];
34173
34172
  const numericType = !types.includes("string") && types.some((t) => t === "number" || t === "integer");
34174
- const values2 = json2.enum ?? (json2.const !== void 0 ? [json2.const] : void 0);
34175
- if (!numericType && !values2?.some((v) => typeof v === "number"))
34173
+ const values = json2.enum ?? (json2.const !== void 0 ? [json2.const] : void 0);
34174
+ if (!numericType && !values?.some((v) => typeof v === "number"))
34176
34175
  return json2;
34177
34176
  const { minimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf, format, id, ...rest } = json2;
34178
34177
  if (rest.enum)
@@ -34182,7 +34181,7 @@ function stringifyKeyNames(bySchema, json2, visited) {
34182
34181
  if (!numericType)
34183
34182
  return rest;
34184
34183
  rest.type = "string";
34185
- if (!values2)
34184
+ if (!values)
34186
34185
  rest.pattern = (types.includes("number") ? number : integer).source;
34187
34186
  return rest;
34188
34187
  }
@@ -34737,11 +34736,11 @@ function isZ4Schema(s) {
34737
34736
  return !!schema._zod;
34738
34737
  }
34739
34738
  function objectFromShape(shape) {
34740
- const values2 = Object.values(shape);
34741
- if (values2.length === 0)
34739
+ const values = Object.values(shape);
34740
+ if (values.length === 0)
34742
34741
  return object({});
34743
- const allV4 = values2.every(isZ4Schema);
34744
- const allV3 = values2.every((s) => !isZ4Schema(s));
34742
+ const allV4 = values.every(isZ4Schema);
34743
+ const allV3 = values.every((s) => !isZ4Schema(s));
34745
34744
  if (allV4)
34746
34745
  return object(shape);
34747
34746
  if (allV3)
@@ -34795,8 +34794,8 @@ function normalizeObjectSchema(schema) {
34795
34794
  const asV3 = schema;
34796
34795
  const asV4 = schema;
34797
34796
  if (!asV3._def && !asV4._zod) {
34798
- const values2 = Object.values(schema);
34799
- if (values2.length > 0 && values2.every((v) => typeof v === "object" && v !== null && (v._def !== void 0 || v._zod !== void 0 || typeof v.parse === "function"))) {
34797
+ const values = Object.values(schema);
34798
+ if (values.length > 0 && values.every((v) => typeof v === "object" && v !== null && (v._def !== void 0 || v._zod !== void 0 || typeof v.parse === "function"))) {
34800
34799
  return objectFromShape(schema);
34801
34800
  }
34802
34801
  }
@@ -36429,9 +36428,9 @@ var ZodEnum2 = /* @__PURE__ */ $constructor("ZodEnum", (inst, def) => {
36429
36428
  inst.enum = def.entries;
36430
36429
  inst.options = Object.values(def.entries);
36431
36430
  const keys = new Set(Object.keys(def.entries));
36432
- inst.extract = (values2, params) => {
36431
+ inst.extract = (values, params) => {
36433
36432
  const newEntries = {};
36434
- for (const value of values2) {
36433
+ for (const value of values) {
36435
36434
  if (keys.has(value)) {
36436
36435
  newEntries[value] = def.entries[value];
36437
36436
  } else
@@ -36444,9 +36443,9 @@ var ZodEnum2 = /* @__PURE__ */ $constructor("ZodEnum", (inst, def) => {
36444
36443
  entries: newEntries
36445
36444
  });
36446
36445
  };
36447
- inst.exclude = (values2, params) => {
36446
+ inst.exclude = (values, params) => {
36448
36447
  const newEntries = { ...def.entries };
36449
- for (const value of values2) {
36448
+ for (const value of values) {
36450
36449
  if (keys.has(value)) {
36451
36450
  delete newEntries[value];
36452
36451
  } else
@@ -36460,8 +36459,8 @@ var ZodEnum2 = /* @__PURE__ */ $constructor("ZodEnum", (inst, def) => {
36460
36459
  });
36461
36460
  };
36462
36461
  });
36463
- function _enum2(values2, params) {
36464
- const entries = Array.isArray(values2) ? Object.fromEntries(values2.map((v) => [v, v])) : values2;
36462
+ function _enum2(values, params) {
36463
+ const entries = Array.isArray(values) ? Object.fromEntries(values.map((v) => [v, v])) : values;
36465
36464
  return new ZodEnum2({
36466
36465
  type: "enum",
36467
36466
  entries,
@@ -39735,7 +39734,7 @@ function parseMapDef(def, refs) {
39735
39734
  ...refs,
39736
39735
  currentPath: [...refs.currentPath, "items", "items", "0"]
39737
39736
  }) || parseAnyDef(refs);
39738
- const values2 = parseDef(def.valueType._def, {
39737
+ const values = parseDef(def.valueType._def, {
39739
39738
  ...refs,
39740
39739
  currentPath: [...refs.currentPath, "items", "items", "1"]
39741
39740
  }) || parseAnyDef(refs);
@@ -39744,7 +39743,7 @@ function parseMapDef(def, refs) {
39744
39743
  maxItems: 125,
39745
39744
  items: {
39746
39745
  type: "array",
39747
- items: [keys, values2],
39746
+ items: [keys, values],
39748
39747
  minItems: 2,
39749
39748
  maxItems: 2
39750
39749
  }
@@ -39758,7 +39757,7 @@ function parseNativeEnumDef(def) {
39758
39757
  return typeof object3[object3[key]] !== "number";
39759
39758
  });
39760
39759
  const actualValues = actualKeys.map((key) => object3[key]);
39761
- const parsedTypes = Array.from(new Set(actualValues.map((values2) => typeof values2)));
39760
+ const parsedTypes = Array.from(new Set(actualValues.map((values) => typeof values)));
39762
39761
  return {
39763
39762
  type: parsedTypes.length === 1 ? parsedTypes[0] === "string" ? "string" : "number" : ["string", "number"],
39764
39763
  enum: actualValues
@@ -42977,7 +42976,37 @@ var StdioServerTransport = class {
42977
42976
  };
42978
42977
 
42979
42978
  // package.json
42980
- var version2 = "0.1.0";
42979
+ var version2 = "0.1.2";
42980
+
42981
+ // src/bin/vault.ts
42982
+ import { existsSync, statSync } from "node:fs";
42983
+ import { dirname, isAbsolute, join, resolve as resolvePath } from "node:path";
42984
+ function findVault(from = process.cwd()) {
42985
+ let dir = resolvePath(from);
42986
+ for (; ; ) {
42987
+ if (isDirectory(join(dir, ".obsidian"))) return dir;
42988
+ const parent = dirname(dir);
42989
+ if (parent === dir) return void 0;
42990
+ dir = parent;
42991
+ }
42992
+ }
42993
+ function notePath(arg) {
42994
+ const named = arg.endsWith(".md") ? arg : `${arg}.md`;
42995
+ if (isAbsolute(named)) return resolvePath(named);
42996
+ const fromCwd = resolvePath(named);
42997
+ if (existsSync(fromCwd)) return fromCwd;
42998
+ const vault = findVault();
42999
+ if (!vault) return fromCwd;
43000
+ const fromVault = resolvePath(join(vault, named));
43001
+ return existsSync(fromVault) ? fromVault : fromCwd;
43002
+ }
43003
+ function isDirectory(path) {
43004
+ try {
43005
+ return statSync(path).isDirectory();
43006
+ } catch {
43007
+ return false;
43008
+ }
43009
+ }
42981
43010
 
42982
43011
  // src/core/id.ts
42983
43012
  var ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
@@ -43641,15 +43670,11 @@ function bodyLineAt(frame, offset) {
43641
43670
  }
43642
43671
 
43643
43672
  // src/mcp/main.ts
43644
- var { values } = parseArgs({
43645
- args: process.argv.slice(2),
43646
- options: { vault: { type: "string" } },
43647
- allowPositionals: false
43648
- });
43649
- var VAULT = values.vault ?? process.env.OBELISK_VAULT ?? process.cwd();
43650
43673
  var RUN = newCommentId();
43651
43674
  var server = new McpServer({ name: "obelisk", version: version2 });
43652
- var noteArg = external_exports.string().describe("Path to the note, relative to the vault root. `.md` optional.");
43675
+ var noteArg = external_exports.string().describe(
43676
+ "Path to the note, relative to the vault root or to the working directory. Absolute paths reach a note in another vault. `.md` optional."
43677
+ );
43653
43678
  server.registerTool(
43654
43679
  "obelisk_list",
43655
43680
  {
@@ -43742,7 +43767,7 @@ server.registerTool(
43742
43767
  "obelisk_resolve",
43743
43768
  {
43744
43769
  title: "Resolve or reopen a comment",
43745
- description: "Mark a comment settled \u2014 the reader's sidebar greys it out and drops it from the open count.\n\nThis is what closes the loop on a comment a person left for you: read it, make the edit in the note yourself, then resolve it. Resolving a comment you did not write is allowed, but it says the thing it asked for has been done, so only say that when it has.",
43770
+ description: "Mark a comment settled \u2014 the reader's sidebar greys it out and drops it from the open count.\n\nThis is what closes the loop on a comment a person left for you: read it, make the edit in the note yourself, then resolve it. Resolving a comment you did not write is allowed, but it records that what the comment asked for has been done, so resolve only once it has.",
43746
43771
  inputSchema: {
43747
43772
  note: noteArg,
43748
43773
  id: external_exports.string().describe("The comment's id, from obelisk_list."),
@@ -43771,10 +43796,6 @@ async function write(note, operation) {
43771
43796
  await writeFile(path, result.value.text, "utf8");
43772
43797
  return result;
43773
43798
  }
43774
- function notePath(note) {
43775
- const path = isAbsolute(note) ? resolvePath(note) : resolvePath(join(VAULT, note));
43776
- return path.endsWith(".md") ? path : `${path}.md`;
43777
- }
43778
43799
  function summarize(comment2, listing) {
43779
43800
  return {
43780
43801
  id: comment2.id,
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "obelisk-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "The Obelisk CLI and MCP server: comment on a passage of an Obsidian note, and propose suggested edits, from outside the app.",
5
+ "mcpName": "io.github.zachhannum/obelisk",
5
6
  "bin": {
6
7
  "obelisk": "dist/cli.mjs",
7
8
  "obelisk-mcp": "dist/mcp.mjs"