claude-memory-explorer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/.claude-plugin/plugin.json +19 -0
  2. package/LICENSE +21 -0
  3. package/README.md +177 -0
  4. package/dist/cli/commands/dedupe.d.ts +1 -0
  5. package/dist/cli/commands/dedupe.js +187 -0
  6. package/dist/cli/commands/dedupe.js.map +1 -0
  7. package/dist/cli/commands/doctor.d.ts +1 -0
  8. package/dist/cli/commands/doctor.js +177 -0
  9. package/dist/cli/commands/doctor.js.map +1 -0
  10. package/dist/cli/commands/lint.d.ts +1 -0
  11. package/dist/cli/commands/lint.js +139 -0
  12. package/dist/cli/commands/lint.js.map +1 -0
  13. package/dist/cli/commands/list.d.ts +1 -0
  14. package/dist/cli/commands/list.js +97 -0
  15. package/dist/cli/commands/list.js.map +1 -0
  16. package/dist/cli/commands/mcp.d.ts +1 -0
  17. package/dist/cli/commands/mcp.js +105 -0
  18. package/dist/cli/commands/mcp.js.map +1 -0
  19. package/dist/cli/commands/merge.d.ts +1 -0
  20. package/dist/cli/commands/merge.js +111 -0
  21. package/dist/cli/commands/merge.js.map +1 -0
  22. package/dist/cli/commands/promote.d.ts +1 -0
  23. package/dist/cli/commands/promote.js +157 -0
  24. package/dist/cli/commands/promote.js.map +1 -0
  25. package/dist/cli/commands/tui.d.ts +1 -0
  26. package/dist/cli/commands/tui.js +60 -0
  27. package/dist/cli/commands/tui.js.map +1 -0
  28. package/dist/cli/commands/undo.d.ts +1 -0
  29. package/dist/cli/commands/undo.js +157 -0
  30. package/dist/cli/commands/undo.js.map +1 -0
  31. package/dist/cli/index.d.ts +2 -0
  32. package/dist/cli/index.js +85 -0
  33. package/dist/cli/index.js.map +1 -0
  34. package/dist/cli/tui/App.d.ts +8 -0
  35. package/dist/cli/tui/App.js +333 -0
  36. package/dist/cli/tui/App.js.map +1 -0
  37. package/dist/core/apply.d.ts +27 -0
  38. package/dist/core/apply.js +191 -0
  39. package/dist/core/apply.js.map +1 -0
  40. package/dist/core/claudeMd.d.ts +27 -0
  41. package/dist/core/claudeMd.js +103 -0
  42. package/dist/core/claudeMd.js.map +1 -0
  43. package/dist/core/dedupe.d.ts +78 -0
  44. package/dist/core/dedupe.js +212 -0
  45. package/dist/core/dedupe.js.map +1 -0
  46. package/dist/core/doctor.d.ts +35 -0
  47. package/dist/core/doctor.js +106 -0
  48. package/dist/core/doctor.js.map +1 -0
  49. package/dist/core/journal.d.ts +31 -0
  50. package/dist/core/journal.js +64 -0
  51. package/dist/core/journal.js.map +1 -0
  52. package/dist/core/lint.d.ts +26 -0
  53. package/dist/core/lint.js +254 -0
  54. package/dist/core/lint.js.map +1 -0
  55. package/dist/core/memoryIndex.d.ts +42 -0
  56. package/dist/core/memoryIndex.js +81 -0
  57. package/dist/core/memoryIndex.js.map +1 -0
  58. package/dist/core/merge.d.ts +19 -0
  59. package/dist/core/merge.js +58 -0
  60. package/dist/core/merge.js.map +1 -0
  61. package/dist/core/parse.d.ts +2 -0
  62. package/dist/core/parse.js +84 -0
  63. package/dist/core/parse.js.map +1 -0
  64. package/dist/core/plan.d.ts +34 -0
  65. package/dist/core/plan.js +85 -0
  66. package/dist/core/plan.js.map +1 -0
  67. package/dist/core/promote.d.ts +29 -0
  68. package/dist/core/promote.js +103 -0
  69. package/dist/core/promote.js.map +1 -0
  70. package/dist/core/scan.d.ts +16 -0
  71. package/dist/core/scan.js +81 -0
  72. package/dist/core/scan.js.map +1 -0
  73. package/dist/core/types.d.ts +36 -0
  74. package/dist/core/types.js +4 -0
  75. package/dist/core/types.js.map +1 -0
  76. package/dist/mcp/server.d.ts +13 -0
  77. package/dist/mcp/server.js +211 -0
  78. package/dist/mcp/server.js.map +1 -0
  79. package/package.json +71 -0
  80. package/skills/curate-memory/SKILL.md +60 -0
