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

7.6 KiB

Plugin Development Guide

This guide helps developers work with the claude-mem plugin structure during development.

Quick Start

1. Build the Plugin

# 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:

# 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:

# 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:

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

Create a development marketplace to test your plugin:

# 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:

# 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:

DEBUG=claude-mem:* bun scripts/hooks/context-hook.js

Check Hook Output

Hooks write to stdout/stderr. Capture output:

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:

# 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

bun test tests/

Test Database Operations

# 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

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

Getting Help