obelisk-mcp 0.1.3 → 0.1.5

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
@@ -12,10 +12,15 @@ rename.
12
12
 
13
13
  ## Status
14
14
 
15
- Pre-release. Anchoring, decoration, the sidebar, suggested edits and the agent
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.
15
+ Early. Anchoring, decoration, the sidebar, suggested edits and the agent
16
+ integration are implemented. The plugin is in the Obsidian community plugin
17
+ directory, and the CLI and the MCP server are on npm as `obelisk-mcp`. None of
18
+ it has been exercised against a large vault.
19
+
20
+ ## Install
21
+
22
+ Search for **Obelisk** in *Settings → Community plugins → Browse*, install it,
23
+ and enable it. Updates come from the same panel.
19
24
 
20
25
  ## Features
21
26
 
@@ -32,7 +37,7 @@ and none of it has been exercised against a large vault.
32
37
  - **Sidebar.** All of a note's comments in document order, or newest first from
33
38
  the sort toggle. Click one to scroll the editor to it. Chips filter to open
34
39
  comments, to comments carrying a suggestion, or to one agent's review pass.
35
- Resolved comments stay in the list, greyed rather than hidden, since they are
40
+ Resolved comments stay in the list, grayed rather than hidden, since they are
36
41
  still highlighted in the note.
37
42
  - **In-text highlighting.** Commented passages are highlighted, with a †
38
43
  marker that opens the comment in the sidebar.
@@ -58,9 +63,9 @@ and none of it has been exercised against a large vault.
58
63
  from the trash icon in its header, so striking one remark out of a thread
59
64
  does not take the conversation with it. Both offer an undo rather than a
60
65
  confirmation dialog.
61
- - **Open to agents.** A command-line tool and an MCP server can read and write
62
- the same comments from outside Obsidian, so a model can review a note into
63
- your sidebar, or answer the comments you left for it. Its comments are
66
+ - **Open to agents.** A command-line tool and an MCP server read and write the
67
+ same comments from outside Obsidian, so a model can review a note into the
68
+ sidebar, or answer the comments left for it. Its comments are
64
69
  badged, and a whole review pass is one chip in the header with a *dismiss
65
70
  all* on it. See below.
66
71
 
@@ -125,12 +130,12 @@ agent is running in, and an absolute path reaches a note in one it is not. The
125
130
  server is listed in the official MCP registry as `io.github.zachhannum/obelisk`
126
131
  for a client that installs from there.
127
132
 
128
- **There is nothing to start.** It speaks MCP over stdio: the agent spawns a
129
- process when a session opens and kills it when the session ends, so
130
- `obelisk-mcp` is never run by hand and there is no port and no daemon. One
131
- process per session is also what makes a session's comments share one run chip
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`.
133
+ **The agent starts it.** It speaks MCP over stdio: the agent spawns a process
134
+ when a session opens and kills it when the session ends, so there is no port
135
+ and no daemon. One process per session is also what makes a session's comments
136
+ share one run chip in the sidebar. A session keeps the process it spawned, so a
137
+ new version of the package arrives at the next one. To take it sooner,
138
+ reconnect from `/mcp`.
134
139
 
135
140
  To check the registration, `claude mcp list`, or `/mcp` inside a session. To
136
141
  check the server itself with no agent in the way:
@@ -143,22 +148,21 @@ printf '%s\n' \
143
148
  | npx -y obelisk-mcp
144
149
  ```
145
150
 
146
- That should print the handshake and then the four tools. A server that fails
147
- this fails the same way for an agent, which is worth knowing before you go
148
- looking at the agent's end of it.
151
+ That prints the handshake and then the four tools. A server that fails this
152
+ fails the same way for an agent.
149
153
 
150
154
  For an agent that does not speak MCP, paste
151
155
  [`docs/agents-fragment.md`](docs/agents-fragment.md) into the vault's
152
156
  `AGENTS.md` or `CLAUDE.md`.
153
157
 
154
158
  The one rule worth knowing: **the quote is the anchor.** A line number is never
