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 +20 -41
- package/dist/cli.mjs +42 -11
- package/dist/mcp.mjs +108 -87
- package/package.json +2 -1
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
|
|
8
|
-
|
|
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.
|
|
17
|
-
plugin
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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.
|
|
154
|
-
|
|
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
|
-
|
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
8330
|
+
function notePath2(arg) {
|
|
8298
8331
|
if (!arg) throw new UsageError("Which note? Pass a path.");
|
|
8299
|
-
|
|
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,
|
|
299
|
-
return this._reduceValues(
|
|
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(
|
|
306
|
-
return this._reduceValues(
|
|
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(
|
|
312
|
+
_reduceValues(values, valueCode, usedValues = {}, getCode) {
|
|
313
313
|
let code = code_1.nil;
|
|
314
|
-
for (const prefix in
|
|
315
|
-
const vs =
|
|
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
|
|
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(
|
|
17825
|
+
function createZodEnum(values, params) {
|
|
17827
17826
|
return new ZodEnum({
|
|
17828
|
-
values
|
|
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(
|
|
17885
|
-
return _ZodEnum.create(
|
|
17883
|
+
extract(values, newDef = this._def) {
|
|
17884
|
+
return _ZodEnum.create(values, {
|
|
17886
17885
|
...this._def,
|
|
17887
17886
|
...newDef
|
|
17888
17887
|
});
|
|
17889
17888
|
}
|
|
17890
|
-
exclude(
|
|
17891
|
-
return _ZodEnum.create(this.options.filter((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 = (
|
|
17928
|
+
ZodNativeEnum.create = (values, params) => {
|
|
17930
17929
|
return new ZodNativeEnum({
|
|
17931
|
-
values
|
|
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
|
|
18814
|
-
return
|
|
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
|
|
21971
|
-
if (!
|
|
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
|
|
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
|
|
22257
|
-
if (
|
|
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
|
|
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 (
|
|
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
|
|
22496
|
-
const valuesSet = new Set(
|
|
22494
|
+
const values = getEnumValues(def.entries);
|
|
22495
|
+
const valuesSet = new Set(values);
|
|
22497
22496
|
inst._zod.values = valuesSet;
|
|
22498
|
-
const patternValues =
|
|
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
|
|
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
|
|
22517
|
-
inst._zod.values =
|
|
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 (
|
|
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
|
|
22581
|
-
return
|
|
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
|
|
31666
|
-
if (
|
|
31667
|
-
const literalSet = addConstant(ctx, new Set(
|
|
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 =
|
|
31670
|
+
const value = values[0];
|
|
31672
31671
|
if (typeof value === "number" && Number.isNaN(value)) {
|
|
31673
|
-
const literalSet = addConstant(ctx, new Set(
|
|
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
|
|
31694
|
-
if (!
|
|
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,
|
|
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
|
|
31847
|
-
const valuesConst = addConstant(ctx,
|
|
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
|
|
31885
|
-
if (!
|
|
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
|
|
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(
|
|
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,
|
|
32974
|
-
const entries = Array.isArray(
|
|
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
|
|
33891
|
-
if (
|
|
33889
|
+
const values = getEnumValues(def.entries);
|
|
33890
|
+
if (values.length === 0) {
|
|
33892
33891
|
json2.not = {};
|
|
33893
33892
|
return;
|
|
33894
33893
|
}
|
|
33895
|
-
if (
|
|
33894
|
+
if (values.every((v) => typeof v === "number"))
|
|
33896
33895
|
json2.type = "number";
|
|
33897
|
-
if (
|
|
33896
|
+
if (values.every((v) => typeof v === "string"))
|
|
33898
33897
|
json2.type = "string";
|
|
33899
|
-
json2.enum =
|
|
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
|
|
34175
|
-
if (!numericType && !
|
|
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 (!
|
|
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
|
|
34741
|
-
if (
|
|
34739
|
+
const values = Object.values(shape);
|
|
34740
|
+
if (values.length === 0)
|
|
34742
34741
|
return object({});
|
|
34743
|
-
const allV4 =
|
|
34744
|
-
const allV3 =
|
|
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
|
|
34799
|
-
if (
|
|
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 = (
|
|
36431
|
+
inst.extract = (values, params) => {
|
|
36433
36432
|
const newEntries = {};
|
|
36434
|
-
for (const value of
|
|
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 = (
|
|
36446
|
+
inst.exclude = (values, params) => {
|
|
36448
36447
|
const newEntries = { ...def.entries };
|
|
36449
|
-
for (const value of
|
|
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(
|
|
36464
|
-
const entries = Array.isArray(
|
|
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
|
|
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,
|
|
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((
|
|
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.
|
|
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(
|
|
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
|
|
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.
|
|
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"
|