From d3f24af6a033e74dc0ec74c5df4142cb9b5d01dc Mon Sep 17 00:00:00 2001 From: Vinod Muthusamy Date: Wed, 28 Jan 2026 11:53:54 -0600 Subject: [PATCH 1/4] feat: add kaizen plugin for Claude Code guidelines --- .claude-plugin/marketplace.json | 20 +++ plugins/kaizen/.claude-plugin/plugin.json | 9 ++ plugins/kaizen/INSTALL.md | 109 +++++++++++++++ plugins/kaizen/README.md | 99 ++++++++++++++ plugins/kaizen/hooks/hooks.json | 15 ++ plugins/kaizen/scripts/retrieve_guidelines.py | 98 +++++++++++++ plugins/kaizen/scripts/save_guidelines.py | 129 ++++++++++++++++++ .../skills/guideline-generator/SKILL.md | 91 ++++++++++++ .../skills/guideline-retrieval/SKILL.md | 40 ++++++ 9 files changed, 610 insertions(+) create mode 100644 .claude-plugin/marketplace.json create mode 100644 plugins/kaizen/.claude-plugin/plugin.json create mode 100644 plugins/kaizen/INSTALL.md create mode 100644 plugins/kaizen/README.md create mode 100644 plugins/kaizen/hooks/hooks.json create mode 100755 plugins/kaizen/scripts/retrieve_guidelines.py create mode 100755 plugins/kaizen/scripts/save_guidelines.py create mode 100644 plugins/kaizen/skills/guideline-generator/SKILL.md create mode 100644 plugins/kaizen/skills/guideline-retrieval/SKILL.md 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..6be4b345 --- /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/guidelines-plugin + +# Or use a relative path from current directory +claude --plugin-dir ./guidelines-plugin +``` + +**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/guidelines-plugin' +``` + +Then reload your shell: + +```bash +source ~/.zshrc # or source ~/.bashrc +``` + +## Option 2: Install from Marketplace (when published) + +```bash +claude plugin install guidelines +``` + +## Validate Plugin + +Before using, validate the plugin manifest: + +```bash +claude plugin validate /path/to/guidelines-plugin +# 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 ./guidelines-plugin + + # Send any prompt, then check the debug log (in another terminal) + cat /tmp/guidelines.txt + # Should show: "retrieve_guidelines called at " + ``` + +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 ./guidelines-plugin` 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 guidelines-plugin/scripts/` +4. Check debug log: `cat /tmp/guidelines.txt` + +### Guidelines not saving + +1. Verify `.claude/` directory exists in your project +2. Check write permissions on the directory +3. Review `/tmp/guidelines.txt` for error messages diff --git a/plugins/kaizen/README.md b/plugins/kaizen/README.md new file mode 100644 index 00000000..533e9ffe --- /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 ./guidelines-plugin + +# Or with absolute path +claude --plugin-dir /path/to/guidelines-plugin +``` + +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 + +``` +guidelines-plugin/ +├── .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..0b3d30af --- /dev/null +++ b/plugins/kaizen/hooks/hooks.json @@ -0,0 +1,15 @@ +{ + "hooks": { + "UserPromptSubmit": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": "python3 ${CLAUDE_PLUGIN_ROOT}/scripts/retrieve_guidelines.py" + } + ] + } + ] + } +} diff --git a/plugins/kaizen/scripts/retrieve_guidelines.py b/plugins/kaizen/scripts/retrieve_guidelines.py new file mode 100755 index 00000000..257a8a66 --- /dev/null +++ b/plugins/kaizen/scripts/retrieve_guidelines.py @@ -0,0 +1,98 @@ +#!/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: + item = f"- **[{g.get('category', 'general')}]** {g['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..f6b35bcf --- /dev/null +++ b/plugins/kaizen/scripts/save_guidelines.py @@ -0,0 +1,129 @@ +#!/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", + "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" + if claude_dir.exists() or Path(project_root).exists(): + claude_dir.mkdir(parents=True, exist_ok=True) + return (claude_dir / "guidelines.json").resolve() + + # Fall back to current directory + claude_dir = Path(".claude") + if claude_dir.exists(): + return (claude_dir / "guidelines.json").resolve() + return Path("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} + added_count = 0 + + for guideline in new_guidelines: + if guideline.get("content") not in existing_contents: + existing_guidelines.append(guideline) + existing_contents.add(guideline.get("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) From cea3088a8095a0d8d89fad45f1e8b136036bbde4 Mon Sep 17 00:00:00 2001 From: Vinod Muthusamy Date: Wed, 28 Jan 2026 12:32:50 -0600 Subject: [PATCH 2/4] feat: add Stop hook for automatic guideline generation Wire save_guidelines.py to run when conversations end, enabling automatic extraction and storage of guidelines from session context. --- plugins/kaizen/hooks/hooks.json | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/plugins/kaizen/hooks/hooks.json b/plugins/kaizen/hooks/hooks.json index 0b3d30af..d2ec3fc1 100644 --- a/plugins/kaizen/hooks/hooks.json +++ b/plugins/kaizen/hooks/hooks.json @@ -10,6 +10,17 @@ } ] } + ], + "Stop": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": "python3 ${CLAUDE_PLUGIN_ROOT}/scripts/save_guidelines.py" + } + ] + } ] } } From df844ee511facf86656ab1aefd450ada789635ec Mon Sep 17 00:00:00 2001 From: Vinod Muthusamy Date: Wed, 28 Jan 2026 12:35:01 -0600 Subject: [PATCH 3/4] chore: update docs to kaizen name and fix null content handling - Rename plugin references from guidelines-plugin to kaizen in docs - Update log file path references to guidelines-plugin.log - Add null checks for guideline content in retrieve and save scripts --- plugins/kaizen/INSTALL.md | 24 +++++++++---------- plugins/kaizen/README.md | 8 +++---- plugins/kaizen/scripts/retrieve_guidelines.py | 5 +++- plugins/kaizen/scripts/save_guidelines.py | 10 +++++--- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/plugins/kaizen/INSTALL.md b/plugins/kaizen/INSTALL.md index 6be4b345..1ffeec60 100644 --- a/plugins/kaizen/INSTALL.md +++ b/plugins/kaizen/INSTALL.md @@ -11,10 +11,10 @@ Use `--plugin-dir` to load the plugin for a session: ```bash # Load plugin when starting Claude Code (absolute path) -claude --plugin-dir /path/to/guidelines-plugin +claude --plugin-dir /path/to/kaizen # Or use a relative path from current directory -claude --plugin-dir ./guidelines-plugin +claude --plugin-dir ./kaizen ``` **Note:** The plugin is loaded **for that session only**. @@ -30,7 +30,7 @@ claude --plugin-dir ./plugin1 --plugin-dir ./plugin2 Add an alias to your shell profile (`~/.bashrc` or `~/.zshrc`): ```bash -alias claude='claude --plugin-dir /path/to/guidelines-plugin' +alias claude='claude --plugin-dir /path/to/kaizen' ``` Then reload your shell: @@ -42,7 +42,7 @@ source ~/.zshrc # or source ~/.bashrc ## Option 2: Install from Marketplace (when published) ```bash -claude plugin install guidelines +claude plugin install kaizen ``` ## Validate Plugin @@ -50,7 +50,7 @@ claude plugin install guidelines Before using, validate the plugin manifest: ```bash -claude plugin validate /path/to/guidelines-plugin +claude plugin validate /path/to/kaizen # Should output: ✔ Validation passed ``` @@ -68,11 +68,11 @@ After loading the plugin, verify it's working: 1. **Test hook execution:** ```bash # Start Claude Code with the plugin - claude --plugin-dir ./guidelines-plugin + claude --plugin-dir ./kaizen # Send any prompt, then check the debug log (in another terminal) - cat /tmp/guidelines.txt - # Should show: "retrieve_guidelines called at " + cat /tmp/guidelines-plugin.log + # Should show: "[retrieve] Script started" with timestamp ``` 2. **Test guideline storage:** @@ -93,17 +93,17 @@ After loading the plugin, verify it's working: ### Plugin validation fails -Run `claude plugin validate ./guidelines-plugin` to see specific errors. +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 guidelines-plugin/scripts/` -4. Check debug log: `cat /tmp/guidelines.txt` +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.txt` for error messages +3. Review `/tmp/guidelines-plugin.log` for error messages diff --git a/plugins/kaizen/README.md b/plugins/kaizen/README.md index 533e9ffe..5aa5dcaa 100644 --- a/plugins/kaizen/README.md +++ b/plugins/kaizen/README.md @@ -12,10 +12,10 @@ A plugin that helps Claude Code learn from conversations by automatically extrac ```bash # Load plugin for current session -claude --plugin-dir ./guidelines-plugin +claude --plugin-dir ./kaizen # Or with absolute path -claude --plugin-dir /path/to/guidelines-plugin +claude --plugin-dir /path/to/kaizen ``` See [INSTALL.md](INSTALL.md) for making it permanent, loading multiple plugins, and troubleshooting. @@ -77,8 +77,8 @@ After installation, run `claude plugin list` to confirm the plugin is enabled. S ## Plugin Structure -``` -guidelines-plugin/ +```text +kaizen/ ├── .claude-plugin/ │ └── plugin.json # Plugin manifest ├── skills/ diff --git a/plugins/kaizen/scripts/retrieve_guidelines.py b/plugins/kaizen/scripts/retrieve_guidelines.py index 257a8a66..23b1910e 100755 --- a/plugins/kaizen/scripts/retrieve_guidelines.py +++ b/plugins/kaizen/scripts/retrieve_guidelines.py @@ -58,7 +58,10 @@ def format_guidelines(guidelines): """ items = [] for g in guidelines: - item = f"- **[{g.get('category', 'general')}]** {g['content']}" + 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'): diff --git a/plugins/kaizen/scripts/save_guidelines.py b/plugins/kaizen/scripts/save_guidelines.py index f6b35bcf..d97c9218 100755 --- a/plugins/kaizen/scripts/save_guidelines.py +++ b/plugins/kaizen/scripts/save_guidelines.py @@ -108,13 +108,17 @@ def main(): print(f"Creating new file: {guidelines_path}") # Merge guidelines (avoid duplicates by content) - existing_contents = {g.get("content") for g in existing_guidelines} + existing_contents = {g.get("content") for g in existing_guidelines if g.get("content")} added_count = 0 for guideline in new_guidelines: - if guideline.get("content") not in existing_contents: + 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(guideline.get("content")) + existing_contents.add(content) added_count += 1 # Save merged guidelines From e46df49b3f72df26adb4852cf4216296e4a5cb8c Mon Sep 17 00:00:00 2001 From: Vinod Muthusamy Date: Wed, 28 Jan 2026 12:43:33 -0600 Subject: [PATCH 4/4] fix: align guideline search paths to prevent write-but-never-read Ensure save_guidelines.py and retrieve_guidelines.py use identical search paths. Always create .claude/guidelines.json instead of falling back to guidelines.json in cwd which retrieve would never find. --- plugins/kaizen/scripts/save_guidelines.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/plugins/kaizen/scripts/save_guidelines.py b/plugins/kaizen/scripts/save_guidelines.py index d97c9218..df0ecc56 100755 --- a/plugins/kaizen/scripts/save_guidelines.py +++ b/plugins/kaizen/scripts/save_guidelines.py @@ -30,7 +30,8 @@ def find_guidelines_file(): os.path.join(os.environ.get("CLAUDE_PROJECT_ROOT", ""), ".claude/guidelines.json"), # Current working directory ".claude/guidelines.json", - "guidelines.json", + # Plugin-relative path (fallback) + str(Path(__file__).parent.parent / "guidelines.json"), ] for loc in locations: @@ -46,15 +47,12 @@ def get_default_guidelines_path(): project_root = os.environ.get("CLAUDE_PROJECT_ROOT", "") if project_root: claude_dir = Path(project_root) / ".claude" - if claude_dir.exists() or Path(project_root).exists(): - claude_dir.mkdir(parents=True, exist_ok=True) - return (claude_dir / "guidelines.json").resolve() - - # Fall back to current directory - claude_dir = Path(".claude") - if claude_dir.exists(): - return (claude_dir / "guidelines.json").resolve() - return Path("guidelines.json").resolve() + 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):