开发指南¶
从源码构建¶
前置要求¶
- Node.js 20.0.0 或更高版本
- npm(随 Node.js 附带)
- Git
克隆与构建¶
# Clone repository
git clone https://github.com/thedotmack/claude-mem.git
cd claude-mem
# Install dependencies
npm install
# Build all components
npm run build
构建流程¶
构建流程使用 esbuild 编译 TypeScript:
- 将 TypeScript 编译为 JavaScript
- 为
plugin/scripts/中的每个 hook 创建独立可执行文件 - 将 MCP 搜索服务器打包为
plugin/scripts/mcp-server.cjs - 将 worker service 打包为
plugin/scripts/worker-service.cjs - 将 web viewer UI 打包为
plugin/ui/viewer.html
构建输出:
- Hook 可执行文件:*-hook.js(ESM 格式)
- 安装版本检查:version-check.js(ESM 格式,手动编写;安装缺失的插件依赖并记录安装标记)
- Worker service:worker-service.cjs(CJS 格式)
- MCP server:mcp-server.cjs(CJS 格式)
- Viewer UI:viewer.html(自包含 HTML bundle)
已提交的发布 bundle¶
Marketplace 安装会运行 plugin/scripts/ 中已提交的文件。当更改发布版本或 worker 生命周期代码时,请使用 npm run build 重新生成并提交受影响的运行时 bundle。较新的 manifest 与较旧的 worker bundle 搭配使用,可能会触发重复的版本不匹配重启。
CI 会在重新构建之前检查已提交的 bundle 版本:
如果替换后的 worker 仍然报告旧版本,hooks 会在数据目录中记录失败的回收尝试,并跳过对该未更改 bundle 的进一步回收。重新构建或重新安装 bundle 以修正版本;已更改的 bundle 允许再次进行回收尝试。
构建脚本¶
# Build everything
npm run build
# Build only hooks
npm run build:hooks
# The build script is defined in scripts/build-hooks.js
开发工作流¶
1. 进行更改¶
编辑 src/ 中的 TypeScript 源文件:
src/
├── hooks/ # Hook implementations (entry points + logic)
├── services/ # Worker service and database
├── servers/ # MCP search server
├── sdk/ # Claude Agent SDK integration
├── shared/ # Shared utilities
├── ui/
│ └── viewer/ # React web viewer UI components
└── utils/ # General utilities
2. 构建¶
3. 测试¶
# Run all tests
npm test
# Test specific file
node --test tests/session-lifecycle.test.ts
# Test context injection
npm run test:context
# Verbose context test
npm run test:context:verbose
4. 手动测试¶
# Start worker manually
npm run worker:start
# Check worker status
npm run worker:status
# View logs
npm run worker:logs
# Test hooks manually
echo '{"session_id":"test-123","cwd":"'$(pwd)'","source":"startup"}' | node plugin/scripts/context-hook.js
5. 迭代¶
重复步骤 1-4,直到你的更改按预期工作。
Viewer UI 开发¶
使用 React Viewer¶
Web viewer UI 是一个 React 应用,被打包成自包含的 HTML bundle。
位置:src/ui/viewer/
结构:
src/ui/viewer/
├── index.tsx # Entry point
├── App.tsx # Main application component
├── components/ # React components
│ ├── Header.tsx # Header with logo and actions
│ ├── Sidebar.tsx # Project filter sidebar
│ ├── Feed.tsx # Main feed with infinite scroll
│ ├── cards/ # Card components
│ │ ├── ObservationCard.tsx
│ │ ├── PromptCard.tsx
│ │ ├── SummaryCard.tsx
│ │ └── SkeletonCard.tsx
├── hooks/ # Custom React hooks
│ ├── useSSE.ts # Server-Sent Events connection
│ ├── usePagination.ts # Infinite scroll pagination
│ ├── useSettings.ts # Settings persistence
│ └── useStats.ts # Database statistics
├── utils/ # Utilities
│ ├── constants.ts # Constants (API URLs, etc.)
│ ├── formatters.ts # Date/time formatting
│ └── merge.ts # Data merging and deduplication
└── assets/ # Static assets (fonts, logos)
构建 Viewer UI¶
# Build everything including viewer
npm run build
# The viewer is built to plugin/ui/viewer.html
# It's a self-contained HTML file with inlined JS and CSS
测试 Viewer 更改¶
- 在
src/ui/viewer/中对 React 组件进行更改 - 构建:
npm run build - 同步到已安装的插件:
npm run sync-marketplace - 重启 worker:
npm run worker:restart - 刷新
npm run worker:status打印的 worker URL
热重载:目前不支持。更改需要完全重新构建 + 重启。
添加新的 Viewer 功能¶
示例:添加新的卡片类型
- 在
src/ui/viewer/components/cards/YourCard.tsx中创建组件:
import React from 'react';
export interface YourCardProps {
// Your data structure
}
export const YourCard: React.FC<YourCardProps> = ({ ... }) => {
return (
<div className="card">
{/* Your UI */}
</div>
);
};
- 在
Feed.tsx中导入并使用:
import { YourCard } from './cards/YourCard';
// In render logic:
{item.type === 'your_type' && <YourCard {...item} />}
-
如有需要,在
src/ui/viewer/types.ts中更新类型 -
重新构建并测试
Viewer UI 架构¶
数据流: 1. Worker service 公开 HTTP + SSE 端点 2. React 应用通过 HTTP(分页)获取初始数据 3. SSE 连接提供实时更新 4. 自定义 hooks 处理状态管理和数据合并 5. 组件根据条目类型渲染卡片
关键模式:
- 无限滚动:usePagination hook 搭配 Intersection Observer
- 实时更新:useSSE hook 支持自动重连
- 去重:merge.ts 工具函数防止重复条目
- 设置持久化:useSettings hook 搭配 localStorage
- 主题支持:CSS 变量,支持浅色/深色/系统主题
添加新功能¶
添加新 Hook¶
- 在
src/hooks/your-hook.ts中创建 Hook 实现:
#!/usr/bin/env node
import { readStdin } from '../shared/stdin';
async function main() {
const input = await readStdin();
// Hook implementation
const result = {
hookSpecificOutput: 'Optional output'
};
console.log(JSON.stringify(result));
}
main().catch(console.error);
注意:从 v4.3.1 开始,Hook 是自包含文件。shebang 将在构建过程中由 esbuild 自动添加。
- 添加到
plugin/hooks/hooks.json:
{
"YourHook": [{
"hooks": [{
"type": "command",
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/your-hook.js",
"timeout": 120
}]
}]
}
- 重新构建:
修改数据库 Schema¶
- 向
src/services/sqlite/migrations.ts添加迁移:
export const migration011: Migration = {
version: 11,
up: (db: Database) => {
db.run(`
ALTER TABLE observations ADD COLUMN new_field TEXT;
`);
},
down: (db: Database) => {
// Optional: define rollback
}
};
- 更新
src/services/sqlite/types.ts中的类型:
- 更新
src/services/sqlite/SessionStore.ts中的数据库方法:
- 测试迁移:
# Backup database first!
cp ~/.claude-mem/claude-mem.db ~/.claude-mem/claude-mem.db.backup
# Run tests
npm test
扩展 SDK Prompts¶
- 修改
src/sdk/prompts.ts中的 Prompts:
export function buildObservationPrompt(observation: Observation): string {
return `
<observation>
</observation>
`;
}
- 更新
src/sdk/parser.ts中的解析器:
- 测试:
添加 MCP 搜索工具¶
- 在
src/servers/mcp-server.ts中添加工具定义:
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === 'your_new_tool') {
// Implement tool logic
const results = await search.yourNewSearch(params);
return formatResults(results);
}
});
- 在
src/services/sqlite/SessionSearch.ts中添加搜索方法:
- 重新构建并测试:
测试¶
测试理念¶
Claude-mem 依赖真实使用场景和手动测试,而非传统的单元测试。项目理念优先考虑:
- 手动验证 - 在实际的 Claude Code 会话中测试功能
- 集成测试 - 端到端运行完整系统
- 数据库检查 - 通过 SQLite 查询验证数据正确性
- CLI 工具 - 用于检查系统状态的交互式工具
- 可观测性 - 全面的日志记录和 worker 健康检查
选择这种方法是因为: - Hook 行为在很大程度上依赖于 Claude Code 的运行时环境 - SDK 交互需要真实的 API 调用和响应 - SQLite 和 Bun 运行时提供了稳定性保证 - 手动测试能捕获单元测试遗漏的集成问题
手动测试工作流程¶
在开发新功能时:
-
构建并同步:
-
在真实会话中测试:
- 启动 Claude Code
- 触发你要测试的功能
-
验证预期行为
-
检查数据库状态:
-
监控 worker 日志:
-
验证队列健康状态(用于恢复功能):
测试工具¶
健康检查:
# Worker status
npm run worker:status
# Queue inspection
WORKER_PORT=$(jq -r .CLAUDE_MEM_WORKER_PORT ~/.claude-mem/settings.json)
curl http://127.0.0.1:$WORKER_PORT/api/pending-queue
# Database integrity
sqlite3 ~/.claude-mem/claude-mem.db "PRAGMA integrity_check;"
Hook 测试:
# Test context hook manually
echo '{"session_id":"test-123","cwd":"'$(pwd)'","source":"startup"}' | node plugin/scripts/context-hook.js
# Test new hook
echo '{"session_id":"test-123","cwd":"'$(pwd)'","prompt":"test"}' | node plugin/scripts/new-hook.js
数据验证:
# Check recent observations
sqlite3 ~/.claude-mem/claude-mem.db "
SELECT id, tool_name, created_at
FROM observations
ORDER BY created_at_epoch DESC
LIMIT 10;
"
# Check summaries
sqlite3 ~/.claude-mem/claude-mem.db "
SELECT id, request, completed
FROM session_summaries
ORDER BY created_at_epoch DESC
LIMIT 5;
"
恢复功能测试¶
具体针对手动恢复功能:
-
模拟卡住的消息:
-
测试恢复:
-
验证结果:
回归测试¶
在发布之前:
- 测试所有 Hook 触发器:
- SessionStart:启动新的 Claude Code 会话
- UserPromptSubmit:提交一个 Prompt
- PostToolUse:使用 Read 之类的工具
- Summary:让会话完成
-
SessionEnd:关闭 Claude Code
-
测试核心功能:
- 上下文注入(显示最近的会话)
- Observation 处理(生成 summaries)
- MCP 搜索工具(搜索返回结果)
- Viewer UI(在 worker URL 加载)
-
手动恢复(恢复卡住的消息)
-
测试边缘情况:
- Worker 崩溃恢复
- 数据库锁
- 端口冲突
-
大型数据库
-
跨平台(如适用):
- macOS
- Linux
- Windows
代码风格¶
TypeScript 指南¶
- 使用 TypeScript 严格模式
- 为所有数据结构定义接口
- 对异步代码使用 async/await
- 显式处理错误
- 为公共 API 添加 JSDoc 注释
格式化¶
- 遵循现有的代码格式
- 使用 2 空格缩进
- 字符串使用单引号
- 在对象/数组末尾添加尾随逗号
示例¶
/**
* Create a new observation in the database
*/
export async function createObservation(
obs: Observation
): Promise<number> {
try {
const result = await db.insert('observations', {
session_id: obs.session_id,
tool_name: obs.tool_name,
// ...
});
return result.id;
} catch (error) {
logger.error('Failed to create observation', error);
throw error;
}
}
调试¶
启用调试日志¶
检查数据库¶
sqlite3 ~/.claude-mem/claude-mem.db
# View schema
.schema observations
# Query data
SELECT * FROM observations LIMIT 10;
追踪观测记录¶
使用关联 ID 在管道中追踪观测记录:
sqlite3 ~/.claude-mem/claude-mem.db
SELECT correlation_id, tool_name, created_at
FROM observations
WHERE session_id = 'YOUR_SESSION_ID'
ORDER BY created_at;
调试 Hooks¶
使用测试输入手动运行 hooks:
# Test context hook
echo '{"session_id":"test-123","cwd":"'$(pwd)'","source":"startup"}' | node plugin/scripts/context-hook.js
# Test new hook
echo '{"session_id":"test-123","cwd":"'$(pwd)'","prompt":"test"}' | node plugin/scripts/new-hook.js
发布¶
发布仅从 main 分支进行,拉取请求也以 main 为目标。
版本号更新¶
更新每个包含包版本的 manifest:
package.jsonplugin/package.json.claude-plugin/marketplace.json.claude-plugin/plugin.jsonplugin/.claude-plugin/plugin.json.codex-plugin/plugin.jsonplugin/.codex-plugin/plugin.jsonopenclaw/openclaw.plugin.json
确认旧版本已全部移除,新版本出现在所有预期位置:
构建、打标签和发布¶
npm run build-and-sync
git add -A
git commit -m "chore: bump version to NEW_VERSION"
git tag -a vNEW_VERSION -m "Version NEW_VERSION"
git push origin main
git push origin vNEW_VERSION
发布到 npm 需要维护者的 npm 凭据和双重身份验证(2FA):
只有在 npm 确认版本可用之后,再创建 GitHub release 并重新生成变更日志:
gh release create vNEW_VERSION --title "vNEW_VERSION" --notes "..."
npm run changelog:generate
git add CHANGELOG.md
git commit -m "docs: regenerate changelog for vNEW_VERSION"
git push origin main
请勿手动编辑 CHANGELOG.md;它由 GitHub releases 自动生成。
贡献¶
贡献流程¶
- Fork 仓库
- 创建功能分支(
git checkout -b feature/amazing-feature) - 进行你的更改
- 编写测试
- 更新文档
- 提交你的更改(
git commit -m 'Add amazing feature') - 推送到分支(
git push origin feature/amazing-feature) - 开启一个 Pull Request
Pull Request 指南¶
- 清晰的标题:描述 PR 的作用
- 描述:解释为什么需要此更改
- 测试:包含新功能的测试
- 文档:根据需要更新文档
- 变更日志:请勿手动编辑
CHANGELOG.md;它在发布时自动生成 - 提交信息:使用清晰、描述性的提交信息
代码审查流程¶
- 自动化测试必须通过
- 维护者进行代码审查
- 处理反馈
- 最终批准
- 合并到 main
开发工具¶
推荐的 VSCode 扩展¶
- TypeScript
- ESLint
- Prettier
- SQLite Viewer
常用命令¶
# Check TypeScript types
npx tsc --noEmit
# Lint code (if configured)
npm run lint
# Format code (if configured)
npm run format
# Clean build artifacts
rm -rf plugin/scripts/*.js plugin/scripts/*.cjs
开发故障排查¶
构建失败¶
-
清理 node_modules:
-
检查 Node.js 版本:
-
检查语法错误:
测试失败¶
-
检查数据库:
-
检查 Worker 状态:
-
查看日志:
Worker 无法启动¶
-
终止现有进程:
-
检查端口:
-
尝试自定义端口:
后续步骤¶
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/claude-mem