--- title: "Development" description: "Build from source, run tests, and contribute to Claude-Mem" --- # Development Guide ## Building from Source ### Prerequisites - Node.js 18.0.0 or higher - npm (comes with Node.js) - Git ### Clone and Build ```bash # 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: 1. Compiles TypeScript to JavaScript 2. Creates standalone executables for each hook in `plugin/scripts/` 3. Bundles MCP search server to `plugin/scripts/mcp-server.cjs` 4. Bundles worker service to `plugin/scripts/worker-service.cjs` 5. Bundles web viewer UI to `plugin/ui/viewer.html` **Build Output**: - Hook executables: `*-hook.js` (ESM format) - Smart installer: `smart-install.js` (ESM format) - Worker service: `worker-service.cjs` (CJS format) - MCP server: `mcp-server.cjs` (CJS format) - Viewer UI: `viewer.html` (self-contained HTML bundle) ### Build Scripts ```bash # 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 ```bash npm run build ``` ### 3. Test ```bash # 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 ```bash # 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 ```bash # 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 1. Make changes to React components in `src/ui/viewer/` 2. Build: `npm run build` 3. Sync to installed plugin: `npm run sync-marketplace` 4. Restart worker: `claude-mem restart` 5. 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** 1. Create component in `src/ui/viewer/components/cards/YourCard.tsx`: ```tsx import React from 'react'; export interface YourCardProps { // Your data structure } export const YourCard: React.FC = ({ ... }) => { return (
{/* Your UI */}
); }; ``` 2. Import and use in `Feed.tsx`: ```tsx import { YourCard } from './cards/YourCard'; // In render logic: {item.type === 'your_type' && } ``` 3. Update types if needed in `src/ui/viewer/types.ts` 4. Rebuild and test ### Viewer UI Architecture **Data Flow**: 1. Worker service exposes HTTP + SSE endpoints 2. React app fetches initial data via HTTP (paginated) 3. SSE connection provides real-time updates 4. Custom hooks handle state management and data merging 5. 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 1. Create hook implementation in `src/hooks/your-hook.ts`: ```typescript #!/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. 2. Add to `plugin/hooks/hooks.json`: ```json { "YourHook": [{ "hooks": [{ "type": "command", "command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/your-hook.js", "timeout": 120 }] }] } ``` 4. Rebuild: ```bash npm run build ``` ### Modifying Database Schema 1. Add migration to `src/services/sqlite/migrations.ts`: ```typescript 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 } }; ``` 2. Update types in `src/services/sqlite/types.ts`: ```typescript export interface Observation { // ... existing fields new_field?: string; } ``` 3. Update database methods in `src/services/sqlite/SessionStore.ts`: ```typescript createObservation(obs: Observation) { // Include new_field in INSERT } ``` 4. Test migration: ```bash # Backup database first! cp ~/.claude-mem/claude-mem.db ~/.claude-mem/claude-mem.db.backup # Run tests npm test ``` ### Extending SDK Prompts 1. Modify prompts in `src/sdk/prompts.ts`: ```typescript export function buildObservationPrompt(observation: Observation): string { return ` `; } ``` 2. Update parser in `src/sdk/parser.ts`: ```typescript export function parseObservation(xml: string): ParsedObservation { // Parse new XML fields } ``` 3. Test: ```bash npm test ``` ### Adding MCP Search Tools 1. Add tool definition in `src/servers/mcp-server.ts`: ```typescript server.setRequestHandler(CallToolRequestSchema, async (request) => { if (request.params.name === 'your_new_tool') { // Implement tool logic const results = await search.yourNewSearch(params); return formatResults(results); } }); ``` 2. Add search method in `src/services/sqlite/SessionSearch.ts`: ```typescript yourNewSearch(params: YourParams): SearchResult[] { // Implement FTS5 search } ``` 3. Rebuild and test: ```bash npm run build npm test ``` ## Testing ### Testing Philosophy Claude-mem relies on **real-world usage and manual testing** rather than traditional unit tests. The project philosophy prioritizes: 1. **Manual verification** - Testing features in actual Claude Code sessions 2. **Integration testing** - Running the full system end-to-end 3. **Database inspection** - Verifying data correctness via SQLite queries 4. **CLI tools** - Interactive tools for checking system state 5. **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: 1. **Build and sync**: ```bash npm run build npm run sync-marketplace claude-mem restart ``` 2. **Test in real session**: - Start Claude Code - Trigger the feature you're testing - Verify expected behavior 3. **Check database state**: ```bash sqlite3 ~/.claude-mem/claude-mem.db "SELECT * FROM your_table;" ``` 4. **Monitor worker logs**: ```bash npm run worker:logs ``` 5. **Verify queue health** (for recovery features): ```bash bun scripts/check-pending-queue.ts ``` ### Testing Tools **Health Checks**: ```bash # 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**: ```bash # 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**: ```bash # 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: 1. **Simulate stuck messages**: ```bash # 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; " ``` 2. **Test recovery**: ```bash bun scripts/check-pending-queue.ts ``` 3. **Verify results**: ```bash curl http://localhost:37777/api/pending-queue | jq '.queue' ``` ### Regression Testing Before releasing: 1. **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 2. **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) 3. **Test edge cases**: - Worker crash recovery - Database locks - Port conflicts - Large databases 4. **Cross-platform** (if applicable): - macOS - Linux - Windows ## 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 ### Formatting - Follow existing code formatting - Use 2-space indentation - Use single quotes for strings - Add trailing commas in objects/arrays ### Example ```typescript /** * Create a new observation in the database */ export async function createObservation( obs: Observation ): Promise { 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 ```bash export DEBUG=claude-mem:* claude-mem restart npm run worker:logs ``` ### Inspect Database ```bash 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: ```bash 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: ```bash # 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 ```bash # 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: 1. Runs tests 2. Builds all components 3. Publishes to NPM registry ### Creating a Release 1. Update version in `package.json` 2. Update `CHANGELOG.md` 3. Commit changes 4. Create git tag 5. Push to GitHub 6. Publish to NPM ```bash # 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 1. Fork the repository 2. Create a feature branch (`git checkout -b feature/amazing-feature`) 3. Make your changes 4. Write tests 5. Update documentation 6. Commit your changes (`git commit -m 'Add amazing feature'`) 7. Push to the branch (`git push origin feature/amazing-feature`) 8. 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 1. Automated tests must pass 2. Code review by maintainer 3. Address feedback 4. Final approval 5. Merge to main ## Development Tools ### Recommended VSCode Extensions - TypeScript - ESLint - Prettier - SQLite Viewer ### Useful Commands ```bash # 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 1. Clean node_modules: ```bash rm -rf node_modules npm install ``` 2. Check Node.js version: ```bash node --version # Should be >= 18.0.0 ``` 3. Check for syntax errors: ```bash npx tsc --noEmit ``` ### Tests Fail 1. Check database: ```bash rm ~/.claude-mem/claude-mem.db npm test ``` 2. Check worker status: ```bash npm run worker:status ``` 3. View logs: ```bash npm run worker:logs ``` ### Worker Won't Start 1. Kill existing process: ```bash npm run worker:stop ``` 2. Check port: ```bash lsof -i :37777 ``` 3. Try custom port: ```bash export CLAUDE_MEM_WORKER_PORT=38000 npm run worker:start ``` ## Next Steps - [Architecture Overview](architecture/overview) - Understand the system - [Configuration](configuration) - Customize Claude-Mem - [Troubleshooting](troubleshooting) - Common issues