@theglitchking/semantic-pages 0.4.9 → 0.6.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.
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "semantic-pages-marketplace",
3
+ "owner": {
4
+ "name": "TheGlitchKing",
5
+ "email": "theglitchking@users.noreply.github.com"
6
+ },
7
+ "metadata": {
8
+ "description": "Official marketplace for semantic-pages - Semantic search + knowledge graph MCP server with auto-wiring for .claude/.vault and hit-em-with-the-docs companion",
9
+ "version": "0.6.0"
10
+ },
11
+ "plugins": [
12
+ {
13
+ "name": "semantic-pages",
14
+ "description": "Semantic search + knowledge graph MCP server for markdown vaults. Auto-wires a read/write vault at .claude/.vault, and a read-only docs index at .documentation when hit-em-with-the-docs is also installed.",
15
+ "version": "0.6.0",
16
+ "author": {
17
+ "name": "TheGlitchKing"
18
+ },
19
+ "homepage": "https://github.com/TheGlitchKing/semantic-pages",
20
+ "repository": "https://github.com/TheGlitchKing/semantic-pages.git",
21
+ "license": "MIT",
22
+ "keywords": [
23
+ "mcp",
24
+ "semantic-search",
25
+ "knowledge-graph",
26
+ "markdown",
27
+ "vector-search",
28
+ "embeddings",
29
+ "wikilinks",
30
+ "vault",
31
+ "claude-code"
32
+ ],
33
+ "category": "search",
34
+ "tags": [
35
+ "mcp-server",
36
+ "semantic-search",
37
+ "knowledge-graph",
38
+ "auto-wire"
39
+ ],
40
+ "source": {
41
+ "source": "npm",
42
+ "package": "@theglitchking/semantic-pages"
43
+ },
44
+ "strict": true
45
+ }
46
+ ]
47
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "semantic-pages",
3
+ "description": "Semantic search + knowledge graph MCP server for markdown vaults. Auto-wires a read/write vault at .claude/.vault, and a read-only docs index at .documentation when hit-em-with-the-docs is also installed.",
4
+ "version": "0.6.0",
5
+ "author": {
6
+ "name": "TheGlitchKing",
7
+ "email": "theglitchking@users.noreply.github.com"
8
+ },
9
+ "license": "MIT",
10
+ "homepage": "https://github.com/TheGlitchKing/semantic-pages",
11
+ "repository": "https://github.com/TheGlitchKing/semantic-pages",
12
+ "keywords": [
13
+ "mcp",
14
+ "semantic-search",
15
+ "knowledge-graph",
16
+ "markdown",
17
+ "vector-search",
18
+ "embeddings",
19
+ "wikilinks",
20
+ "vault"
21
+ ]
22
+ }
package/LICENSE CHANGED
File without changes
package/README.md CHANGED
@@ -39,6 +39,48 @@ The index is stored in `.semantic-pages-index/` alongside your notes (gitignore
39
39
  - **File Watcher**: Incremental re-indexing on file changes with debounce
40
40
  - **Local Embeddings**: No API key, no network after first model download
41
41
  - **Zero Dependencies Beyond Node**: No Docker, no Python, no Obsidian, no GUI
42
+ - **Auto-Wire**: Installing the Claude Code plugin auto-creates `./.claude/.vault/` and wires it as a read/write MCP server — no manual `.mcp.json` editing required
43
+ - **Sister Plugin Companion**: When [`hit-em-with-the-docs`](https://github.com/TheGlitchKing/hit-em-with-the-docs) is also installed, a second **read-only** MCP server is auto-wired at `./.documentation/` so you can semantically search your docs without risking accidental writes to a tree that hewtd owns
44
+
45
+ ---
46
+
47
+ ## Sister Plugin: hit-em-with-the-docs
48
+
49
+ [`hit-em-with-the-docs`](https://github.com/TheGlitchKing/hit-em-with-the-docs) is the **canonical writer** of `./.documentation/`. It scaffolds, classifies, maintains, and validates docs across 15 domains with a 22-field metadata schema. Semantic Pages is the **canonical reader** — when both plugins are installed, Semantic Pages auto-wires a read-only index of `./.documentation/` so Claude can search it, traverse its wikilinks, and list/read docs without any write primitives exposed. That split keeps hewtd the sole authority for writes while giving you semantic discovery over the result.
50
+
51
+ ### The matrix
52
+
53
+ | `semantic-pages` installed | `hit-em-with-the-docs` installed | Result |
54
+ |---|---|---|
55
+ | ✗ | ✗ | nothing |
56
+ | ✗ | ✓ | hewtd CLI only; `.documentation/` managed but not indexed |
57
+ | ✓ | ✗ | single MCP at `./.claude/.vault` (read/write, auto-created) |
58
+ | ✓ | ✓ | `./.claude/.vault` (read/write) **+** `./.documentation` (read-only) |
59
+
60
+ ### How the auto-wire works
61
+
62
+ Semantic Pages ships a `SessionStart` hook that runs at the start of every Claude Code session and reconciles the project's `.mcp.json`:
63
+
64
+ 1. Always ensures `./.claude/.vault/` exists (creates if missing)
65
+ 2. Always ensures a `semantic-vault` MCP entry pointed at `./.claude/.vault` (read/write — your personal research notes, session artifacts, scratch graph)
66
+ 3. If `hit-em-with-the-docs` is in your enabled plugins **and** `./.documentation/` exists in the current project → adds a `semantic-pages` MCP entry pointed at `./.documentation` with `--read-only` (the 7 write tools are suppressed at the MCP tool list level)
67
+ 4. If either condition stops being true → idempotently removes the `semantic-pages` entry (self-healing when you uninstall hewtd or the docs tree goes away)
68
+
69
+ The hook is a no-op when the computed `.mcp.json` matches what's already on disk, so there's no git churn from repeated session starts. It only touches its own entries — any custom MCP servers you've added (playwright, custom tools, etc.) are left untouched. If you've manually defined a `semantic-pages` entry pointing at a non-`.documentation` path, the hook respects it and leaves it alone.
70
+
71
+ ### Why read-only for `.documentation/`
72
+
73
+ `./.documentation/` is a **managed tree** — hewtd owns the lifecycle (creates files, classifies them, maintains frontmatter, prunes stale entries, checks links). If Semantic Pages also exposed `create_note`, `update_note`, `delete_note`, `move_note`, `update_frontmatter`, `manage_tags`, and `rename_tag` over that tree, you'd have two writers racing over the same schema and hewtd couldn't guarantee its invariants. Read-only gives you semantic discovery without that risk. Your personal `.claude/.vault/` stays fully read/write — hewtd doesn't touch it, so Semantic Pages is the sole writer there and all 21 tools are available.
74
+
75
+ ### The `--read-only` flag
76
+
77
+ The auto-wire uses a new `--read-only` CLI flag (v0.6.0+) that filters out the 7 write tools from the MCP server's tool list at startup. You can use it manually anywhere:
78
+
79
+ ```bash
80
+ semantic-pages --notes ./any-shared-vault --read-only
81
+ ```
82
+
83
+ Only the 14 read tools are exposed (`search_*`, `read_note`, `read_multiple_notes`, `list_notes`, `backlinks`, `forwardlinks`, `graph_path`, `graph_statistics`, `get_frontmatter`, `get_stats`, `reindex`).
42
84
 
43
85
  ---
44
86
 
@@ -120,6 +162,24 @@ npm install --save-dev @theglitchking/semantic-pages
120
162
  }
121
163
  ```
122
164
 
165
+ #### Method E: Claude Code Plugin (Recommended — zero config, auto-wires)
166
+
167
+ This is the easiest path if you use Claude Code and want `.claude/.vault/` + (optionally) `.documentation/` indexed automatically.
168
+
169
+ ```bash
170
+ # Inside a Claude Code session:
171
+ /plugin marketplace add TheGlitchKing/semantic-pages
172
+ /plugin install semantic-pages@semantic-pages-marketplace
173
+ ```
174
+
175
+ What happens next session:
176
+ 1. A `SessionStart` hook runs and ensures `./.claude/.vault/` exists
177
+ 2. Your project's `.mcp.json` gets a `semantic-vault` entry pointed at `./.claude/.vault` (read/write)
178
+ 3. **If** `hit-em-with-the-docs` is also installed **and** `./.documentation/` exists → a second `semantic-pages` entry is added, pointed at `./.documentation` with `--read-only`
179
+ 4. You get 21 tools (14 if the docs server is the one being used) for semantic search, graph traversal, and (for the vault) note CRUD
180
+
181
+ No manual `.mcp.json` editing. Uninstalling the plugin cleanly leaves your existing entries alone on the next session.
182
+
123
183
  ---
124
184
 
125
185
  ### 2. How to Use
@@ -515,7 +575,7 @@ src/
515
575
 
516
576
  **Frontmatter is optional.** Every note gets a modification timestamp regardless — resolved from frontmatter date fields if present, otherwise from the file's `fs.stat` mtime. When frontmatter fields like `status`, `tier`, `domains`, `load_priority`, or `purpose` are present, they're indexed and exposed through all search tools as filters and score boosters. Plain notes with no frontmatter work exactly as before.
517
577
 
518
- If you want structured frontmatter with a full schema (22 fields, 15 domains, health scoring), [**hit-em-with-the-docs**](https://github.com/TheGlitchKing/hit-em-with-the-docs) is a companion tool that manages docs for Claude Code projects its schema is natively understood by Semantic Pages.
578
+ If you want structured frontmatter with a full schema (22 fields, 15 domains, health scoring), [**hit-em-with-the-docs**](https://github.com/TheGlitchKing/hit-em-with-the-docs) is Semantic Pages' **sister plugin** (see [Sister Plugin: hit-em-with-the-docs](#sister-plugin-hit-em-with-the-docs) above). It manages `./.documentation/` as a writer-owned tree; Semantic Pages auto-wires a read-only index of it when both plugins are installed. All hewtd frontmatter fields are natively understood by the indexer.
519
579
 
520
580
  #### Step 2: Chunk
521
581
  ```
File without changes
package/dist/cli/index.js CHANGED
@@ -208,7 +208,7 @@ program.command("tools [name]").description("List all MCP tools, or show details
208
208
  }
209
209
  process.exit(0);
210
210
  });
