@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.
- package/.claude-plugin/marketplace.json +47 -0
- package/.claude-plugin/plugin.json +22 -0
- package/LICENSE +0 -0
- package/README.md +61 -1
- package/bin/semantic-pages +0 -0
- package/dist/cli/index.js +9 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +93 -89
- package/dist/mcp/server.js.map +1 -1
- package/hooks/hooks.json +15 -0
- package/hooks/session-start.sh +147 -0
- package/package.json +4 -2
package/hooks/hooks.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "semantic-pages SessionStart hook — auto-wires .claude/.vault and, when hit-em-with-the-docs is installed, adds a read-only .documentation index",
|
|
3
|
+
"hooks": {
|
|
4
|
+
"SessionStart": [
|
|
5
|
+
{
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh\""
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
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
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theglitchking/semantic-pages",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Semantic search + knowledge graph MCP server for
|
|
3
|
+
"version": "0.6.0",
|
|
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",
|
|
7
7
|
"types": "./dist/core/index.d.ts",
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"files": [
|
|
16
16
|
"bin",
|
|
17
17
|
"dist",
|
|
18
|
+
".claude-plugin",
|
|
19
|
+
"hooks",
|
|
18
20
|
"README.md",
|
|
19
21
|
"LICENSE"
|
|
20
22
|
],
|