diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 00000000..135bdb2a --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://anthropic.com/claude-code/marketplace.schema.json", + "name": "kaizen-marketplace", + "description": "Kaizen plugins for Claude Code - continuous improvement through auto-generated guidelines", + "owner": { + "name": "Vinod Muthusamy" + }, + "plugins": [ + { + "name": "kaizen", + "description": "Learn from conversations with auto-generated guidelines", + "version": "1.0.0", + "author": { + "name": "Vinod Muthusamy" + }, + "source": "./plugins/kaizen", + "category": "productivity" + } + ] +} diff --git a/plugins/kaizen/.claude-plugin/plugin.json b/plugins/kaizen/.claude-plugin/plugin.json new file mode 100644 index 00000000..5be3ae58 --- /dev/null +++ b/plugins/kaizen/.claude-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "kaizen", + "version": "1.0.0", + "description": "Learn from conversations with auto-generated guidelines", + "author": { + "name": "Vinod Muthusamy" + }, + "skills": "./skills/" +} \ No newline at end of file diff --git a/plugins/kaizen/INSTALL.md b/plugins/kaizen/INSTALL.md new file mode 100644 index 00000000..1ffeec60 --- /dev/null +++ b/plugins/kaizen/INSTALL.md @@ -0,0 +1,109 @@ +# Installation Guide + +## Prerequisites + +- [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) installed +- Python 3.8+ available in PATH + +## Option 1: Load Local Plugin (Development) + +Use `--plugin-dir` to load the plugin for a session: + +```bash +# Load plugin when starting Claude Code (absolute path) +claude --plugin-dir /path/to/kaizen + +# Or use a relative path from current directory +claude --plugin-dir ./kaizen +``` + +**Note:** The plugin is loaded **for that session only**. + +### Loading Multiple Plugins + +```bash +claude --plugin-dir ./plugin1 --plugin-dir ./plugin2 +``` + +### Making It Permanent + +Add an alias to your shell profile (`~/.bashrc` or `~/.zshrc`): + +```bash +alias claude='claude --plugin-dir /path/to/kaizen' +``` + +Then reload your shell: + +```bash +source ~/.zshrc # or source ~/.bashrc +``` + +## Option 2: Install from Marketplace (when published) + +```bash +claude plugin install kaizen +``` + +## Validate Plugin + +Before using, validate the plugin manifest: + +```bash +claude plugin validate /path/to/kaizen +# Should output: ✔ Validation passed +``` + +## Initialize Guidelines File (Optional) + +```bash +mkdir -p .claude +echo '{"guidelines": []}' > .claude/guidelines.json +``` + +## Verification + +After loading the plugin, verify it's working: + +1. **Test hook execution:** + ```bash + # Start Claude Code with the plugin + claude --plugin-dir ./kaizen + + # Send any prompt, then check the debug log (in another terminal) + cat /tmp/guidelines-plugin.log + # Should show: "[retrieve] Script started" with timestamp + ``` + +2. **Test guideline storage:** + ```bash + # After ending a conversation where guidelines were generated + cat .claude/guidelines.json + # Should contain the extracted guidelines + ``` + +3. **Test skills manually:** + ```bash + # In a Claude Code session with the plugin loaded, invoke: + /guidelines:generator + /guidelines:retrieval + ``` + +## Troubleshooting + +### Plugin validation fails + +Run `claude plugin validate ./kaizen` to see specific errors. + +### Hooks not firing + +1. Verify the plugin is loaded with `--plugin-dir` +2. Verify Python is in PATH: `which python3` +3. Check script permissions: `ls -la kaizen/scripts/` +4. Check debug log: `cat /tmp/guidelines-plugin.log` + +### Guidelines not saving + +1. Verify `.claude/` directory exists in your project +2. Check write permissions on the directory +3. Review `/tmp/guidelines-plugin.log` for error messages diff --git a/plugins/kaizen/README.md b/plugins/kaizen/README.md new file mode 100644 index 00000000..5aa5dcaa --- /dev/null +++ b/plugins/kaizen/README.md @@ -0,0 +1,99 @@ +# Guidelines Plugin for Claude Code + +A plugin that helps Claude Code learn from conversations by automatically extracting and applying guidelines. + +## Features + +- **Automatic Learning**: At the end of each conversation, guidelines are extracted and saved +- **Context-Aware Retrieval**: At the start of each prompt, relevant guidelines are injected +- **No Manual Configuration**: Hooks are automatically installed when the plugin is enabled + +## Installation + +```bash +# Load plugin for current session +claude --plugin-dir ./kaizen + +# Or with absolute path +claude --plugin-dir /path/to/kaizen +``` + +See [INSTALL.md](INSTALL.md) for making it permanent, loading multiple plugins, and troubleshooting. + +## How It Works + +### Guideline Retrieval (UserPromptSubmit hook) + +When you submit a prompt, the plugin: +1. Loads all stored guidelines from `.claude/guidelines.json` +2. Formats and injects them into the conversation context +3. Claude applies relevant guidelines to the current task + +### Guideline Generation (Stop hook) + +When a conversation ends, the plugin: +1. Analyzes the conversation trajectory +2. Extracts actionable guidelines from what worked/failed +3. Saves new guidelines to `.claude/guidelines.json` + +## Skills Included + +### `/guidelines:generator` + +Manually invoke to extract guidelines from the current conversation: +- Analyzes task, steps taken, successes and failures +- Generates proactive guidelines (what to do, not what to avoid) +- Outputs JSON for storage + +### `/guidelines:retrieval` + +Manually invoke to retrieve and display stored guidelines. + +## Guidelines Storage + +Guidelines are stored in `.claude/guidelines.json`: + +```json +{ + "guidelines": [ + { + "content": "Use Python PIL/Pillow for image metadata extraction in sandboxed environments", + "rationale": "System tools like exiftool may not be available", + "category": "strategy", + "trigger": "When extracting image metadata in containerized environments" + } + ] +} +``` + +## Environment Variables + +- `GUIDELINES_FILE`: Override the default guidelines storage location +- `CLAUDE_PROJECT_ROOT`: Set by Claude Code, used to locate project-level guidelines + +## Verification + +After installation, run `claude plugin list` to confirm the plugin is enabled. See [INSTALL.md](INSTALL.md) for detailed verification steps. + +## Plugin Structure + +```text +kaizen/ +├── .claude-plugin/ +│ └── plugin.json # Plugin manifest +├── skills/ +│ ├── guideline-generator/ +│ │ └── SKILL.md +│ └── guideline-retrieval/ +│ └── SKILL.md +├── hooks/ +│ └── hooks.json # Auto-configured hooks +├── scripts/ +│ ├── save_guidelines.py +│ └── retrieve_guidelines.py +└── README.md +``` + +## License + +MIT diff --git a/plugins/kaizen/hooks/hooks.json b/plugins/kaizen/hooks/hooks.json new file mode 100644 index 00000000..d2ec3fc1 --- /dev/null +++ b/plugins/kaizen/hooks/hooks.json @@ -0,0 +1,26 @@ +{ + "hooks": { + "UserPromptSubmit": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": "python3 ${CLAUDE_PLUGIN_ROOT}/scripts/retrieve_guidelines.py" + } + ] + } + ], + "Stop": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": "python3 ${CLAUDE_PLUGIN_ROOT}/scripts/save_guidelines.py" + } + ] + } + ] + } +} diff --git a/plugins/kaizen/scripts/retrieve_guidelines.py b/plugins/kaizen/scripts/retrieve_guidelines.py new file mode 100755 index 00000000..23b1910e --- /dev/null +++ b/plugins/kaizen/scripts/retrieve_guidelines.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +"""Retrieve and output guidelines for Claude to filter.""" + +import json +import os +import sys +from pathlib import Path +import datetime + +# Debug logging +LOG_FILE = "/tmp/guidelines-plugin.log" + +def log(message): + """Append a timestamped message to the log file.""" + timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + with open(LOG_FILE, "a") as f: + f.write(f"[{timestamp}] [retrieve] {message}\n") + +log("Script started") + + +def find_guidelines_file(): + """Find the guidelines file in common locations.""" + locations = [ + os.environ.get("GUIDELINES_FILE"), + # Project root from Claude Code + os.path.join(os.environ.get("CLAUDE_PROJECT_ROOT", ""), ".claude/guidelines.json"), + # Current working directory + ".claude/guidelines.json", + # Plugin-relative path (fallback) + str(Path(__file__).parent.parent / "guidelines.json"), + ] + for loc in locations: + if loc and Path(loc).exists(): + return Path(loc) + return None + + +def load_guidelines(): + """Load guidelines from the guidelines file.""" + guidelines_file = find_guidelines_file() + if not guidelines_file: + return [] + try: + with open(guidelines_file) as f: + data = json.load(f) + return data.get("guidelines", []) + except (json.JSONDecodeError, IOError): + return [] + + +def format_guidelines(guidelines): + """Format all guidelines for Claude to review.""" + header = """## Guidelines for this task + +Review these guidelines and apply any relevant ones: + +""" + items = [] + for g in guidelines: + content = g.get('content') + if not content: + continue + item = f"- **[{g.get('category', 'general')}]** {content}" + if g.get('rationale'): + item += f"\n - _Rationale: {g['rationale']}_" + if g.get('trigger'): + item += f"\n - _When: {g['trigger']}_" + items.append(item) + + return header + "\n".join(items) + + +def main(): + # Read input from stdin (hook provides JSON with prompt) + try: + input_data = json.load(sys.stdin) + log(f"Received input with keys: {list(input_data.keys())}") + except json.JSONDecodeError as e: + log(f"Failed to parse JSON input: {e}") + return + + # Load all guidelines + guidelines_file = find_guidelines_file() + log(f"Guidelines file: {guidelines_file}") + + guidelines = load_guidelines() + if not guidelines: + log("No guidelines found") + return + + log(f"Loaded {len(guidelines)} guidelines") + + # Output all guidelines - Claude will filter for relevance + output = format_guidelines(guidelines) + print(output) + log(f"Output {len(output)} chars to stdout") + + +if __name__ == "__main__": + main() diff --git a/plugins/kaizen/scripts/save_guidelines.py b/plugins/kaizen/scripts/save_guidelines.py new file mode 100755 index 00000000..df0ecc56 --- /dev/null +++ b/plugins/kaizen/scripts/save_guidelines.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +""" +Save Guidelines Script +Reads guidelines from stdin and appends them to the guidelines file. +""" + +import json +import os +import sys +from pathlib import Path +import datetime + +# Debug logging +LOG_FILE = "/tmp/guidelines-plugin.log" + +def log(message): + """Append a timestamped message to the log file.""" + timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + with open(LOG_FILE, "a") as f: + f.write(f"[{timestamp}] [save] {message}\n") + +log("Script started") + + +def find_guidelines_file(): + """Find existing guidelines file, checking multiple locations.""" + locations = [ + os.environ.get("GUIDELINES_FILE"), + # Project root from Claude Code + os.path.join(os.environ.get("CLAUDE_PROJECT_ROOT", ""), ".claude/guidelines.json"), + # Current working directory + ".claude/guidelines.json", + # Plugin-relative path (fallback) + str(Path(__file__).parent.parent / "guidelines.json"), + ] + + for loc in locations: + if loc and Path(loc).exists(): + return Path(loc).resolve() + + return None + + +def get_default_guidelines_path(): + """Get default path for new guidelines file.""" + # Prefer project root if available + project_root = os.environ.get("CLAUDE_PROJECT_ROOT", "") + if project_root: + claude_dir = Path(project_root) / ".claude" + else: + # Fall back to current directory's .claude/ + claude_dir = Path(".claude") + + claude_dir.mkdir(parents=True, exist_ok=True) + return (claude_dir / "guidelines.json").resolve() + + +def load_existing_guidelines(path): + """Load existing guidelines from file.""" + try: + with open(path) as f: + data = json.load(f) + return data.get("guidelines", []) + except (json.JSONDecodeError, FileNotFoundError): + return [] + + +def save_guidelines(path, guidelines): + """Save guidelines to file.""" + path.parent.mkdir(parents=True, exist_ok=True) + with open(path, "w") as f: + json.dump({"guidelines": guidelines}, f, indent=2) + f.write("\n") + + +def main(): + # Read guidelines from stdin + try: + input_data = json.load(sys.stdin) + log(f"Received input with keys: {list(input_data.keys())}") + except json.JSONDecodeError as e: + log(f"Failed to parse JSON input: {e}") + print(f"Error: Invalid JSON input - {e}", file=sys.stderr) + sys.exit(1) + + new_guidelines = input_data.get("guidelines", []) + if not new_guidelines: + log("No guidelines in input") + print("No guidelines provided in input.", file=sys.stderr) + sys.exit(0) + + log(f"Received {len(new_guidelines)} new guidelines") + + # Find or create guidelines file + existing_path = find_guidelines_file() + + if existing_path: + guidelines_path = existing_path + existing_guidelines = load_existing_guidelines(guidelines_path) + log(f"Found existing file: {guidelines_path} with {len(existing_guidelines)} guidelines") + print(f"Appending to existing file: {guidelines_path}") + else: + guidelines_path = get_default_guidelines_path() + existing_guidelines = [] + log(f"Creating new file: {guidelines_path}") + print(f"Creating new file: {guidelines_path}") + + # Merge guidelines (avoid duplicates by content) + existing_contents = {g.get("content") for g in existing_guidelines if g.get("content")} + added_count = 0 + + for guideline in new_guidelines: + content = guideline.get("content") + if not content: + log(f"Skipping guideline without content: {guideline}") + continue + if content not in existing_contents: + existing_guidelines.append(guideline) + existing_contents.add(content) + added_count += 1 + + # Save merged guidelines + save_guidelines(guidelines_path, existing_guidelines) + + log(f"Added {added_count} new guidelines. Total: {len(existing_guidelines)}") + print(f"Added {added_count} new guideline(s). Total: {len(existing_guidelines)}") + print(f"Guidelines stored in: {guidelines_path}") + + +if __name__ == "__main__": + main() diff --git a/plugins/kaizen/skills/guideline-generator/SKILL.md b/plugins/kaizen/skills/guideline-generator/SKILL.md new file mode 100644 index 00000000..4a594337 --- /dev/null +++ b/plugins/kaizen/skills/guideline-generator/SKILL.md @@ -0,0 +1,91 @@ +--- +name: guideline-generator +description: Extract actionable guidelines from conversation trajectories. Analyzes user requests, steps taken, successes and failures to generate proactive guidelines that help on similar future tasks. +--- + +# Guideline Generator + +## Overview + +This skill analyzes conversation trajectories to extract actionable guidelines that would help on similar tasks in the future. It transforms reactive learnings (what failed) into proactive recommendations (what to do first). + +## Workflow + +### Step 1: Analyze the Conversation + +Identify from your current conversation: + +- **Task/Request**: What was the user asking for? +- **Steps Taken**: What reasoning, actions, and observations occurred? +- **What Worked**: Which approaches succeeded? +- **What Failed**: Which approaches didn't work and why? + +### Step 2: Extract Guidelines + +Extract 3-5 proactive guidelines following these principles: + +1. **Reframe failures as proactive recommendations:** + - If an approach failed due to permissions → recommend the alternative FIRST + - If a system tool wasn't available → recommend what worked instead + - If an approach hit environment constraints → recommend the constraint-aware approach + +2. **Focus on what worked, stated as the primary approach:** + - Bad: "If exiftool fails, use PIL instead" + - Good: "In sandboxed environments, use Python libraries (PIL/Pillow) for image metadata extraction" + +3. **Triggers should be situational context, not failure conditions:** + - Bad trigger: "When apt-get fails" + - Good trigger: "When working in containerized/sandboxed environments" + +### Step 3: Output Guidelines JSON + +Output guidelines in the following JSON format: + +```json +{ + "guidelines": [ + { + "content": "Proactive guideline stating what TO DO", + "rationale": "Why this approach works better", + "category": "strategy|recovery|optimization", + "trigger": "Situational context when this applies" + } + ] +} +``` + +## Guideline Categories + +- **strategy**: High-level approach or methodology choices +- **recovery**: Handling errors, edge cases, or unexpected situations +- **optimization**: Improving efficiency, performance, or code quality + +## Examples + +### Good vs Bad Guidelines + +**BAD (reactive):** +```json +{ + "content": "Fall back to Python PIL when exiftool is not available", + "trigger": "When exiftool command fails" +} +``` + +**GOOD (proactive):** +```json +{ + "content": "Use Python PIL/Pillow for image metadata extraction in sandboxed environments", + "rationale": "System tools like exiftool may not be available; PIL is always installable via pip", + "category": "strategy", + "trigger": "When extracting image metadata in containerized or sandboxed environments" +} +``` + +## Best Practices + +1. **Be specific**: Generic guidelines are less useful than context-specific ones +2. **Be actionable**: Guidelines should clearly state what to do +3. **Include rationale**: Explain why the approach works +4. **Use situational triggers**: Context-based triggers are more useful than failure-based ones +5. **Limit to 3-5 guidelines**: Focus on the most impactful learnings diff --git a/plugins/kaizen/skills/guideline-retrieval/SKILL.md b/plugins/kaizen/skills/guideline-retrieval/SKILL.md new file mode 100644 index 00000000..fc046bd9 --- /dev/null +++ b/plugins/kaizen/skills/guideline-retrieval/SKILL.md @@ -0,0 +1,40 @@ +--- +name: guideline-retrieval +description: Retrieves relevant guidelines from a knowledge base. Designed to be invoked automatically via hooks to inject context-appropriate guidelines before task execution. +--- + +# Guideline Retrieval + +## Overview + +This skill retrieves relevant guidelines from a stored knowledge base based on the current task context. It loads all stored guidelines and presents them to Claude for relevance filtering. + +## How It Works + +1. Hook fires on user prompt submission +2. Script reads prompt from stdin (JSON with `prompt` field) +3. Loads all guidelines from the guidelines JSON file +4. Outputs formatted guidelines to stdout +5. Claude receives guidelines as additional context and applies relevant ones + +## Guidelines Storage + +Guidelines are stored in `.claude/guidelines.json` in the project root: + +```json +{ + "guidelines": [ + { + "content": "Use context managers for file operations", + "rationale": "Ensures proper resource cleanup", + "category": "strategy", + "trigger": "When processing files or managing resources" + } + ] +} +``` + +## Environment Variables + +- `GUIDELINES_FILE`: Optional path to guidelines JSON file (defaults to `.claude/guidelines.json`) +- `CLAUDE_PROJECT_ROOT`: Project root directory (set by Claude Code)