211
- program.command("serve", { isDefault: true }).description("Start the MCP server (default command)").requiredOption("--notes <path>", "Path to markdown notes directory").option("--reindex", "Force full reindex and exit").option("--stats", "Show vault statistics and exit").option("--model <name>", "Embedding model to use (default: all-MiniLM-L6-v2, fast; use nomic-ai/nomic-embed-text-v1.5 for higher quality)").option("--workers <n>", "Number of worker threads for parallel embedding", parseInt).option("--batch-size <n>", "Texts per ONNX forward pass (default: 8)", parseInt).option("--no-quantized", "Use full-precision model instead of quantized (slower, slightly higher quality)").option("--no-watch", "Disable file watcher").action(async (opts) => {
211
+ program.command("serve", { isDefault: true }).description("Start the MCP server (default command)").requiredOption("--notes <path>", "Path to markdown notes directory").option("--reindex", "Force full reindex and exit").option("--stats", "Show vault statistics and exit").option("--read-only", "Suppress write tools (create_note, update_note, delete_note, move_note, update_frontmatter, manage_tags, rename_tag) \u2014 use for shared docs vaults owned by another tool").option("--model <name>", "Embedding model to use (default: all-MiniLM-L6-v2, fast; use nomic-ai/nomic-embed-text-v1.5 for higher quality)").option("--workers <n>", "Number of worker threads for parallel embedding", parseInt).option("--batch-size <n>", "Texts per ONNX forward pass (default: 8)", parseInt).option("--no-quantized", "Use full-precision model instead of quantized (slower, slightly higher quality)").option("--no-watch", "Disable file watcher").action(async (opts) => {
212
212
  const notesPath = resolve(opts.notes);
213
213
  if (!existsSync(notesPath)) {
214
214
  console.error(`Error: notes directory not found: ${notesPath}`);
@@ -242,7 +242,14 @@ program.command("serve", { isDefault: true }).description("Start the MCP server
242
242
  process.exit(0);
243
243
  }
244
244
  const { startServer } = await import("../mcp/server.js");
245
- await startServer(notesPath, { watch: opts.watch, model: opts.model, workers: opts.workers, batchSize: opts.batchSize, quantized: opts.quantized });
245
+ await startServer(notesPath, {
246
+ watch: opts.watch,
247
+ model: opts.model,
248
+ workers: opts.workers,
249
+ batchSize: opts.batchSize,
250
+ quantized: opts.quantized,
251
+ readOnly: opts.readOnly
252
+ });
246
253
  });
247
254
  program.parse();
248
255
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { program } from \"commander\";\nimport { resolve } from \"node:path\";\nimport { existsSync } from \"node:fs\";\n\nconst TOOL_HELP: Record<string, { description: string; args: string; examples: string[] }> = {\n // Search\n search_semantic: {\n description: \"Vector similarity search — find notes by meaning, not just keywords\",\n args: '{ \"query\": \"string\", \"limit?\": 10 }',\n examples: [\n '{ \"query\": \"microservices architecture\", \"limit\": 5 }',\n '{ \"query\": \"how to deploy to production\" }',\n ],\n },\n search_text: {\n description: \"Full-text keyword or regex search with optional filters\",\n args: '{ \"pattern\": \"string\", \"regex?\": false, \"caseSensitive?\": false, \"pathGlob?\": \"string\", \"tagFilter?\": [\"string\"], \"limit?\": 20 }',\n examples: [\n '{ \"pattern\": \"RabbitMQ\" }',\n '{ \"pattern\": \"OAuth\\\\\\\\d\", \"regex\": true }',\n '{ \"pattern\": \"deploy\", \"pathGlob\": \"devops/**\", \"tagFilter\": [\"kubernetes\"] }',\n ],\n },\n search_graph: {\n description: \"Graph traversal — find notes connected to a concept via wikilinks and tags\",\n args: '{ \"concept\": \"string\", \"maxDepth?\": 2 }',\n examples: [\n '{ \"concept\": \"microservices\" }',\n '{ \"concept\": \"auth\", \"maxDepth\": 3 }',\n ],\n },\n search_hybrid: {\n description: \"Combined semantic + graph search — vector results re-ranked by graph proximity\",\n args: '{ \"query\": \"string\", \"limit?\": 10 }',\n examples: [\n '{ \"query\": \"event driven architecture\", \"limit\": 5 }',\n ],\n },\n\n // Read\n read_note: {\n description: \"Read the full content of a specific note by path\",\n args: '{ \"path\": \"string\" }',\n examples: [\n '{ \"path\": \"project-overview.md\" }',\n '{ \"path\": \"notes/meeting-2024-01-15.md\" }',\n ],\n },\n read_multiple_notes: {\n description: \"Batch read multiple notes in one call\",\n args: '{ \"paths\": [\"string\"] }',\n examples: [\n '{ \"paths\": [\"overview.md\", \"architecture.md\", \"deployment.md\"] }',\n ],\n },\n list_notes: {\n description: \"List all indexed notes with metadata (title, tags, link count)\",\n args: \"{}\",\n examples: [\"{}\"],\n },\n\n // Write\n create_note: {\n description: \"Create a new markdown note with optional YAML frontmatter\",\n args: '{ \"path\": \"string\", \"content\": \"string\", \"frontmatter?\": {} }',\n examples: [\n '{ \"path\": \"new-guide.md\", \"content\": \"# Guide\\\\n\\\\nContent here.\" }',\n '{ \"path\": \"tagged.md\", \"content\": \"Content.\", \"frontmatter\": { \"title\": \"Tagged Note\", \"tags\": [\"test\"] } }',\n ],\n },\n update_note: {\n description: \"Edit note content — overwrite, append, prepend, or patch by heading\",\n args: '{ \"path\": \"string\", \"content\": \"string\", \"mode\": \"overwrite|append|prepend|patch-by-heading\", \"heading?\": \"string\" }',\n examples: [\n '{ \"path\": \"guide.md\", \"content\": \"New content.\", \"mode\": \"overwrite\" }',\n '{ \"path\": \"guide.md\", \"content\": \"\\\\n## Appendix\\\\nExtra info.\", \"mode\": \"append\" }',\n '{ \"path\": \"guide.md\", \"content\": \"Updated architecture section.\", \"mode\": \"patch-by-heading\", \"heading\": \"Architecture\" }',\n ],\n },\n delete_note: {\n description: \"Delete a note permanently (requires confirm=true)\",\n args: '{ \"path\": \"string\", \"confirm\": true }',\n examples: [\n '{ \"path\": \"old-note.md\", \"confirm\": true }',\n '{ \"path\": \"old-note.md\", \"confirm\": false } // returns warning, does not delete',\n ],\n },\n move_note: {\n description: \"Move or rename a note — automatically updates wikilinks across the vault\",\n args: '{ \"from\": \"string\", \"to\": \"string\" }',\n examples: [\n '{ \"from\": \"user-service.md\", \"to\": \"auth-service.md\" }',\n '{ \"from\": \"old/note.md\", \"to\": \"new/location/note.md\" }',\n ],\n },\n\n // Metadata\n get_frontmatter: {\n description: \"Read parsed YAML frontmatter from a note as JSON\",\n args: '{ \"path\": \"string\" }',\n examples: ['{ \"path\": \"project-overview.md\" }'],\n },\n update_frontmatter: {\n description: \"Set or delete YAML frontmatter keys — pass null to delete a key\",\n args: '{ \"path\": \"string\", \"fields\": {} }',\n examples: [\n '{ \"path\": \"note.md\", \"fields\": { \"status\": \"active\", \"priority\": 1 } }',\n '{ \"path\": \"note.md\", \"fields\": { \"deprecated_field\": null } } // deletes the key',\n ],\n },\n manage_tags: {\n description: \"Add, remove, or list tags on a note (frontmatter and inline)\",\n args: '{ \"path\": \"string\", \"action\": \"add|remove|list\", \"tags?\": [\"string\"] }',\n examples: [\n '{ \"path\": \"note.md\", \"action\": \"list\" }',\n '{ \"path\": \"note.md\", \"action\": \"add\", \"tags\": [\"important\", \"reviewed\"] }',\n '{ \"path\": \"note.md\", \"action\": \"remove\", \"tags\": [\"draft\"] }',\n ],\n },\n rename_tag: {\n description: \"Rename a tag across all notes in the vault (frontmatter + inline)\",\n args: '{ \"oldTag\": \"string\", \"newTag\": \"string\" }',\n examples: ['{ \"oldTag\": \"architecture\", \"newTag\": \"arch\" }'],\n },\n\n // Graph\n backlinks: {\n description: \"Find all notes that link TO a given note via [[wikilinks]]\",\n args: '{ \"path\": \"string\" }',\n examples: ['{ \"path\": \"microservices.md\" }'],\n },\n forwardlinks: {\n description: \"Find all notes linked FROM a given note\",\n args: '{ \"path\": \"string\" }',\n examples: ['{ \"path\": \"project-overview.md\" }'],\n },\n graph_path: {\n description: \"Find the shortest path between two notes in the knowledge graph\",\n args: '{ \"from\": \"string\", \"to\": \"string\" }',\n examples: ['{ \"from\": \"project-overview.md\", \"to\": \"user-service.md\" }'],\n },\n graph_statistics: {\n description: \"Knowledge graph stats — most connected nodes, orphans, density\",\n args: \"{}\",\n examples: [\"{}\"],\n },\n\n // System\n get_stats: {\n description: \"Vault and index statistics — note count, chunks, embeddings, graph density\",\n args: \"{}\",\n examples: [\"{}\"],\n },\n reindex: {\n description: \"Force a full reindex of the vault\",\n args: \"{}\",\n examples: [\"{}\"],\n },\n};\n\nconst TOOL_CATEGORIES: Record<string, string[]> = {\n Search: [\"search_semantic\", \"search_text\", \"search_graph\", \"search_hybrid\"],\n Read: [\"read_note\", \"read_multiple_notes\", \"list_notes\"],\n Write: [\"create_note\", \"update_note\", \"delete_note\", \"move_note\"],\n Metadata: [\"get_frontmatter\", \"update_frontmatter\", \"manage_tags\", \"rename_tag\"],\n Graph: [\"backlinks\", \"forwardlinks\", \"graph_path\", \"graph_statistics\"],\n System: [\"get_stats\", \"reindex\"],\n};\n\nfunction printToolList() {\n console.log(\"\\nSemantic Pages — 21 MCP Tools\\n\");\n console.log(\"Usage: These tools are available via MCP when the server is running.\");\n console.log(\" Run `semantic-pages tools <name>` for details on a specific tool.\\n\");\n\n for (const [category, tools] of Object.entries(TOOL_CATEGORIES)) {\n console.log(` ${category}:`);\n for (const name of tools) {\n const tool = TOOL_HELP[name];\n console.log(` ${name.padEnd(24)} ${tool.description}`);\n }\n console.log();\n }\n\n console.log(\"Run `semantic-pages tools <tool-name>` for arguments and examples.\");\n}\n\nfunction printToolDetail(name: string) {\n const tool = TOOL_HELP[name];\n if (!tool) {\n console.error(`Unknown tool: ${name}`);\n console.error(`Run \\`semantic-pages tools\\` to see all available tools.`);\n process.exit(1);\n }\n\n console.log(`\\n ${name}`);\n console.log(` ${\"─\".repeat(name.length)}`);\n console.log(` ${tool.description}\\n`);\n console.log(` Arguments:`);\n console.log(` ${tool.args}\\n`);\n console.log(` Examples:`);\n for (const ex of tool.examples) {\n console.log(` ${ex}`);\n }\n console.log();\n}\n\nprogram\n .name(\"semantic-pages\")\n .description(\n \"Semantic search + knowledge graph MCP server for markdown files\\n\\n\" +\n \" Start MCP server: semantic-pages --notes ./vault\\n\" +\n \" Show vault stats: semantic-pages --notes ./vault --stats\\n\" +\n \" Force reindex: semantic-pages --notes ./vault --reindex\\n\" +\n \" List MCP tools: semantic-pages tools\\n\" +\n \" Tool details: semantic-pages tools search_semantic\"\n )\n .version(\"0.4.3\");\n\nprogram\n .command(\"tools [name]\")\n .description(\"List all MCP tools, or show details for a specific tool\")\n .action((name?: string) => {\n if (name) {\n printToolDetail(name);\n } else {\n printToolList();\n }\n process.exit(0);\n });\n\nprogram\n .command(\"serve\", { isDefault: true })\n .description(\"Start the MCP server (default command)\")\n .requiredOption(\"--notes <path>\", \"Path to markdown notes directory\")\n .option(\"--reindex\", \"Force full reindex and exit\")\n .option(\"--stats\", \"Show vault statistics and exit\")\n .option(\"--model <name>\", \"Embedding model to use (default: all-MiniLM-L6-v2, fast; use nomic-ai/nomic-embed-text-v1.5 for higher quality)\")\n .option(\"--workers <n>\", \"Number of worker threads for parallel embedding\", parseInt)\n .option(\"--batch-size <n>\", \"Texts per ONNX forward pass (default: 8)\", parseInt)\n .option(\"--no-quantized\", \"Use full-precision model instead of quantized (slower, slightly higher quality)\")\n .option(\"--no-watch\", \"Disable file watcher\")\n .action(async (opts) => {\n const notesPath = resolve(opts.notes);\n\n if (!existsSync(notesPath)) {\n console.error(`Error: notes directory not found: ${notesPath}`);\n process.exit(1);\n }\n\n if (opts.stats) {\n const { Indexer } = await import(\"../core/indexer.js\");\n const indexer = new Indexer(notesPath);\n const docs = await indexer.indexAll();\n console.log(`Notes: ${docs.length}`);\n console.log(`Chunks: ${docs.reduce((n: number, d: any) => n + d.chunks.length, 0)}`);\n console.log(`Wikilinks: ${docs.reduce((n: number, d: any) => n + d.wikilinks.length, 0)}`);\n console.log(`Tags: ${new Set(docs.flatMap((d: any) => d.tags)).size} unique`);\n process.exit(0);\n }\n\n if (opts.reindex) {\n const { createServer } = await import(\"../mcp/server.js\");\n await createServer(notesPath, {\n watch: false,\n waitForReady: true,\n model: opts.model,\n workers: opts.workers,\n batchSize: opts.batchSize,\n quantized: opts.quantized,\n onProgress: (embedded, total) => {\n process.stderr.write(`\\rEmbedding ${embedded}/${total} chunks...`);\n },\n });\n process.stderr.write(\"\\n\");\n console.log(\"Reindex complete.\");\n process.exit(0);\n }\n\n // Default: start MCP server on stdio\n const { startServer } = await import(\"../mcp/server.js\");\n await startServer(notesPath, { watch: opts.watch, model: opts.model, workers: opts.workers, batchSize: opts.batchSize, quantized: opts.quantized });\n });\n\nprogram.parse();\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { program } from \"commander\";\nimport { resolve } from \"node:path\";\nimport { existsSync } from \"node:fs\";\n\nconst TOOL_HELP: Record<string, { description: string; args: string; examples: string[] }> = {\n // Search\n search_semantic: {\n description: \"Vector similarity search — find notes by meaning, not just keywords\",\n args: '{ \"query\": \"string\", \"limit?\": 10 }',\n examples: [\n '{ \"query\": \"microservices architecture\", \"limit\": 5 }',\n '{ \"query\": \"how to deploy to production\" }',\n ],\n },\n search_text: {\n description: \"Full-text keyword or regex search with optional filters\",\n args: '{ \"pattern\": \"string\", \"regex?\": false, \"caseSensitive?\": false, \"pathGlob?\": \"string\", \"tagFilter?\": [\"string\"], \"limit?\": 20 }',\n examples: [\n '{ \"pattern\": \"RabbitMQ\" }',\n '{ \"pattern\": \"OAuth\\\\\\\\d\", \"regex\": true }',\n '{ \"pattern\": \"deploy\", \"pathGlob\": \"devops/**\", \"tagFilter\": [\"kubernetes\"] }',\n ],\n },\n search_graph: {\n description: \"Graph traversal — find notes connected to a concept via wikilinks and tags\",\n args: '{ \"concept\": \"string\", \"maxDepth?\": 2 }',\n examples: [\n '{ \"concept\": \"microservices\" }',\n '{ \"concept\": \"auth\", \"maxDepth\": 3 }',\n ],\n },\n search_hybrid: {\n description: \"Combined semantic + graph search — vector results re-ranked by graph proximity\",\n args: '{ \"query\": \"string\", \"limit?\": 10 }',\n examples: [\n '{ \"query\": \"event driven architecture\", \"limit\": 5 }',\n ],\n },\n\n // Read\n read_note: {\n description: \"Read the full content of a specific note by path\",\n args: '{ \"path\": \"string\" }',\n examples: [\n '{ \"path\": \"project-overview.md\" }',\n '{ \"path\": \"notes/meeting-2024-01-15.md\" }',\n ],\n },\n read_multiple_notes: {\n description: \"Batch read multiple notes in one call\",\n args: '{ \"paths\": [\"string\"] }',\n examples: [\n '{ \"paths\": [\"overview.md\", \"architecture.md\", \"deployment.md\"] }',\n ],\n },\n list_notes: {\n description: \"List all indexed notes with metadata (title, tags, link count)\",\n args: \"{}\",\n examples: [\"{}\"],\n },\n\n // Write\n create_note: {\n description: \"Create a new markdown note with optional YAML frontmatter\",\n args: '{ \"path\": \"string\", \"content\": \"string\", \"frontmatter?\": {} }',\n examples: [\n '{ \"path\": \"new-guide.md\", \"content\": \"# Guide\\\\n\\\\nContent here.\" }',\n '{ \"path\": \"tagged.md\", \"content\": \"Content.\", \"frontmatter\": { \"title\": \"Tagged Note\", \"tags\": [\"test\"] } }',\n ],\n },\n update_note: {\n description: \"Edit note content — overwrite, append, prepend, or patch by heading\",\n args: '{ \"path\": \"string\", \"content\": \"string\", \"mode\": \"overwrite|append|prepend|patch-by-heading\", \"heading?\": \"string\" }',\n examples: [\n '{ \"path\": \"guide.md\", \"content\": \"New content.\", \"mode\": \"overwrite\" }',\n '{ \"path\": \"guide.md\", \"content\": \"\\\\n## Appendix\\\\nExtra info.\", \"mode\": \"append\" }',\n '{ \"path\": \"guide.md\", \"content\": \"Updated architecture section.\", \"mode\": \"patch-by-heading\", \"heading\": \"Architecture\" }',\n ],\n },\n delete_note: {\n description: \"Delete a note permanently (requires confirm=true)\",\n args: '{ \"path\": \"string\", \"confirm\": true }',\n examples: [\n '{ \"path\": \"old-note.md\", \"confirm\": true }',\n '{ \"path\": \"old-note.md\", \"confirm\": false } // returns warning, does not delete',\n ],\n },\n move_note: {\n description: \"Move or rename a note — automatically updates wikilinks across the vault\",\n args: '{ \"from\": \"string\", \"to\": \"string\" }',\n examples: [\n '{ \"from\": \"user-service.md\", \"to\": \"auth-service.md\" }',\n '{ \"from\": \"old/note.md\", \"to\": \"new/location/note.md\" }',\n ],\n },\n\n // Metadata\n get_frontmatter: {\n description: \"Read parsed YAML frontmatter from a note as JSON\",\n args: '{ \"path\": \"string\" }',\n examples: ['{ \"path\": \"project-overview.md\" }'],\n },\n update_frontmatter: {\n description: \"Set or delete YAML frontmatter keys — pass null to delete a key\",\n args: '{ \"path\": \"string\", \"fields\": {} }',\n examples: [\n '{ \"path\": \"note.md\", \"fields\": { \"status\": \"active\", \"priority\": 1 } }',\n '{ \"path\": \"note.md\", \"fields\": { \"deprecated_field\": null } } // deletes the key',\n ],\n },\n manage_tags: {\n description: \"Add, remove, or list tags on a note (frontmatter and inline)\",\n args: '{ \"path\": \"string\", \"action\": \"add|remove|list\", \"tags?\": [\"string\"] }',\n examples: [\n '{ \"path\": \"note.md\", \"action\": \"list\" }',\n '{ \"path\": \"note.md\", \"action\": \"add\", \"tags\": [\"important\", \"reviewed\"] }',\n '{ \"path\": \"note.md\", \"action\": \"remove\", \"tags\": [\"draft\"] }',\n ],\n },\n rename_tag: {\n description: \"Rename a tag across all notes in the vault (frontmatter + inline)\",\n args: '{ \"oldTag\": \"string\", \"newTag\": \"string\" }',\n examples: ['{ \"oldTag\": \"architecture\", \"newTag\": \"arch\" }'],\n },\n\n // Graph\n backlinks: {\n description: \"Find all notes that link TO a given note via [[wikilinks]]\",\n args: '{ \"path\": \"string\" }',\n examples: ['{ \"path\": \"microservices.md\" }'],\n },\n forwardlinks: {\n description: \"Find all notes linked FROM a given note\",\n args: '{ \"path\": \"string\" }',\n examples: ['{ \"path\": \"project-overview.md\" }'],\n },\n graph_path: {\n description: \"Find the shortest path between two notes in the knowledge graph\",\n args: '{ \"from\": \"string\", \"to\": \"string\" }',\n examples: ['{ \"from\": \"project-overview.md\", \"to\": \"user-service.md\" }'],\n },\n graph_statistics: {\n description: \"Knowledge graph stats — most connected nodes, orphans, density\",\n args: \"{}\",\n examples: [\"{}\"],\n },\n\n // System\n get_stats: {\n description: \"Vault and index statistics — note count, chunks, embeddings, graph density\",\n args: \"{}\",\n examples: [\"{}\"],\n },\n reindex: {\n description: \"Force a full reindex of the vault\",\n args: \"{}\",\n examples: [\"{}\"],\n },\n};\n\nconst TOOL_CATEGORIES: Record<string, string[]> = {\n Search: [\"search_semantic\", \"search_text\", \"search_graph\", \"search_hybrid\"],\n Read: [\"read_note\", \"read_multiple_notes\", \"list_notes\"],\n Write: [\"create_note\", \"update_note\", \"delete_note\", \"move_note\"],\n Metadata: [\"get_frontmatter\", \"update_frontmatter\", \"manage_tags\", \"rename_tag\"],\n Graph: [\"backlinks\", \"forwardlinks\", \"graph_path\", \"graph_statistics\"],\n System: [\"get_stats\", \"reindex\"],\n};\n\nfunction printToolList() {\n console.log(\"\\nSemantic Pages — 21 MCP Tools\\n\");\n console.log(\"Usage: These tools are available via MCP when the server is running.\");\n console.log(\" Run `semantic-pages tools <name>` for details on a specific tool.\\n\");\n\n for (const [category, tools] of Object.entries(TOOL_CATEGORIES)) {\n console.log(` ${category}:`);\n for (const name of tools) {\n const tool = TOOL_HELP[name];\n console.log(` ${name.padEnd(24)} ${tool.description}`);\n }\n console.log();\n }\n\n console.log(\"Run `semantic-pages tools <tool-name>` for arguments and examples.\");\n}\n\nfunction printToolDetail(name: string) {\n const tool = TOOL_HELP[name];\n if (!tool) {\n console.error(`Unknown tool: ${name}`);\n console.error(`Run \\`semantic-pages tools\\` to see all available tools.`);\n process.exit(1);\n }\n\n console.log(`\\n ${name}`);\n console.log(` ${\"─\".repeat(name.length)}`);\n console.log(` ${tool.description}\\n`);\n console.log(` Arguments:`);\n console.log(` ${tool.args}\\n`);\n console.log(` Examples:`);\n for (const ex of tool.examples) {\n console.log(` ${ex}`);\n }\n console.log();\n}\n\nprogram\n .name(\"semantic-pages\")\n .description(\n \"Semantic search + knowledge graph MCP server for markdown files\\n\\n\" +\n \" Start MCP server: semantic-pages --notes ./vault\\n\" +\n \" Show vault stats: semantic-pages --notes ./vault --stats\\n\" +\n \" Force reindex: semantic-pages --notes ./vault --reindex\\n\" +\n \" List MCP tools: semantic-pages tools\\n\" +\n \" Tool details: semantic-pages tools search_semantic\"\n )\n .version(\"0.4.3\");\n\nprogram\n .command(\"tools [name]\")\n .description(\"List all MCP tools, or show details for a specific tool\")\n .action((name?: string) => {\n if (name) {\n printToolDetail(name);\n } else {\n printToolList();\n }\n process.exit(0);\n });\n\nprogram\n .command(\"serve\", { isDefault: true })\n .description(\"Start the MCP server (default command)\")\n .requiredOption(\"--notes <path>\", \"Path to markdown notes directory\")\n .option(\"--reindex\", \"Force full reindex and exit\")\n .option(\"--stats\", \"Show vault statistics and exit\")\n .option(\"--read-only\", \"Suppress write tools (create_note, update_note, delete_note, move_note, update_frontmatter, manage_tags, rename_tag) — use for shared docs vaults owned by another tool\")\n .option(\"--model <name>\", \"Embedding model to use (default: all-MiniLM-L6-v2, fast; use nomic-ai/nomic-embed-text-v1.5 for higher quality)\")\n .option(\"--workers <n>\", \"Number of worker threads for parallel embedding\", parseInt)\n .option(\"--batch-size <n>\", \"Texts per ONNX forward pass (default: 8)\", parseInt)\n .option(\"--no-quantized\", \"Use full-precision model instead of quantized (slower, slightly higher quality)\")\n .option(\"--no-watch\", \"Disable file watcher\")\n .action(async (opts) => {\n const notesPath = resolve(opts.notes);\n\n if (!existsSync(notesPath)) {\n console.error(`Error: notes directory not found: ${notesPath}`);\n process.exit(1);\n }\n\n if (opts.stats) {\n const { Indexer } = await import(\"../core/indexer.js\");\n const indexer = new Indexer(notesPath);\n const docs = await indexer.indexAll();\n console.log(`Notes: ${docs.length}`);\n console.log(`Chunks: ${docs.reduce((n: number, d: any) => n + d.chunks.length, 0)}`);\n console.log(`Wikilinks: ${docs.reduce((n: number, d: any) => n + d.wikilinks.length, 0)}`);\n console.log(`Tags: ${new Set(docs.flatMap((d: any) => d.tags)).size} unique`);\n process.exit(0);\n }\n\n if (opts.reindex) {\n const { createServer } = await import(\"../mcp/server.js\");\n await createServer(notesPath, {\n watch: false,\n waitForReady: true,\n model: opts.model,\n workers: opts.workers,\n batchSize: opts.batchSize,\n quantized: opts.quantized,\n onProgress: (embedded, total) => {\n process.stderr.write(`\\rEmbedding ${embedded}/${total} chunks...`);\n },\n });\n process.stderr.write(\"\\n\");\n console.log(\"Reindex complete.\");\n process.exit(0);\n }\n\n // Default: start MCP server on stdio\n const { startServer } = await import(\"../mcp/server.js\");\n await startServer(notesPath, {\n watch: opts.watch,\n model: opts.model,\n workers: opts.workers,\n batchSize: opts.batchSize,\n quantized: opts.quantized,\n readOnly: opts.readOnly,\n });\n });\n\nprogram.parse();\n"],"mappings":"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","names":[]}
@@ -7,6 +7,7 @@ interface ServerOptions {
7
7
  workers?: number;
8
8
  batchSize?: number;
9
9
  quantized?: boolean;
10
+ readOnly?: boolean;
10
11
  onProgress?: (embedded: number, total: number) => void;
11
12
  }
12
13
  declare function createServer(notesPath: string, options?: ServerOptions): Promise<McpServer>;
@@ -4393,52 +4393,54 @@ async function createServer(notesPath, options = {}) {
4393
4393
  return textResponse(JSON.stringify(list, null, 2));
4394
4394
  }
4395
4395
  );
