mirror of
https://github.com/thedotmack/claude-mem
synced 2026-04-25 17:15:04 +02:00
776 lines
16 KiB
Plaintext
776 lines
16 KiB
Plaintext
---
|
|
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: `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 '../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 `
|
|
<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/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
|
|
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-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<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-mem:*
|
|
npm run worker: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
|