@theglitchking/semantic-memory 1.0.1 → 1.1.1

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.
@@ -5,14 +5,14 @@
5
5
  "email": "theglitchking@users.noreply.github.com"
6
6
  },
7
7
  "metadata": {
8
- "description": "Single-plugin marketplace for semantic-memory — the unified memory-layer plugin (rebrand of semantic-sidekick at v1.0.0). One MCP server, multiple typed corpora, unified cross-corpus knowledge graph, conditional tool registration, drift detection, and human↔code translation.",
9
- "version": "1.0.1"
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.1.1"
10
10
  },
11
11
  "plugins": [
12
12
  {
13
13
  "name": "semantic-memory",
14
- "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 based on active corpora, in-process drift detection, and human↔code translation. Rebrand of semantic-sidekick at v1.0.0; existing semantic-sidekick plugins continue to work under the legacy name during the transition period.",
15
- "version": "1.0.1",
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.1.1",
16
16
  "author": {
17
17
  "name": "TheGlitchKing"
18
18
  },
package/CHANGELOG.md CHANGED
@@ -2,6 +2,100 @@
2
2
 
3
3
  All notable changes to semantic-memory (formerly semantic-sidekick) will be documented here.
4
4
 
5
+ ## [1.1.1] - 2026-05-08 — Hotfix: complete the rebrand wiring
6
+
7
+ v1.1.0 published with the rebrand-to-`semantic-memory` half-finished: the npm package, marketplace entry, and tool surface all moved to the new name, but the postinstall, SessionStart reconcile, slash commands, and CLI helpers were still hardcoded to the legacy `@theglitchking/semantic-sidekick` package path. Fresh `npm install @theglitchking/semantic-memory@1.1.0` installs failed to register hooks correctly and slash commands hit ancient code from the legacy npm package (still on the registry at 0.2.x).
8
+
9
+ ### Fixed
10
+
11
+ - **Postinstall (`scripts/link-skills.js`)** — `runPostinstall` now passes `packageName: "@theglitchking/semantic-memory"`, `pluginName: "semantic-memory"`, and `hookCommand` pointing at the new node_modules path. Hooks now register correctly on fresh installs.
12
+ - **SessionStart reconcile (`hooks/reconcile.js`)** — `findLocalBin` checks the rebranded package path first, falls back to the legacy path for machines mid-migration. `isOurEntry` recognizes both old and new shapes when removing stale `.mcp.json` entries.
13
+ - **SessionStart hook (`hooks/session-start.js`)** — `runSessionStart` delegate now passes the new package name + plugin name + config file.
14
+ - **vault-context hook (`hooks/vault-context.js`)** — vault path discovery now matches `semantic-memory` server entries in `.mcp.json` (was only matching `semantic-vault` and `semantic-sidekick`). CLI bin discovery prefers the new path with legacy fallback.
15
+ - **Slash commands** — all 7 (`/healthcheck`, `/status`, `/normalize-config`, `/policy`, `/mode`, `/update`, `/relink`) now invoke `@theglitchking/semantic-memory` instead of the legacy package.
16
+ - **CLI helpers (`src/cli/index.ts`)** — `PKG_NAME`, `findLocalBin`, `runRelink`, `isLocalForm` updated. Error messages reference the new path. `registerUpdateCommands` passes the new plugin name + config file.
17
+
18
+ ### Storage paths preserved (unchanged)
19
+
20
+ - `<vault>/.semantic-sidekick-index/` — vector index location, kept for backwards-compat with existing v1.0.x indices
21
+ - `~/.semantic-sidekick/models/` — model cache location, kept for backwards-compat
22
+
23
+ These intentionally retain the legacy `semantic-sidekick` name in the path — renaming would invalidate every existing user's index and force a re-download of the embedding model. The path-name mismatch with the package name is a one-time cost; we eat it.
24
+
25
+ ### Backwards compatibility
26
+
27
+ All legacy fallbacks preserved. Users with `@theglitchking/semantic-sidekick` still installed (from v0.x or v1.0 pre-rebrand) continue to work — `findLocalBin`, `runRelink`, hook discovery, and `isOurEntry` all check both paths. The DOCS_KEY entry name in `.mcp.json` stays as `"semantic-sidekick"` for existing-user compatibility.
28
+
29
+ ### How v1.1.0 slipped through
30
+
31
+ Phase 9's automated validation ran tests against `createServer()` directly with in-memory MCP clients — it never simulated a fresh `npm install` from the published tarball. The bug only fires at install time and slash-command invocation time, both of which were on the manual-smoke-deferred list. The hotfix re-emphasizes that "manual smoke against an existing v1.0.x install" needs to happen BEFORE publish, not after.
32
+
33
+ ## [1.1.0] - 2026-05-08 — brain-absorption
34
+
35
+ Adapts four targeted strengths from `JimmyMcBride/brain` into semantic-memory: AGENTS.md contract artifact, hard-gated verification sessions, multi-agent skill bundler, distill/synthesize unification — plus a server.ts refactor that makes future additions cheap, a deprecation-shimmed tool consolidation, and an opt-in SessionStart drift-detection layer.
36
+
37
+ ### Added
38
+
39
+ - **AGENTS.md contract artifact** at the project root with managed-block markers (`<!-- semantic-memory:begin contract -->`...`<!-- end -->`). Frontmatter records `generated_by`, `version`, `last_generated`. The block lists Active Modes, Required Workflow, Tool Surface (live + deprecated), and Memory Policy. Local Notes tail outside the markers is preserved verbatim across regenerations. New tools: `regenerate_contract`, `inspect_contract`. New slash command: `/contract`.
40
+ - **Hard-gated verification sessions.** New module `src/core/session.ts` owns `<project>/.claude/.semantic-memory/session.json`. New MCP tools: `session_start`, `session_run`, `session_finish`, `session_status`. `session_finish` refuses without recorded verifications unless `verified: false` AND a non-empty `reason` is provided. State is opt-in; sessions auto-record `notes_touched` from `apply_patch` / `synthesize_note` / `synthesize_promote` operations.
41
+ - **Distill ↔ synthesize_note unification.** `synthesize_note` gains `proposal: boolean` (writes to `proposals/<date>-<slug>.md` with `status: proposal`), `proposal_subdir` (override), and `from_session: boolean` (auto-pulls task context, verifications, and touched notes from the active session). New tool `synthesize_promote` atomically moves a reviewed proposal to its canonical destination.
42
+ - **Multi-agent skill bundler.** New CLI subcommand tree `bin/semantic-memory skills {targets,install,uninstall,list}` for codex, copilot, and pi. Each install writes a `.semantic-memory-skill-manifest.json` with sha256s for stale/drift detection. Non-destructive by default; `--force` overrides. Existing Claude flow via `claude-plugin-runtime` postinstall is unchanged.
43
+ - **SessionStart drift detection.** Fast-tier checks (file-system probes only) for `.mcp.json` server entry, hook registration, AGENTS.md managed-block presence, and session staleness. Inline JS in `hooks/vault-context.js` — no spawn cost. Healthy installs see nothing; drifted installs see a single `<vault-drift>` block at session start with a pointer to `/healthcheck`.
44
+ - **Manual `/healthcheck` deep audit.** New module `src/core/healthcheck.ts` provides fast and slow tiers (slow includes full `lint_vault`). 5-minute result cache. CLI command `semantic-memory healthcheck` accepts `--fast` and `--json`. The `--fix` flag is reserved for v1.2.
45
+
46
+ ### Changed
47
+
48
+ - `src/mcp/server.ts` (1039 lines, 33 tools in one file) split into per-domain modules under `src/mcp/tools/*.ts` with a shared `src/mcp/context.ts`. server.ts shrinks to 96 lines (composition root + lifecycle). **Tool surface unchanged** — every tool name, schema, and handler is preserved bit-for-bit, gated by the existing regression snapshot suite.
49
+ - `lint_vault` accepts a new optional `checks: ("schema"|"provenance"|"stale"|"broken_links")[]` filter. Omit for the existing full report. Additive schema change.
50
+ - `manage_tags` `action` enum widens to include `"rename"`, with new optional `from` / `to` fields. `action='rename'` calls the existing `tagManager.renameVaultWide`. Additive schema change.
51
+ - `.claude/CLAUDE.md` now opens with a pointer to `AGENTS.md` as the primary contract; CLAUDE.md remains for repo-specific augmentation.
52
+
53
+ ### Deprecated (callable through v1.x via shim, removed in v2.0.0)
54
+
55
+ - `find_schema_violations` → use `lint_vault({checks: ["schema"]})`
56
+ - `find_missing_provenance` → use `lint_vault({checks: ["provenance"]})`
57
+ - `find_stale` → use `lint_vault({checks: ["stale"]})`
58
+ - `find_broken_links` → use `lint_vault({checks: ["broken_links"]})`
59
+ - `read_multiple_notes` → call `read_note` in a loop or via batched MCP calls
60
+ - `rename_tag` → use `manage_tags({action: "rename", from, to})`
61
+
62
+ All shims emit `[DEPRECATED — removed in v2.0.0; use ...]` prefix in their tool descriptions, visible to agents in the MCP tool list.
63
+
64
+ ### Tool-surface delta
65
+
66
+ ```
67
+ v1.0.1 → v1.1.0
68
+ +regenerate_contract +inspect_contract (Phase 3)
69
+ +synthesize_promote (Phase 4)
70
+ +session_start +session_run +session_finish
71
+ +session_status (Phase 5)
72
+ = 33 → 40 tools (write mode)
73
+ = 21 → 21 tools (read-only mode, unchanged)
74
+ ```
75
+
76
+ ### Backwards-compatibility contract
77
+
78
+ Four statements that remain true after v1.1.0 ships:
79
+
80
+ 1. **Every existing MCP tool name remains callable.** Eliminated tools become deprecation shims that delegate to their survivors. No automation needs to change in v1.x.
81
+ 2. **Hook output is unchanged when no session is active and no drift exists.** The Stop hook only adds new prompts when a session is open; the SessionStart hook only adds drift output when actual drift is detected.
82
+ 3. **AGENTS.md generation is opt-in.** Existing repos see no new files until they call `regenerate_contract` or run `/contract`.
83
+ 4. **Drift detection is silent on healthy installs.** Auto-check at SessionStart adds <100ms latency and emits zero output when no drift is present.
84
+
85
+ Storage layout (`.semantic-sidekick-index/`, `~/.semantic-sidekick/models/`) preserved per the v1.0 promise. No re-index, no model re-download required when upgrading from v1.0.x.
86
+
87
+ ### Tests
88
+
89
+ Suite expanded from 211 (v1.0.1) to 277 tests across 31 files. New coverage:
90
+ - `test/unit/agents-contract.test.ts` (7 tests)
91
+ - Extended `test/unit/synthesize.test.ts` (6 new tests for proposal mode)
92
+ - `test/unit/session.test.ts` (24 tests)
93
+ - `test/phase5/stop-hook-session.test.ts` (5 tests)
94
+ - `test/unit/skills-bundler.test.ts` (12 tests)
95
+ - `test/unit/healthcheck.test.ts` (12 tests)
96
+
97
+ All pre-existing tests pass unchanged. Snapshot diffs are verified additive-only across all phases.
98
+
5
99
  ## [1.0.1] - 2026-05-08 — Republish (no functional changes)
6
100
 
7
101
  ### Republish only
@@ -0,0 +1,14 @@
1
+ ---
2
+ description: Generate or refresh AGENTS.md at the project root — the versionable agent contract. Preserves Local Notes across regenerations.
3
+ allowed-tools: mcp__semantic-vault__regenerate_contract, mcp__semantic-vault__inspect_contract
4
+ ---
5
+
6
+ When the user runs `/contract`, dispatch by argument:
7
+
8
+ - `/contract` (no arg) or `/contract refresh` — call `regenerate_contract` to write or refresh `AGENTS.md` at the project root. Report whether the file was written, whether hand-edits were detected, and how many characters of Local Notes were preserved.
9
+ - `/contract inspect` — call `inspect_contract` (read-only) and report whether `AGENTS.md` exists, whether managed-block markers are present, and the size of the Local Notes tail.
10
+ - `/contract force` — call `regenerate_contract` with `force=true` to overwrite hand-edited managed-block content. Use only after the user has confirmed they accept losing in-block hand-edits.
11
+
12
+ If the call returns `hand_edit_detected: true`, do NOT pass `force=true` automatically — surface the warning to the user and let them decide whether to move their changes to the Local Notes section or overwrite.
13
+
14
+ `AGENTS.md` is the canonical agent contract for this project. The managed block is regenerable from the current tool surface and active modes; the Local Notes section is user-owned and preserved verbatim across regenerations.
@@ -1,6 +1,24 @@
1
1
  ---
2
- description: Verify the local semantic-sidekick install starts cleanly; self-heals npx-cache ERR_MODULE_NOT_FOUND
3
- allowed-tools: Bash(npx:*)
2
+ description: Verify the local install + run drift detection. Self-heals npx-cache ERR_MODULE_NOT_FOUND. Pass --fast to skip the slow tier (full vault lint).
3
+ allowed-tools: Bash(npx:*), Bash(node:*)
4
4
  ---
5
5
 
6
- Run `npx --no @theglitchking/semantic-sidekick healthcheck` and relay the output. If it reports a fragile `.mcp.json` form, suggest running `/semantic-sidekick:normalize-config`.
6
+ When the user runs `/healthcheck`:
7
+
8
+ - `/healthcheck` (no arg) — Run the local install verification AND the full drift check (fast tier + slow tier). Report the results: install status, drift findings (if any), and remediation pointers (e.g. `/relink`, `/normalize-config`, `regenerate_contract`).
9
+ - `/healthcheck --fast` — Run the install verification + ONLY the fast-tier drift checks. Skips the full vault lint (slow tier). Use when the agent only wants quick install/manifest checks without the multi-second vault scan.
10
+
11
+ The fast-tier auto-check also runs on every `SessionStart` via `vault-context.js` — healthy installs see nothing at session start, drifted installs see a one-block warning with a pointer to this command.
12
+
13
+ Implementation: shell out to `node ./node_modules/@theglitchking/semantic-memory/bin/semantic-memory healthcheck [--fast]` (or `npx --no @theglitchking/semantic-memory healthcheck [--fast]` if no local install). Falls back to the legacy `@theglitchking/semantic-sidekick` package on machines mid-migration.
14
+
15
+ Drift findings are categorized by check:
16
+ - `mcp_json_entry` — `.mcp.json` missing the semantic-memory server entry. Fix: `/normalize-config`.
17
+ - `hook_registration` — `.claude/settings.json` missing one of SessionStart/UserPromptSubmit/Stop. Fix: `/relink` or reinstall.
18
+ - `agents_contract` — `AGENTS.md` exists but lacks managed-block markers. Fix: move custom content to a `Local Notes` section, or delete and re-run `regenerate_contract`.
19
+ - `session_staleness` — A session has been open >24h with no activity. Fix: resume (run `session_run` / `session_finish`) or remove the stale session file.
20
+ - `index_freshness` — No vector index built yet. Fix: call `reindex`.
21
+ - `skill_manifest:*` — Per-agent skill install state. Informational unless missing.
22
+ - `lint_vault` (slow tier only) — Vault lint findings. Many require human review (stale notes, broken wikilinks).
23
+
24
+ `--fix` flag is reserved for v1.2 — auto-applying safe fixes (re-link skills, reconcile `.mcp.json`, regenerate contract) is the planned v1.2 work. For v1.1, drift detection is read-only and points the user at the right manual command.
package/commands/mode.md CHANGED
@@ -1,12 +1,12 @@
1
1
  ---
2
- description: Set or query the active semantic-sidekick mode (vault-first | research | outage-silence)
2
+ description: Set or query the active semantic-memory mode (vault-first | research | outage-silence)
3
3
  argument-hint: "[research|vault-first|outage-silence|status]"
4
4
  allowed-tools: Bash(cat:*), Bash(cp:*), Bash(test:*), Bash(node:*), Read, Write
5
5
  ---
6
6
 
7
- # /mode — semantic-sidekick mode control
7
+ # /mode — semantic-memory mode control
8
8
 
9
- The user invoked `/mode $ARGUMENTS`. This is the explicit ground-truth override for the semantic-sidekick routing layer.
9
+ The user invoked `/mode $ARGUMENTS`. This is the explicit ground-truth override for the semantic-memory routing layer.
10
10
 
11
11
  ## Your job
12
12
 
@@ -3,4 +3,4 @@ description: Rewrite fragile npx-@latest entries in .mcp.json to the stable node
3
3
  allowed-tools: Bash(npx:*)
4
4
  ---
5
5
 
6
- Run `npx --no @theglitchking/semantic-sidekick normalize-config` and report the result to the user. Mention that `.mcp.json.bak` was created as a safety backup, and that the MCPs need to be toggled in `/mcp` to pick up the change.
6
+ Run `npx --no @theglitchking/semantic-memory normalize-config` and report the result to the user. Mention that `.mcp.json.bak` was created as a safety backup, and that the MCPs need to be toggled in `/mcp` to pick up the change.
@@ -1,16 +1,16 @@
1
1
  ---
2
- description: Get or set the semantic-sidekick update policy (auto | nudge | off)
2
+ description: Get or set the semantic-memory update policy (auto | nudge | off)
3
3
  allowed-tools: Bash(npx:*)
4
4
  argument-hint: "[auto|nudge|off]"
5
5
  ---
6
6
 
7
7
  Arguments: $ARGUMENTS
8
8
 
9
- - If `$ARGUMENTS` is empty, run `npx --no @theglitchking/semantic-sidekick policy` and report the current policy and config path.
10
- - If `$ARGUMENTS` is one of `auto`, `nudge`, `off`, run `npx --no @theglitchking/semantic-sidekick policy $ARGUMENTS` and confirm the new setting to the user.
9
+ - If `$ARGUMENTS` is empty, run `npx --no @theglitchking/semantic-memory policy` and report the current policy and config path.
10
+ - If `$ARGUMENTS` is one of `auto`, `nudge`, `off`, run `npx --no @theglitchking/semantic-memory policy $ARGUMENTS` and confirm the new setting to the user.
11
11
  - If `$ARGUMENTS` is anything else, tell the user the valid values are `auto`, `nudge`, `off`.
12
12
 
13
13
  Policies:
14
- - `auto` — auto-run `npm update @theglitchking/semantic-sidekick` at session start when a newer version is available.
14
+ - `auto` — auto-run `npm update @theglitchking/semantic-memory` at session start when a newer version is available.
15
15
  - `nudge` — print a one-liner when a newer version is available (default).
16
16
  - `off` — do not check for updates.
@@ -3,4 +3,4 @@ description: Re-run the skill linker — refresh symlinks from node_modules into
3
3
  allowed-tools: Bash(npx:*)
4
4
  ---
5
5
 
6
- Run `npx --no @theglitchking/semantic-sidekick relink` and report the result.
6
+ Run `npx --no @theglitchking/semantic-memory relink` and report the result.
@@ -3,4 +3,4 @@ description: Show installed version, latest available version, current update po
3
3
  allowed-tools: Bash(npx:*)
4
4
  ---
5
5
 
6
- Run `npx --no @theglitchking/semantic-sidekick status` and relay the output to the user verbatim.
6
+ Run `npx --no @theglitchking/semantic-memory status` and relay the output to the user verbatim.
@@ -1,6 +1,6 @@
1
1
  ---
2
- description: Update semantic-sidekick to the latest version (runs npm update + re-links skills)
2
+ description: Update semantic-memory to the latest version (runs npm update + re-links skills)
3
3
  allowed-tools: Bash(npx:*)
4
4
  ---
5
5
 
6
- Run `npx --no @theglitchking/semantic-sidekick 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-sidekick update` and note that in your summary.
6
+ Run `npx --no @theglitchking/semantic-memory 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-memory update` and note that in your summary.
@@ -0,0 +1,273 @@
1
+ import {
2
+ lintVault
3
+ } from "./chunk-CAN2GWUG.js";
4
+
5
+ // src/core/healthcheck.ts
6
+ import { readFile, writeFile, mkdir } from "fs/promises";
7
+ import { existsSync } from "fs";
8
+ import { join } from "path";
9
+ import { homedir } from "os";
10
+ var CACHE_TTL_MS = 5 * 60 * 1e3;
11
+ async function runHealthcheck(opts) {
12
+ const start = opts.nowMs ?? Date.now();
13
+ const tier = opts.tier ?? "fast";
14
+ const cachePath = opts.cachePath ?? join(opts.projectRoot, ".claude", ".semantic-memory", "healthcheck-cache.json");
15
+ if (!opts.force && existsSync(cachePath)) {
16
+ try {
17
+ const raw = await readFile(cachePath, "utf-8");
18
+ const cached = JSON.parse(raw);
19
+ if (cached.tier_run === tier && start - cached.ran_at_ms < CACHE_TTL_MS) {
20
+ return { ...cached, cached: true };
21
+ }
22
+ } catch {
23
+ }
24
+ }
25
+ const findings = tier === "fast" ? await runFastTier(opts) : tier === "slow" ? await runSlowTier(opts) : [...await runFastTier(opts), ...await runSlowTier(opts)];
26
+ const counts = countBySeverity(findings);
27
+ const ranAt = new Date(start).toISOString();
28
+ const result = {
29
+ tier_run: tier,
30
+ ran_at: ranAt,
31
+ duration_ms: Date.now() - start,
32
+ findings,
33
+ counts
34
+ };
35
+ try {
36
+ await mkdir(join(opts.projectRoot, ".claude", ".semantic-memory"), { recursive: true });
37
+ await writeFile(cachePath, JSON.stringify({ ...result, ran_at_ms: start }, null, 2), "utf-8");
38
+ } catch {
39
+ }
40
+ return result;
41
+ }
42
+ function filterToDrift(result) {
43
+ return result.findings.filter((f) => f.severity !== "ok");
44
+ }
45
+ function countBySeverity(findings) {
46
+ const counts = { ok: 0, warn: 0, error: 0 };
47
+ for (const f of findings) counts[f.severity]++;
48
+ return counts;
49
+ }
50
+ async function runFastTier(opts) {
51
+ const checks = [
52
+ checkPluginVersion(opts.projectRoot),
53
+ checkMcpJsonEntry(opts.projectRoot),
54
+ checkHookRegistration(opts.projectRoot),
55
+ checkIndexFreshness(opts.projectRoot, opts.vaultPath),
56
+ checkSessionStaleness(opts.projectRoot),
57
+ checkAgentsContract(opts.projectRoot),
58
+ ...await skillManifestChecks(opts.projectRoot)
59
+ ];
60
+ const results = await Promise.all(checks.map((p) => p.catch((err) => failOpenWarn(err))));
61
+ return results.filter((r) => r !== null);
62
+ }
63
+ async function runSlowTier(opts) {
64
+ const findings = [];
65
+ if (opts.vaultPath && existsSync(opts.vaultPath)) {
66
+ try {
67
+ const report = await lintVault(opts.vaultPath);
68
+ const total = report.findings.length;
69
+ if (total === 0) {
70
+ findings.push({ check: "lint_vault", severity: "ok", summary: `vault lint clean (${report.counts.filesLinted} files)` });
71
+ } else {
72
+ findings.push({
73
+ check: "lint_vault",
74
+ severity: report.counts.errors > 0 ? "error" : "warn",
75
+ summary: `vault lint: ${total} findings (errors=${report.counts.errors}, warnings=${report.counts.warnings})`,
76
+ detail: `Run \`lint_vault\` for the full report. Many lint findings need human review (stale notes, broken wikilinks).`
77
+ });
78
+ }
79
+ } catch (err) {
80
+ findings.push({ check: "lint_vault", severity: "warn", summary: `lint_vault threw: ${err?.message ?? err}` });
81
+ }
82
+ }
83
+ return findings;
84
+ }
85
+ function failOpenWarn(err) {
86
+ return {
87
+ check: "internal",
88
+ severity: "warn",
89
+ summary: `healthcheck internal error: ${err instanceof Error ? err.message : String(err)}`
90
+ };
91
+ }
92
+ async function checkPluginVersion(projectRoot) {
93
+ const pkgPaths = [
94
+ join(projectRoot, "node_modules", "@theglitchking", "semantic-memory", "package.json"),
95
+ join(projectRoot, "node_modules", "@theglitchking", "semantic-sidekick", "package.json")
96
+ ];
97
+ for (const p of pkgPaths) {
98
+ if (!existsSync(p)) continue;
99
+ try {
100
+ const installed = JSON.parse(await readFile(p, "utf-8"));
101
+ if (typeof installed?.version === "string") {
102
+ return { check: "plugin_version", severity: "ok", summary: `installed=${installed.version}` };
103
+ }
104
+ } catch {
105
+ }
106
+ }
107
+ return null;
108
+ }
109
+ async function checkMcpJsonEntry(projectRoot) {
110
+ const mcpPath = join(projectRoot, ".mcp.json");
111
+ if (!existsSync(mcpPath)) return null;
112
+ const data = JSON.parse(await readFile(mcpPath, "utf-8"));
113
+ const servers = data?.mcpServers ?? {};
114
+ const hasEntry = Object.keys(servers).some((k) => k === "semantic-vault" || k === "semantic-sidekick" || k === "semantic-memory");
115
+ if (!hasEntry) {
116
+ return {
117
+ check: "mcp_json_entry",
118
+ severity: "warn",
119
+ summary: ".mcp.json present but no semantic-memory/sidekick/vault server entry",
120
+ fixable_via: "mcp-reconcile"
121
+ };
122
+ }
123
+ return { check: "mcp_json_entry", severity: "ok", summary: ".mcp.json server entry present" };
124
+ }
125
+ async function checkHookRegistration(projectRoot) {
126
+ const settings = join(projectRoot, ".claude", "settings.json");
127
+ if (!existsSync(settings)) return null;
128
+ try {
129
+ const data = JSON.parse(await readFile(settings, "utf-8"));
130
+ const hooks = data?.hooks ?? {};
131
+ const required = ["SessionStart", "UserPromptSubmit", "Stop"];
132
+ const missing = [];
133
+ for (const event of required) {
134
+ const arr = hooks[event];
135
+ if (!Array.isArray(arr) || arr.length === 0) missing.push(event);
136
+ }
137
+ if (missing.length > 0) {
138
+ return {
139
+ check: "hook_registration",
140
+ severity: "warn",
141
+ summary: `missing hook registrations: ${missing.join(", ")}`,
142
+ detail: `Run \`/relink\` or reinstall the plugin to refresh hook registrations.`
143
+ };
144
+ }
145
+ return { check: "hook_registration", severity: "ok", summary: "all 3 hook events registered" };
146
+ } catch (err) {
147
+ return failOpenWarn(err);
148
+ }
149
+ }
150
+ async function checkIndexFreshness(projectRoot, vaultPath) {
151
+ const vault = vaultPath ?? defaultVaultPath(projectRoot);
152
+ if (!vault || !existsSync(vault)) return null;
153
+ const indexDir = join(vault, ".semantic-sidekick-index");
154
+ const meta = join(indexDir, "meta.json");
155
+ if (!existsSync(meta)) {
156
+ return {
157
+ check: "index_freshness",
158
+ severity: "warn",
159
+ summary: "no index built yet",
160
+ detail: "Run `reindex` to build the vector index.",
161
+ fixable_via: "reindex"
162
+ };
163
+ }
164
+ try {
165
+ const data = JSON.parse(await readFile(meta, "utf-8"));
166
+ if (typeof data.indexedAt === "string") {
167
+ return { check: "index_freshness", severity: "ok", summary: `last reindex ${data.indexedAt}` };
168
+ }
169
+ } catch {
170
+ }
171
+ return null;
172
+ }
173
+ async function checkSessionStaleness(projectRoot) {
174
+ const sessionPath = join(projectRoot, ".claude", ".semantic-memory", "session.json");
175
+ if (!existsSync(sessionPath)) return null;
176
+ try {
177
+ const state = JSON.parse(await readFile(sessionPath, "utf-8"));
178
+ if (state?.closed_at) return null;
179
+ const last = state?.last_activity_at;
180
+ if (typeof last !== "string") return null;
181
+ const ageMs = Date.now() - new Date(last).getTime();
182
+ if (ageMs > 24 * 60 * 60 * 1e3) {
183
+ return {
184
+ check: "session_staleness",
185
+ severity: "warn",
186
+ summary: `session ${state.id} stale (>${Math.round(ageMs / (60 * 60 * 1e3))}h since activity)`,
187
+ detail: `Either resume the session (run session_run / session_finish) or remove .claude/.semantic-memory/session.json to abandon it.`
188
+ };
189
+ }
190
+ return { check: "session_staleness", severity: "ok", summary: `session ${state.id} active` };
191
+ } catch {
192
+ return null;
193
+ }
194
+ }
195
+ async function checkAgentsContract(projectRoot) {
196
+ const path = join(projectRoot, "AGENTS.md");
197
+ if (!existsSync(path)) return null;
198
+ try {
199
+ const raw = await readFile(path, "utf-8");
200
+ const begin = raw.indexOf("<!-- semantic-memory:begin contract -->");
201
+ const end = raw.indexOf("<!-- semantic-memory:end contract -->");
202
+ if (begin === -1 || end === -1) {
203
+ return {
204
+ check: "agents_contract",
205
+ severity: "warn",
206
+ summary: "AGENTS.md exists but lacks managed-block markers",
207
+ detail: "Either move custom content to a Local Notes section, or delete AGENTS.md and run `regenerate_contract` to bootstrap.",
208
+ fixable_via: "regenerate-contract"
209
+ };
210
+ }
211
+ return { check: "agents_contract", severity: "ok", summary: "AGENTS.md managed block intact" };
212
+ } catch {
213
+ return null;
214
+ }
215
+ }
216
+ async function skillManifestChecks(projectRoot) {
217
+ const home = homedir();
218
+ const targets = [
219
+ { agent: "claude", path: join(home, ".claude", "skills") },
220
+ { agent: "codex", path: join(home, ".codex", "skills") },
221
+ { agent: "copilot", path: join(home, ".copilot", "skills") },
222
+ { agent: "pi", path: join(home, ".pi", "agent", "skills") },
223
+ { agent: "claude-local", path: join(projectRoot, ".claude", "skills") }
224
+ ];
225
+ const out = [];
226
+ for (const t of targets) {
227
+ out.push(checkOneSkillManifest(t.agent, t.path));
228
+ }
229
+ return out;
230
+ }
231
+ async function checkOneSkillManifest(agentLabel, skillsRoot) {
232
+ const manifestPath = join(skillsRoot, ".semantic-memory-skill-manifest.json");
233
+ if (!existsSync(manifestPath)) return null;
234
+ try {
235
+ const raw = await readFile(manifestPath, "utf-8");
236
+ const manifest = JSON.parse(raw);
237
+ if (typeof manifest?.installed_at !== "string") return null;
238
+ const age = Date.now() - new Date(manifest.installed_at).getTime();
239
+ return {
240
+ check: `skill_manifest:${agentLabel}`,
241
+ severity: "ok",
242
+ summary: `${agentLabel} skills installed v${manifest.version} (${Math.round(age / (24 * 60 * 60 * 1e3))}d ago)`
243
+ };
244
+ } catch {
245
+ return null;
246
+ }
247
+ }
248
+ function defaultVaultPath(projectRoot) {
249
+ return join(projectRoot, ".claude", ".vault");
250
+ }
251
+ function formatDriftBanner(result) {
252
+ const drift = filterToDrift(result);
253
+ if (drift.length === 0) return "";
254
+ const lines = [
255
+ `<vault-drift count="${drift.length}">`,
256
+ `\u26A0\uFE0F semantic-memory: ${drift.length} drift issue${drift.length === 1 ? "" : "s"} detected`
257
+ ];
258
+ for (const f of drift.slice(0, 5)) {
259
+ const sev = f.severity === "error" ? "\u2717" : "\u26A0";
260
+ lines.push(` ${sev} ${f.check}: ${f.summary}`);
261
+ }
262
+ if (drift.length > 5) lines.push(` \u2026and ${drift.length - 5} more`);
263
+ lines.push(`Run /healthcheck for details, or /healthcheck --fix to auto-fix safe items.`);
264
+ lines.push(`</vault-drift>`);
265
+ return lines.join("\n");
266
+ }
267
+
268
+ export {
269
+ runHealthcheck,
270
+ filterToDrift,
271
+ formatDriftBanner
272
+ };
273
+ //# sourceMappingURL=chunk-CCSX3BHF.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 } 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 ...(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 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":[]}