4396
- server.tool(
4397
- "create_note",
4398
- "Create a new markdown note",
4399
- {
4400
- path: external_exports.string(),
4401
- content: external_exports.string(),
4402
- frontmatter: external_exports.record(external_exports.unknown()).optional()
4403
- },
4404
- async ({ path, content, frontmatter }) => {
4405
- await crud.create(path, content, frontmatter);
4406
- return textResponse(`Created: ${path}`);
4407
- }
4408
- );
4409
- server.tool(
4410
- "update_note",
4411
- "Edit note content \u2014 overwrite, append, prepend, or patch by heading",
4412
- {
4413
- path: external_exports.string(),
4414
- content: external_exports.string(),
4415
- mode: external_exports.enum(["overwrite", "append", "prepend", "patch-by-heading"]),
4416
- heading: external_exports.string().optional()
4417
- },
4418
- async ({ path, content, mode, heading }) => {
4419
- await crud.update(path, content, { mode, heading });
4420
- return textResponse(`Updated: ${path} (${mode})`);
4421
- }
4422
- );
4423
- server.tool(
4424
- "delete_note",
4425
- "Delete a note permanently",
4426
- { path: external_exports.string(), confirm: external_exports.boolean().default(false) },
4427
- async ({ path, confirm }) => {
4428
- if (!confirm) return textResponse(`Set confirm=true to delete ${path}`);
4429
- await crud.delete(path);
4430
- return textResponse(`Deleted: ${path}`);
4431
- }
4432
- );
4433
- server.tool(
4434
- "move_note",
4435
- "Move or rename a note \u2014 updates wikilinks across the vault",
4436
- { from: external_exports.string(), to: external_exports.string() },
4437
- async ({ from, to }) => {
4438
- await crud.move(from, to);
4439
- return textResponse(`Moved: ${from} \u2192 ${to}`);
4440
- }
4441
- );
4396
+ if (!options.readOnly) {
4397
+ server.tool(
4398
+ "create_note",
4399
+ "Create a new markdown note",
4400
+ {
4401
+ path: external_exports.string(),
4402
+ content: external_exports.string(),
4403
+ frontmatter: external_exports.record(external_exports.unknown()).optional()
4404
+ },
4405
+ async ({ path, content, frontmatter }) => {
4406
+ await crud.create(path, content, frontmatter);
4407
+ return textResponse(`Created: ${path}`);
4408
+ }
4409
+ );
4410
+ server.tool(
4411
+ "update_note",
4412
+ "Edit note content \u2014 overwrite, append, prepend, or patch by heading",
4413
+ {
4414
+ path: external_exports.string(),
4415
+ content: external_exports.string(),
4416
+ mode: external_exports.enum(["overwrite", "append", "prepend", "patch-by-heading"]),
4417
+ heading: external_exports.string().optional()
4418
+ },
4419
+ async ({ path, content, mode, heading }) => {
4420
+ await crud.update(path, content, { mode, heading });
4421
+ return textResponse(`Updated: ${path} (${mode})`);
4422
+ }
4423
+ );
4424
+ server.tool(
4425
+ "delete_note",
4426
+ "Delete a note permanently",
4427
+ { path: external_exports.string(), confirm: external_exports.boolean().default(false) },
4428
+ async ({ path, confirm }) => {
4429
+ if (!confirm) return textResponse(`Set confirm=true to delete ${path}`);
4430
+ await crud.delete(path);
4431
+ return textResponse(`Deleted: ${path}`);
4432
+ }
4433
+ );
4434
+ server.tool(
4435
+ "move_note",
4436
+ "Move or rename a note \u2014 updates wikilinks across the vault",
4437
+ { from: external_exports.string(), to: external_exports.string() },
4438
+ async ({ from, to }) => {
4439
+ await crud.move(from, to);
4440
+ return textResponse(`Moved: ${from} \u2192 ${to}`);
4441
+ }
4442
+ );
4443
+ }
4442
4444
  server.tool(
4443
4445
  "get_frontmatter",
4444
4446
  "Read parsed YAML frontmatter from a note as JSON",
@@ -4448,51 +4450,53 @@ async function createServer(notesPath, options = {}) {
4448
4450
  return textResponse(JSON.stringify(fm, null, 2));
4449
4451
  }
4450
4452
  );
