@theglitchking/semantic-pages 0.6.6 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +107 -0
- package/commands/policy.md +16 -0
- package/commands/relink.md +6 -0
- package/commands/status.md +6 -0
- package/commands/update.md +6 -0
- package/dist/cli/index.js +25 -2
- package/dist/cli/index.js.map +1 -1
- package/hooks/hooks.json +2 -2
- package/hooks/reconcile.js +90 -0
- package/hooks/session-start.js +14 -0
- package/package.json +7 -3
- package/scripts/link-skills.js +25 -0
- package/hooks/session-start.sh +0 -147
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
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.
|
|
9
|
+
"version": "0.9.0"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "semantic-pages",
|
|
14
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.
|
|
15
|
+
"version": "0.9.0",
|
|
16
16
|
"author": {
|
|
17
17
|
"name": "TheGlitchKing"
|
|
18
18
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "semantic-pages",
|
|
3
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.
|
|
4
|
+
"version": "0.9.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "TheGlitchKing",
|
|
7
7
|
"email": "theglitchking@users.noreply.github.com"
|
package/README.md
CHANGED
|
@@ -479,6 +479,113 @@ Each flow probes its MCP server independently and degrades gracefully when one i
|
|
|
479
479
|
|
|
480
480
|
When you install `semantic-pages` via the Claude Code plugin marketplace or via npm, Claude Code picks up this skill automatically — no extra wiring required.
|
|
481
481
|
|
|
482
|
+
### How auto-linking works *(0.7.0+)*
|
|
483
|
+
|
|
484
|
+
When installed as an npm dependency, a `postinstall` script symlinks every directory under the package's `skills/` into your project's `.claude/skills/` (creating that directory if missing). Claude Code only scans `<project>/.claude/skills/` and `~/.claude/skills/` — it does not look inside `node_modules/` — so this link is what makes bundled skills discoverable.
|
|
485
|
+
|
|
486
|
+
- The link is relative, pointing into `node_modules/@theglitchking/semantic-pages/skills/<name>`, and is refreshed on every install/update.
|
|
487
|
+
- If a regular file or directory already exists at the destination (i.e. someone copied a skill manually), the script leaves it alone and prints a warning.
|
|
488
|
+
- The script is a no-op when you're developing the plugin itself (`INIT_CWD` equals the package root), so the plugin never self-links into its own repo.
|
|
489
|
+
- The linker never hard-fails an install — permission errors and odd filesystems downgrade to a warning.
|
|
490
|
+
|
|
491
|
+
Add the linked skills to `.gitignore` so they don't get committed in consuming projects:
|
|
492
|
+
|
|
493
|
+
```
|
|
494
|
+
/.claude/skills/semantic-first/
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
**Opt-out**: set `SEMANTIC_PAGES_SKIP_LINK=1` to skip linking during install — useful if you want to manage `.claude/skills/` manually.
|
|
498
|
+
|
|
499
|
+
```bash
|
|
500
|
+
SEMANTIC_PAGES_SKIP_LINK=1 npm install @theglitchking/semantic-pages
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
---
|
|
504
|
+
|
|
505
|
+
## Update Policy & Session-Start Hook *(0.8.0+)*
|
|
506
|
+
|
|
507
|
+
When you install `semantic-pages` as an npm dependency, the postinstall step also:
|
|
508
|
+
|
|
509
|
+
1. Writes `<project>/.claude/semantic-pages.json` with `{ "updatePolicy": "nudge" }` if one doesn't exist.
|
|
510
|
+
2. Registers a `SessionStart` hook in `<project>/.claude/settings.json` — but **only** if `settings.json` exists and neither the Claude Code plugin nor an existing semantic-pages hook is already handling it.
|
|
511
|
+
|
|
512
|
+
### What the hook does
|
|
513
|
+
|
|
514
|
+
At the start of every Claude Code session, the hook:
|
|
515
|
+
|
|
516
|
+
- Reconciles `.mcp.json` (the same `semantic-vault` + conditional `.documentation` wiring you'd get from the plugin).
|
|
517
|
+
- Checks npm for a newer version of `@theglitchking/semantic-pages` (~3s budget, results cached for 6h, skipped in CI).
|
|
518
|
+
- Acts on the result according to your policy.
|
|
519
|
+
|
|
520
|
+
### Policies
|
|
521
|
+
|
|
522
|
+
| Policy | Behavior |
|
|
523
|
+
|--------|----------|
|
|
524
|
+
| `nudge` *(default)* | Print a one-liner when a newer version is available. No changes. |
|
|
525
|
+
| `auto` | Run `npm update @theglitchking/semantic-pages`, re-link skills, print `⬆️ vX → vY`. |
|
|
526
|
+
| `off` | Silent — no update check. |
|
|
527
|
+
|
|
528
|
+
### Setting the policy
|
|
529
|
+
|
|
530
|
+
Preferred:
|
|
531
|
+
|
|
532
|
+
```
|
|
533
|
+
/semantic-pages:policy auto
|
|
534
|
+
/semantic-pages:policy nudge
|
|
535
|
+
/semantic-pages:policy off
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
Or from the terminal:
|
|
539
|
+
|
|
540
|
+
```bash
|
|
541
|
+
npx --no @theglitchking/semantic-pages policy auto
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
Resolution order:
|
|
545
|
+
|
|
546
|
+
1. `SEMANTIC_PAGES_UPDATE_POLICY` env var (one-shot override).
|
|
547
|
+
2. `<project>/.claude/semantic-pages.json` → `updatePolicy`.
|
|
548
|
+
3. Default: `nudge`.
|
|
549
|
+
|
|
550
|
+
### Plugin-command menu
|
|
551
|
+
|
|
552
|
+
With `semantic-pages` installed (plugin or npm), you get these slash commands:
|
|
553
|
+
|
|
554
|
+
| Command | Does |
|
|
555
|
+
|---------|------|
|
|
556
|
+
| `/semantic-pages:update` | Runs `npm update`, re-links skills, reports before/after. |
|
|
557
|
+
| `/semantic-pages:policy [auto\|nudge\|off]` | Get or set the update policy. |
|
|
558
|
+
| `/semantic-pages:status` | Installed version, latest on npm, policy, hook state. |
|
|
559
|
+
| `/semantic-pages:relink` | Re-symlink bundled skills into `.claude/skills/`. |
|
|
560
|
+
|
|
561
|
+
Each has a CLI equivalent: `npx --no @theglitchking/semantic-pages <subcommand>`.
|
|
562
|
+
|
|
563
|
+
### Dedup between plugin and npm installs
|
|
564
|
+
|
|
565
|
+
If you install via **both** the Claude Code plugin and the npm dep, the postinstall detects the plugin in `~/.claude/settings.json` → `enabledPlugins` and skips the settings.json hook registration. One hook fires per session, not two. If you later uninstall the plugin, the next `npm install` re-registers the project-level hook.
|
|
566
|
+
|
|
567
|
+
### Opt-out
|
|
568
|
+
|
|
569
|
+
```bash
|
|
570
|
+
# Skip settings.json hook registration:
|
|
571
|
+
SEMANTIC_PAGES_SKIP_HOOK_REGISTER=1 npm install @theglitchking/semantic-pages
|
|
572
|
+
|
|
573
|
+
# Skip skill symlinking:
|
|
574
|
+
SEMANTIC_PAGES_SKIP_LINK=1 npm install @theglitchking/semantic-pages
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
### Uninstalling cleanly
|
|
578
|
+
|
|
579
|
+
```bash
|
|
580
|
+
# 1. Remove the hook entry from .claude/settings.json (look for any SessionStart
|
|
581
|
+
# entry whose command references "semantic-pages").
|
|
582
|
+
# 2. Remove the policy file:
|
|
583
|
+
rm .claude/semantic-pages.json
|
|
584
|
+
|
|
585
|
+
# 3. Remove the package:
|
|
586
|
+
npm uninstall @theglitchking/semantic-pages
|
|
587
|
+
```
|
|
588
|
+
|
|
482
589
|
---
|
|
483
590
|
|
|
484
591
|
## Common Workflows
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Get or set the semantic-pages update policy (auto | nudge | off)
|
|
3
|
+
allowed-tools: Bash(npx:*)
|
|
4
|
+
argument-hint: "[auto|nudge|off]"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Arguments: $ARGUMENTS
|
|
8
|
+
|
|
9
|
+
- If `$ARGUMENTS` is empty, run `npx --no @theglitchking/semantic-pages policy` and report the current policy and config path.
|
|
10
|
+
- If `$ARGUMENTS` is one of `auto`, `nudge`, `off`, run `npx --no @theglitchking/semantic-pages policy $ARGUMENTS` and confirm the new setting to the user.
|
|
11
|
+
- If `$ARGUMENTS` is anything else, tell the user the valid values are `auto`, `nudge`, `off`.
|
|
12
|
+
|
|
13
|
+
Policies:
|
|
14
|
+
- `auto` — auto-run `npm update @theglitchking/semantic-pages` at session start when a newer version is available.
|
|
15
|
+
- `nudge` — print a one-liner when a newer version is available (default).
|
|
16
|
+
- `off` — do not check for updates.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Update semantic-pages to the latest version (runs npm update + re-links skills)
|
|
3
|
+
allowed-tools: Bash(npx:*)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Run `npx --no @theglitchking/semantic-pages update` and report the before/after versions to the user. If the project doesn't have a local install, fall back to `npx -y @theglitchking/semantic-pages update` and note that in your summary.
|
package/dist/cli/index.js
CHANGED
|
@@ -2,10 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli/index.ts
|
|
4
4
|
import { program } from "commander";
|
|
5
|
-
import { resolve } from "path";
|
|
5
|
+
import { resolve, join } from "path";
|
|
6
6
|
import { existsSync } from "fs";
|
|
7
7
|
import { createRequire } from "module";
|
|
8
|
-
|
|
8
|
+
import { spawnSync } from "child_process";
|
|
9
|
+
import { registerUpdateCommands } from "@theglitchking/claude-plugin-runtime";
|
|
10
|
+
var require_ = createRequire(import.meta.url);
|
|
11
|
+
var { version } = require_("../../package.json");
|
|
12
|
+
var PKG_NAME = "@theglitchking/semantic-pages";
|
|
13
|
+
function runRelink(cwd) {
|
|
14
|
+
const linker = join(cwd, "node_modules", "@theglitchking", "semantic-pages", "scripts", "link-skills.js");
|
|
15
|
+
const script = existsSync(linker) ? linker : resolve(process.cwd(), "scripts", "link-skills.js");
|
|
16
|
+
if (!existsSync(script)) {
|
|
17
|
+
console.error("link-skills.js not found \u2014 is the package installed?");
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
spawnSync(process.execPath, [script], {
|
|
21
|
+
cwd,
|
|
22
|
+
env: { ...process.env, INIT_CWD: cwd },
|
|
23
|
+
stdio: "inherit"
|
|
24
|
+
});
|
|
25
|
+
}
|
|
9
26
|
var TOOL_HELP = {
|
|
10
27
|
// Search
|
|
11
28
|
search_semantic: {
|
|
@@ -254,5 +271,11 @@ program.command("serve", { isDefault: true }).description("Start the MCP server
|
|
|
254
271
|
readOnly: opts.readOnly
|
|
255
272
|
});
|
|
256
273
|
});
|
|
274
|
+
registerUpdateCommands(program, {
|
|
275
|
+
packageName: PKG_NAME,
|
|
276
|
+
pluginName: "semantic-pages",
|
|
277
|
+
configFile: "semantic-pages.json",
|
|
278
|
+
onAfterUpdate: (cwd) => runRelink(cwd)
|
|
279
|
+
});
|
|
257
280
|
program.parse();
|
|
258
281
|
//# sourceMappingURL=index.js.map
|
package/dist/cli/index.js.map
CHANGED
|
@@ -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\";\nimport { createRequire } from \"node:module\";\n\nconst { version } = createRequire(import.meta.url)(\"../../package.json\") as { version: string };\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(version);\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(\"--wait-for-ready\", \"Block startup until index is fully built before serving (default: index in background; tools return 'Indexing in progress' until ready)\")\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 waitForReady: opts.waitForReady,\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":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { program } from \"commander\";\nimport { resolve, join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { spawnSync } from \"node:child_process\";\nimport { registerUpdateCommands } from \"@theglitchking/claude-plugin-runtime\";\n\nconst require_ = createRequire(import.meta.url);\nconst { version } = require_(\"../../package.json\") as { version: string };\n\nconst PKG_NAME = \"@theglitchking/semantic-pages\";\n\nfunction runRelink(cwd: string) {\n const linker = join(cwd, \"node_modules\", \"@theglitchking\", \"semantic-pages\", \"scripts\", \"link-skills.js\");\n const script = existsSync(linker) ? linker : resolve(process.cwd(), \"scripts\", \"link-skills.js\");\n if (!existsSync(script)) {\n console.error(\"link-skills.js not found — is the package installed?\");\n return;\n }\n spawnSync(process.execPath, [script], {\n cwd,\n env: { ...process.env, INIT_CWD: cwd },\n stdio: \"inherit\",\n });\n}\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(version);\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(\"--wait-for-ready\", \"Block startup until index is fully built before serving (default: index in background; tools return 'Indexing in progress' until ready)\")\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 waitForReady: opts.waitForReady,\n model: opts.model,\n workers: opts.workers,\n batchSize: opts.batchSize,\n quantized: opts.quantized,\n readOnly: opts.readOnly,\n });\n });\n\nregisterUpdateCommands(program, {\n packageName: PKG_NAME,\n pluginName: \"semantic-pages\",\n configFile: \"semantic-pages.json\",\n onAfterUpdate: (cwd) => runRelink(cwd),\n});\n\nprogram.parse();\n"],"mappings":"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","names":[]}
|
package/hooks/hooks.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
|
-
"description": "semantic-pages SessionStart hook —
|
|
2
|
+
"description": "semantic-pages SessionStart hook — reconciles .mcp.json wiring and nudges/auto-updates per policy.",
|
|
3
3
|
"hooks": {
|
|
4
4
|
"SessionStart": [
|
|
5
5
|
{
|
|
6
6
|
"hooks": [
|
|
7
7
|
{
|
|
8
8
|
"type": "command",
|
|
9
|
-
"command": "
|
|
9
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/session-start.js\""
|
|
10
10
|
}
|
|
11
11
|
]
|
|
12
12
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Plugin-specific SessionStart reconciliation for semantic-pages.
|
|
2
|
+
//
|
|
3
|
+
// Ensures <project>/.claude/.vault exists. Reconciles .mcp.json so the
|
|
4
|
+
// "semantic-vault" entry always points at ./.claude/.vault, and adds
|
|
5
|
+
// (or removes) a read-only "semantic-pages" entry pointed at
|
|
6
|
+
// ./.documentation only when hit-em-with-the-docs is enabled and the
|
|
7
|
+
// .documentation directory exists.
|
|
8
|
+
//
|
|
9
|
+
// Idempotent: only writes .mcp.json when the computed JSON differs from
|
|
10
|
+
// what's on disk.
|
|
11
|
+
|
|
12
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
13
|
+
import { homedir } from "node:os";
|
|
14
|
+
import { dirname, join } from "node:path";
|
|
15
|
+
|
|
16
|
+
const PKG = "@theglitchking/semantic-pages";
|
|
17
|
+
|
|
18
|
+
function readJson(path, fallback = null) {
|
|
19
|
+
try {
|
|
20
|
+
const raw = readFileSync(path, "utf8");
|
|
21
|
+
return raw.trim() ? JSON.parse(raw) : fallback;
|
|
22
|
+
} catch {
|
|
23
|
+
return fallback;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function hewtdEnabled() {
|
|
28
|
+
const settings = readJson(join(homedir(), ".claude", "settings.json"));
|
|
29
|
+
const enabled = settings?.enabledPlugins || {};
|
|
30
|
+
return Object.keys(enabled).some(
|
|
31
|
+
(k) => k.startsWith("hit-em-with-the-docs@") && enabled[k] === true,
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function reconcile(projectRoot) {
|
|
36
|
+
const vaultDir = join(projectRoot, ".claude", ".vault");
|
|
37
|
+
const docsDir = join(projectRoot, ".documentation");
|
|
38
|
+
const mcpPath = join(projectRoot, ".mcp.json");
|
|
39
|
+
|
|
40
|
+
try { mkdirSync(vaultDir, { recursive: true }); } catch {}
|
|
41
|
+
|
|
42
|
+
const docsWired = hewtdEnabled() && existsSync(docsDir);
|
|
43
|
+
|
|
44
|
+
let data = { mcpServers: {} };
|
|
45
|
+
if (existsSync(mcpPath)) {
|
|
46
|
+
const parsed = readJson(mcpPath, null);
|
|
47
|
+
if (parsed === null) {
|
|
48
|
+
let raw = "";
|
|
49
|
+
try { raw = readFileSync(mcpPath, "utf8"); } catch {}
|
|
50
|
+
if (raw.trim()) {
|
|
51
|
+
process.stderr.write(`semantic-pages hook: could not parse ${mcpPath}; leaving untouched\n`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
} else if (parsed && typeof parsed === "object") {
|
|
55
|
+
data = parsed;
|
|
56
|
+
if (!data.mcpServers || typeof data.mcpServers !== "object") {
|
|
57
|
+
data.mcpServers = {};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const before = JSON.stringify(data);
|
|
63
|
+
|
|
64
|
+
data.mcpServers["semantic-vault"] = {
|
|
65
|
+
type: "stdio",
|
|
66
|
+
command: "npx",
|
|
67
|
+
args: ["-y", `${PKG}@latest`, "--notes", "./.claude/.vault"],
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
if (docsWired) {
|
|
71
|
+
data.mcpServers["semantic-pages"] = {
|
|
72
|
+
type: "stdio",
|
|
73
|
+
command: "npx",
|
|
74
|
+
args: ["-y", `${PKG}@latest`, "--notes", "./.documentation", "--read-only"],
|
|
75
|
+
};
|
|
76
|
+
} else if (data.mcpServers["semantic-pages"]) {
|
|
77
|
+
const existing = data.mcpServers["semantic-pages"];
|
|
78
|
+
const args = Array.isArray(existing.args) ? existing.args : [];
|
|
79
|
+
const looksLikeOurs = args.some(
|
|
80
|
+
(a) => typeof a === "string" && a.includes(".documentation"),
|
|
81
|
+
);
|
|
82
|
+
if (looksLikeOurs) delete data.mcpServers["semantic-pages"];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const after = JSON.stringify(data);
|
|
86
|
+
if (after === before) return;
|
|
87
|
+
|
|
88
|
+
mkdirSync(dirname(mcpPath), { recursive: true });
|
|
89
|
+
writeFileSync(mcpPath, JSON.stringify(data, null, 2) + "\n");
|
|
90
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// semantic-pages SessionStart hook. Delegates lifecycle + update-check to
|
|
3
|
+
// @theglitchking/claude-plugin-runtime; plugin-specific .mcp.json wiring
|
|
4
|
+
// lives in ./reconcile.js.
|
|
5
|
+
|
|
6
|
+
import { runSessionStart } from "@theglitchking/claude-plugin-runtime";
|
|
7
|
+
import { reconcile } from "./reconcile.js";
|
|
8
|
+
|
|
9
|
+
await runSessionStart({
|
|
10
|
+
packageName: "@theglitchking/semantic-pages",
|
|
11
|
+
pluginName: "semantic-pages",
|
|
12
|
+
configFile: "semantic-pages.json",
|
|
13
|
+
reconcile,
|
|
14
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theglitchking/semantic-pages",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Semantic search + knowledge graph MCP server for markdown vaults. Claude Code plugin with auto-wiring for .claude/.vault and read-only .documentation companion when hit-em-with-the-docs is installed.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/core/index.js",
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
"dist",
|
|
18
18
|
".claude-plugin",
|
|
19
19
|
"hooks",
|
|
20
|
+
"commands",
|
|
21
|
+
"scripts/link-skills.js",
|
|
20
22
|
"skills/semantic-first",
|
|
21
23
|
"README.md",
|
|
22
24
|
"LICENSE"
|
|
@@ -27,7 +29,8 @@
|
|
|
27
29
|
"test": "vitest run",
|
|
28
30
|
"test:watch": "vitest",
|
|
29
31
|
"lint": "tsc --noEmit",
|
|
30
|
-
"prepublishOnly": "npm run build"
|
|
32
|
+
"prepublishOnly": "npm run build",
|
|
33
|
+
"postinstall": "node scripts/link-skills.js"
|
|
31
34
|
},
|
|
32
35
|
"keywords": [
|
|
33
36
|
"mcp",
|
|
@@ -55,7 +58,7 @@
|
|
|
55
58
|
"dependencies": {
|
|
56
59
|
"@huggingface/transformers": "^4.0.1",
|
|
57
60
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
58
|
-
"
|
|
61
|
+
"@theglitchking/claude-plugin-runtime": "^0.1.0",
|
|
59
62
|
"chokidar": "^5.0.0",
|
|
60
63
|
"commander": "^14.0.3",
|
|
61
64
|
"glob": "^13.0.6",
|
|
@@ -66,6 +69,7 @@
|
|
|
66
69
|
"gray-matter": "^4.0.3",
|
|
67
70
|
"hnswlib-node": "^3.0.0",
|
|
68
71
|
"minimatch": "^10.2.5",
|
|
72
|
+
"onnxruntime-web": "^1.24.3",
|
|
69
73
|
"remark-parse": "^11.0.0",
|
|
70
74
|
"remark-wiki-link": "^2.0.1",
|
|
71
75
|
"unified": "^11.0.5"
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Postinstall — delegates to @theglitchking/claude-plugin-runtime.
|
|
3
|
+
// The runtime handles: skill symlinking, default policy config write, and
|
|
4
|
+
// settings.json hook registration with plugin/npm dedup. See the runtime's
|
|
5
|
+
// docs/PLUGIN_AUTHORING_SCAFFOLD.md for the full contract.
|
|
6
|
+
|
|
7
|
+
import { runPostinstall } from "@theglitchking/claude-plugin-runtime";
|
|
8
|
+
import { dirname, resolve } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
|
|
11
|
+
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
runPostinstall({
|
|
15
|
+
packageName: "@theglitchking/semantic-pages",
|
|
16
|
+
pluginName: "semantic-pages",
|
|
17
|
+
configFile: "semantic-pages.json",
|
|
18
|
+
skillsDir: "skills",
|
|
19
|
+
packageRoot,
|
|
20
|
+
hookCommand:
|
|
21
|
+
"node ./node_modules/@theglitchking/semantic-pages/hooks/session-start.js",
|
|
22
|
+
});
|
|
23
|
+
} catch (err) {
|
|
24
|
+
console.warn(`[semantic-pages] postinstall failed: ${err?.message || err}`);
|
|
25
|
+
}
|
package/hooks/session-start.sh
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# semantic-pages SessionStart hook
|
|
4
|
-
#
|
|
5
|
-
# Reconciles the project's .mcp.json so that:
|
|
6
|
-
# 1. A "semantic-vault" entry always points at ./.claude/.vault (read/write).
|
|
7
|
-
# 2. A "semantic-pages" entry points at ./.documentation (read-only) ONLY IF
|
|
8
|
-
# both conditions hold:
|
|
9
|
-
# - hit-em-with-the-docs is installed+enabled as a Claude Code plugin
|
|
10
|
-
# - ./.documentation/ exists in this project
|
|
11
|
-
# Otherwise any stale "semantic-pages" entry is removed.
|
|
12
|
-
#
|
|
13
|
-
# Idempotent: only writes .mcp.json when the computed JSON differs from disk.
|
|
14
|
-
# Fails open: any error logs to stderr and exits 0 so Claude Code isn't blocked.
|
|
15
|
-
#
|
|
16
|
-
# Runs on every SessionStart event. Keeps wiring in sync with plugin state.
|
|
17
|
-
|
|
18
|
-
set -u
|
|
19
|
-
|
|
20
|
-
# Fail-open: if anything goes sideways, swallow and return the empty SessionStart
|
|
21
|
-
# response so the session keeps going.
|
|
22
|
-
trap 'emit_empty_response; exit 0' ERR
|
|
23
|
-
|
|
24
|
-
emit_empty_response() {
|
|
25
|
-
# SessionStart hooks need to return JSON; empty additionalContext is fine.
|
|
26
|
-
printf '{"hookSpecificOutput":{"hookEventName":"SessionStart"}}\n'
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
log() {
|
|
30
|
-
printf 'semantic-pages hook: %s\n' "$*" >&2
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
# Project root is cwd when Claude Code invokes the hook.
|
|
34
|
-
PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-$PWD}"
|
|
35
|
-
cd "$PROJECT_ROOT" || { emit_empty_response; exit 0; }
|
|
36
|
-
|
|
37
|
-
VAULT_DIR="$PROJECT_ROOT/.claude/.vault"
|
|
38
|
-
DOCS_DIR="$PROJECT_ROOT/.documentation"
|
|
39
|
-
MCP_JSON="$PROJECT_ROOT/.mcp.json"
|
|
40
|
-
CLAUDE_SETTINGS="$HOME/.claude/settings.json"
|
|
41
|
-
|
|
42
|
-
# 1. Ensure .claude/.vault exists (idempotent).
|
|
43
|
-
mkdir -p "$VAULT_DIR" 2>/dev/null || true
|
|
44
|
-
|
|
45
|
-
# 2. Detect hit-em-with-the-docs: must be listed in enabledPlugins in
|
|
46
|
-
# ~/.claude/settings.json (covers any marketplace source).
|
|
47
|
-
HEWTD_ENABLED=0
|
|
48
|
-
if [ -f "$CLAUDE_SETTINGS" ]; then
|
|
49
|
-
if node -e '
|
|
50
|
-
const fs = require("fs");
|
|
51
|
-
try {
|
|
52
|
-
const s = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
|
|
53
|
-
const enabled = s.enabledPlugins || {};
|
|
54
|
-
const hit = Object.keys(enabled).some(
|
|
55
|
-
(k) => k.startsWith("hit-em-with-the-docs@") && enabled[k] === true
|
|
56
|
-
);
|
|
57
|
-
process.exit(hit ? 0 : 1);
|
|
58
|
-
} catch { process.exit(1); }
|
|
59
|
-
' "$CLAUDE_SETTINGS" 2>/dev/null; then
|
|
60
|
-
HEWTD_ENABLED=1
|
|
61
|
-
fi
|
|
62
|
-
fi
|
|
63
|
-
|
|
64
|
-
# 3. Docs MCP entry is conditional on BOTH hewtd enabled AND .documentation present.
|
|
65
|
-
DOCS_WIRED=0
|
|
66
|
-
if [ "$HEWTD_ENABLED" = "1" ] && [ -d "$DOCS_DIR" ]; then
|
|
67
|
-
DOCS_WIRED=1
|
|
68
|
-
fi
|
|
69
|
-
|
|
70
|
-
# 4. Reconcile .mcp.json using node (cross-platform JSON edit, idempotent write).
|
|
71
|
-
node - "$MCP_JSON" "$DOCS_WIRED" <<'NODE' || { log "reconcile failed"; emit_empty_response; exit 0; }
|
|
72
|
-
const fs = require("fs");
|
|
73
|
-
const path = require("path");
|
|
74
|
-
const [, , mcpPath, docsWiredArg] = process.argv;
|
|
75
|
-
const docsWired = docsWiredArg === "1";
|
|
76
|
-
|
|
77
|
-
let data = { mcpServers: {} };
|
|
78
|
-
if (fs.existsSync(mcpPath)) {
|
|
79
|
-
try {
|
|
80
|
-
const raw = fs.readFileSync(mcpPath, "utf8");
|
|
81
|
-
const parsed = raw.trim() ? JSON.parse(raw) : {};
|
|
82
|
-
if (parsed && typeof parsed === "object") data = parsed;
|
|
83
|
-
if (!data.mcpServers || typeof data.mcpServers !== "object") data.mcpServers = {};
|
|
84
|
-
} catch (err) {
|
|
85
|
-
// Corrupt .mcp.json — leave it alone and emit a note to stderr
|
|
86
|
-
process.stderr.write(
|
|
87
|
-
`semantic-pages hook: could not parse ${mcpPath} (${err.message}); leaving untouched\n`
|
|
88
|
-
);
|
|
89
|
-
process.exit(0);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Canonical entries
|
|
94
|
-
const vaultEntry = {
|
|
95
|
-
type: "stdio",
|
|
96
|
-
command: "npx",
|
|
97
|
-
args: [
|
|
98
|
-
"-y",
|
|
99
|
-
"@theglitchking/semantic-pages@latest",
|
|
100
|
-
"--notes",
|
|
101
|
-
"./.claude/.vault",
|
|
102
|
-
],
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
const docsEntry = {
|
|
106
|
-
type: "stdio",
|
|
107
|
-
command: "npx",
|
|
108
|
-
args: [
|
|
109
|
-
"-y",
|
|
110
|
-
"@theglitchking/semantic-pages@latest",
|
|
111
|
-
"--notes",
|
|
112
|
-
"./.documentation",
|
|
113
|
-
"--read-only",
|
|
114
|
-
],
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
const before = JSON.stringify(data);
|
|
118
|
-
|
|
119
|
-
// Always ensure semantic-vault
|
|
120
|
-
data.mcpServers["semantic-vault"] = vaultEntry;
|
|
121
|
-
|
|
122
|
-
// semantic-pages (docs) is conditional
|
|
123
|
-
if (docsWired) {
|
|
124
|
-
data.mcpServers["semantic-pages"] = docsEntry;
|
|
125
|
-
} else if (data.mcpServers["semantic-pages"]) {
|
|
126
|
-
// Only remove if it looks like ours (points at .documentation). Don't clobber
|
|
127
|
-
// a user-custom entry under the same name.
|
|
128
|
-
const existing = data.mcpServers["semantic-pages"];
|
|
129
|
-
const args = Array.isArray(existing.args) ? existing.args : [];
|
|
130
|
-
const looksLikeOurs =
|
|
131
|
-
args.some((a) => typeof a === "string" && a.includes(".documentation"));
|
|
132
|
-
if (looksLikeOurs) delete data.mcpServers["semantic-pages"];
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const after = JSON.stringify(data, null, 2) + "\n";
|
|
136
|
-
|
|
137
|
-
// Only write if content actually changed — prevents needless git churn.
|
|
138
|
-
if (JSON.stringify(JSON.parse(after)) === before) {
|
|
139
|
-
process.exit(0);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
fs.mkdirSync(path.dirname(mcpPath), { recursive: true });
|
|
143
|
-
fs.writeFileSync(mcpPath, after);
|
|
144
|
-
NODE
|
|
145
|
-
|
|
146
|
-
emit_empty_response
|
|
147
|
-
exit 0
|