---
title: "Development"
description: "Build from source, run tests, and contribute to claude-recall"
---
# 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/nhevers/claude-recall.git
cd claude-recall
# 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 `extension/runtime/`
3. Bundles MCP search server to `extension/runtime/search-server.cjs`
4. Bundles worker service to `extension/runtime/engine-runtime.cjs`
5. Bundles web viewer UI to `extension/web/viewer.html`
**Build Output**:
- Hook executables: `*-hook.js` (ESM format)
- Smart installer: `smart-install.js` (ESM format)
- Worker service: `engine-runtime.cjs` (CJS format)
- MCP server: `search-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 extension/runtime/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 extension/web/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: `npm run worker: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<YourCardProps> = ({ ... }) => {
return (
<div className="card">
{/* Your UI */}
</div>
);
};
```
2. Import and use in `Feed.tsx`:
```tsx
import { YourCard } from './cards/YourCard';
// In render logic:
{item.type === 'your_type' && <YourCard {...item} />}
```
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 '../common/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 `extension/lifecycle/lifecycle.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/core/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/core/sqlite/types.ts`:
```typescript
export interface Observation {
// ... existing fields
new_field?: string;
}
```
3. Update database methods in `src/core/sqlite/SessionStore.ts`:
```typescript
createObservation(obs: Observation) {
// Include new_field in INSERT
}
```
4. Test migration:
```bash
# Backup database first!
cp ~/.claude-recall/claude-recall.db ~/.claude-recall/claude-recall.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 `
<observation>
<!-- Add new XML structure -->
</observation>
`;
}
```
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/core/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-recall 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
npm run worker: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-recall/claude-recall.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-recall/claude-recall.db "PRAGMA integrity_check;"
```
**Hook Testing**:
```bash
# Test context hook manually
echo '{"session_id":"test-123","cwd":"'$(pwd)'","source":"startup"}' | node extension/runtime/context-hook.js
# Test new hook
echo '{"session_id":"test-123","cwd":"'$(pwd)'","prompt":"test"}' | node extension/runtime/new-hook.js
```
**Data Verification**:
```bash
# Check recent observations
sqlite3 ~/.claude-recall/claude-recall.db "
SELECT id, tool_name, created_at
FROM observations
ORDER BY created_at_epoch DESC
LIMIT 10;
"
# Check summaries
sqlite3 ~/.claude-recall/claude-recall.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-recall/claude-recall.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<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
```bash
export DEBUG=claude-recall:*
npm run worker:restart
npm run worker:logs
```
### Inspect Database
```bash
sqlite3 ~/.claude-recall/claude-recall.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-recall/claude-recall.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 extension/runtime/context-hook.js
# Test new hook
echo '{"session_id":"test-123","cwd":"'$(pwd)'","prompt":"test"}' | node extension/runtime/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 extension/.claude-extension/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 extension/runtime/*.js extension/runtime/*.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-recall/claude-recall.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_RECALL_WORKER_PORT=38000
npm run worker:start
```
## Next Steps
- [Architecture Overview](architecture/overview) - Understand the system
- [Configuration](configuration) - Customize claude-recall
- [Troubleshooting](troubleshooting) - Common issues