@@ -0,0 +1,85 @@
1
+ // Mutation primitives — pure data, no I/O.
2
+ //
3
+ // Every change `memex` makes to disk is expressed as a `Plan`: an ordered list
4
+ // of small `Operation`s (write, delete, ensure-dir). The plan is the only
5
+ // thing that gets serialized to JSON, journaled to ~/.claude/.memex/log/, and
6
+ // passed to `applyPlan` (the single writer).
7
+ //
8
+ // Why this shape:
9
+ // - Plans are reviewable before they execute — `--dry-run` prints the plan
10
+ // and asks for confirmation.
11
+ // - Plans are reversible — every op carries enough information to compute its
12
+ // inverse from current disk state, captured inside `applyPlan` at execution
13
+ // time (we don't compute inverses ahead of time because the on-disk state
14
+ // may not exist yet when the plan is built).
15
+ // - Plans are composable — the merge command in Phase 6 builds a multi-op
16
+ // plan; promote builds another; the user (or Claude via MCP) can chain them.
17
+ // Monotonic counter — guarantees journal filenames sort by creation order
18
+ // even when two plans land in the same millisecond. Without this, `memex
19
+ // undo` (which uses the lex-sorted filename to pick the "newest") can flip
20
+ // the wrong direction when apply + undo happen in quick succession.
21
+ let lastTimeMs = 0;
22
+ let counter = 0;
23
+ function shortId() {
24
+ const now = Date.now();
25
+ if (now === lastTimeMs) {
26
+ counter++;
27
+ }
28
+ else {
29
+ lastTimeMs = now;
30
+ counter = 0;
31
+ }
32
+ const iso = new Date(now).toISOString().replace(/[:.]/g, "-");
33
+ // 4-digit counter keeps the field width fixed so lex sort tracks time order.
34
+ const counterStr = counter.toString().padStart(4, "0");
35
+ const hex = Math.random().toString(16).slice(2, 8);
36
+ return `${iso}-${counterStr}-${hex}`;
37
+ }
38
+ export function planBuilder(label, description = "") {
39
+ const ops = [];
40
+ return {
41
+ write(path, content) {
42
+ ops.push({ kind: "write", path, content });
43
+ return this;
44
+ },
45
+ delete(path) {
46
+ ops.push({ kind: "delete", path });
47
+ return this;
48
+ },
49
+ ensureDir(path) {
50
+ ops.push({ kind: "ensure-dir", path });
51
+ return this;
52
+ },
53
+ build() {
54
+ return {
55
+ id: shortId(),
56
+ label,
57
+ description: description || label,
58
+ createdAt: new Date().toISOString(),
59
+ ops: [...ops],
60
+ };
61
+ },
62
+ };
63
+ }
64
+ /** Pretty-print a plan for `--dry-run` output. */
65
+ export function describePlan(plan) {
66
+ const lines = [
67
+ `Plan: ${plan.label}`,
68
+ ` id: ${plan.id}`,
69
+ ` description: ${plan.description}`,
70
+ ` ${plan.ops.length} op(s):`,
71
+ ];
72
+ for (const op of plan.ops) {
73
+ if (op.kind === "write") {
74
+ lines.push(` write ${op.path} (${Buffer.byteLength(op.content, "utf8")} bytes)`);
75
+ }
76
+ else if (op.kind === "delete") {
77
+ lines.push(` delete ${op.path}`);
78
+ }
79
+ else {
80
+ lines.push(` ensure-dir ${op.path}`);
81
+ }
82
+ }
83
+ return lines.join("\n");
84
+ }
85
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/core/plan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import { type Plan } from "./plan.js";
2
+ import type { DuplicateCluster } from "./dedupe.js";
3
+ import type { Memory } from "./types.js";
4
+ export interface PromotePlanOptions {
5
+ /** Override the destination path (testing). */
6
+ claudeMdPath?: string;
7
+ /** When true, MEMORY.md indexes are not modified. */
8
+ skipIndexUpdate?: boolean;
9
+ }
10
+ export interface PromoteSummary {
11
+ blockSlug: string;
12
+ claudeMdPath: string;
13
+ deletedIds: string[];
14
+ indexesUpdated: string[];
15
+ }
16
+ export interface PromotePlanResult {
17
+ plan: Plan;
18
+ summary: PromoteSummary;
19
+ }
20
+ /** Promote a single memory file. */
21
+ export declare function buildPromoteMemoryPlan(memory: Memory, opts?: PromotePlanOptions): PromotePlanResult;
22
+ /**
23
+ * Promote a whole duplicate cluster: write the representative's body into
24
+ * CLAUDE.md as one managed block, then delete EVERY cluster member (since the
25
+ * block now replaces all copies).
26
+ *
27
+ * Used when `memex dedupe` flags a cluster as `promotionCandidate`.
28
+ */
29
+ export declare function buildPromoteClusterPlan(cluster: DuplicateCluster, representativeBody: string, opts?: PromotePlanOptions): PromotePlanResult;
@@ -0,0 +1,103 @@
1
+ // Build a Plan that promotes a memory (or whole cluster) into
2
+ // `~/.claude/CLAUDE.md` as a managed block.
3
+ //
4
+ // memex never touches content in CLAUDE.md outside its own `<!-- memex:start
5
+ // -->` / `<!-- memex:end -->` markers. The upsert is idempotent — promoting
6
+ // the same slug twice replaces the existing block instead of appending.
7
+ import { existsSync, readFileSync } from "node:fs";
8
+ import { basename, dirname, join } from "node:path";
9
+ import { planBuilder } from "./plan.js";
10
+ import { defaultClaudeMdPath, toBlockSlug, upsertBlock, } from "./claudeMd.js";
11
+ import { removeIndexEntry } from "./memoryIndex.js";
12
+ function indexUpdateOps(builder, memoryDir, fileName, indexesUpdated) {
13
+ const indexPath = join(memoryDir, "MEMORY.md");
14
+ if (!existsSync(indexPath))
15
+ return;
16
+ try {
17
+ const current = readFileSync(indexPath, "utf8");
18
+ const { content, removedLines } = removeIndexEntry(current, fileName);
19
+ if (removedLines > 0) {
20
+ builder.write(indexPath, content);
21
+ indexesUpdated.push(indexPath);
22
+ }
23
+ }
24
+ catch {
25
+ // ignore — lint will catch any resulting dangling ref
26
+ }
27
+ }
28
+ function blockFromMemory(memory) {
29
+ const heading = memory.name ?? memory.fileName.replace(/\.md$/, "").replace(/_/g, " ");
30
+ return {
31
+ slug: toBlockSlug(memory.fileName),
32
+ heading,
33
+ body: memory.body,
34
+ };
35
+ }
36
+ function blockFromClusterMember(member, body) {
37
+ const heading = member.name ?? basename(member.filePath, ".md").replace(/_/g, " ");
38
+ return {
39
+ slug: toBlockSlug(member.filePath.split("/").pop()),
40
+ heading,
41
+ body,
42
+ };
43
+ }
44
+ /** Promote a single memory file. */
45
+ export function buildPromoteMemoryPlan(memory, opts = {}) {
46
+ const claudeMdPath = opts.claudeMdPath ?? defaultClaudeMdPath();
47
+ const block = blockFromMemory(memory);
48
+ const current = existsSync(claudeMdPath) ? readFileSync(claudeMdPath, "utf8") : "";
49
+ const next = upsertBlock(current, block);
50
+ const builder = planBuilder(`promote:${block.slug}`, `Promote ${memory.id} to ${claudeMdPath} as block "${block.slug}"`);
51
+ builder.write(claudeMdPath, next);
52
+ builder.delete(memory.filePath);
53
+ const indexesUpdated = [];
54
+ if (!opts.skipIndexUpdate) {
55
+ indexUpdateOps(builder, dirname(memory.filePath), memory.fileName, indexesUpdated);
56
+ }
57
+ return {
58
+ plan: builder.build(),
59
+ summary: {
60
+ blockSlug: block.slug,
61
+ claudeMdPath,
62
+ deletedIds: [memory.id],
63
+ indexesUpdated,
64
+ },
65
+ };
66
+ }
67
+ /**
68
+ * Promote a whole duplicate cluster: write the representative's body into
69
+ * CLAUDE.md as one managed block, then delete EVERY cluster member (since the
70
+ * block now replaces all copies).
71
+ *
72
+ * Used when `memex dedupe` flags a cluster as `promotionCandidate`.
73
+ */
74
+ export function buildPromoteClusterPlan(cluster, representativeBody, opts = {}) {
75
+ const claudeMdPath = opts.claudeMdPath ?? defaultClaudeMdPath();
76
+ const rep = cluster.members.find((m) => m.id === cluster.representative.id);
77
+ if (!rep) {
78
+ throw new Error(`cluster ${cluster.id} representative not found in members`);
79
+ }
80
+ const block = blockFromClusterMember(rep, representativeBody);
81
+ const current = existsSync(claudeMdPath) ? readFileSync(claudeMdPath, "utf8") : "";
82
+ const next = upsertBlock(current, block);
83
+ const builder = planBuilder(`promote-cluster:${cluster.id}`, `Promote cluster ${cluster.id} (${cluster.count} members → 1 block in ${claudeMdPath})`);
84
+ builder.write(claudeMdPath, next);
85
+ for (const m of cluster.members)
86
+ builder.delete(m.filePath);
87
+ const indexesUpdated = [];
88
+ if (!opts.skipIndexUpdate) {
89
+ for (const m of cluster.members) {
90
+ indexUpdateOps(builder, dirname(m.filePath), m.filePath.split("/").pop(), indexesUpdated);
91
+ }
92
+ }
93
+ return {
94
+ plan: builder.build(),
95
+ summary: {
96
+ blockSlug: block.slug,
97
+ claudeMdPath,
98
+ deletedIds: cluster.members.map((m) => m.id),
99
+ indexesUpdated,
100
+ },
101
+ };
102
+ }
103
+ //# sourceMappingURL=promote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promote.js","sourceRoot":"","sources":["../../src/core/promote.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { Memory, ProjectMemoryDir } from "./types.js";
2
+ export interface ScanOptions {
3
+ /** Override the projects root. Defaults to `~/.claude/projects/`. */
4
+ projectsRoot?: string;
5
+ /**
6
+ * Sink for non-fatal warnings (unreadable files, broken frontmatter, etc.).
7
+ * Defaults to a no-op — callers that need this info (the lint rules) should
8
+ * pass an explicit sink. The CLI surfaces a `--verbose` flag for users who
9
+ * want to see scan-level warnings on stderr.
10
+ */
11
+ onWarning?: (msg: string) => void;
12
+ }
13
+ export declare function defaultProjectsRoot(): string;
14
+ export declare function scanAll(opts?: ScanOptions): ProjectMemoryDir[];
15
+ /** Flatten all memories across all projects into one list. */
16
+ export declare function allMemories(projects: ProjectMemoryDir[]): Memory[];
@@ -0,0 +1,81 @@
1
+ // Walks `~/.claude/projects/<slug>/memory/` and returns one ProjectMemoryDir
2
+ // per project that has a memory directory.
3
+ //
4
+ // Projects without a `memory/` subdir are skipped silently — that's the normal
5
+ // state for projects Claude hasn't written memory for yet.
6
+ import { readdirSync, existsSync, statSync } from "node:fs";
7
+ import { homedir } from "node:os";
8
+ import { join } from "node:path";
9
+ import { parseMemoryFile } from "./parse.js";
10
+ const INDEX_FILE = "MEMORY.md";
11
+ export function defaultProjectsRoot() {
12
+ return join(homedir(), ".claude", "projects");
13
+ }
14
+ function safeIsDirectory(path) {
15
+ try {
16
+ return statSync(path).isDirectory();
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ }
22
+ export function scanAll(opts = {}) {
23
+ const root = opts.projectsRoot ?? defaultProjectsRoot();
24
+ const warn = opts.onWarning ?? (() => { });
25
+ if (!existsSync(root))
26
+ return [];
27
+ const projects = [];
28
+ let slugs;
29
+ try {
30
+ slugs = readdirSync(root);
31
+ }
32
+ catch (err) {
33
+ warn(`memex: cannot read ${root}: ${err instanceof Error ? err.message : String(err)}`);
34
+ return [];
35
+ }
36
+ for (const slug of slugs) {
37
+ const projectDir = join(root, slug);
38
+ const memoryDir = join(projectDir, "memory");
39
+ if (!safeIsDirectory(projectDir))
40
+ continue;
41
+ if (!safeIsDirectory(memoryDir))
42
+ continue;
43
+ let entries;
44
+ try {
45
+ entries = readdirSync(memoryDir);
46
+ }
47
+ catch (err) {
48
+ warn(`memex: cannot read ${memoryDir}: ${err instanceof Error ? err.message : String(err)}`);
49
+ continue;
50
+ }
51
+ const memories = [];
52
+ for (const name of entries) {
53
+ if (!name.endsWith(".md"))
54
+ continue;
55
+ if (name === INDEX_FILE)
56
+ continue;
57
+ const filePath = join(memoryDir, name);
58
+ try {
59
+ memories.push(parseMemoryFile(filePath, slug));
60
+ }
61
+ catch (err) {
62
+ warn(`memex: skip ${filePath}: ${err instanceof Error ? err.message : String(err)}`);
63
+ }
64
+ }
65
+ // Deterministic order across runs — keep filename sort.
66
+ memories.sort((a, b) => a.fileName.localeCompare(b.fileName));
67
+ projects.push({
68
+ slug,
69
+ path: projectDir,
70
+ hasIndex: existsSync(join(memoryDir, INDEX_FILE)),
71
+ memories,
72
+ });
73
+ }
74
+ projects.sort((a, b) => a.slug.localeCompare(b.slug));
75
+ return projects;
76
+ }
77
+ /** Flatten all memories across all projects into one list. */
78
+ export function allMemories(projects) {
79
+ return projects.flatMap((p) => p.memories);
80
+ }
81
+ //# sourceMappingURL=scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/core/scan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ export declare const MEMORY_TYPES: readonly ["user", "feedback", "project", "reference"];
2
+ export type MemoryType = (typeof MEMORY_TYPES)[number];
3
+ export type MemoryTypeOrUntyped = MemoryType | "untyped";
4
+ export type TypeSource = "frontmatter" | "filename-prefix" | "unknown";
5
+ export interface Memory {
6
+ /** Stable id: `<project-slug>/<filename>`. Survives across scans. */
7
+ id: string;
8
+ /** The directory name under `~/.claude/projects/` (slug-encoded path). */
9
+ projectSlug: string;
10
+ /** Absolute path to the file on disk. */
11
+ filePath: string;
12
+ /** Just the filename, e.g. `user_git_rules.md`. */
13
+ fileName: string;
14
+ type: MemoryTypeOrUntyped;
15
+ typeSource: TypeSource;
16
+ /** From frontmatter `name:`. Null if absent. */
17
+ name: string | null;
18
+ /** From frontmatter `description:`. Null if absent. */
19
+ description: string | null;
20
+ /** Body content with frontmatter stripped. */
21
+ body: string;
22
+ /** Full frontmatter object — we tolerate unknown fields like `originSessionId`. */
23
+ frontmatter: Record<string, unknown>;
24
+ /** File mtime in ms since epoch, for staleness checks. */
25
+ mtimeMs: number;
26
+ }
27
+ export interface ProjectMemoryDir {
28
+ /** Directory name under `~/.claude/projects/` (slug-encoded). */
29
+ slug: string;
30
+ /** Absolute path to the project dir (`<root>/<slug>`). */
31
+ path: string;
32
+ /** Whether a `MEMORY.md` index file exists in the memory dir. */
33
+ hasIndex: boolean;
34
+ /** Parsed memories — does not include `MEMORY.md` itself. */
35
+ memories: Memory[];
36
+ }
@@ -0,0 +1,4 @@
1
+ // Shared types for the memex core. Kept dependency-free so this module can be
2
+ // imported from anywhere (CLI, TUI, MCP) without dragging in IO concerns.
3
+ export const MEMORY_TYPES = ["user", "feedback", "project", "reference"];
4
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,0EAA0E;AAE1E,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,CAAU,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export interface ServerOptions {
3
+ /** Override projects root for testing. */
4
+ projectsRoot?: string;
5
+ /** Override CLAUDE.md path for testing. */
6
+ claudeMdPath?: string;
7
+ /** Override journal dir for testing. */
8
+ journalDir?: string;
9
+ /** Read package.json for the version string. */
10
+ version: string;
11
+ }
12
+ export declare function buildMcpServer(opts: ServerOptions): McpServer;
13
+ export declare function runMcpServerStdio(opts: ServerOptions): Promise<void>;
@@ -0,0 +1,211 @@
1
+ // memex MCP server — exposes the same operations as the CLI over MCP stdio.
2
+ //
3
+ // The pitch: install this once as a Claude Code plugin, and Claude itself
4
+ // can call list_memories / find_duplicates / lint / doctor / plan_merge /
5
+ // plan_promote / apply_plan / undo as tools. Periodic memory maintenance
6
+ // stops being a manual chore and becomes something Claude does during a
7
+ // session when the user asks "clean up my memory".
8
+ //
9
+ // Safety model (mirrors the CLI):
10
+ // - Read-only tools: list_memories, find_duplicates, lint, doctor
11
+ // - Plan generators: plan_merge, plan_promote_memory, plan_promote_cluster
12
+ // → return a Plan, do not execute
13
+ // - Mutating: apply_plan (executes a Plan it was just given), undo
14
+ // - Everything mutating goes through Phase 5's journal, so undo always works
15
+ //
16
+ // We pin to @modelcontextprotocol/sdk v1.x — v2 is pre-alpha as of 2026-05.
17
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
18
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
19
+ import { z } from "zod";
20
+ import { scanAll, allMemories } from "../core/scan.js";
21
+ import { lintAll } from "../core/lint.js";
22
+ import { runDoctor } from "../core/doctor.js";
23
+ import { findDuplicates } from "../core/dedupe.js";
24
+ import { buildMergePlan } from "../core/merge.js";
25
+ import { buildPromoteMemoryPlan, buildPromoteClusterPlan } from "../core/promote.js";
26
+ import { applyPlan, undoPlan } from "../core/apply.js";
27
+ import { latestJournalEntry, listJournal } from "../core/journal.js";
28
+ import { readFileSync } from "node:fs";
29
+ // Plan schema — used by apply_plan so callers send back exactly what a plan_*
30
+ // tool returned. We don't trust arbitrary disk paths here, but we don't need
31
+ // to: every plan went through our generator and is just JSON.
32
+ const PlanSchema = z.object({
33
+ id: z.string(),
34
+ label: z.string(),
35
+ description: z.string(),
36
+ createdAt: z.string(),
37
+ ops: z.array(z.union([
38
+ z.object({
39
+ kind: z.literal("write"),
40
+ path: z.string(),
41
+ content: z.string(),
42
+ }),
43
+ z.object({
44
+ kind: z.literal("delete"),
45
+ path: z.string(),
46
+ }),
47
+ z.object({
48
+ kind: z.literal("ensure-dir"),
49
+ path: z.string(),
50
+ }),
51
+ ])),
52
+ });
53
+ const SCAN_OPTS_DESC = "Optional projectsRoot — default is ~/.claude/projects. Set CLAUDE_PROJECT_DIR for project-scoped Claude Code MCP usage.";
54
+ function projectsRootFromEnv() {
55
+ // Claude Code auto-sets CLAUDE_PROJECT_DIR for MCP servers it launches.
56
+ // We don't use it as the projects root (that's always ~/.claude/projects/),
57
+ // but we keep this hook for future "current project" defaults.
58
+ return undefined;
59
+ }
60
+ function jsonReply(payload) {
61
+ return {
62
+ content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
63
+ };
64
+ }
65
+ export function buildMcpServer(opts) {
66
+ const server = new McpServer({
67
+ name: "memex",
68
+ version: opts.version,
69
+ });
70
+ const root = opts.projectsRoot ?? projectsRootFromEnv();
71
+ // -------- read-only tools --------
72
+ server.tool("list_memories", `List memories across all projects under ~/.claude/projects (or projectsRoot). ${SCAN_OPTS_DESC}`, {
73
+ type: z.enum(["user", "feedback", "project", "reference", "untyped"]).optional()
74
+ .describe("Filter by memory type"),
75
+ project: z.string().optional()
76
+ .describe("Filter to projects whose slug contains this substring"),
77
+ }, async ({ type, project }) => {
78
+ const projects = scanAll({ projectsRoot: root });
79
+ const filtered = project ? projects.filter((p) => p.slug.includes(project)) : projects;
80
+ const memories = allMemories(filtered).filter((m) => !type || m.type === type);
81
+ return jsonReply(memories);
82
+ });
83
+ server.tool("find_duplicates", "Find duplicate memory clusters across projects. Cross-project user/feedback duplicates are flagged as promotion candidates for ~/.claude/CLAUDE.md.", {
84
+ project: z.string().optional()
85
+ .describe("Filter to projects whose slug contains this substring"),
86
+ promotionOnly: z.boolean().optional()
87
+ .describe("Return only clusters flagged as promote-to-global candidates"),
88
+ }, async ({ project, promotionOnly }) => {
89
+ const result = findDuplicates({ projectsRoot: root, projectFilter: project });
90
+ if (promotionOnly) {
91
+ result.clusters = result.clusters.filter((c) => c.promotionCandidate);
92
+ }
93
+ return jsonReply(result);
94
+ });
95
+ server.tool("lint", "Lint MEMORY.md indexes and memory files. Surfaces broken-yaml, dangling-ref, missing-index, orphan-file, untyped-file, and over-cap issues.", {
96
+ project: z.string().optional(),
97
+ }, async ({ project }) => {
98
+ const result = lintAll({ projectsRoot: root, projectFilter: project });
99
+ return jsonReply(result);
100
+ });
101
+ server.tool("doctor", "One-shot memory health report: per-project status, severity counts, index size warnings, and prioritized next actions.", {
102
+ project: z.string().optional(),
103
+ }, async ({ project }) => {
104
+ const report = runDoctor({ projectsRoot: root, projectFilter: project });
105
+ return jsonReply(report);
106
+ });
107
+ // -------- plan generators (return Plan, do not execute) --------
108
+ server.tool("plan_merge", "Build (but do not execute) a Plan to merge a duplicate cluster down to its representative. The returned Plan can be sent to apply_plan to commit.", {
109
+ clusterId: z.string().describe("Cluster id from find_duplicates, e.g. c1"),
110
+ keepId: z.string().optional()
111
+ .describe("Override which member to keep (default: longest body + most recent mtime)"),
112
+ skipIndexUpdate: z.boolean().optional()
113
+ .describe("Don't modify MEMORY.md indexes"),
114
+ }, async ({ clusterId, keepId, skipIndexUpdate }) => {
115
+ const dedupe = findDuplicates({ projectsRoot: root });
116
+ const cluster = dedupe.clusters.find((c) => c.id === clusterId);
117
+ if (!cluster) {
118
+ return jsonReply({ error: `cluster "${clusterId}" not found`, available: dedupe.clusters.map((c) => c.id) });
119
+ }
120
+ const result = buildMergePlan(cluster, { keepId, skipIndexUpdate });
121
+ return jsonReply(result);
122
+ });
123
+ server.tool("plan_promote_memory", "Build a Plan to promote a single memory to ~/.claude/CLAUDE.md as a managed block. apply_plan it to commit.", {
124
+ memoryId: z.string().describe("Memory id from list_memories, e.g. <project-slug>/<filename>"),
125
+ skipIndexUpdate: z.boolean().optional(),
126
+ }, async ({ memoryId, skipIndexUpdate }) => {
127
+ const projects = scanAll({ projectsRoot: root });
128
+ const memory = allMemories(projects).find((m) => m.id === memoryId);
129
+ if (!memory) {
130
+ return jsonReply({ error: `memory "${memoryId}" not found` });
131
+ }
132
+ const result = buildPromoteMemoryPlan(memory, {
133
+ claudeMdPath: opts.claudeMdPath,
134
+ skipIndexUpdate,
135
+ });
136
+ return jsonReply(result);
137
+ });
138
+ server.tool("plan_promote_cluster", "Build a Plan to promote a whole duplicate cluster (representative → CLAUDE.md, all members deleted).", {
139
+ clusterId: z.string(),
140
+ skipIndexUpdate: z.boolean().optional(),
141
+ }, async ({ clusterId, skipIndexUpdate }) => {
142
+ const dedupe = findDuplicates({ projectsRoot: root });
143
+ const cluster = dedupe.clusters.find((c) => c.id === clusterId);
144
+ if (!cluster) {
145
+ return jsonReply({ error: `cluster "${clusterId}" not found` });
146
+ }
147
+ const rep = cluster.members.find((m) => m.id === cluster.representative.id);
148
+ if (!rep) {
149
+ return jsonReply({ error: "representative missing from members" });
150
+ }
151
+ let repBody = "";
152
+ try {
153
+ const raw = readFileSync(rep.filePath, "utf8");
154
+ repBody = raw.replace(/^---[\s\S]*?---\n?/, "").trim();
155
+ }
156
+ catch (err) {
157
+ return jsonReply({ error: `cannot read representative: ${err instanceof Error ? err.message : String(err)}` });
158
+ }
159
+ const result = buildPromoteClusterPlan(cluster, repBody, {
160
+ claudeMdPath: opts.claudeMdPath,
161
+ skipIndexUpdate,
162
+ });
163
+ return jsonReply(result);
164
+ });
165
+ // -------- mutating tools (always journaled, undoable) --------
166
+ server.tool("apply_plan", "Execute a Plan returned by plan_merge / plan_promote_*. The mutation is journaled at ~/.claude/.memex/log/ and reversible via undo.", {
167
+ plan: PlanSchema,
168
+ }, async ({ plan }) => {
169
+ const entry = applyPlan(plan, { journalDir: opts.journalDir });
170
+ return jsonReply({ status: entry.status, entryId: entry.plan.id, ops: entry.ops });
171
+ });
172
+ server.tool("undo", "Reverse the most recent applied plan, or a specific plan by id. Writes a new journal entry that itself can be undone (redo).", {
173
+ planId: z.string().optional().describe("Specific plan id; default is the most recent"),
174
+ }, async ({ planId }) => {
175
+ const { undone, newEntry } = undoPlan(planId ?? "latest", { journalDir: opts.journalDir });
176
+ if (!undone) {
177
+ return jsonReply({ error: planId ? `no journal entry with id "${planId}"` : "journal is empty" });
178
+ }
179
+ return jsonReply({
180
+ undoneId: undone.plan.id,
181
+ undoneLabel: undone.plan.label,
182
+ newEntryId: newEntry?.plan.id ?? null,
183
+ status: newEntry?.status ?? "no-op",
184
+ });
185
+ });
186
+ server.tool("journal_list", "List all entries in the mutation journal, newest first. Includes status, op counts, and undoOf links.", {}, async () => {
187
+ const entries = listJournal({ journalDir: opts.journalDir });
188
+ return jsonReply(entries.map((e) => ({
189
+ id: e.plan.id,
190
+ label: e.plan.label,
191
+ status: e.status,
192
+ startedAt: e.startedAt,
193
+ finishedAt: e.finishedAt,
194
+ opCount: e.plan.ops.length,
195
+ undoOf: e.undoOf ?? null,
196
+ })));
197
+ });
198
+ server.tool("journal_latest", "Return the most recent journal entry (for inspection before calling undo).", {}, async () => {
199
+ const latest = latestJournalEntry({ journalDir: opts.journalDir });
200
+ if (!latest)
201
+ return jsonReply(null);
202
+ return jsonReply(latest);
203
+ });
204
+ return server;
205
+ }
206
+ export async function runMcpServerStdio(opts) {
207
+ const server = buildMcpServer(opts);
208
+ const transport = new StdioServerTransport();
209
+ await server.connect(transport);
210
+ }
211
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}