Documentation Index
Fetch the complete documentation index at: https://docs.claude-mem.ai/llms.txt
Use this file to discover all available pages before exploring further.
Development Guide
Building from Source
Prerequisites
- Node.js 18.0.0 or higher
- npm (comes with Node.js)
- Git
Clone and Build
# Clone repository
git clone https://github.com/thedotmack/claude-mem.git
cd claude-mem
# Install dependencies
npm install
# Build all components
npm run build
Build Process
The build process uses esbuild to compile TypeScript:
- Compiles TypeScript to JavaScript
- Creates standalone executables for each hook in
plugin/scripts/
- Bundles MCP search server to
plugin/scripts/mcp-server.cjs
- Bundles worker service to
plugin/scripts/worker-service.cjs
- Bundles web viewer UI to
plugin/ui/viewer.html
Build Output:
- Hook executables:
*-hook.js (ESM format)
- Setup version-check:
version-check.js (ESM format, sub-100ms)
- Worker service:
worker-service.cjs (CJS format)
- MCP server:
mcp-server.cjs (CJS format)
- Viewer UI:
viewer.html (self-contained HTML bundle)
Build Scripts
# Build everything
npm run build
# Build only hooks
npm run build:hooks
# The build script is defined in scripts/build-hooks.js
Development Workflow
1. Make Changes
Edit TypeScript source files in src/:
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. Build
3. Test
# 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. Manual Testing
# 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. Iterate
Repeat steps 1-4 until your changes work as expected.
Viewer UI Development
Working with the React Viewer
The web viewer UI is a React application built into a self-contained HTML bundle.
Location: src/ui/viewer/
Structure:
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)
Building 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
Testing Viewer Changes
- Make changes to React components in
src/ui/viewer/
- Build:
npm run build
- Sync to installed plugin:
npm run sync-marketplace
- Restart worker:
npm run worker:restart
- Refresh browser at http://localhost:37777
Hot Reload: Not currently supported. Full rebuild + restart required for changes.
Adding New Viewer Features
Example: Adding a new card type
- Create component in
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>
);
};
- Import and use in
Feed.tsx:
import { YourCard } from './cards/YourCard';
// In render logic:
{item.type === 'your_type' && <YourCard {...item} />}
-
Update types if needed in
src/ui/viewer/types.ts
-
Rebuild and test
Viewer UI Architecture
Data Flow:
- Worker service exposes HTTP + SSE endpoints
- React app fetches initial data via HTTP (paginated)
- SSE connection provides real-time updates
- Custom hooks handle state management and data merging
- Components render cards based on item type
Key Patterns:
- Infinite Scroll:
usePagination hook with Intersection Observer
- Real-Time Updates:
useSSE hook with auto-reconnection
- Deduplication:
merge.ts utilities prevent duplicate items
- Settings Persistence:
useSettings hook with localStorage
- Theme Support: CSS variables with light/dark/system themes
Adding New Features
Adding a New Hook
- Create hook implementation in
src/hooks/your-hook.ts:
#!/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);
Note: As of v4.3.1, hooks are self-contained files. The shebang will be added automatically by esbuild during the build process.
- Add to
plugin/hooks/hooks.json:
{
"YourHook": [{
"hooks": [{
"type": "command",
"command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/your-hook.js",
"timeout": 120
}]
}]
}
- Rebuild:
Modifying Database Schema
- Add migration to
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
}
};
- Update types in
src/services/sqlite/types.ts:
export interface Observation {
// ... existing fields
new_field?: string;
}
- Update database methods in
src/services/sqlite/SessionStore.ts:
createObservation(obs: Observation) {
// Include new_field in INSERT
}
- Test migration:
# Backup database first!
cp ~/.claude-mem/claude-mem.db ~/.claude-mem/claude-mem.db.backup
# Run tests
npm test
Extending SDK Prompts
- Modify prompts in
src/sdk/prompts.ts:
export function buildObservationPrompt(observation: Observation): string {
return `
<observation>
</observation>
`;
}
- Update parser in
src/sdk/parser.ts:
export function parseObservation(xml: string): ParsedObservation {
// Parse new XML fields
}
- Test:
- Add tool definition in
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);
}
});
- Add search method in
src/services/sqlite/SessionSearch.ts:
yourNewSearch(params: YourParams): SearchResult[] {
// Implement FTS5 search
}
- Rebuild and test:
Testing
Testing Philosophy
Claude-mem relies on real-world usage and manual testing rather than traditional unit tests. The project philosophy prioritizes:
- Manual verification - Testing features in actual Claude Code sessions
- Integration testing - Running the full system end-to-end
- Database inspection - Verifying data correctness via SQLite queries
- CLI tools - Interactive tools for checking system state
- Observability - Comprehensive logging and worker health checks
This approach was chosen because:
- Hook behavior depends heavily on Claude Code’s runtime environment
- SDK interactions require real API calls and responses
- SQLite and Bun runtime provide stability guarantees
- Manual testing catches integration issues that unit tests miss
Manual Testing Workflow
When developing new features:
-
Build and sync:
npm run build
npm run sync-marketplace
npm run worker:restart
-
Test in real session:
- Start Claude Code
- Trigger the feature you’re testing
- Verify expected behavior
-
Check database state:
sqlite3 ~/.claude-mem/claude-mem.db "SELECT * FROM your_table;"
-
Monitor worker logs:
-
Verify queue health (for recovery features):
bun scripts/check-pending-queue.ts
Health Checks:
# Worker status
npm run worker:status
# Queue inspection
curl http://localhost:37777/api/pending-queue
# Database integrity
sqlite3 ~/.claude-mem/claude-mem.db "PRAGMA integrity_check;"
Hook Testing:
# 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
Data Verification:
# 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;
"
Recovery Feature Testing
For manual recovery features specifically:
-
Simulate stuck messages:
# Manually create stuck message (for testing only)
sqlite3 ~/.claude-mem/claude-mem.db "
UPDATE pending_messages
SET status = 'processing',
started_processing_at_epoch = strftime('%s', 'now', '-10 minutes') * 1000
WHERE id = 123;
"
-
Test recovery:
bun scripts/check-pending-queue.ts
-
Verify results:
curl http://localhost:37777/api/pending-queue | jq '.queue'
Regression Testing
Before releasing:
-
Test all hook triggers:
- SessionStart: Start new Claude Code session
- UserPromptSubmit: Submit a prompt
- PostToolUse: Use a tool like Read
- Summary: Let session complete
- SessionEnd: Close Claude Code
-
Test core features:
- Context injection (recent sessions appear)
- Observation processing (summaries generated)
- MCP search tools (search returns results)
- Viewer UI (loads at http://localhost:37777)
- Manual recovery (stuck messages recovered)
-
Test edge cases:
- Worker crash recovery
- Database locks
- Port conflicts
- Large databases
-
Cross-platform (if applicable):
Code Style
TypeScript Guidelines
- Use TypeScript strict mode
- Define interfaces for all data structures
- Use async/await for asynchronous code
- Handle errors explicitly
- Add JSDoc comments for public APIs
- Follow existing code formatting
- Use 2-space indentation
- Use single quotes for strings
- Add trailing commas in objects/arrays
Example
/**
* 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;
}
}
Debugging
Enable Debug Logging
export DEBUG=claude-mem:*
npm run worker:restart
npm run worker:logs
Inspect Database
sqlite3 ~/.claude-mem/claude-mem.db
# View schema
.schema observations
# Query data
SELECT * FROM observations LIMIT 10;
Trace Observations
Use correlation IDs to trace observations through the pipeline:
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;
Debug Hooks
Run hooks manually with test input:
# 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
Publishing
NPM Publishing
# Update version in package.json
npm version patch # or minor, or major
# Build
npm run build
# Publish to NPM
npm run release
The release script:
- Runs tests
- Builds all components
- Publishes to NPM registry
Creating a Release
- Update version in
package.json
- Update
CHANGELOG.md
- Commit changes
- Create git tag
- Push to GitHub
- Publish to NPM
# Manual version bump:
# 1. Update version in package.json
# 2. Update version in plugin/.claude-plugin/plugin.json
# 3. Update version at top of CLAUDE.md
# 4. Update version badge in README.md
# 5. Run: npm run build && npm run sync-marketplace
# Or use npm version command:
npm version 4.3.2
# Update changelog
# Edit CHANGELOG.md manually
# Commit
git add .
git commit -m "chore: Release v4.3.2"
# Tag
git tag v4.3.2
# Push
git push origin main --tags
# Publish to NPM
npm run release
Contributing
Contribution Workflow
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature)
- Make your changes
- Write tests
- Update documentation
- Commit your changes (
git commit -m 'Add amazing feature')
- Push to the branch (
git push origin feature/amazing-feature)
- Open a Pull Request
Pull Request Guidelines
- Clear title: Describe what the PR does
- Description: Explain why the change is needed
- Tests: Include tests for new features
- Documentation: Update docs as needed
- Changelog: Add entry to CHANGELOG.md
- Commits: Use clear, descriptive commit messages
Code Review Process
- Automated tests must pass
- Code review by maintainer
- Address feedback
- Final approval
- Merge to main
Recommended VSCode Extensions
- TypeScript
- ESLint
- Prettier
- SQLite Viewer
Useful Commands
# 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
Troubleshooting Development
Build Fails
-
Clean node_modules:
rm -rf node_modules
npm install
-
Check Node.js version:
node --version # Should be >= 18.0.0
-
Check for syntax errors:
Tests Fail
-
Check database:
rm ~/.claude-mem/claude-mem.db
npm test
-
Check worker status:
-
View logs:
Worker Won’t Start
-
Kill existing process:
-
Check port:
-
Try custom port:
export CLAUDE_MEM_WORKER_PORT=38000
npm run worker:start
Next Steps