Files
claude-mem/docs/PLUGIN_DEVELOPMENT.md
T
Alex Newman 834cf4095e Add standalone hook entry points for context, new, save, summary, and worker
- Implemented context-hook.ts for handling session start events.
- Created new-hook.ts for user prompt submission events.
- Developed save-hook.ts for post tool use events.
- Added summary-hook.ts for handling stop events.
- Introduced worker.ts as a standalone background process for the SDK agent.
- Each hook reads input from stdin, processes it, and handles errors gracefully.
2025-10-16 15:39:30 -04:00

309 lines
7.6 KiB
Markdown

# Plugin Development Guide
This guide helps developers work with the claude-mem plugin structure during development.
## Quick Start
### 1. Build the Plugin
```bash
# Build both CLI and hooks
npm run build
# Or build separately
npm run build:cli # Just the CLI
npm run build:hooks # Just the hooks
```
### 2. Test Hooks Locally
Test individual hooks by piping JSON input:
```bash
# Test context hook (SessionStart)
printf '{"session_id":"test-123","cwd":"/Users/you/project","source":"startup"}' | \
bun scripts/hooks/context-hook.js
# Test new hook (UserPromptSubmit)
printf '{"session_id":"test-123","cwd":"/Users/you/project","prompt":"help me code"}' | \
bun scripts/hooks/new-hook.js
# Test save hook (PostToolUse)
printf '{"session_id":"test-123","cwd":"/Users/you/project","tool_name":"Read","tool_input":{},"tool_output":{}}' | \
bun scripts/hooks/save-hook.js
# Test summary hook (Stop)
printf '{"session_id":"test-123","cwd":"/Users/you/project"}' | \
bun scripts/hooks/summary-hook.js
# Test worker (requires valid session ID in database)
bun scripts/hooks/worker.js 999
```
### 3. Test Worker with Plugin Root
Verify the new-hook correctly detects plugin mode:
```bash
# Without CLAUDE_PLUGIN_ROOT (traditional mode)
printf '{"session_id":"test-new","cwd":"/path","prompt":"test"}' | \
bun scripts/hooks/new-hook.js
# With CLAUDE_PLUGIN_ROOT (plugin mode)
CLAUDE_PLUGIN_ROOT=$(pwd) printf '{"session_id":"test-plugin","cwd":"/path","prompt":"test"}' | \
bun scripts/hooks/new-hook.js
```
In plugin mode, the new-hook will attempt to spawn `bun ${CLAUDE_PLUGIN_ROOT}/scripts/hooks/worker.js`.
In traditional mode, it will attempt to spawn `claude-mem worker`.
### 4. Test With No Input
Each hook should handle missing input gracefully:
```bash
echo '' | bun scripts/hooks/context-hook.js
# Output: No input provided - this script is designed to run as a Claude Code SessionStart hook
```
## Local Plugin Testing
### Option 1: Dev Marketplace (Recommended)
Create a development marketplace to test your plugin:
```bash
# Create marketplace structure
mkdir -p ~/dev-marketplace/.claude-plugin
# Create marketplace manifest
cat > ~/dev-marketplace/.claude-plugin/marketplace.json << 'EOF'
{
"name": "dev-marketplace",
"owner": {
"name": "Developer"
},
"plugins": [
{
"name": "claude-mem",
"source": "./claude-mem-plugin",
"description": "Persistent memory system for Claude Code"
}
]
}
EOF
# Symlink your working directory
ln -s /path/to/your/claude-mem ~/dev-marketplace/claude-mem-plugin
```
Then in Claude Code:
```
/plugin marketplace add /absolute/path/to/dev-marketplace
/plugin install claude-mem@dev-marketplace
```
### Option 2: Direct Testing
Test the CLI commands directly:
```bash
# Build first
npm run build
# Test CLI commands
./dist/claude-mem.min.js --version
./dist/claude-mem.min.js status
./dist/claude-mem.min.js --help
```
## Development Workflow
### Making Changes to Hooks
1. **Edit TypeScript source** in `src/hooks/` or `src/bin/hooks/`
2. **Rebuild hooks**: `npm run build:hooks`
3. **Test locally**: Use echo piping method above
4. **Reinstall plugin** (if testing in Claude Code):
```
/plugin uninstall claude-mem@dev-marketplace
/plugin install claude-mem@dev-marketplace
```
### Making Changes to CLI
1. **Edit TypeScript source** in `src/`
2. **Rebuild CLI**: `npm run build:cli`
3. **Test directly**: `./dist/claude-mem.min.js [command]`
### Making Changes to Commands
1. **Edit markdown files** in `commands/`
2. **No rebuild needed** (commands are read directly)
3. **Reinstall plugin** to pick up changes:
```
/plugin uninstall claude-mem@dev-marketplace
/plugin install claude-mem@dev-marketplace
```
## Debugging
### Enable Verbose Logging
Set environment variables for more detailed output:
```bash
DEBUG=claude-mem:* bun scripts/hooks/context-hook.js
```
### Check Hook Output
Hooks write to stdout/stderr. Capture output:
```bash
echo '{"session_id":"test","cwd":"/path"}' | \
bun scripts/hooks/context-hook.js 2>&1 | tee hook-output.log
```
### Verify Plugin Root Variable
Test that `${CLAUDE_PLUGIN_ROOT}` resolves correctly:
```bash
# Manually set it for testing
export CLAUDE_PLUGIN_ROOT=/path/to/your/plugin
echo '{"session_id":"test","cwd":"/path"}' | \
bun ${CLAUDE_PLUGIN_ROOT}/scripts/hooks/context-hook.js
```
## Build System Details
### Hook Build Process
The `scripts/build-hooks.js` script:
1. Reads each entry point from `src/bin/hooks/`
2. Bundles with Bun build system
3. Minifies output
4. Adds shebang for direct execution
5. Sets executable permissions
6. Outputs to `scripts/hooks/`
### CLI Build Process
The `scripts/build.js` script:
1. Bundles main CLI from `src/bin/cli.ts`
2. Externalizes large dependencies
3. Minifies output
4. Adds shebang
5. Sets executable permissions
6. Outputs to `dist/claude-mem.min.js`
### Build Configuration
Both builds use similar Bun configuration:
- **Target**: `bun` runtime
- **Minify**: `true`
- **External**: `bun:sqlite` (native module)
- **Define**: `__DEFAULT_PACKAGE_VERSION__` from package.json
## Testing
### Run Tests
```bash
bun test tests/
```
### Test Database Operations
```bash
# Test hooks database
bun test tests/hooks-database-integration.test.ts
# Test session lifecycle
bun test tests/session-lifecycle.test.ts
```
## Publishing
### Pre-publish Checklist
- [ ] All tests pass: `bun test tests/`
- [ ] Build succeeds: `npm run build`
- [ ] Version updated in `package.json`
- [ ] Changelog updated in `docs/CHANGELOG.md`
- [ ] Plugin.json version matches package.json
- [ ] Hooks tested locally
- [ ] CLI tested locally
### Publish to npm
```bash
npm run publish:npm
```
This will:
1. Run `prepublishOnly` script (builds everything)
2. Publish to npm registry
3. Include files listed in `package.json` "files" array
### Files Included in Package
The npm package includes:
- `dist/` - Compiled CLI
- `scripts/` - Compiled hooks
- `commands/` - Slash command definitions
- `hooks/` - Hook configuration
- `.claude-plugin/` - Plugin metadata
- `src/` - TypeScript source (for reference)
- `docs/` - Documentation
- `.mcp.json` - MCP server configuration
## Troubleshooting
### Build Fails
**Problem**: `bun: command not found`
**Solution**: Install Bun from https://bun.sh
**Problem**: Build errors with external dependencies
**Solution**: Check that `bun:sqlite` is not bundled (should be external)
### Hooks Don't Execute
**Problem**: `Permission denied` when executing hooks
**Solution**: Ensure scripts are executable: `chmod +x scripts/hooks/*.js`
**Problem**: Hooks exit silently
**Solution**: Check error handling - hooks catch all errors and exit gracefully
### Plugin Not Found
**Problem**: `/plugin install` can't find claude-mem
**Solution**:
1. Verify marketplace is added: `/plugin marketplace list`
2. Check marketplace manifest includes claude-mem
3. Refresh marketplace: `/plugin marketplace refresh`
## Tips
1. **Use symlinks** in dev marketplace for faster iteration
2. **Test hooks with edge cases** (empty input, malformed JSON)
3. **Check file sizes** after build to catch bloat
4. **Version everything together** (CLI, hooks, plugin.json)
5. **Document breaking changes** in CHANGELOG.md
## Resources
- [Plugin Structure Documentation](./PLUGIN_STRUCTURE.md)
- [Plugin Installation Guide](./PLUGIN_INSTALLATION.md)
- [Build Documentation](./BUILD.md)
- [Claude Code Plugins Docs](https://docs.claude.com/en/docs/claude-code/plugins)
- [Bun Documentation](https://bun.sh/docs)
## Getting Help
- **Issues**: https://github.com/thedotmack/claude-mem/issues
- **Discussions**: https://github.com/thedotmack/claude-mem/discussions