155
- one. A model asked for a line will produce a plausible wrong number, and a
156
- plausible wrong number attaches a comment to the wrong paragraph without ever
157
- looking like an error, so `--near-line` only picks between identical quotes and
158
- takes a number copied out of `obelisk list`. `--quote` has to appear in the
159
- note character for character, and if it does not, or appears twice, nothing is
160
- written and the reason is printed. `obelisk list` prints the body numbered so
161
- the exact text is there to copy.
159
+ one. A model asked for a line produces a plausible wrong number, which attaches
160
+ a comment to the wrong paragraph without looking like an error, so
161
+ `--near-line` only picks between identical quotes and takes a number copied out
162
+ of `obelisk list`. `--quote` has to appear in the note character for character,
163
+ and if it does not, or appears twice, nothing is written and the reason is
164
+ printed. `obelisk list` prints the body numbered so the exact text is there to
165
+ copy.
162
166
 
163
167
  Writes are frontmatter-only and leave the body byte-identical, so they are safe
164
168
  while the note is open in Obsidian. A write also re-reads the file first and
package/dist/cli.mjs CHANGED
@@ -7796,7 +7796,7 @@ function readNote(text, source = "<note>") {
7796
7796
  const frame = frameFrom(text);
7797
7797
  const doc = frontmatterOf(text, frame, source);
7798
7798
  const fm = doc?.toJS();
7799
- const data = fm && typeof fm === "object" && !Array.isArray(fm) ? fm : {};
7799
+ const data = isRecord2(fm) ? fm : {};
7800
7800
  return {
7801
7801
  text,
7802
7802
  frame,
@@ -7844,6 +7844,9 @@ function frontmatterOf(text, frame, source) {
7844
7844
  }
7845
7845
  return doc;
7846
7846
  }
7847
+ function isRecord2(value) {
7848
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7849
+ }
7847
7850
  function isEmptyDoc(doc) {
7848
7851
  const value = doc.toJS();
7849
7852
  return value == null || typeof value === "object" && Object.keys(value).length === 0;
@@ -7983,7 +7986,7 @@ The closest thing in the note is:
7983
7986
  Quote that, character for character.` : "";
7984
7987
  return fail(
7985
7988
  "quote-not-found",
7986
- "That text is not in the note. The quote has to be verbatim \u2014 copy it straight out of the listed note body, including its punctuation, capitalisation and spacing." + hint
7989
+ "That text is not in the note. The quote has to be verbatim \u2014 copy it straight out of the listed note body, including its punctuation, capitalization and spacing." + hint
7987
7990
  );
7988
7991
  }
7989
7992
  function looseMatch(body, quote) {
package/dist/mcp.mjs CHANGED
@@ -42976,7 +42976,7 @@ var StdioServerTransport = class {
42976
42976
  };
42977
42977
 
42978
42978
  // package.json
42979
- var version2 = "0.1.3";
42979
+ var version2 = "0.1.5";
42980
42980
 
42981
42981
  // src/bin/vault.ts
42982
42982
  import { existsSync, statSync } from "node:fs";
@@ -43430,7 +43430,7 @@ function readNote(text2, source = "<note>") {
43430
43430
  const frame = frameFrom(text2);
43431
43431
  const doc = frontmatterOf(text2, frame, source);
43432
43432
  const fm = doc?.toJS();
43433
- const data = fm && typeof fm === "object" && !Array.isArray(fm) ? fm : {};
43433
+ const data = isRecord2(fm) ? fm : {};
43434
43434
  return {
43435
43435
  text: text2,
43436
43436
  frame,
@@ -43478,6 +43478,9 @@ function frontmatterOf(text2, frame, source) {
43478
43478
  }
43479
43479
  return doc;
43480
43480
  }
43481
+ function isRecord2(value) {
43482
+ return typeof value === "object" && value !== null && !Array.isArray(value);
43483
+ }
43481
43484
  function isEmptyDoc(doc) {
43482
43485
  const value = doc.toJS();
43483
43486
  return value == null || typeof value === "object" && Object.keys(value).length === 0;
@@ -43600,7 +43603,7 @@ The closest thing in the note is:
43600
43603
  Quote that, character for character.` : "";
43601
43604
  return fail(
43602
43605
  "quote-not-found",
43603
- "That text is not in the note. The quote has to be verbatim \u2014 copy it straight out of the listed note body, including its punctuation, capitalisation and spacing." + hint
43606
+ "That text is not in the note. The quote has to be verbatim \u2014 copy it straight out of the listed note body, including its punctuation, capitalization and spacing." + hint
43604
43607
  );
43605
43608
  }
43606
43609
  function looseMatch(body, quote) {
@@ -43709,7 +43712,7 @@ server.registerTool(
43709
43712
  "obelisk_comment",
43710
43713
  {
43711
43714
  title: "Comment on a passage",
43712
- description: "Leave a comment on one passage of a note. It appears in the reader's Obsidian sidebar, badged as coming from an agent.\n\n`quote` is the anchor and must appear in the note verbatim \u2014 the same characters, punctuation, capitalisation and spacing. Copy it from obelisk_list; do not retype it or tidy it up. There is no way to pass a line or column, and you should not try to count them: the quote is the anchor and the tool does the arithmetic. If the quote is not found, or is found more than once, nothing is written and you are told which.\n\n`body` is markdown. To propose a specific rewrite, put it in a ```suggestion fenced block inside the body \u2014 its contents replace exactly the quoted passage when the reader clicks Apply, so a comment can explain itself and propose the change at once.",
43715
+ description: "Leave a comment on one passage of a note. It appears in the reader's Obsidian sidebar, badged as coming from an agent.\n\n`quote` is the anchor and must appear in the note verbatim \u2014 the same characters, punctuation, capitalization and spacing. Copy it from obelisk_list; do not retype it or tidy it up. There is no way to pass a line or column, and you should not try to count them: the quote is the anchor and the tool does the arithmetic. If the quote is not found, or is found more than once, nothing is written and you are told which.\n\n`body` is markdown. To propose a specific rewrite, put it in a ```suggestion fenced block inside the body \u2014 its contents replace exactly the quoted passage when the reader clicks Apply, so a comment can explain itself and propose the change at once.",
43713
43716
  inputSchema: {
43714
43717
  note: noteArg,
43715
43718
  quote: external_exports.string().describe(
@@ -43767,7 +43770,7 @@ server.registerTool(
43767
43770
  "obelisk_resolve",
43768
43771
  {
43769
43772
  title: "Resolve or reopen a comment",
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.",
43773
+ description: "Mark a comment settled \u2014 the reader's sidebar grays 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.",
43771
43774
  inputSchema: {
43772
43775
  note: noteArg,
43773
43776
  id: external_exports.string().describe("The comment's id, from obelisk_list."),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "obelisk-mcp",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
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
5
  "mcpName": "io.github.zachhannum/obelisk",
6
6
  "bin": {
@@ -14,6 +14,7 @@
14
14
  },
15
15
  "scripts": {
16
16
  "dev": "node esbuild.config.mjs",
17
+ "lint": "eslint --max-warnings 0 .",
17
18
  "build": "tsc --noEmit --skipLibCheck && node esbuild.config.mjs production",
18
19
  "prepublishOnly": "npm run build",
19
20
  "shots": "npm run build && playwright test",
@@ -28,10 +29,13 @@
28
29
  "@playwright/test": "^1.62.1",
29
30
  "@types/node": "^20.14.0",
30
31
  "esbuild": "^0.21.5",
31
- "obsidian": "^1.7.2",
32
+ "eslint": "9.37.0",
33
+ "eslint-plugin-obsidianmd": "0.4.1",
34
+ "obsidian": "^1.13.1",
32
35
  "obsidian-launcher": "^3.2.0",
33
36
  "tslib": "^2.6.3",
34
37
  "typescript": "^5.5.0",
38
+ "typescript-eslint": "8.61.1",
35
39
  "yaml": "^2.9.0",
36
40
  "zod": "^4.5.4"
37
41
  },