Skip to content

bbingz/engram

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1,052 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Engram

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 ⚠️ cache-only
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-callinput / 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)。之后通过文件监听增量更新,无需手动维护。

注册为 MCP Server

Claude Code

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

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 的客户端

任何支持 MCP stdio transport 的客户端均可使用。将以下内容加入对应的 MCP 配置:

{
  "command": "/Applications/Engram.app/Contents/Helpers/EngramMCP",
  "args": []
}

macOS App

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 当前已启用语义搜索。

MCP Tools 参考

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 门控;不可用时 searchModeUnavailable hard-error(静默降级为 keyword 结果体)。
  • App UI:故意 keyword-only(mode pills 不暴露 semantic);不代表 service/MCP 无向量路径。
  • insights memoryget_memory 在有 insight embeddings 时走 hybrid,否则 FTS/recent fallback。

搜索覆盖范围:用户消息 + 助手回复 + 会话摘要 + 部分 adapter 归一化后的工具/诊断文本。

搜索还支持直接粘贴会话 UUID 进行精确查找,无需关键词或语义匹配。

Embedding 配置状态

字段 / 环境变量 当前状态
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_contextsearchlist_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:ollamaopenaicustom
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>  // 流式读取消息
}

步骤:

  1. 先在 Swift adapter/indexer/backfill 路径实现产品行为
  2. 补 Swift fixture/parity tests
  3. 仅当 retained TypeScript fixture tooling 需要时,再同步 src/adapters/**
  4. 参照现有测试编写对应 Swift/TypeScript 回归测试

实现参考: Swift adapters 是产品实现;src/adapters/codex.ts 仍可作为历史/reference 参考。

数据存储与隐私

  • 索引库位置: ~/.engram/index.sqlite,存储元数据(会话 ID、时间、路径、摘要)、FTS 全文搜索索引和兼容保留的 embedding 表
  • 原始文件: 完整消息内容始终从 AI 工具的原始日志文件流式读取,不做额外拷贝
  • 隐私脱敏: 导出和 Web 预览使用内置敏感内容脱敏;索引库按原始本地日志建立 FTS,不支持用户配置的索引时正则替换
  • 数据不离本机: EngramServiceEngramMCP 和 macOS App 本地运行,不向任何远程服务发送数据(除非你显式配置并使用远程 AI 摘要/标题 provider)

Restoring user data

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 fixtures

macOS 应用(Menu Bar):

cd macos
xcodegen generate     # 从 project.yml 生成 Xcode 项目
open Engram.xcodeproj # 在 Xcode 中构建和运行

License

MIT

About

Cross-tool AI session aggregator: TypeScript MCP server and macOS menu bar app

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors