Native Swift MCP helper + macOS App:聚合 14 个默认启用来源 + 3 个归档默认关闭来源的 AI 编程助手历史会话,实现跨工具上下文共享、关键词搜索和项目迁移。
在 Codex 里做了半天,切到 Claude Code 继续时,AI 不需要你手动解释之前做了什么——它可以直接调用 get_context 查询你的历史。
Current product state (2026-07-10): shipped runtime is the native Swift
macOS app + bundled Swift MCP helper. The active MCP surface has 27 tools. The
App UI is intentionally keyword-only; service and MCP expose availability-gated
semantic/hybrid search when compatible embeddings exist. Session ingestion
defaults to 14 active sources plus 3 archived default-off sources.
docs/TODO.md has no open engineering items, docs/followups.md has no open
implementation-ready engineering follow-ups, and docs/roadmap.md keeps 12
product decisions visible rather than reporting them as implemented.
┌─────────┐ ┌───────────┐ ┌──────────┐ ┌──────────┐ ┌─────────┐
│ Codex │ │Claude Code│ │Gemini CLI│ │ Cursor │ │ Cline …│
│ sessions│ │ projects │ │ tmp │ │ vscdb │ │ tasks │
└────┬────┘ └─────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬────┘
└────────────┴──────┬─────┴─────────────┴────────────┘
▼
┌──────────────────────┐
│ engram │
│ Swift MCP + App UI │
│ ~/.engram/ │
│ index.sqlite │
└──────────┬───────────┘
┌────┴────┐
▼ ▼
MCP Tools macOS App
(AI 调用) (桌面 UI)
| 工具 | 日志路径 | 状态 |
|---|---|---|
| Codex CLI | ~/.codex/sessions/ |
✅ 完整支持 |
| Claude Code | ~/.claude/projects/ |
✅ 完整支持 |
| Gemini CLI | ~/.gemini/tmp/ |
✅ 完整支持 |
| Antigravity | CLI brain ~/.gemini/antigravity-cli/brain/ + legacy cache when present |
✅ 完整支持 |
| Windsurf | ~/.engram/cache/windsurf cache only; live gRPC sync is disabled |
|
| Cursor | ~/Library/Application Support/Cursor/…/state.vscdb |
✅ 完整支持 |
| VS Code Copilot | ~/Library/Application Support/Code/…/chatSessions/ |
✅ 完整支持 |
| GitHub Copilot | ~/.copilot/session-state/<uuid>/events.jsonl |
✅ 完整支持 |
| iflow | ~/.iflow/projects/ |
归档默认关闭,可在 Sources > Archived 启用 |
| Qwen Code | ~/.qwen/projects/ |
✅ 完整支持 |
| Qoder | ~/.qoder/projects/ |
✅ 完整支持 |
| OpenCode | ~/.local/share/opencode/opencode.db |
✅ 完整支持 |
| Kimi | ~/.kimi/sessions/ |
✅ 完整支持 |
| MiniMax | Claude Code-derived sessions under ~/.claude/projects/ |
✅ 完整支持 |
| Lobster AI | Claude Code-derived sessions under ~/.claude/projects/ |
归档默认关闭,可在 Sources > Archived 启用 |
| Command Code | ~/.commandcode/projects/ |
✅ 完整支持 |
| Cline | ~/.cline/data/tasks/ |
归档默认关闭,可在 Sources > Archived 启用 |
Engram 的产品解析路径在 Swift 侧:macos/Shared/EngramCore/Adapters/Sources/ 负责读取各工具原始日志,macos/EngramCoreWrite/Indexing/ 负责写入索引库。TypeScript 适配器保留为 dev/reference/fixture tooling,用于生成和校验 fixture parity。
会话详情只把 user / assistant 消息作为可见正文展示;tool/event/system-like 数据用于索引、工具统计或诊断,不作为普通对话气泡混入正文。分页 offset 按 adapter 原始消费位置推进,避免隐藏消息造成可见消息漏页或重复页。
Antigravity CLI、Command Code、Qoder 是重点覆盖来源。Antigravity CLI 使用 ~/.gemini/antigravity-cli/brain/ 下的 CLI transcript,并兼容旧 gRPC cache;Qoder 覆盖顶层 session 与 nested subagents/ 父子关系;Command Code 覆盖 tool-call 的 input / args 形态。发布前的 parser/parity smoke:
npm run check:adapter-parity-fixtures
npm test -- tests/adapters
xcodebuild test -project macos/Engram.xcodeproj -scheme EngramCoreTests -destination 'platform=macOS' -only-testing:EngramCoreTests/AdapterParityTests/testSwiftAdaptersMatchNodeParityGoldensForAllProviders
xcodebuild test -project macos/Engram.xcodeproj -scheme Engram -destination 'platform=macOS' -only-testing:EngramTests/MessageParserTests当前 fixture/parity gate 覆盖 15 个独立产品适配器:Antigravity CLI、Claude Code、Cline、Codex CLI、Command Code、GitHub Copilot、Cursor、Gemini CLI、iflow、Kimi、OpenCode、Qoder、Qwen Code、VS Code Copilot、Windsurf。其中 Cline 和 iflow parser/fixture 继续保留,但作为归档来源默认不扫描,需在 Sources > Archived 手动启用;MiniMax 保持默认启用。MiniMax 和 Lobster AI 作为 Claude-compatible derived sources 走 Claude Code parser,但会以独立 source 写入索引;Lobster AI 同样保留 parser/fixture 并默认归档关闭。Swift/Node 回归测试覆盖该派生分类。Swift App、Swift MCP、Swift Service export 都只展示非空 user / assistant 可见正文;tool/system/event-like 行保留给索引、工具统计和诊断,不混入普通对话气泡。provider parser parity 由 tests/fixtures/adapter-parity/** 约束;Swift/MCP/export 的可见消息一致性由 Swift service/core 测试覆盖。如果出现同一会话在两端解析或显示不同,先补对应 fixture,再改 adapter 或可见消息过滤逻辑。
最近一次完整 provider/parser ship 记录见 docs/verification/provider-parser-parity-2026-05-20.md,其中包含两轮 Polycli review 与最终验证命令。
前置要求: 已安装 Engram.app,或从源码构建 macOS App。
# 源码构建
git clone https://github.com/bbingz/engram
cd engram
macos/scripts/build-release.sh --local-only
# 2. 安装到 /Applications
macos/scripts/deploy-local.sh macos/build/EngramExport/Engram.app
# 如果没有 Developer ID 证书,--local-only 会生成
# macos/build/EngramExport/Engram-local-only.app,只能本机安装,不能分发/公证。
# 3. 重启 AI 工具,在对话中调用:
# get_context cwd=/your/project/path首次启动 Engram.app 时,EngramService 会自动扫描所有会话文件并建立索引(存储在 ~/.engram/index.sqlite)。之后通过文件监听增量更新,无需手动维护。
claude mcp add --scope user engram /Applications/Engram.app/Contents/Helpers/EngramMCP或者手动编辑 ~/.claude/settings.json:
{
"mcpServers": {
"engram": {
"command": "/Applications/Engram.app/Contents/Helpers/EngramMCP",
"args": []
}
}
}Codex 会在会话启动时持有 MCP stdio 子进程。为了避免发布时替换
/Applications/Engram.app 影响已经打开的 Codex 会话,建议先创建一个稳定
shim:
mkdir -p ~/.engram/bin
cat > ~/.engram/bin/engram-mcp <<'EOF'
#!/bin/sh
set -eu
HELPER="/Applications/Engram.app/Contents/Helpers/EngramMCP"
if [ ! -x "$HELPER" ]; then
echo "Engram MCP helper is not executable at $HELPER" >&2
exit 127
fi
exec "$HELPER" "$@"
EOF
chmod 755 ~/.engram/bin/engram-mcp编辑 ~/.codex/config.toml:
[mcp_servers.engram]
command = "/Users/<you>/.engram/bin/engram-mcp"
args = []任何支持 MCP stdio transport 的客户端均可使用。将以下内容加入对应的 MCP 配置:
{
"command": "/Applications/Engram.app/Contents/Helpers/EngramMCP",
"args": []
}Engram 的产品 UI 是原生 macOS App。Engram.app 启动后会管理 EngramService,并通过 Swift/GRDB 读取索引库。
侧边栏按四个分区组织页面,并支持 ⌘K 命令面板在页面/操作间快速跳转:
- OVERVIEW
- Today:默认入口工作台,聚合 Continue、Follow-ups、Changed Repos、Service State,面向继续工作和收口
- Search:SQLite FTS5 trigram 关键词搜索,支持中英文、UUID 直查和结果高亮;project/source/time 等低层过滤收在 Advanced filters 里
- MONITOR
- Sessions:按来源/项目/时间筛选,多选过滤、分页;Claude Code → Codex/Gemini 等派发子会话自动归组到父会话下,可展开查看
- Favorites / Timeline / Activity:收藏列表、时间线、按天/工具的活动视图
- Hygiene:索引健康体检,列出空会话、待确认的父子建议、孤儿会话等可处理项
- Observability:日志流 / 错误面板 / 性能 / 追踪 / 系统健康等开发诊断,默认收在「Settings → General → Show Developer Tools」开关后(普通用户侧边栏不显示)
- WORKSPACE
- Projects:项目 rename / move / archive / undo,走 Swift service single-writer pipeline
- Sources / Repos / Work Graph:来源状态、Git 仓库工作量、跨会话工作图
- CONFIG
- Agents / Memory:子代理与记忆条目配置一览
其他贯穿全局的能力:
- 会话详情:完整对话内容、Markdown 渲染、收藏、导出,工具栏含 Handoff / Replay / Resume
- 价值提示:索引时计算
quality_score;搜索结果页可显示高/中/低 value band(会话卡片徽章仍为 roadmap decision-pending) - 用量与成本:按来源/项目/天/周分组的用量统计、模型成本、工具调用、文件活动,以及费用洞察建议
- 今日父会话 badge:菜单栏徽标显示今天的顶层父会话数量,而不是全部子任务总数
历史 TypeScript Web/API 入口已删除;当前 macOS 产品路径通过 Swift App、Swift Service 和 Swift MCP 提供功能。TypeScript 搜索代码仅作为 retained tooling/reference,不代表 Swift App/MCP 当前已启用语义搜索。
Engram 的 Swift MCP runtime 当前暴露 27 个工具,覆盖上下文获取、搜索、记忆管理、统计分析、会话整理和项目迁移等场景:
| 工具 | 说明 |
|---|---|
get_context |
🔑 核心工具 — 自动提取当前项目的历史上下文,开始新任务时调用 |
search |
FTS 关键词搜索;当 session vectors 可用时 tools/list 可广告 semantic/hybrid,不可用时 hard-error(不静默降级)。Service IPC 路径可 soft-fallback 到 keyword 并带 reason |
list_sessions |
列出历史会话,支持按来源/项目/时间过滤 |
get_session |
读取单个会话完整对话,支持分页 |
save_insight |
保存重要知识片段,跨会话持久化 |
delete_insight |
删除已保存的 insight |
get_memory |
检索已保存的记忆和知识 |
project_timeline |
查看项目跨工具的操作时间线 |
stats |
用量统计(按来源/项目/天/周分组) |
get_costs |
Token 用量和费用统计 |
tool_analytics |
分析各工具(Read/Edit/Bash 等)调用频率 |
file_activity |
项目中最常编辑/读取的文件 |
handoff |
生成项目交接简报 |
export |
导出会话为 Markdown/JSON |
generate_summary |
AI 生成会话摘要 |
link_sessions |
在项目目录创建会话文件软链接 |
manage_project_alias |
管理项目别名(目录移动后保持关联) |
live_sessions |
MCP mode 下返回明确 unavailable 结果;App/service IPC 路径有本地活跃会话扫描 |
get_insights |
获取费用优化建议 |
hide_session |
隐藏或恢复指定会话 |
project_move |
移动项目目录并保持 AI 会话历史可达 |
project_archive |
将项目归档到 _archive/ 分类目录 |
project_undo |
回滚已提交的 project move |
project_move_batch |
批量执行 project move/archive |
project_list_migrations |
查看 project move 迁移记录 |
project_recover |
诊断失败或卡住的迁移 |
project_review |
扫描迁移后的旧路径残留引用 |
快速示例:
// 获取项目上下文
{ "cwd": "/Users/me/my-project", "task": "重构认证模块" }
// 搜索历史
{ "query": "JWT 认证", "mode": "keyword" }
// 保存知识
{ "content": "项目使用 Hono 作为 HTTP 框架", "wing": "engram" }完整参数文档见 MCP Tools Reference。
当前 Swift 产品路径使用 SQLite FTS5 trigram 关键词搜索,并在配置了 embedding provider 且 semantic_chunks 可用时提供 brute-force cosine semantic/hybrid 检索(不是 sqlite-vec)。
- keyword:已上线。覆盖用户消息、助手回复、摘要和索引文本;支持中英文、代码片段、函数名、UUID 直查。
- semantic / hybrid(service):可用时执行向量 KNN + 可选 RRF;不可用时 soft-fallback 到 keyword 并返回 warning reason。
- semantic / hybrid(MCP):tools/list 按
SessionVectorSearchAvailability门控;不可用时searchModeUnavailablehard-error(不静默降级为 keyword 结果体)。 - App UI:故意 keyword-only(mode pills 不暴露 semantic);不代表 service/MCP 无向量路径。
- insights memory:
get_memory在有 insight embeddings 时走 hybrid,否则 FTS/recent fallback。
搜索覆盖范围:用户消息 + 助手回复 + 会话摘要 + 部分 adapter 归一化后的工具/诊断文本。
搜索还支持直接粘贴会话 UUID 进行精确查找,无需关键词或语义匹配。
| 字段 / 环境变量 | 当前状态 |
|---|---|
ENGRAM_EMBEDDING_API_KEY / embeddingApiKey / aiApiKey |
用于 service embedding 与 summary;敏感值通过 Keychain 存储,旧明文设置会在校验成功后迁移 |
embeddingModel / dimension |
写入 embedding_meta;query 须与存储 model+dim 一致 |
session_embeddings / semantic_chunks |
产品 brute-force KNN 语料;vec_sessions 为历史兼容残留 |
当你移动了项目目录(例如从 ~/Code/wechat-decrypt 移到 ~/Code/wechat-decrypt-bing),AI 助手会找不到旧路径下的历史会话。通过项目别名,可以声明两个项目名是同一个项目,所有查询自动展开。
通过 MCP 工具(在 AI 对话中):
{ "name": "manage_project_alias", "arguments": { "action": "add", "old_project": "wechat-decrypt", "new_project": "wechat-decrypt-bing" } }历史 TypeScript Web/API 入口已删除;当前产品路径优先使用 Swift MCP 工具。macOS App 暂未提供 Project Aliases 管理 UI。
添加后,在任意路径下调用 get_context、search、list_sessions 等工具,两个项目名的会话都会出现。别名是双向的——查哪个名字都能找到另一个。
// 列出所有别名
{ "action": "list" }
// 删除别名
{ "action": "remove", "old_project": "wechat-decrypt", "new_project": "wechat-decrypt-bing" }注意: 项目别名解决的是 Engram 层面的查询问题。各 AI 工具自身的
/resume等功能仍然依赖原始路径,这不在 Engram 的控制范围内。
可选功能:把冷/归档会话的可再生索引产物(sessions_fts 正文 + summary,AES-GCM 加密)卸载到你自托管的服务器以回收本地磁盘,原始 transcript 文件绝不移动;卸载的会话保留一条 keyword shadow 仍可搜索,打开时透明回填。默认关闭,需在 ~/.engram/settings.json 显式开启并配置服务器。
{
"remoteOffloadEnabled": false,
"remoteOffloadBackend": "http",
"remoteOffloadServerURL": "https://100.x.x.x:8443",
"remoteOffloadColdAgeDays": 365
}注意:卸载跑在后台 helper 进程里,受 macOS 本地网络隐私限制——
remoteOffloadServerURL要用 Tailscale IP / tailnet 名,不能用局域网 IP。部署与运维见docs/remote-offload.md,隐私说明见docs/PRIVACY.md。
配置文件路径:~/.engram/settings.json
{
"aiProtocol": "openai",
"aiApiKey": "@keychain",
"aiModel": "gpt-4o-mini",
"titleProvider": "ollama",
"titleModel": "qwen2.5:3b"
}| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
aiProtocol |
string | "openai" |
AI 摘要协议;当前 Swift service 支持 OpenAI-compatible chat completions |
aiApiKey |
string | — | AI 摘要 API Key;优先存在 macOS Keychain,JSON 中可能是 @keychain |
aiModel |
string | "gpt-4o-mini" |
AI 摘要模型 |
titleProvider |
string | "ollama" |
标题生成 provider:ollama、openai 或 custom |
titleApiKey |
string | — | 标题生成 API Key;非 Ollama provider 使用,优先存在 macOS Keychain |
titleModel |
string | "qwen2.5:3b" |
标题生成模型 |
ollamaUrl |
string | "http://localhost:11434" |
旧版 embedding 兼容字段;Swift service 当前不使用 |
ollamaModel |
string | "nomic-embed-text" |
旧版 embedding 兼容字段;Swift service 当前不使用 |
embeddingDimension |
number | 768 |
旧版 embedding 兼容字段;Swift service 当前不使用 |
旧版 peer-sync settings keys (syncNodeName, syncEnabled, syncIntervalMinutes, syncPeers) 可能仍存在于旧配置中;Swift service 兼容保留但不会读取、启用或触发 peer sync。
产品适配器优先在 Swift 中实现;TypeScript 适配器保留为 dev/reference/fixture material。
Swift 产品路径:
- 适配器:
macos/Shared/EngramCore/Adapters/Sources/ - 索引与 backfill:
macos/EngramCoreWrite/Indexing/ - 服务命令:
macos/EngramService/Core/ - MCP 路由:
macos/EngramMCP/Core/MCPToolRegistry.swift
TypeScript reference 接口(src/adapters/types.ts):
interface SessionAdapter {
readonly name: SourceName // 工具标识符
detect(): Promise<boolean> // 检测该工具是否已安装
listSessionFiles(): AsyncGenerator<string> // 枚举所有会话文件路径
parseSessionInfo(filePath: string): Promise<SessionInfo | null> // 解析文件元数据
streamMessages(filePath: string, opts?: StreamMessagesOptions): AsyncGenerator<Message> // 流式读取消息
}步骤:
- 先在 Swift adapter/indexer/backfill 路径实现产品行为
- 补 Swift fixture/parity tests
- 仅当 retained TypeScript fixture tooling 需要时,再同步
src/adapters/** - 参照现有测试编写对应 Swift/TypeScript 回归测试
实现参考: Swift adapters 是产品实现;src/adapters/codex.ts 仍可作为历史/reference 参考。
- 索引库位置:
~/.engram/index.sqlite,存储元数据(会话 ID、时间、路径、摘要)、FTS 全文搜索索引和兼容保留的 embedding 表 - 原始文件: 完整消息内容始终从 AI 工具的原始日志文件流式读取,不做额外拷贝
- 隐私脱敏: 导出和 Web 预览使用内置敏感内容脱敏;索引库按原始本地日志建立 FTS,不支持用户配置的索引时正则替换
- 数据不离本机:
EngramService、EngramMCP和 macOS App 本地运行,不向任何远程服务发送数据(除非你显式配置并使用远程 AI 摘要/标题 provider)
EngramService periodically writes small user-data backups to
~/.engram/backups/user-data-<UTC yyyyMMdd-HHmmss>.sqlite. These are not whole
database snapshots and they are not project-directory file backups. They contain
only user-authored Engram rows: insights, manual/hidden/named session columns,
session_local_state without local_readable_path, project_aliases, and
migration_log, plus favorites and manually curated related-session links.
To restore after rebuilding ~/.engram/index.sqlite, first let Engram re-index
the original AI-tool session files so sessions.id rows exist again. Then quit
Engram and run a manual SQLite restore using the backup file:
ATTACH DATABASE '/Users/<you>/.engram/backups/user-data-YYYYMMDD-HHMMSS.sqlite' AS backup;
INSERT OR IGNORE INTO insights(
id, content, wing, room, source_session_id, importance, has_embedding,
created_at, insight_type, superseded_by, last_accessed_at, access_count
)
SELECT id, content, wing, room, source_session_id, importance, has_embedding,
created_at, insight_type, superseded_by, last_accessed_at, access_count
FROM backup.insights;
UPDATE sessions
SET parent_session_id = COALESCE(
(SELECT parent_session_id FROM backup.sessions b WHERE b.id = sessions.id AND b.link_source = 'manual'),
parent_session_id
),
link_source = COALESCE(
(SELECT link_source FROM backup.sessions b WHERE b.id = sessions.id AND b.link_source = 'manual'),
link_source
),
hidden_at = COALESCE((SELECT hidden_at FROM backup.sessions b WHERE b.id = sessions.id), hidden_at),
custom_name = COALESCE((SELECT custom_name FROM backup.sessions b WHERE b.id = sessions.id), custom_name)
WHERE id IN (SELECT id FROM backup.sessions);
INSERT OR IGNORE INTO session_local_state(session_id, hidden_at, custom_name)
SELECT session_id, hidden_at, custom_name FROM backup.session_local_state;
UPDATE session_local_state
SET hidden_at = COALESCE((SELECT hidden_at FROM backup.session_local_state b WHERE b.session_id = session_local_state.session_id), hidden_at),
custom_name = COALESCE((SELECT custom_name FROM backup.session_local_state b WHERE b.session_id = session_local_state.session_id), custom_name)
WHERE session_id IN (SELECT session_id FROM backup.session_local_state);
INSERT OR IGNORE INTO project_aliases(alias, canonical, created_at)
SELECT alias, canonical, created_at FROM backup.project_aliases;
INSERT OR IGNORE INTO migration_log(
id, old_path, new_path, old_basename, new_basename, state, files_patched,
occurrences, sessions_updated, alias_created, cc_dir_renamed, started_at,
finished_at, dry_run, rolled_back_of, audit_note, archived, actor, detail, error
)
SELECT id, old_path, new_path, old_basename, new_basename, state, files_patched,
occurrences, sessions_updated, alias_created, cc_dir_renamed, started_at,
finished_at, dry_run, rolled_back_of, audit_note, archived, actor, detail, error
FROM backup.migration_log;
CREATE TABLE IF NOT EXISTS favorites (
session_id TEXT PRIMARY KEY,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
INSERT OR IGNORE INTO favorites(session_id, created_at)
SELECT session_id, created_at FROM backup.favorites;
CREATE TABLE IF NOT EXISTS session_relations (
a_id TEXT NOT NULL,
b_id TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
PRIMARY KEY (a_id, b_id)
);
INSERT OR IGNORE INTO session_relations(a_id, b_id, created_at)
SELECT a_id, b_id, created_at FROM backup.session_relations;
DETACH DATABASE backup;Do not restore derived tables such as insights_fts, insight_embeddings,
session_tools, analytics tables, session_local_state.local_readable_path, or
legacy memory_insights; FTS, embedding state, and local readable paths are
regenerated from the source rows when maintenance runs.
npm test # 运行测试
npm run test:watch # 监听模式
npm run test:coverage # 覆盖率报告
npm run build # 编译 TypeScript dev/reference tooling -> dist/
npm run check:adapter-parity-fixtures # 校验 provider parser parity fixturesmacOS 应用(Menu Bar):
cd macos
xcodegen generate # 从 project.yml 生成 Xcode 项目
open Engram.xcodeproj # 在 Xcode 中构建和运行MIT