@theglitchking/semantic-memory 1.2.1 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +3 -3
- package/CHANGELOG.md +7 -0
- package/dist/{chunk-EVAJGOCB.js → chunk-UQSASB7Q.js} +18 -3
- package/dist/chunk-UQSASB7Q.js.map +1 -0
- package/dist/cli/index.js +1 -1
- package/dist/core/index.js +1 -1
- package/dist/{healthcheck-7YOPNDXN.js → healthcheck-E4GI5RMZ.js} +2 -2
- package/hooks/vault-context.js +26 -1
- package/package.json +1 -1
- package/dist/chunk-EVAJGOCB.js.map +0 -1
- /package/dist/{healthcheck-7YOPNDXN.js.map → healthcheck-E4GI5RMZ.js.map} +0 -0
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Single-plugin marketplace for semantic-memory — the unified memory-layer plugin. v1.1.0 absorbs brain's strengths: AGENTS.md contract artifact, hard-gated verification sessions, multi-agent skill bundler (codex/copilot/pi), and SessionStart drift detection — all backwards-compatible with v1.0.x via deprecation shims.",
|
|
9
|
-
"version": "1.2.
|
|
9
|
+
"version": "1.2.2"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "semantic-memory",
|
|
14
14
|
"description": "Unified memory-layer plugin for Claude Code. One MCP server, 40 tools across multi-corpus search/read/write/graph/lint/sessions/contract. v1.1.0 adds: AGENTS.md contract artifact, hard-gated verification sessions (session_start/run/finish/status), proposal-mode synthesize_note + synthesize_promote, multi-agent skill bundler (codex/copilot/pi via the new `skills` CLI), and SessionStart drift detection. Backwards-compatible with v1.0.x — every previous tool name remains callable through deprecation shims (removed in v2.0.0).",
|
|
15
|
-
"version": "1.2.
|
|
15
|
+
"version": "1.2.2",
|
|
16
16
|
"author": {
|
|
17
17
|
"name": "TheGlitchKing"
|
|
18
18
|
},
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "semantic-memory",
|
|
3
3
|
"description": "Unified memory-layer plugin for Claude Code. One MCP server, multiple typed corpora (vault / code / plans / docs / research / project-map), unified cross-corpus knowledge graph, conditional tool registration, in-process drift detection, and human↔code translation. Rebrand of `semantic-sidekick` at v1.0.0 — see CHANGELOG for migration.",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.2.2",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "TheGlitchKing",
|
|
7
7
|
"email": "theglitchking@users.noreply.github.com"
|
|
8
8
|
},
|
|
9
9
|
"license": "MIT",
|
|
10
|
-
"homepage": "https://github.com/TheGlitchKing/semantic-
|
|
11
|
-
"repository": "https://github.com/TheGlitchKing/semantic-
|
|
10
|
+
"homepage": "https://github.com/TheGlitchKing/semantic-memory",
|
|
11
|
+
"repository": "https://github.com/TheGlitchKing/semantic-memory",
|
|
12
12
|
"keywords": [
|
|
13
13
|
"mcp",
|
|
14
14
|
"memory-layer",
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to semantic-memory (formerly semantic-sidekick) will be documented here.
|
|
4
4
|
|
|
5
|
+
## [1.2.2] - 2026-06-27 — Plugin-aware hook-registration check + manifest version sync
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **`hook_registration` drift no longer false-positives on plugin-style installs.** The check (both the SessionStart fast path in `hooks/vault-context.js` and the CLI `checkHookRegistration` in `src/core/healthcheck.ts`) only inspected the project's `.claude/settings.json`. When semantic-memory is installed as a Claude Code plugin, its SessionStart/UserPromptSubmit/Stop hooks live in the plugin's own `hooks/hooks.json` (resolved via `CLAUDE_PLUGIN_ROOT`) and fire correctly, so the check warned about "missing" hooks that were in fact registered. Both sites now treat events declared in the plugin's `hooks/hooks.json` as registered. Npm-dependency installs (no `CLAUDE_PLUGIN_ROOT`) are unaffected and still require the hooks in `settings.json`.
|
|
10
|
+
- **Plugin manifest version sync.** `.claude-plugin/plugin.json` was frozen at `1.0.1` while `package.json`, git tags, and `marketplace.json` advanced — Claude Code reads the plugin version from `plugin.json`, so `claude plugin update` always reported "already at latest (1.0.1)" and never pulled newer code. Bumped `plugin.json` to match, and corrected its stale `homepage`/`repository` URLs (they pointed at the old `semantic-sidekick` repo).
|
|
11
|
+
|
|
5
12
|
## [1.2.1] - 2026-06-11 — Capture-cue false-positive fixes
|
|
6
13
|
|
|
7
14
|
Fixes two classes of false positives in the Stop-hook capture nudge (`hooks/vault-context.js`), where the cue scanner over-fired and asked to synthesize moments that carried no new knowledge.
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
|
|
5
5
|
// src/core/healthcheck.ts
|
|
6
6
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
7
|
-
import { existsSync } from "fs";
|
|
7
|
+
import { existsSync, readFileSync } from "fs";
|
|
8
8
|
import { join } from "path";
|
|
9
9
|
import { homedir } from "os";
|
|
10
10
|
var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
@@ -123,6 +123,19 @@ async function checkMcpJsonEntry(projectRoot) {
|
|
|
123
123
|
}
|
|
124
124
|
return { check: "mcp_json_entry", severity: "ok", summary: ".mcp.json server entry present" };
|
|
125
125
|
}
|
|
126
|
+
function pluginRegisteredHookEvents() {
|
|
127
|
+
try {
|
|
128
|
+
const root = process.env.CLAUDE_PLUGIN_ROOT;
|
|
129
|
+
if (!root) return [];
|
|
130
|
+
const hooksPath = join(root, "hooks", "hooks.json");
|
|
131
|
+
if (!existsSync(hooksPath)) return [];
|
|
132
|
+
const data = JSON.parse(readFileSync(hooksPath, "utf-8"));
|
|
133
|
+
const hooks = data?.hooks ?? {};
|
|
134
|
+
return Object.keys(hooks).filter((e) => Array.isArray(hooks[e]) && hooks[e].length > 0);
|
|
135
|
+
} catch {
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
126
139
|
async function checkHookRegistration(projectRoot) {
|
|
127
140
|
const settings = join(projectRoot, ".claude", "settings.json");
|
|
128
141
|
if (!existsSync(settings)) return null;
|
|
@@ -130,10 +143,12 @@ async function checkHookRegistration(projectRoot) {
|
|
|
130
143
|
const data = JSON.parse(await readFile(settings, "utf-8"));
|
|
131
144
|
const hooks = data?.hooks ?? {};
|
|
132
145
|
const required = ["SessionStart", "UserPromptSubmit", "Stop"];
|
|
146
|
+
const pluginEvents = pluginRegisteredHookEvents();
|
|
133
147
|
const missing = [];
|
|
134
148
|
for (const event of required) {
|
|
135
149
|
const arr = hooks[event];
|
|
136
|
-
|
|
150
|
+
const inSettings = Array.isArray(arr) && arr.length > 0;
|
|
151
|
+
if (!inSettings && !pluginEvents.includes(event)) missing.push(event);
|
|
137
152
|
}
|
|
138
153
|
if (missing.length > 0) {
|
|
139
154
|
return {
|
|
@@ -294,4 +309,4 @@ export {
|
|
|
294
309
|
filterToDrift,
|
|
295
310
|
formatDriftBanner
|
|
296
311
|
};
|
|
297
|
-
//# sourceMappingURL=chunk-
|
|
312
|
+
//# sourceMappingURL=chunk-UQSASB7Q.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/healthcheck.ts"],"sourcesContent":["import { readFile, writeFile, mkdir, stat } from \"node:fs/promises\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport { homedir } from \"node:os\";\nimport { lintVault } from \"./lint.js\";\nimport { deriveProjectRoot } from \"./session.js\";\n\nexport type Tier = \"fast\" | \"slow\" | \"all\";\nexport type Severity = \"ok\" | \"warn\" | \"error\";\n\nexport interface DriftFinding {\n check: string;\n severity: Severity;\n summary: string;\n detail?: string;\n fixable_via?: \"skill-link\" | \"mcp-reconcile\" | \"regenerate-contract\" | \"reindex\" | \"none\";\n}\n\nexport interface HealthcheckResult {\n tier_run: Tier;\n ran_at: string;\n duration_ms: number;\n findings: DriftFinding[];\n counts: { ok: number; warn: number; error: number };\n cached?: boolean;\n}\n\nexport interface HealthcheckOptions {\n projectRoot: string;\n /** Vault path. When omitted, derived from projectRoot's .claude/.vault. */\n vaultPath?: string;\n tier?: Tier;\n /** Cache file path. Default: <projectRoot>/.claude/.semantic-memory/healthcheck-cache.json */\n cachePath?: string;\n /** Force re-run even if cache is <5 minutes old. */\n force?: boolean;\n /** Override \"now\" — used by tests. */\n nowMs?: number;\n}\n\nconst CACHE_TTL_MS = 5 * 60 * 1000;\n\n/**\n * Run the drift-detection healthcheck.\n *\n * - Fast tier: cheap file-system probes (~ms each). Plugin version, skill manifests,\n * .mcp.json registration, hook registration, index freshness, session staleness,\n * AGENTS.md managed-block sha. All independent, run in parallel. Total budget: <100ms\n * on a representative install.\n * - Slow tier: full vault lint. Adds tens of seconds on large vaults — invoked by\n * explicit /healthcheck, never by the SessionStart auto path.\n * - Cache: when force=false and the cache file is <5min old, return the cached result.\n *\n * Fail-open: any individual check that throws produces a `severity: \"warn\"` finding\n * with the error message; never propagates.\n */\nexport async function runHealthcheck(opts: HealthcheckOptions): Promise<HealthcheckResult> {\n const start = (opts.nowMs ?? Date.now());\n const tier: Tier = opts.tier ?? \"fast\";\n const cachePath = opts.cachePath ?? join(opts.projectRoot, \".claude\", \".semantic-memory\", \"healthcheck-cache.json\");\n\n if (!opts.force && existsSync(cachePath)) {\n try {\n const raw = await readFile(cachePath, \"utf-8\");\n const cached = JSON.parse(raw) as HealthcheckResult & { ran_at_ms: number };\n if (cached.tier_run === tier && start - cached.ran_at_ms < CACHE_TTL_MS) {\n return { ...cached, cached: true };\n }\n } catch {\n // ignore corrupt cache; will overwrite below\n }\n }\n\n const findings = tier === \"fast\" ? await runFastTier(opts) : tier === \"slow\" ? await runSlowTier(opts) : [...(await runFastTier(opts)), ...(await runSlowTier(opts))];\n\n const counts = countBySeverity(findings);\n const ranAt = new Date(start).toISOString();\n const result: HealthcheckResult = {\n tier_run: tier,\n ran_at: ranAt,\n duration_ms: Date.now() - start,\n findings,\n counts,\n };\n\n // Persist cache (best-effort; failure is non-fatal).\n try {\n await mkdir(join(opts.projectRoot, \".claude\", \".semantic-memory\"), { recursive: true });\n await writeFile(cachePath, JSON.stringify({ ...result, ran_at_ms: start }, null, 2), \"utf-8\");\n } catch {\n // never let cache write block the result\n }\n\n return result;\n}\n\n/**\n * Surface filter — return findings with severity >= warn. Used by the SessionStart hook\n * to decide whether to emit anything at all.\n */\nexport function filterToDrift(result: HealthcheckResult): DriftFinding[] {\n return result.findings.filter((f) => f.severity !== \"ok\");\n}\n\nfunction countBySeverity(findings: DriftFinding[]): { ok: number; warn: number; error: number } {\n const counts = { ok: 0, warn: 0, error: 0 };\n for (const f of findings) counts[f.severity]++;\n return counts;\n}\n\nasync function runFastTier(opts: HealthcheckOptions): Promise<DriftFinding[]> {\n const checks: Promise<DriftFinding | null>[] = [\n checkPluginVersion(opts.projectRoot),\n checkMcpJsonEntry(opts.projectRoot),\n checkHookRegistration(opts.projectRoot),\n checkIndexFreshness(opts.projectRoot, opts.vaultPath),\n checkSessionStaleness(opts.projectRoot),\n checkAgentsContract(opts.projectRoot),\n checkLegacyStateFiles(opts.projectRoot),\n ...(await skillManifestChecks(opts.projectRoot)),\n ];\n const results = await Promise.all(checks.map((p) => p.catch((err) => failOpenWarn(err))));\n return results.filter((r): r is DriftFinding => r !== null);\n}\n\nasync function runSlowTier(opts: HealthcheckOptions): Promise<DriftFinding[]> {\n const findings: DriftFinding[] = [];\n if (opts.vaultPath && existsSync(opts.vaultPath)) {\n try {\n const report = await lintVault(opts.vaultPath);\n const total = report.findings.length;\n if (total === 0) {\n findings.push({ check: \"lint_vault\", severity: \"ok\", summary: `vault lint clean (${report.counts.filesLinted} files)` });\n } else {\n findings.push({\n check: \"lint_vault\",\n severity: report.counts.errors > 0 ? \"error\" : \"warn\",\n summary: `vault lint: ${total} findings (errors=${report.counts.errors}, warnings=${report.counts.warnings})`,\n detail: `Run \\`lint_vault\\` for the full report. Many lint findings need human review (stale notes, broken wikilinks).`,\n });\n }\n } catch (err: any) {\n findings.push({ check: \"lint_vault\", severity: \"warn\", summary: `lint_vault threw: ${err?.message ?? err}` });\n }\n }\n return findings;\n}\n\nfunction failOpenWarn(err: unknown): DriftFinding {\n return {\n check: \"internal\",\n severity: \"warn\",\n summary: `healthcheck internal error: ${err instanceof Error ? err.message : String(err)}`,\n };\n}\n\n// --- individual checks ---\n\nasync function checkPluginVersion(projectRoot: string): Promise<DriftFinding | null> {\n const pkgPaths = [\n join(projectRoot, \"node_modules\", \"@theglitchking\", \"semantic-memory\", \"package.json\"),\n join(projectRoot, \"node_modules\", \"@theglitchking\", \"semantic-sidekick\", \"package.json\"),\n ];\n for (const p of pkgPaths) {\n if (!existsSync(p)) continue;\n try {\n const installed = JSON.parse(await readFile(p, \"utf-8\"));\n if (typeof installed?.version === \"string\") {\n return { check: \"plugin_version\", severity: \"ok\", summary: `installed=${installed.version}` };\n }\n } catch {\n // continue to next candidate\n }\n }\n return null; // no installation present — not a drift event for non-npm dev environments\n}\n\nasync function checkMcpJsonEntry(projectRoot: string): Promise<DriftFinding | null> {\n const mcpPath = join(projectRoot, \".mcp.json\");\n if (!existsSync(mcpPath)) return null; // no .mcp.json — fine for dev\n const data = JSON.parse(await readFile(mcpPath, \"utf-8\"));\n const servers = data?.mcpServers ?? {};\n const hasEntry = Object.keys(servers).some((k) => k === \"semantic-vault\" || k === \"semantic-sidekick\" || k === \"semantic-memory\");\n if (!hasEntry) {\n return {\n check: \"mcp_json_entry\",\n severity: \"warn\",\n summary: \".mcp.json present but no semantic-memory/sidekick/vault server entry\",\n fixable_via: \"mcp-reconcile\",\n };\n }\n return { check: \"mcp_json_entry\", severity: \"ok\", summary: \".mcp.json server entry present\" };\n}\n\n// Events the plugin registers through its own hooks/hooks.json (resolved via\n// CLAUDE_PLUGIN_ROOT). On a plugin-style install these live in the plugin, not\n// the project's .claude/settings.json, so they must count as registered.\nfunction pluginRegisteredHookEvents(): string[] {\n try {\n const root = process.env.CLAUDE_PLUGIN_ROOT;\n if (!root) return [];\n const hooksPath = join(root, \"hooks\", \"hooks.json\");\n if (!existsSync(hooksPath)) return [];\n const data = JSON.parse(readFileSync(hooksPath, \"utf-8\"));\n const hooks = data?.hooks ?? {};\n return Object.keys(hooks).filter((e) => Array.isArray(hooks[e]) && hooks[e].length > 0);\n } catch {\n return [];\n }\n}\n\nasync function checkHookRegistration(projectRoot: string): Promise<DriftFinding | null> {\n const settings = join(projectRoot, \".claude\", \"settings.json\");\n if (!existsSync(settings)) return null;\n try {\n const data = JSON.parse(await readFile(settings, \"utf-8\"));\n const hooks = data?.hooks ?? {};\n const required = [\"SessionStart\", \"UserPromptSubmit\", \"Stop\"];\n const pluginEvents = pluginRegisteredHookEvents();\n const missing: string[] = [];\n for (const event of required) {\n const arr = hooks[event];\n const inSettings = Array.isArray(arr) && arr.length > 0;\n if (!inSettings && !pluginEvents.includes(event)) missing.push(event);\n }\n if (missing.length > 0) {\n return {\n check: \"hook_registration\",\n severity: \"warn\",\n summary: `missing hook registrations: ${missing.join(\", \")}`,\n detail: `Run \\`/relink\\` or reinstall the plugin to refresh hook registrations.`,\n };\n }\n return { check: \"hook_registration\", severity: \"ok\", summary: \"all 3 hook events registered\" };\n } catch (err: any) {\n return failOpenWarn(err);\n }\n}\n\nasync function checkIndexFreshness(projectRoot: string, vaultPath?: string): Promise<DriftFinding | null> {\n const vault = vaultPath ?? defaultVaultPath(projectRoot);\n if (!vault || !existsSync(vault)) return null;\n const indexDir = join(vault, \".semantic-sidekick-index\");\n const meta = join(indexDir, \"meta.json\");\n if (!existsSync(meta)) {\n return {\n check: \"index_freshness\",\n severity: \"warn\",\n summary: \"no index built yet\",\n detail: \"Run `reindex` to build the vector index.\",\n fixable_via: \"reindex\",\n };\n }\n try {\n const data = JSON.parse(await readFile(meta, \"utf-8\"));\n if (typeof data.indexedAt === \"string\") {\n return { check: \"index_freshness\", severity: \"ok\", summary: `last reindex ${data.indexedAt}` };\n }\n } catch {\n // ignore\n }\n return null;\n}\n\nasync function checkSessionStaleness(projectRoot: string): Promise<DriftFinding | null> {\n const sessionPath = join(projectRoot, \".claude\", \".semantic-memory\", \"session.json\");\n if (!existsSync(sessionPath)) return null;\n try {\n const state = JSON.parse(await readFile(sessionPath, \"utf-8\"));\n if (state?.closed_at) return null;\n const last = state?.last_activity_at;\n if (typeof last !== \"string\") return null;\n const ageMs = Date.now() - new Date(last).getTime();\n if (ageMs > 24 * 60 * 60 * 1000) {\n return {\n check: \"session_staleness\",\n severity: \"warn\",\n summary: `session ${state.id} stale (>${Math.round(ageMs / (60 * 60 * 1000))}h since activity)`,\n detail: `Either resume the session (run session_run / session_finish) or remove .claude/.semantic-memory/session.json to abandon it.`,\n };\n }\n return { check: \"session_staleness\", severity: \"ok\", summary: `session ${state.id} active` };\n } catch {\n return null;\n }\n}\n\nasync function checkLegacyStateFiles(projectRoot: string): Promise<DriftFinding | null> {\n // Detect v1.1-and-earlier state files at .claude/.sidekick-*. v1.2 moved these to\n // .claude/.semantic-memory/. The hook still reads them via fallback for backwards-\n // compat, but they should be migrated explicitly so the legacy paths stop existing.\n const legacy = [\n \".sidekick-mode\",\n \".sidekick-fingerprints.json\",\n \".sidekick-capture-pending.json\",\n ];\n const present: string[] = [];\n for (const name of legacy) {\n if (existsSync(join(projectRoot, \".claude\", name))) {\n present.push(name);\n }\n }\n if (present.length === 0) {\n return { check: \"legacy_state_files\", severity: \"ok\", summary: \"no legacy state files\" };\n }\n return {\n check: \"legacy_state_files\",\n severity: \"warn\",\n summary: `${present.length} legacy state file${present.length === 1 ? \"\" : \"s\"} at .claude/.sidekick-*`,\n detail:\n `Files: ${present.join(\", \")}. Run \\`bin/semantic-memory migrate-state\\` to move them under .claude/.semantic-memory/. ` +\n `These files are still being read for backwards-compat through v1.x; the legacy fallback is removed in v2.0.`,\n fixable_via: \"none\",\n };\n}\n\nasync function checkAgentsContract(projectRoot: string): Promise<DriftFinding | null> {\n const path = join(projectRoot, \"AGENTS.md\");\n if (!existsSync(path)) return null; // optional artifact\n try {\n const raw = await readFile(path, \"utf-8\");\n const begin = raw.indexOf(\"<!-- semantic-memory:begin contract -->\");\n const end = raw.indexOf(\"<!-- semantic-memory:end contract -->\");\n if (begin === -1 || end === -1) {\n return {\n check: \"agents_contract\",\n severity: \"warn\",\n summary: \"AGENTS.md exists but lacks managed-block markers\",\n detail: \"Either move custom content to a Local Notes section, or delete AGENTS.md and run `regenerate_contract` to bootstrap.\",\n fixable_via: \"regenerate-contract\",\n };\n }\n return { check: \"agents_contract\", severity: \"ok\", summary: \"AGENTS.md managed block intact\" };\n } catch {\n return null;\n }\n}\n\nasync function skillManifestChecks(projectRoot: string): Promise<Promise<DriftFinding | null>[]> {\n const home = homedir();\n const targets = [\n { agent: \"claude\", path: join(home, \".claude\", \"skills\") },\n { agent: \"codex\", path: join(home, \".codex\", \"skills\") },\n { agent: \"copilot\", path: join(home, \".copilot\", \"skills\") },\n { agent: \"pi\", path: join(home, \".pi\", \"agent\", \"skills\") },\n { agent: \"claude-local\", path: join(projectRoot, \".claude\", \"skills\") },\n ];\n const out: Promise<DriftFinding | null>[] = [];\n for (const t of targets) {\n out.push(checkOneSkillManifest(t.agent, t.path));\n }\n return out;\n}\n\nasync function checkOneSkillManifest(agentLabel: string, skillsRoot: string): Promise<DriftFinding | null> {\n const manifestPath = join(skillsRoot, \".semantic-memory-skill-manifest.json\");\n if (!existsSync(manifestPath)) return null; // not installed for this agent — fine\n try {\n const raw = await readFile(manifestPath, \"utf-8\");\n const manifest = JSON.parse(raw);\n if (typeof manifest?.installed_at !== \"string\") return null;\n const age = Date.now() - new Date(manifest.installed_at).getTime();\n // Manifest itself is the source of truth; deeper sha-recheck happens via slow tier\n return {\n check: `skill_manifest:${agentLabel}`,\n severity: \"ok\",\n summary: `${agentLabel} skills installed v${manifest.version} (${Math.round(age / (24 * 60 * 60 * 1000))}d ago)`,\n };\n } catch {\n return null;\n }\n}\n\nfunction defaultVaultPath(projectRoot: string): string {\n return join(projectRoot, \".claude\", \".vault\");\n}\n\n/**\n * Format a drift summary for the SessionStart hook. Returns the text to inject as\n * additionalContext, or an empty string when there's nothing to surface.\n */\nexport function formatDriftBanner(result: HealthcheckResult): string {\n const drift = filterToDrift(result);\n if (drift.length === 0) return \"\";\n const lines = [\n `<vault-drift count=\"${drift.length}\">`,\n `⚠️ semantic-memory: ${drift.length} drift issue${drift.length === 1 ? \"\" : \"s\"} detected`,\n ];\n for (const f of drift.slice(0, 5)) {\n const sev = f.severity === \"error\" ? \"✗\" : \"⚠\";\n lines.push(` ${sev} ${f.check}: ${f.summary}`);\n }\n if (drift.length > 5) lines.push(` …and ${drift.length - 5} more`);\n lines.push(`Run /healthcheck for details, or /healthcheck --fix to auto-fix safe items.`);\n lines.push(`</vault-drift>`);\n return lines.join(\"\\n\");\n}\n"],"mappings":";;;;;AAAA,SAAS,UAAU,WAAW,aAAmB;AACjD,SAAS,YAAY,oBAAoB;AACzC,SAAS,YAAsB;AAE/B,SAAS,eAAe;AAqCxB,IAAM,eAAe,IAAI,KAAK;AAgB9B,eAAsB,eAAe,MAAsD;AACzF,QAAM,QAAS,KAAK,SAAS,KAAK,IAAI;AACtC,QAAM,OAAa,KAAK,QAAQ;AAChC,QAAM,YAAY,KAAK,aAAa,KAAK,KAAK,aAAa,WAAW,oBAAoB,wBAAwB;AAElH,MAAI,CAAC,KAAK,SAAS,WAAW,SAAS,GAAG;AACxC,QAAI;AACF,YAAM,MAAM,MAAM,SAAS,WAAW,OAAO;AAC7C,YAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,UAAI,OAAO,aAAa,QAAQ,QAAQ,OAAO,YAAY,cAAc;AACvE,eAAO,EAAE,GAAG,QAAQ,QAAQ,KAAK;AAAA,MACnC;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,QAAM,WAAW,SAAS,SAAS,MAAM,YAAY,IAAI,IAAI,SAAS,SAAS,MAAM,YAAY,IAAI,IAAI,CAAC,GAAI,MAAM,YAAY,IAAI,GAAI,GAAI,MAAM,YAAY,IAAI,CAAE;AAEpK,QAAM,SAAS,gBAAgB,QAAQ;AACvC,QAAM,QAAQ,IAAI,KAAK,KAAK,EAAE,YAAY;AAC1C,QAAM,SAA4B;AAAA,IAChC,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,aAAa,KAAK,IAAI,IAAI;AAAA,IAC1B;AAAA,IACA;AAAA,EACF;AAGA,MAAI;AACF,UAAM,MAAM,KAAK,KAAK,aAAa,WAAW,kBAAkB,GAAG,EAAE,WAAW,KAAK,CAAC;AACtF,UAAM,UAAU,WAAW,KAAK,UAAU,EAAE,GAAG,QAAQ,WAAW,MAAM,GAAG,MAAM,CAAC,GAAG,OAAO;AAAA,EAC9F,QAAQ;AAAA,EAER;AAEA,SAAO;AACT;AAMO,SAAS,cAAc,QAA2C;AACvE,SAAO,OAAO,SAAS,OAAO,CAAC,MAAM,EAAE,aAAa,IAAI;AAC1D;AAEA,SAAS,gBAAgB,UAAuE;AAC9F,QAAM,SAAS,EAAE,IAAI,GAAG,MAAM,GAAG,OAAO,EAAE;AAC1C,aAAW,KAAK,SAAU,QAAO,EAAE,QAAQ;AAC3C,SAAO;AACT;AAEA,eAAe,YAAY,MAAmD;AAC5E,QAAM,SAAyC;AAAA,IAC7C,mBAAmB,KAAK,WAAW;AAAA,IACnC,kBAAkB,KAAK,WAAW;AAAA,IAClC,sBAAsB,KAAK,WAAW;AAAA,IACtC,oBAAoB,KAAK,aAAa,KAAK,SAAS;AAAA,IACpD,sBAAsB,KAAK,WAAW;AAAA,IACtC,oBAAoB,KAAK,WAAW;AAAA,IACpC,sBAAsB,KAAK,WAAW;AAAA,IACtC,GAAI,MAAM,oBAAoB,KAAK,WAAW;AAAA,EAChD;AACA,QAAM,UAAU,MAAM,QAAQ,IAAI,OAAO,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,aAAa,GAAG,CAAC,CAAC,CAAC;AACxF,SAAO,QAAQ,OAAO,CAAC,MAAyB,MAAM,IAAI;AAC5D;AAEA,eAAe,YAAY,MAAmD;AAC5E,QAAM,WAA2B,CAAC;AAClC,MAAI,KAAK,aAAa,WAAW,KAAK,SAAS,GAAG;AAChD,QAAI;AACF,YAAM,SAAS,MAAM,UAAU,KAAK,SAAS;AAC7C,YAAM,QAAQ,OAAO,SAAS;AAC9B,UAAI,UAAU,GAAG;AACf,iBAAS,KAAK,EAAE,OAAO,cAAc,UAAU,MAAM,SAAS,qBAAqB,OAAO,OAAO,WAAW,UAAU,CAAC;AAAA,MACzH,OAAO;AACL,iBAAS,KAAK;AAAA,UACZ,OAAO;AAAA,UACP,UAAU,OAAO,OAAO,SAAS,IAAI,UAAU;AAAA,UAC/C,SAAS,eAAe,KAAK,qBAAqB,OAAO,OAAO,MAAM,cAAc,OAAO,OAAO,QAAQ;AAAA,UAC1G,QAAQ;AAAA,QACV,CAAC;AAAA,MACH;AAAA,IACF,SAAS,KAAU;AACjB,eAAS,KAAK,EAAE,OAAO,cAAc,UAAU,QAAQ,SAAS,qBAAqB,KAAK,WAAW,GAAG,GAAG,CAAC;AAAA,IAC9G;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,aAAa,KAA4B;AAChD,SAAO;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS,+BAA+B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,EAC1F;AACF;AAIA,eAAe,mBAAmB,aAAmD;AACnF,QAAM,WAAW;AAAA,IACf,KAAK,aAAa,gBAAgB,kBAAkB,mBAAmB,cAAc;AAAA,IACrF,KAAK,aAAa,gBAAgB,kBAAkB,qBAAqB,cAAc;AAAA,EACzF;AACA,aAAW,KAAK,UAAU;AACxB,QAAI,CAAC,WAAW,CAAC,EAAG;AACpB,QAAI;AACF,YAAM,YAAY,KAAK,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC;AACvD,UAAI,OAAO,WAAW,YAAY,UAAU;AAC1C,eAAO,EAAE,OAAO,kBAAkB,UAAU,MAAM,SAAS,aAAa,UAAU,OAAO,GAAG;AAAA,MAC9F;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAe,kBAAkB,aAAmD;AAClF,QAAM,UAAU,KAAK,aAAa,WAAW;AAC7C,MAAI,CAAC,WAAW,OAAO,EAAG,QAAO;AACjC,QAAM,OAAO,KAAK,MAAM,MAAM,SAAS,SAAS,OAAO,CAAC;AACxD,QAAM,UAAU,MAAM,cAAc,CAAC;AACrC,QAAM,WAAW,OAAO,KAAK,OAAO,EAAE,KAAK,CAAC,MAAM,MAAM,oBAAoB,MAAM,uBAAuB,MAAM,iBAAiB;AAChI,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,MACL,OAAO;AAAA,MACP,UAAU;AAAA,MACV,SAAS;AAAA,MACT,aAAa;AAAA,IACf;AAAA,EACF;AACA,SAAO,EAAE,OAAO,kBAAkB,UAAU,MAAM,SAAS,iCAAiC;AAC9F;AAKA,SAAS,6BAAuC;AAC9C,MAAI;AACF,UAAM,OAAO,QAAQ,IAAI;AACzB,QAAI,CAAC,KAAM,QAAO,CAAC;AACnB,UAAM,YAAY,KAAK,MAAM,SAAS,YAAY;AAClD,QAAI,CAAC,WAAW,SAAS,EAAG,QAAO,CAAC;AACpC,UAAM,OAAO,KAAK,MAAM,aAAa,WAAW,OAAO,CAAC;AACxD,UAAM,QAAQ,MAAM,SAAS,CAAC;AAC9B,WAAO,OAAO,KAAK,KAAK,EAAE,OAAO,CAAC,MAAM,MAAM,QAAQ,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC,EAAE,SAAS,CAAC;AAAA,EACxF,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,eAAe,sBAAsB,aAAmD;AACtF,QAAM,WAAW,KAAK,aAAa,WAAW,eAAe;AAC7D,MAAI,CAAC,WAAW,QAAQ,EAAG,QAAO;AAClC,MAAI;AACF,UAAM,OAAO,KAAK,MAAM,MAAM,SAAS,UAAU,OAAO,CAAC;AACzD,UAAM,QAAQ,MAAM,SAAS,CAAC;AAC9B,UAAM,WAAW,CAAC,gBAAgB,oBAAoB,MAAM;AAC5D,UAAM,eAAe,2BAA2B;AAChD,UAAM,UAAoB,CAAC;AAC3B,eAAW,SAAS,UAAU;AAC5B,YAAM,MAAM,MAAM,KAAK;AACvB,YAAM,aAAa,MAAM,QAAQ,GAAG,KAAK,IAAI,SAAS;AACtD,UAAI,CAAC,cAAc,CAAC,aAAa,SAAS,KAAK,EAAG,SAAQ,KAAK,KAAK;AAAA,IACtE;AACA,QAAI,QAAQ,SAAS,GAAG;AACtB,aAAO;AAAA,QACL,OAAO;AAAA,QACP,UAAU;AAAA,QACV,SAAS,+BAA+B,QAAQ,KAAK,IAAI,CAAC;AAAA,QAC1D,QAAQ;AAAA,MACV;AAAA,IACF;AACA,WAAO,EAAE,OAAO,qBAAqB,UAAU,MAAM,SAAS,+BAA+B;AAAA,EAC/F,SAAS,KAAU;AACjB,WAAO,aAAa,GAAG;AAAA,EACzB;AACF;AAEA,eAAe,oBAAoB,aAAqB,WAAkD;AACxG,QAAM,QAAQ,aAAa,iBAAiB,WAAW;AACvD,MAAI,CAAC,SAAS,CAAC,WAAW,KAAK,EAAG,QAAO;AACzC,QAAM,WAAW,KAAK,OAAO,0BAA0B;AACvD,QAAM,OAAO,KAAK,UAAU,WAAW;AACvC,MAAI,CAAC,WAAW,IAAI,GAAG;AACrB,WAAO;AAAA,MACL,OAAO;AAAA,MACP,UAAU;AAAA,MACV,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,aAAa;AAAA,IACf;AAAA,EACF;AACA,MAAI;AACF,UAAM,OAAO,KAAK,MAAM,MAAM,SAAS,MAAM,OAAO,CAAC;AACrD,QAAI,OAAO,KAAK,cAAc,UAAU;AACtC,aAAO,EAAE,OAAO,mBAAmB,UAAU,MAAM,SAAS,gBAAgB,KAAK,SAAS,GAAG;AAAA,IAC/F;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAEA,eAAe,sBAAsB,aAAmD;AACtF,QAAM,cAAc,KAAK,aAAa,WAAW,oBAAoB,cAAc;AACnF,MAAI,CAAC,WAAW,WAAW,EAAG,QAAO;AACrC,MAAI;AACF,UAAM,QAAQ,KAAK,MAAM,MAAM,SAAS,aAAa,OAAO,CAAC;AAC7D,QAAI,OAAO,UAAW,QAAO;AAC7B,UAAM,OAAO,OAAO;AACpB,QAAI,OAAO,SAAS,SAAU,QAAO;AACrC,UAAM,QAAQ,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,QAAQ;AAClD,QAAI,QAAQ,KAAK,KAAK,KAAK,KAAM;AAC/B,aAAO;AAAA,QACL,OAAO;AAAA,QACP,UAAU;AAAA,QACV,SAAS,WAAW,MAAM,EAAE,YAAY,KAAK,MAAM,SAAS,KAAK,KAAK,IAAK,CAAC;AAAA,QAC5E,QAAQ;AAAA,MACV;AAAA,IACF;AACA,WAAO,EAAE,OAAO,qBAAqB,UAAU,MAAM,SAAS,WAAW,MAAM,EAAE,UAAU;AAAA,EAC7F,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,sBAAsB,aAAmD;AAItF,QAAM,SAAS;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,UAAoB,CAAC;AAC3B,aAAW,QAAQ,QAAQ;AACzB,QAAI,WAAW,KAAK,aAAa,WAAW,IAAI,CAAC,GAAG;AAClD,cAAQ,KAAK,IAAI;AAAA,IACnB;AAAA,EACF;AACA,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,EAAE,OAAO,sBAAsB,UAAU,MAAM,SAAS,wBAAwB;AAAA,EACzF;AACA,SAAO;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS,GAAG,QAAQ,MAAM,qBAAqB,QAAQ,WAAW,IAAI,KAAK,GAAG;AAAA,IAC9E,QACE,UAAU,QAAQ,KAAK,IAAI,CAAC;AAAA,IAE9B,aAAa;AAAA,EACf;AACF;AAEA,eAAe,oBAAoB,aAAmD;AACpF,QAAM,OAAO,KAAK,aAAa,WAAW;AAC1C,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO;AAC9B,MAAI;AACF,UAAM,MAAM,MAAM,SAAS,MAAM,OAAO;AACxC,UAAM,QAAQ,IAAI,QAAQ,yCAAyC;AACnE,UAAM,MAAM,IAAI,QAAQ,uCAAuC;AAC/D,QAAI,UAAU,MAAM,QAAQ,IAAI;AAC9B,aAAO;AAAA,QACL,OAAO;AAAA,QACP,UAAU;AAAA,QACV,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,aAAa;AAAA,MACf;AAAA,IACF;AACA,WAAO,EAAE,OAAO,mBAAmB,UAAU,MAAM,SAAS,iCAAiC;AAAA,EAC/F,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,oBAAoB,aAA8D;AAC/F,QAAM,OAAO,QAAQ;AACrB,QAAM,UAAU;AAAA,IACd,EAAE,OAAO,UAAU,MAAM,KAAK,MAAM,WAAW,QAAQ,EAAE;AAAA,IACzD,EAAE,OAAO,SAAS,MAAM,KAAK,MAAM,UAAU,QAAQ,EAAE;AAAA,IACvD,EAAE,OAAO,WAAW,MAAM,KAAK,MAAM,YAAY,QAAQ,EAAE;AAAA,IAC3D,EAAE,OAAO,MAAM,MAAM,KAAK,MAAM,OAAO,SAAS,QAAQ,EAAE;AAAA,IAC1D,EAAE,OAAO,gBAAgB,MAAM,KAAK,aAAa,WAAW,QAAQ,EAAE;AAAA,EACxE;AACA,QAAM,MAAsC,CAAC;AAC7C,aAAW,KAAK,SAAS;AACvB,QAAI,KAAK,sBAAsB,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACjD;AACA,SAAO;AACT;AAEA,eAAe,sBAAsB,YAAoB,YAAkD;AACzG,QAAM,eAAe,KAAK,YAAY,sCAAsC;AAC5E,MAAI,CAAC,WAAW,YAAY,EAAG,QAAO;AACtC,MAAI;AACF,UAAM,MAAM,MAAM,SAAS,cAAc,OAAO;AAChD,UAAM,WAAW,KAAK,MAAM,GAAG;AAC/B,QAAI,OAAO,UAAU,iBAAiB,SAAU,QAAO;AACvD,UAAM,MAAM,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS,YAAY,EAAE,QAAQ;AAEjE,WAAO;AAAA,MACL,OAAO,kBAAkB,UAAU;AAAA,MACnC,UAAU;AAAA,MACV,SAAS,GAAG,UAAU,sBAAsB,SAAS,OAAO,KAAK,KAAK,MAAM,OAAO,KAAK,KAAK,KAAK,IAAK,CAAC;AAAA,IAC1G;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,iBAAiB,aAA6B;AACrD,SAAO,KAAK,aAAa,WAAW,QAAQ;AAC9C;AAMO,SAAS,kBAAkB,QAAmC;AACnE,QAAM,QAAQ,cAAc,MAAM;AAClC,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAM,QAAQ;AAAA,IACZ,uBAAuB,MAAM,MAAM;AAAA,IACnC,kCAAwB,MAAM,MAAM,eAAe,MAAM,WAAW,IAAI,KAAK,GAAG;AAAA,EAClF;AACA,aAAW,KAAK,MAAM,MAAM,GAAG,CAAC,GAAG;AACjC,UAAM,MAAM,EAAE,aAAa,UAAU,WAAM;AAC3C,UAAM,KAAK,KAAK,GAAG,IAAI,EAAE,KAAK,KAAK,EAAE,OAAO,EAAE;AAAA,EAChD;AACA,MAAI,MAAM,SAAS,EAAG,OAAM,KAAK,eAAU,MAAM,SAAS,CAAC,OAAO;AAClE,QAAM,KAAK,6EAA6E;AACxF,QAAM,KAAK,gBAAgB;AAC3B,SAAO,MAAM,KAAK,IAAI;AACxB;","names":[]}
|
package/dist/cli/index.js
CHANGED
|
@@ -778,7 +778,7 @@ ${r2.stderr?.toString() || ""}`);
|
|
|
778
778
|
});
|
|
779
779
|
var driftHealthcheckActionPostprocess = async (opts) => {
|
|
780
780
|
try {
|
|
781
|
-
const { runHealthcheck, formatDriftBanner } = await import("../healthcheck-
|
|
781
|
+
const { runHealthcheck, formatDriftBanner } = await import("../healthcheck-E4GI5RMZ.js");
|
|
782
782
|
const projectRoot = process.cwd();
|
|
783
783
|
const vaultCandidate = join3(projectRoot, ".claude", ".vault");
|
|
784
784
|
const vaultPath = existsSync3(vaultCandidate) ? vaultCandidate : void 0;
|
package/dist/core/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
filterToDrift,
|
|
3
3
|
formatDriftBanner,
|
|
4
4
|
runHealthcheck
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-UQSASB7Q.js";
|
|
6
6
|
import "./chunk-CAN2GWUG.js";
|
|
7
7
|
import "./chunk-E2Z4J4MK.js";
|
|
8
8
|
import "./chunk-PZ5AY32C.js";
|
|
@@ -11,4 +11,4 @@ export {
|
|
|
11
11
|
formatDriftBanner,
|
|
12
12
|
runHealthcheck
|
|
13
13
|
};
|
|
14
|
-
//# sourceMappingURL=healthcheck-
|
|
14
|
+
//# sourceMappingURL=healthcheck-E4GI5RMZ.js.map
|
package/hooks/vault-context.js
CHANGED
|
@@ -406,6 +406,25 @@ async function handleSessionStart(projectRoot, vaultPath, cliBin) {
|
|
|
406
406
|
* Heavy work (full vault lint, code-symbol drift) is reserved for the manual
|
|
407
407
|
* `/healthcheck` command; this auto path stays cheap.
|
|
408
408
|
*/
|
|
409
|
+
|
|
410
|
+
// Events the plugin registers through its own hooks/hooks.json (resolved via
|
|
411
|
+
// CLAUDE_PLUGIN_ROOT). On a plugin-style install these live in the plugin, not
|
|
412
|
+
// the project's .claude/settings.json, so they must count as registered.
|
|
413
|
+
function pluginRegisteredHookEvents() {
|
|
414
|
+
try {
|
|
415
|
+
const root = process.env.CLAUDE_PLUGIN_ROOT;
|
|
416
|
+
if (!root) return [];
|
|
417
|
+
const hooksPath = join(root, "hooks", "hooks.json");
|
|
418
|
+
if (!existsSync(hooksPath)) return [];
|
|
419
|
+
const data = JSON.parse(readFileSync(hooksPath, "utf8"));
|
|
420
|
+
const hooks = (data && data.hooks) || {};
|
|
421
|
+
return Object.keys(hooks).filter((e) => Array.isArray(hooks[e]) && hooks[e].length > 0);
|
|
422
|
+
} catch (e) {
|
|
423
|
+
debug(`pluginRegisteredHookEvents threw: ${e.message}`);
|
|
424
|
+
return [];
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
409
428
|
async function fastDriftCheck(projectRoot) {
|
|
410
429
|
const findings = [];
|
|
411
430
|
// .mcp.json
|
|
@@ -426,7 +445,13 @@ async function fastDriftCheck(projectRoot) {
|
|
|
426
445
|
const data = JSON.parse(readFileSync(settings, "utf8"));
|
|
427
446
|
const hooks = (data && data.hooks) || {};
|
|
428
447
|
const required = ["SessionStart", "UserPromptSubmit", "Stop"];
|
|
429
|
-
|
|
448
|
+
// Plugin-style installs register these via the plugin's own hooks/hooks.json
|
|
449
|
+
// (resolved through CLAUDE_PLUGIN_ROOT), not the project settings.json. Count
|
|
450
|
+
// those as registered so the check doesn't false-positive on plugin installs.
|
|
451
|
+
const pluginEvents = pluginRegisteredHookEvents();
|
|
452
|
+
const missing = required.filter(
|
|
453
|
+
(e) => (!Array.isArray(hooks[e]) || hooks[e].length === 0) && !pluginEvents.includes(e)
|
|
454
|
+
);
|
|
430
455
|
if (missing.length > 0) findings.push({ check: "hook_registration", summary: `missing hook events: ${missing.join(", ")}` });
|
|
431
456
|
}
|
|
432
457
|
} catch (e) { debug(`hook check threw: ${e.message}`); }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theglitchking/semantic-memory",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "Unified memory-layer plugin for Claude Code. One MCP server, multiple typed corpora (vault, code, plans, docs, research, project-map), unified cross-corpus knowledge graph, conditional tool registration, in-process drift detection, and human↔code translation. Rebrand of @theglitchking/semantic-sidekick at v1.0 — see CHANGELOG for migration.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/core/index.js",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/healthcheck.ts"],"sourcesContent":["import { readFile, writeFile, mkdir, stat } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport { homedir } from \"node:os\";\nimport { lintVault } from \"./lint.js\";\nimport { deriveProjectRoot } from \"./session.js\";\n\nexport type Tier = \"fast\" | \"slow\" | \"all\";\nexport type Severity = \"ok\" | \"warn\" | \"error\";\n\nexport interface DriftFinding {\n check: string;\n severity: Severity;\n summary: string;\n detail?: string;\n fixable_via?: \"skill-link\" | \"mcp-reconcile\" | \"regenerate-contract\" | \"reindex\" | \"none\";\n}\n\nexport interface HealthcheckResult {\n tier_run: Tier;\n ran_at: string;\n duration_ms: number;\n findings: DriftFinding[];\n counts: { ok: number; warn: number; error: number };\n cached?: boolean;\n}\n\nexport interface HealthcheckOptions {\n projectRoot: string;\n /** Vault path. When omitted, derived from projectRoot's .claude/.vault. */\n vaultPath?: string;\n tier?: Tier;\n /** Cache file path. Default: <projectRoot>/.claude/.semantic-memory/healthcheck-cache.json */\n cachePath?: string;\n /** Force re-run even if cache is <5 minutes old. */\n force?: boolean;\n /** Override \"now\" — used by tests. */\n nowMs?: number;\n}\n\nconst CACHE_TTL_MS = 5 * 60 * 1000;\n\n/**\n * Run the drift-detection healthcheck.\n *\n * - Fast tier: cheap file-system probes (~ms each). Plugin version, skill manifests,\n * .mcp.json registration, hook registration, index freshness, session staleness,\n * AGENTS.md managed-block sha. All independent, run in parallel. Total budget: <100ms\n * on a representative install.\n * - Slow tier: full vault lint. Adds tens of seconds on large vaults — invoked by\n * explicit /healthcheck, never by the SessionStart auto path.\n * - Cache: when force=false and the cache file is <5min old, return the cached result.\n *\n * Fail-open: any individual check that throws produces a `severity: \"warn\"` finding\n * with the error message; never propagates.\n */\nexport async function runHealthcheck(opts: HealthcheckOptions): Promise<HealthcheckResult> {\n const start = (opts.nowMs ?? Date.now());\n const tier: Tier = opts.tier ?? \"fast\";\n const cachePath = opts.cachePath ?? join(opts.projectRoot, \".claude\", \".semantic-memory\", \"healthcheck-cache.json\");\n\n if (!opts.force && existsSync(cachePath)) {\n try {\n const raw = await readFile(cachePath, \"utf-8\");\n const cached = JSON.parse(raw) as HealthcheckResult & { ran_at_ms: number };\n if (cached.tier_run === tier && start - cached.ran_at_ms < CACHE_TTL_MS) {\n return { ...cached, cached: true };\n }\n } catch {\n // ignore corrupt cache; will overwrite below\n }\n }\n\n const findings = tier === \"fast\" ? await runFastTier(opts) : tier === \"slow\" ? await runSlowTier(opts) : [...(await runFastTier(opts)), ...(await runSlowTier(opts))];\n\n const counts = countBySeverity(findings);\n const ranAt = new Date(start).toISOString();\n const result: HealthcheckResult = {\n tier_run: tier,\n ran_at: ranAt,\n duration_ms: Date.now() - start,\n findings,\n counts,\n };\n\n // Persist cache (best-effort; failure is non-fatal).\n try {\n await mkdir(join(opts.projectRoot, \".claude\", \".semantic-memory\"), { recursive: true });\n await writeFile(cachePath, JSON.stringify({ ...result, ran_at_ms: start }, null, 2), \"utf-8\");\n } catch {\n // never let cache write block the result\n }\n\n return result;\n}\n\n/**\n * Surface filter — return findings with severity >= warn. Used by the SessionStart hook\n * to decide whether to emit anything at all.\n */\nexport function filterToDrift(result: HealthcheckResult): DriftFinding[] {\n return result.findings.filter((f) => f.severity !== \"ok\");\n}\n\nfunction countBySeverity(findings: DriftFinding[]): { ok: number; warn: number; error: number } {\n const counts = { ok: 0, warn: 0, error: 0 };\n for (const f of findings) counts[f.severity]++;\n return counts;\n}\n\nasync function runFastTier(opts: HealthcheckOptions): Promise<DriftFinding[]> {\n const checks: Promise<DriftFinding | null>[] = [\n checkPluginVersion(opts.projectRoot),\n checkMcpJsonEntry(opts.projectRoot),\n checkHookRegistration(opts.projectRoot),\n checkIndexFreshness(opts.projectRoot, opts.vaultPath),\n checkSessionStaleness(opts.projectRoot),\n checkAgentsContract(opts.projectRoot),\n checkLegacyStateFiles(opts.projectRoot),\n ...(await skillManifestChecks(opts.projectRoot)),\n ];\n const results = await Promise.all(checks.map((p) => p.catch((err) => failOpenWarn(err))));\n return results.filter((r): r is DriftFinding => r !== null);\n}\n\nasync function runSlowTier(opts: HealthcheckOptions): Promise<DriftFinding[]> {\n const findings: DriftFinding[] = [];\n if (opts.vaultPath && existsSync(opts.vaultPath)) {\n try {\n const report = await lintVault(opts.vaultPath);\n const total = report.findings.length;\n if (total === 0) {\n findings.push({ check: \"lint_vault\", severity: \"ok\", summary: `vault lint clean (${report.counts.filesLinted} files)` });\n } else {\n findings.push({\n check: \"lint_vault\",\n severity: report.counts.errors > 0 ? \"error\" : \"warn\",\n summary: `vault lint: ${total} findings (errors=${report.counts.errors}, warnings=${report.counts.warnings})`,\n detail: `Run \\`lint_vault\\` for the full report. Many lint findings need human review (stale notes, broken wikilinks).`,\n });\n }\n } catch (err: any) {\n findings.push({ check: \"lint_vault\", severity: \"warn\", summary: `lint_vault threw: ${err?.message ?? err}` });\n }\n }\n return findings;\n}\n\nfunction failOpenWarn(err: unknown): DriftFinding {\n return {\n check: \"internal\",\n severity: \"warn\",\n summary: `healthcheck internal error: ${err instanceof Error ? err.message : String(err)}`,\n };\n}\n\n// --- individual checks ---\n\nasync function checkPluginVersion(projectRoot: string): Promise<DriftFinding | null> {\n const pkgPaths = [\n join(projectRoot, \"node_modules\", \"@theglitchking\", \"semantic-memory\", \"package.json\"),\n join(projectRoot, \"node_modules\", \"@theglitchking\", \"semantic-sidekick\", \"package.json\"),\n ];\n for (const p of pkgPaths) {\n if (!existsSync(p)) continue;\n try {\n const installed = JSON.parse(await readFile(p, \"utf-8\"));\n if (typeof installed?.version === \"string\") {\n return { check: \"plugin_version\", severity: \"ok\", summary: `installed=${installed.version}` };\n }\n } catch {\n // continue to next candidate\n }\n }\n return null; // no installation present — not a drift event for non-npm dev environments\n}\n\nasync function checkMcpJsonEntry(projectRoot: string): Promise<DriftFinding | null> {\n const mcpPath = join(projectRoot, \".mcp.json\");\n if (!existsSync(mcpPath)) return null; // no .mcp.json — fine for dev\n const data = JSON.parse(await readFile(mcpPath, \"utf-8\"));\n const servers = data?.mcpServers ?? {};\n const hasEntry = Object.keys(servers).some((k) => k === \"semantic-vault\" || k === \"semantic-sidekick\" || k === \"semantic-memory\");\n if (!hasEntry) {\n return {\n check: \"mcp_json_entry\",\n severity: \"warn\",\n summary: \".mcp.json present but no semantic-memory/sidekick/vault server entry\",\n fixable_via: \"mcp-reconcile\",\n };\n }\n return { check: \"mcp_json_entry\", severity: \"ok\", summary: \".mcp.json server entry present\" };\n}\n\nasync function checkHookRegistration(projectRoot: string): Promise<DriftFinding | null> {\n const settings = join(projectRoot, \".claude\", \"settings.json\");\n if (!existsSync(settings)) return null;\n try {\n const data = JSON.parse(await readFile(settings, \"utf-8\"));\n const hooks = data?.hooks ?? {};\n const required = [\"SessionStart\", \"UserPromptSubmit\", \"Stop\"];\n const missing: string[] = [];\n for (const event of required) {\n const arr = hooks[event];\n if (!Array.isArray(arr) || arr.length === 0) missing.push(event);\n }\n if (missing.length > 0) {\n return {\n check: \"hook_registration\",\n severity: \"warn\",\n summary: `missing hook registrations: ${missing.join(\", \")}`,\n detail: `Run \\`/relink\\` or reinstall the plugin to refresh hook registrations.`,\n };\n }\n return { check: \"hook_registration\", severity: \"ok\", summary: \"all 3 hook events registered\" };\n } catch (err: any) {\n return failOpenWarn(err);\n }\n}\n\nasync function checkIndexFreshness(projectRoot: string, vaultPath?: string): Promise<DriftFinding | null> {\n const vault = vaultPath ?? defaultVaultPath(projectRoot);\n if (!vault || !existsSync(vault)) return null;\n const indexDir = join(vault, \".semantic-sidekick-index\");\n const meta = join(indexDir, \"meta.json\");\n if (!existsSync(meta)) {\n return {\n check: \"index_freshness\",\n severity: \"warn\",\n summary: \"no index built yet\",\n detail: \"Run `reindex` to build the vector index.\",\n fixable_via: \"reindex\",\n };\n }\n try {\n const data = JSON.parse(await readFile(meta, \"utf-8\"));\n if (typeof data.indexedAt === \"string\") {\n return { check: \"index_freshness\", severity: \"ok\", summary: `last reindex ${data.indexedAt}` };\n }\n } catch {\n // ignore\n }\n return null;\n}\n\nasync function checkSessionStaleness(projectRoot: string): Promise<DriftFinding | null> {\n const sessionPath = join(projectRoot, \".claude\", \".semantic-memory\", \"session.json\");\n if (!existsSync(sessionPath)) return null;\n try {\n const state = JSON.parse(await readFile(sessionPath, \"utf-8\"));\n if (state?.closed_at) return null;\n const last = state?.last_activity_at;\n if (typeof last !== \"string\") return null;\n const ageMs = Date.now() - new Date(last).getTime();\n if (ageMs > 24 * 60 * 60 * 1000) {\n return {\n check: \"session_staleness\",\n severity: \"warn\",\n summary: `session ${state.id} stale (>${Math.round(ageMs / (60 * 60 * 1000))}h since activity)`,\n detail: `Either resume the session (run session_run / session_finish) or remove .claude/.semantic-memory/session.json to abandon it.`,\n };\n }\n return { check: \"session_staleness\", severity: \"ok\", summary: `session ${state.id} active` };\n } catch {\n return null;\n }\n}\n\nasync function checkLegacyStateFiles(projectRoot: string): Promise<DriftFinding | null> {\n // Detect v1.1-and-earlier state files at .claude/.sidekick-*. v1.2 moved these to\n // .claude/.semantic-memory/. The hook still reads them via fallback for backwards-\n // compat, but they should be migrated explicitly so the legacy paths stop existing.\n const legacy = [\n \".sidekick-mode\",\n \".sidekick-fingerprints.json\",\n \".sidekick-capture-pending.json\",\n ];\n const present: string[] = [];\n for (const name of legacy) {\n if (existsSync(join(projectRoot, \".claude\", name))) {\n present.push(name);\n }\n }\n if (present.length === 0) {\n return { check: \"legacy_state_files\", severity: \"ok\", summary: \"no legacy state files\" };\n }\n return {\n check: \"legacy_state_files\",\n severity: \"warn\",\n summary: `${present.length} legacy state file${present.length === 1 ? \"\" : \"s\"} at .claude/.sidekick-*`,\n detail:\n `Files: ${present.join(\", \")}. Run \\`bin/semantic-memory migrate-state\\` to move them under .claude/.semantic-memory/. ` +\n `These files are still being read for backwards-compat through v1.x; the legacy fallback is removed in v2.0.`,\n fixable_via: \"none\",\n };\n}\n\nasync function checkAgentsContract(projectRoot: string): Promise<DriftFinding | null> {\n const path = join(projectRoot, \"AGENTS.md\");\n if (!existsSync(path)) return null; // optional artifact\n try {\n const raw = await readFile(path, \"utf-8\");\n const begin = raw.indexOf(\"<!-- semantic-memory:begin contract -->\");\n const end = raw.indexOf(\"<!-- semantic-memory:end contract -->\");\n if (begin === -1 || end === -1) {\n return {\n check: \"agents_contract\",\n severity: \"warn\",\n summary: \"AGENTS.md exists but lacks managed-block markers\",\n detail: \"Either move custom content to a Local Notes section, or delete AGENTS.md and run `regenerate_contract` to bootstrap.\",\n fixable_via: \"regenerate-contract\",\n };\n }\n return { check: \"agents_contract\", severity: \"ok\", summary: \"AGENTS.md managed block intact\" };\n } catch {\n return null;\n }\n}\n\nasync function skillManifestChecks(projectRoot: string): Promise<Promise<DriftFinding | null>[]> {\n const home = homedir();\n const targets = [\n { agent: \"claude\", path: join(home, \".claude\", \"skills\") },\n { agent: \"codex\", path: join(home, \".codex\", \"skills\") },\n { agent: \"copilot\", path: join(home, \".copilot\", \"skills\") },\n { agent: \"pi\", path: join(home, \".pi\", \"agent\", \"skills\") },\n { agent: \"claude-local\", path: join(projectRoot, \".claude\", \"skills\") },\n ];\n const out: Promise<DriftFinding | null>[] = [];\n for (const t of targets) {\n out.push(checkOneSkillManifest(t.agent, t.path));\n }\n return out;\n}\n\nasync function checkOneSkillManifest(agentLabel: string, skillsRoot: string): Promise<DriftFinding | null> {\n const manifestPath = join(skillsRoot, \".semantic-memory-skill-manifest.json\");\n if (!existsSync(manifestPath)) return null; // not installed for this agent — fine\n try {\n const raw = await readFile(manifestPath, \"utf-8\");\n const manifest = JSON.parse(raw);\n if (typeof manifest?.installed_at !== \"string\") return null;\n const age = Date.now() - new Date(manifest.installed_at).getTime();\n // Manifest itself is the source of truth; deeper sha-recheck happens via slow tier\n return {\n check: `skill_manifest:${agentLabel}`,\n severity: \"ok\",\n summary: `${agentLabel} skills installed v${manifest.version} (${Math.round(age / (24 * 60 * 60 * 1000))}d ago)`,\n };\n } catch {\n return null;\n }\n}\n\nfunction defaultVaultPath(projectRoot: string): string {\n return join(projectRoot, \".claude\", \".vault\");\n}\n\n/**\n * Format a drift summary for the SessionStart hook. Returns the text to inject as\n * additionalContext, or an empty string when there's nothing to surface.\n */\nexport function formatDriftBanner(result: HealthcheckResult): string {\n const drift = filterToDrift(result);\n if (drift.length === 0) return \"\";\n const lines = [\n `<vault-drift count=\"${drift.length}\">`,\n `⚠️ semantic-memory: ${drift.length} drift issue${drift.length === 1 ? \"\" : \"s\"} detected`,\n ];\n for (const f of drift.slice(0, 5)) {\n const sev = f.severity === \"error\" ? \"✗\" : \"⚠\";\n lines.push(` ${sev} ${f.check}: ${f.summary}`);\n }\n if (drift.length > 5) lines.push(` …and ${drift.length - 5} more`);\n lines.push(`Run /healthcheck for details, or /healthcheck --fix to auto-fix safe items.`);\n lines.push(`</vault-drift>`);\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":[]}
|
|
File without changes
|