4451
- server.tool(
4452
- "update_frontmatter",
4453
- "Set or delete YAML frontmatter keys \u2014 pass null to delete a key",
4454
- { path: external_exports.string(), fields: external_exports.record(external_exports.unknown()) },
4455
- async ({ path, fields }) => {
4456
- await frontmatterManager.update(path, fields);
4457
- return textResponse(`Frontmatter updated: ${path}`);
4458
- }
4459
- );
4460
- server.tool(
4461
- "manage_tags",
4462
- "Add, remove, or list tags on a note (frontmatter and inline)",
4463
- {
4464
- path: external_exports.string(),
4465
- action: external_exports.enum(["add", "remove", "list"]),
4466
- tags: external_exports.array(external_exports.string()).optional()
4467
- },
4468
- async ({ path, action, tags }) => {
4469
- switch (action) {
4470
- case "list": {
4471
- const result = await tagManager.list(path);
4472
- return textResponse(JSON.stringify(result));
4473
- }
4474
- case "add": {
4475
- if (!tags?.length) return textResponse("No tags provided");
4476
- await tagManager.add(path, tags);
4477
- return textResponse(`Added tags to ${path}: ${tags.join(", ")}`);
4478
- }
4479
- case "remove": {
4480
- if (!tags?.length) return textResponse("No tags provided");
4481
- await tagManager.remove(path, tags);
4482
- return textResponse(`Removed tags from ${path}: ${tags.join(", ")}`);
4453
+ if (!options.readOnly) {
4454
+ server.tool(
4455
+ "update_frontmatter",
4456
+ "Set or delete YAML frontmatter keys \u2014 pass null to delete a key",
4457
+ { path: external_exports.string(), fields: external_exports.record(external_exports.unknown()) },
4458
+ async ({ path, fields }) => {
4459
+ await frontmatterManager.update(path, fields);
4460
+ return textResponse(`Frontmatter updated: ${path}`);
4461
+ }
4462
+ );
4463
+ server.tool(
4464
+ "manage_tags",
4465
+ "Add, remove, or list tags on a note (frontmatter and inline)",
4466
+ {
4467
+ path: external_exports.string(),
4468
+ action: external_exports.enum(["add", "remove", "list"]),
4469
+ tags: external_exports.array(external_exports.string()).optional()
4470
+ },
4471
+ async ({ path, action, tags }) => {
4472
+ switch (action) {
4473
+ case "list": {
4474
+ const result = await tagManager.list(path);
4475
+ return textResponse(JSON.stringify(result));
4476
+ }
4477
+ case "add": {
4478
+ if (!tags?.length) return textResponse("No tags provided");
4479
+ await tagManager.add(path, tags);
4480
+ return textResponse(`Added tags to ${path}: ${tags.join(", ")}`);
4481
+ }
4482
+ case "remove": {
4483
+ if (!tags?.length) return textResponse("No tags provided");
4484
+ await tagManager.remove(path, tags);
4485
+ return textResponse(`Removed tags from ${path}: ${tags.join(", ")}`);
4486
+ }
4483
4487
  }
4484
4488
  }
4485
- }
4486
- );
4487
- server.tool(
4488
- "rename_tag",
4489
- "Rename a tag across all notes in the vault",
4490
- { oldTag: external_exports.string(), newTag: external_exports.string() },
4491
- async ({ oldTag, newTag }) => {
4492
- const count = await tagManager.renameVaultWide(oldTag, newTag);
4493
- return textResponse(`Renamed #${oldTag} \u2192 #${newTag} in ${count} files`);
4494
- }
4495
- );
4489
+ );
4490
+ server.tool(
4491
+ "rename_tag",
4492
+ "Rename a tag across all notes in the vault",
4493
+ { oldTag: external_exports.string(), newTag: external_exports.string() },
4494
+ async ({ oldTag, newTag }) => {
4495
+ const count = await tagManager.renameVaultWide(oldTag, newTag);
4496
+ return textResponse(`Renamed #${oldTag} \u2192 #${newTag} in ${count} files`);
4497
+ }
4498
+ );
4499
+ }
4496
4500
  server.tool(
4497
4501
  "backlinks",
4498
4502
  "Find all notes that link TO a given note",