UNPKG

claudekit

Version:

CLI tools for Claude Code development workflow

318 lines (256 loc) 8.66 kB
# Embedded Hooks System - POC **Status**: ✅ POC Completed **Authors**: Claude, 2025-07-29 **Type**: Proof of Concept **Implementation Date**: 2025-07-30 ## POC Scope Minimal implementation to validate the core concept: TypeScript hooks in a separate binary with configuration support. ## POC Goals 1. ✅ Create `claudekit-hooks` binary with one hook (auto-checkpoint) 2. ✅ Basic configuration loading from `.claudekit/config.json` 3. ✅ Claude Code integration via stdin/stdout 4. ✅ Demonstrate exit code handling ## POC Non-Goals 1. ❌ **Multiple hooks** - Only implement auto-checkpoint, not all 6 2. ❌ **Project root discovery** - Only check current directory 3. ❌ **Error handling** - Basic errors only, no recovery strategies 4. ❌ **Logging system** - Just console.log/error, no log levels 5. ❌ **Package manager detection** - Hardcode to npx/pnpm 6. ❌ **Tests** - Manual testing only 7. ❌ **Performance** - No optimization or caching 8. ❌ **Cross-platform** - Can assume Unix-like environment 9. ❌ **Production build** - Simple tsc compilation is enough 10. ❌ **Installation process** - Manual npm link for testing ## Directory Structure ``` claudekit/ # Existing claudekit project ├── cli/ │ ├── cli.ts # Existing main CLI │ ├── commands/ # Existing commands │ ├── lib/ # Existing libraries │ ├── types/ # Existing types │ └── hooks-poc.ts # NEW: POC hooks CLI ├── bin/ │ ├── claudekit # Existing CLI wrapper │ └── claudekit-hooks-poc # NEW: POC binary wrapper ├── dist/ # Build output │ ├── cli.js # Existing compiled CLI │ ├── index.js # Existing exports │ └── hooks-poc.js # NEW: Compiled POC ├── src/ # Existing source │ ├── commands/ # Existing slash commands │ └── hooks/ # Existing shell hooks ├── .claudekit/ # NEW: Hook configuration │ └── config.json # NEW: POC config file └── package.json # MODIFIED: Add new binary # Test directory (separate) test-repo/ ├── .claudekit/ │ └── config.json └── file.txt # Test file with changes ``` ## Minimal Implementation ### 1. Single Hook Implementation ```typescript // cli/hooks-poc.ts #!/usr/bin/env node import { readFileSync, existsSync } from 'fs'; import { spawn } from 'child_process'; import * as path from 'path'; // Simple config interface interface Config { hooks?: { 'auto-checkpoint'?: { prefix?: string; maxCheckpoints?: number; }; }; } // Read stdin payload async function readStdin(): Promise<string> { return new Promise((resolve) => { let data = ''; process.stdin.on('data', chunk => data += chunk); process.stdin.on('end', () => resolve(data)); setTimeout(() => resolve(''), 1000); // Timeout fallback }); } // Main execution async function main() { const hookName = process.argv[2]; if (hookName !== 'auto-checkpoint') { console.error(`Unknown hook: ${hookName}`); process.exit(1); } // Load config if exists let config: Config = {}; const configPath = path.join(process.cwd(), '.claudekit/config.json'); if (existsSync(configPath)) { try { config = JSON.parse(readFileSync(configPath, 'utf-8')); } catch (e) { console.error('Invalid config file'); } } // Get hook config const hookConfig = config.hooks?.['auto-checkpoint'] || {}; const prefix = hookConfig.prefix || 'claude'; const maxCheckpoints = hookConfig.maxCheckpoints || 10; // Check if git repo const gitStatus = spawn('git', ['status', '--porcelain'], { stdio: ['ignore', 'pipe', 'pipe'] }); let stdout = ''; gitStatus.stdout.on('data', (data) => stdout += data); gitStatus.on('close', (code) => { if (code !== 0) { console.log('Not a git repository, skipping checkpoint'); process.exit(0); } // Check if there are changes if (!stdout.trim()) { console.log('No changes to checkpoint'); process.exit(0); } // Create checkpoint const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const message = `${prefix}-checkpoint-${timestamp}`; const stash = spawn('git', ['stash', 'push', '-m', message], { stdio: ['ignore', 'pipe', 'pipe'] }); stash.on('close', (stashCode) => { if (stashCode !== 0) { console.error('Failed to create checkpoint'); process.exit(1); } // Apply stash to restore working directory spawn('git', ['stash', 'apply'], { stdio: 'ignore' }).on('close', () => { console.log(`✅ Checkpoint created: ${message}`); process.exit(0); }); }); }); } main().catch(err => { console.error('Fatal error:', err); process.exit(1); }); ``` ### 2. Update existing package.json Add to the existing claudekit package.json: ```json { "bin": { "claudekit": "./bin/claudekit", "claudekit-hooks": "./bin/claudekit-hooks-poc" }, "scripts": { "build:hooks-poc": "tsc cli/hooks-poc.ts --outDir dist --module esnext --target es2022 --moduleResolution node --skipLibCheck" } } ``` And create `bin/claudekit-hooks-poc`: ```bash #!/usr/bin/env node import('../dist/hooks-poc.js'); ``` ### 3. Example .claudekit/config.json ```json { "hooks": { "auto-checkpoint": { "prefix": "claude", "maxCheckpoints": 10 } } } ``` ### 4. Claude Code Integration ```json { "hooks": { "Stop": [ { "matcher": "*", "hooks": [{"type": "command", "command": "claudekit-hooks auto-checkpoint"}] } ] } } ``` ## Testing the POC ### Manual Test Script ```bash #!/bin/bash # test-poc.sh # Setup: Create a git repo with changes git init test-repo cd test-repo echo "test content" > file.txt git add . git commit -m "initial" echo "changed" >> file.txt # Test 1: Auto-checkpoint with changes claudekit-hooks auto-checkpoint # Should create a checkpoint # Test 2: No changes git stash drop claudekit-hooks auto-checkpoint # Should say "No changes to checkpoint" # Test 3: With config mkdir .claudekit echo '{"hooks": {"auto-checkpoint": {"prefix": "test", "maxCheckpoints": 5}}}' > .claudekit/config.json echo "another change" >> file.txt claudekit-hooks auto-checkpoint # Should create checkpoint with "test" prefix ``` ## POC Success Criteria 1. ✅ Can execute as separate binary 2. ✅ Can read Claude Code JSON from stdin 3. ✅ Can load and respect configuration 4.Returns proper exit codes (0, 1) 5. ✅ Works with Claude Code hooks ## Next Steps After POC If POC is successful: 1. **Refactor to proper architecture** - Extract BaseHook class - Add HookRunner and Registry - Implement remaining hooks 2. **Add production features** - Proper error handling - Logging system - Build pipeline - Tests 3. **Performance optimization** - Command caching - Incremental checking ## POC Limitations - Single hook only (auto-checkpoint) - No proper error handling - No logging system - Minimal configuration - No tests - Hardcoded logic - Only checks current directory for config (no traversal) ## Implementation Time - POC: 1-2 hours (✅ Completed in ~2 hours) - Full implementation: 1-2 weeks - Testing & Documentation: 3-5 days - Total Project Time: 2-3 weeks ## POC Validation Results Based on the implementation and testing: 1. **Architecture Validated**: TypeScript-based separate binary approach proven successful 2. **Configuration System**: Works as designed with proper defaults 3. **Claude Code Integration**: Successfully integrates via hooks system 4. **Performance**: Acceptable for POC, no noticeable delays 5. **Exit Codes**: Properly returns 0 for success/skip, 1 for errors ## Lessons Learned 1. **Build Configuration**: Required `--moduleResolution node --skipLibCheck` flags for TypeScript compilation 2. **Binary Wrapper**: Simple direct import works best: `import('../dist/hooks-poc.js');` 3. **Testing Approach**: Manual test script sufficient for POC validation 4. **Configuration Location**: Current directory only approach is limiting but acceptable for POC ## Recommendation ✅ **Proceed with full implementation** based on validated POC architecture. For detailed validation results and implementation report, see: [POC Validation Report](../reports/POC_VALIDATION_REPORT.md)