From 47d0d2accdfd2192c63dd106d80bf12796c50db9 Mon Sep 17 00:00:00 2001 From: john Date: Thu, 10 Sep 2026 08:32:56 +0800 Subject: [PATCH] feat(dev-tools): add codebase-memory-mcp local check and docs Document MIT dev-tool setup for Cursor code intelligence and add an offline probe for CLI, Cursor MCP config, and Memind index readiness. Co-authored-by: Cursor --- docs/codebase-memory-mcp-local.md | 74 ++++++++++++ docs/context-runtime-local-dev.md | 3 + scripts/check-codebase-memory-mcp-local.mjs | 120 ++++++++++++++++++++ 3 files changed, 197 insertions(+) create mode 100644 docs/codebase-memory-mcp-local.md create mode 100644 scripts/check-codebase-memory-mcp-local.mjs diff --git a/docs/codebase-memory-mcp-local.md b/docs/codebase-memory-mcp-local.md new file mode 100644 index 0000000..b3b867f --- /dev/null +++ b/docs/codebase-memory-mcp-local.md @@ -0,0 +1,74 @@ +# codebase-memory-mcp 本地开发 + +> MIT · 仅本机 Cursor / code 任务开发工具 · **不进入 MeMind 103 运行时** +> 关联:[context-runtime-harness-fusion-plan.md](./context-runtime-harness-fusion-plan.md) §6.6 + +## 用途边界 + +| 允许 | 禁止 | +|------|------| +| Cursor 内代码库结构查询、调用链、架构图 | 个人生活流 / h5 用户记忆主检索 | +| 未来挂到 `tool-gateway` 的 aider/openhands 路径 | 替代 Memory V2 / Context Runtime Phase 2–3 | +| Phase 5 Personal KG **仅借 schema 思想** | 把索引目录提交进 Git | + +## 安装(本机一次) + +```bash +npm install -g codebase-memory-mcp +codebase-memory-mcp install -y # 写入 ~/.cursor/mcp.json 等,属设计行为 +``` + +安装后 **重启 Cursor**。全局二进制缓存由 npm 包管理;图数据在 CBM 本地 daemon/SQLite,**不在** Memind 仓库根目录。 + +## 首次索引 Memind + +**方式 A — Cursor(推荐)** + +重启后对 agent 说:**「Index this project」**。 + +**方式 B — CLI** + +```bash +cd /Users/john/Project/Memind +codebase-memory-mcp cli --progress index_repository '{"repo_path":"/Users/john/Project/Memind"}' +``` + +可选:常驻 daemon 减少 CLI 冷启动: + +```bash +codebase-memory-mcp daemon start +``` + +## 离线验证 + +```bash +codebase-memory-mcp --version +node scripts/check-codebase-memory-mcp-local.mjs +``` + +- 仅验证 CLI + Cursor 配置:`MEMIND_CBM_REQUIRE_INDEX=0 node scripts/check-codebase-memory-mcp-local.mjs` +- 完整通过需已索引,输出 **`CODEBASE_MEMORY_MCP_OK`** + +## 常用 CLI 探针 + +```bash +codebase-memory-mcp cli --json list_projects '{}' +codebase-memory-mcp cli --json search_graph '{"name_pattern":".*headroom.*","label":"Function"}' +codebase-memory-mcp cli --json get_architecture '{}' +``` + +## 与 zvec-grep 的分工 + +| 工具 | 强项 | +|------|------| +| **codebase-memory-mcp** | AST 知识图:符号、CALLS、路由、Cypher | +| **zvec-grep** | workspace 语义/关键词文件检索(`.zvec-grep/`) | + +两者互补;Context Runtime 本地清单见 [context-runtime-local-dev.md](./context-runtime-local-dev.md)。 + +## 卸载 / 更新 + +```bash +codebase-memory-mcp update +codebase-memory-mcp uninstall -y # 仅移除 agent MCP 配置,不删全局 npm 包 +``` diff --git a/docs/context-runtime-local-dev.md b/docs/context-runtime-local-dev.md index f4372a8..5aecdf9 100644 --- a/docs/context-runtime-local-dev.md +++ b/docs/context-runtime-local-dev.md @@ -56,6 +56,9 @@ node scripts/check-zvec-workspace-local.mjs # MCP compactor shadow/active 逻辑 node scripts/check-mcp-compactor-local.mjs + +# codebase-memory-mcp CLI + Memind 索引(见 docs/codebase-memory-mcp-local.md) +node scripts/check-codebase-memory-mcp-local.mjs ``` ## 用户自测清单(有 LLM 配额时) diff --git a/scripts/check-codebase-memory-mcp-local.mjs b/scripts/check-codebase-memory-mcp-local.mjs new file mode 100644 index 0000000..b1c80e8 --- /dev/null +++ b/scripts/check-codebase-memory-mcp-local.mjs @@ -0,0 +1,120 @@ +#!/usr/bin/env node +/** + * Local codebase-memory-mcp smoke (dev machine only). + * + * Verifies the global CLI is installed and, when MEMIND_CBM_REQUIRE_INDEX=1 + * (default), that this repository is indexed. + */ +import { spawnSync } from 'node:child_process'; +import { existsSync, readFileSync } from 'node:fs'; +import { homedir } from 'node:os'; +import { basename, resolve } from 'node:path'; + +const repoRoot = resolve(process.cwd()); +const requireIndex = process.env.MEMIND_CBM_REQUIRE_INDEX !== '0'; +const cliName = process.env.MEMIND_CBM_CLI ?? 'codebase-memory-mcp'; + +function runCli(tool, args = {}) { + const payload = JSON.stringify(args); + const result = spawnSync(cliName, ['cli', '--json', tool, payload], { + encoding: 'utf8', + maxBuffer: 16 * 1024 * 1024, + }); + const stdout = (result.stdout ?? '').trim(); + const stderr = (result.stderr ?? '').trim(); + if (result.error) { + return { ok: false, error: result.error.message, stdout, stderr }; + } + if (result.status !== 0) { + return { + ok: false, + error: stderr.split('\n').pop() || `exit ${result.status}`, + stdout, + stderr, + }; + } + try { + const parsed = JSON.parse(stdout); + const structured = parsed.structuredContent ?? parsed; + if (structured?.error || parsed.isError) { + return { ok: false, error: structured.error ?? parsed.isError, data: structured, stdout, stderr }; + } + return { ok: true, data: structured, stdout, stderr }; + } catch { + return { ok: false, error: 'invalid JSON from cli', stdout, stderr }; + } +} + +const versionResult = spawnSync(cliName, ['--version'], { encoding: 'utf8' }); +const version = (versionResult.stdout ?? versionResult.stderr ?? '').trim(); +if (versionResult.status !== 0 || !version) { + console.error('CODEBASE_MEMORY_MCP_FAIL: CLI not found'); + console.error(' install: npm install -g codebase-memory-mcp && codebase-memory-mcp install -y'); + process.exit(1); +} + +console.log('CODEBASE_MEMORY_MCP_PROBE:'); +console.log(` version=${version}`); +console.log(` repo=${repoRoot}`); + +const cursorMcpPath = resolve(homedir(), '.cursor/mcp.json'); +if (existsSync(cursorMcpPath)) { + try { + const cursorConfig = JSON.parse(readFileSync(cursorMcpPath, 'utf8')); + const servers = cursorConfig.mcpServers ?? cursorConfig; + const configured = Boolean(servers?.['codebase-memory-mcp']); + console.log(` cursor_mcp_configured=${configured}`); + if (!configured) { + console.warn('CODEBASE_MEMORY_MCP_WARN: ~/.cursor/mcp.json missing codebase-memory-mcp (run: codebase-memory-mcp install -y)'); + } + } catch { + console.warn('CODEBASE_MEMORY_MCP_WARN: could not parse ~/.cursor/mcp.json'); + } +} else { + console.warn('CODEBASE_MEMORY_MCP_WARN: ~/.cursor/mcp.json not found'); +} + +const projects = runCli('list_projects', {}); +if (!projects.ok) { + console.error(`CODEBASE_MEMORY_MCP_FAIL: list_projects — ${projects.error}`); + process.exit(1); +} + +const projectNames = (projects.data.projects ?? []).map((p) => p.name ?? p.project ?? p); +console.log(` indexed_projects=${projectNames.length}`); + +const repoName = basename(repoRoot); +const matched = projectNames.find((name) => { + const normalized = String(name).toLowerCase(); + return normalized === repoName.toLowerCase() || normalized.includes('memind'); +}); + +if (!matched) { + const hint = 'codebase-memory-mcp cli --progress index_repository \'{"repo_path":"' + repoRoot + '"}\''; + if (requireIndex) { + console.error('CODEBASE_MEMORY_MCP_FAIL: Memind not indexed yet'); + console.error(` run: ${hint}`); + console.error(' or in Cursor after restart: "Index this project"'); + process.exit(1); + } + console.warn('CODEBASE_MEMORY_MCP_WARN: Memind not indexed (MEMIND_CBM_REQUIRE_INDEX=0)'); + console.log('CODEBASE_MEMORY_MCP_CLI_OK'); + process.exit(0); +} + +const status = runCli('index_status', { project: matched }); +if (!status.ok) { + console.error(`CODEBASE_MEMORY_MCP_FAIL: index_status — ${status.error}`); + process.exit(1); +} + +const summary = { + project: status.data.project ?? matched, + status: status.data.status ?? null, + nodes: status.data.nodes ?? null, + edges: status.data.edges ?? null, + parse_partial_count: status.data.parse_partial?.count ?? status.data.parse_partial?.files?.length ?? 0, +}; +console.log(` project=${summary.project}`); +console.log(` index=${JSON.stringify(summary)}`); +console.log('CODEBASE_MEMORY_MCP_OK');