feat: add kaizen plugin for Claude Code guidelines - #41
Conversation
📝 WalkthroughWalkthroughIntroduce the Kaizen plugin: marketplace and plugin manifests, documentation, hook configuration, and two scripts to retrieve and save conversation-derived guidelines. Changes
Sequence Diagram(s)sequenceDiagram
participant User as User/Client
participant Hook as Hook System
participant Retrieve as retrieve_guidelines.py
participant File as Guidelines File
participant Claude as Claude Processor
User->>Hook: Submit prompt (UserPromptSubmit)
Hook->>Retrieve: Execute retrieval script
Retrieve->>Retrieve: Discover guidelines file path
Retrieve->>File: Read guidelines.json
File-->>Retrieve: Return guidelines data
Retrieve->>Retrieve: Format guidelines (Markdown)
Retrieve-->>Claude: Write formatted guidelines to stdout
Claude->>Claude: Apply guidelines during response generation
sequenceDiagram
participant Claude as Claude (Generator)
participant Save as save_guidelines.py
participant File as Guidelines File
participant Operator as Operator/User
Claude->>Save: Emit new guidelines JSON via stdin
Save->>Save: Determine target file path (existing or default)
Save->>File: Load existing guidelines (if any)
File-->>Save: Return existing guidelines
Save->>Save: Merge and deduplicate by "content"
Save->>File: Write merged guidelines JSON
Save-->>Operator: Output status (created/appended, counts, path)
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~30 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Comment |
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 Fix all issues with AI agents
In `@plugins/kaizen/INSTALL.md`:
- Around line 73-75: Update the INSTALL.md troubleshooting examples to reference
the actual log path used by the scripts: replace occurrences of
"/tmp/guidelines.txt" with "/tmp/guidelines-plugin.log" (affecting the examples
around the lines mentioning the debug log and the expected output), so the docs
match the scripts that write to /tmp/guidelines-plugin.log.
- Around line 10-46: Update INSTALL.md to use the correct plugin name "kaizen"
everywhere: replace instances of "guidelines-plugin" and "guidelines" in the
example commands and alias with "kaizen" (e.g., change `claude --plugin-dir
./guidelines-plugin`, `alias claude='claude --plugin-dir
/path/to/guidelines-plugin'`, and `claude plugin install guidelines` to use
"kaizen"), ensuring the marketplace install command `claude plugin install
kaizen` and the plugin-dir examples reference the kaizen plugin name
consistently.
In `@plugins/kaizen/README.md`:
- Around line 80-95: Update the README code block so the plugin root name
matches the actual plugin ("kaizen/") instead of "guidelines-plugin/" and add a
language specifier to the fenced block (e.g., ```text) so the tree is rendered
correctly; edit the fenced code block in README.md that contains the directory
tree and replace the first line "guidelines-plugin/" with "kaizen/" and change
the opening backticks to "```text".
- Around line 13-19: The README installation examples use the wrong plugin
directory name; update the examples in README.md to reference the actual plugin
name defined in plugin.json ("kaizen") instead of "guidelines-plugin" — change
occurrences like "claude --plugin-dir ./guidelines-plugin" to "claude
--plugin-dir ./plugins/kaizen" and "claude --plugin-dir
/path/to/guidelines-plugin" to "claude --plugin-dir /path/to/kaizen" so the
example matches the plugin.json name.
- Around line 32-37: The README's "Guideline Generation (Stop hook)" section is
misleading because hooks.json only defines a UserPromptSubmit hook and there is
no Stop/ConversationEnd hook implemented; update the README to either remove or
reword that section to state that guideline extraction is performed manually via
the /guidelines:generator command (or, alternatively, implement a proper
Stop/ConversationEnd hook that triggers guideline extraction and add its details
to hooks.json and README). Ensure references to the existing UserPromptSubmit
hook remain accurate and explicitly note that automatic end-of-conversation
extraction is not currently implemented unless you add the new hook.
In `@plugins/kaizen/scripts/retrieve_guidelines.py`:
- Around line 60-66: The loop over guidelines can raise a KeyError when an entry
lacks 'content'; update the loop that builds items (the for g in guidelines:
block) to skip invalid entries by checking for g.get('content') (or 'content' in
g) and continue if missing, and then use a local content variable (e.g., content
= g.get('content')) when constructing item; this prevents crashes and ensures
only valid guideline objects are formatted and appended to items.
- Around line 10-17: Replace the hard-coded LOG_FILE and update the log()
function so the log path is configurable and created safely: make LOG_FILE a
configurable variable (e.g., default to a project ".claude" dir or the user
state dir) and resolve it at module init, ensure the parent directory exists
(create it if missing) with proper permissions before any writes, and continue
to use the existing log(message) function to append timestamped entries; refer
to LOG_FILE and log() in retrieve_guidelines.py when making these changes.
In `@plugins/kaizen/scripts/save_guidelines.py`:
- Around line 110-118: The merge loop currently appends any guideline lacking a
valid content field; update the loop that iterates new_guidelines (used with
existing_guidelines, existing_contents, added_count) to first validate
guideline.get("content") is a non-empty string (e.g., check for None, empty or
whitespace-only values and optionally strip()) and skip entries that fail
validation before deduplication; ensure existing_contents uses the
normalized/stripped content for comparison so only valid, non-duplicate
guidelines are appended and counted.
- Around line 13-20: The hard-coded LOG_FILE (LOG_FILE) and the log() function
use a predictable /tmp path; change LOG_FILE to be configurable (read from an
environment variable or shared constant used by the retrieve script) and avoid a
fixed "/tmp" value, and before opening the file ensure the parent directory
exists by creating it with os.makedirs(..., exist_ok=True) and proper
permissions; update save_guidelines.py so log() uses that configurable LOG_FILE
and creates the directory if missing to match the retrieve script's behavior.
| 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 | ||
| ``` |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find manifest files in plugins/kaizen directory
find plugins/kaizen -type f -name "manifest.*" -o -name "package.json" | head -20Repository: AgentToolkit/kaizen
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Read the INSTALL.md file to see actual content
cat -n plugins/kaizen/INSTALL.mdRepository: AgentToolkit/kaizen
Length of output: 3304
🏁 Script executed:
#!/bin/bash
# Search for manifest files in the plugins/kaizen directory
find plugins/kaizen -type f \( -name "*.json" \) | grep -E "(manifest|package)" | head -20Repository: AgentToolkit/kaizen
Length of output: 45
🏁 Script executed:
#!/bin/bash
# List all files in plugins/kaizen to understand structure
ls -la plugins/kaizen/Repository: AgentToolkit/kaizen
Length of output: 524
🏁 Script executed:
#!/bin/bash
# Check the .claude-plugin directory
ls -la plugins/kaizen/.claude-plugin/Repository: AgentToolkit/kaizen
Length of output: 231
🏁 Script executed:
#!/bin/bash
# Read manifest files in .claude-plugin
find plugins/kaizen/.claude-plugin -type f -exec cat {} \;Repository: AgentToolkit/kaizen
Length of output: 257
Fix plugin name mismatch in install instructions.
The marketplace manifest registers this plugin as kaizen, but INSTALL.md uses guidelines and guidelines-plugin throughout. The marketplace install command on line 45 will fail, and users following the directory path examples will get incorrect commands.
Update all references to use kaizen:
🛠️ Proposed fix (align naming)
-claude --plugin-dir /path/to/guidelines-plugin
+claude --plugin-dir /path/to/kaizen
-claude --plugin-dir ./guidelines-plugin
+claude --plugin-dir ./kaizen
-alias claude='claude --plugin-dir /path/to/guidelines-plugin'
+alias claude='claude --plugin-dir /path/to/kaizen'
-claude plugin validate /path/to/guidelines-plugin
+claude plugin validate /path/to/kaizen
-claude --plugin-dir ./guidelines-plugin
+claude --plugin-dir ./kaizen
-run `claude plugin validate ./guidelines-plugin`
+run `claude plugin validate ./kaizen`
-check script permissions: `ls -la guidelines-plugin/scripts/`
+check script permissions: `ls -la kaizen/scripts/`
-claude plugin install guidelines
+claude plugin install kaizen🤖 Prompt for AI Agents
In `@plugins/kaizen/INSTALL.md` around lines 10 - 46, Update INSTALL.md to use the
correct plugin name "kaizen" everywhere: replace instances of
"guidelines-plugin" and "guidelines" in the example commands and alias with
"kaizen" (e.g., change `claude --plugin-dir ./guidelines-plugin`, `alias
claude='claude --plugin-dir /path/to/guidelines-plugin'`, and `claude plugin
install guidelines` to use "kaizen"), ensuring the marketplace install command
`claude plugin install kaizen` and the plugin-dir examples reference the kaizen
plugin name consistently.
| # 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") |
There was a problem hiding this comment.
Avoid fixed log file under /tmp (predictable path).
Use a safer, configurable location (project .claude or user state dir) and ensure the parent directory exists before writing.
🔒 Proposed fix (configurable, safer log path)
-# Debug logging
-LOG_FILE = "/tmp/guidelines-plugin.log"
+# Debug logging
+LOG_FILE = os.environ.get(
+ "GUIDELINES_LOG_FILE",
+ str(
+ (
+ Path(os.environ.get("CLAUDE_PROJECT_ROOT", ""))
+ if os.environ.get("CLAUDE_PROJECT_ROOT")
+ else Path.home() / ".local" / "state" / "kaizen"
+ )
+ / ".claude"
+ / "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:
+ Path(LOG_FILE).parent.mkdir(parents=True, exist_ok=True)
+ with open(LOG_FILE, "a") as f:
f.write(f"[{timestamp}] [retrieve] {message}\n")🧰 Tools
🪛 Ruff (0.14.14)
11-11: Probable insecure usage of temporary file or directory: "/tmp/guidelines-plugin.log"
(S108)
🤖 Prompt for AI Agents
In `@plugins/kaizen/scripts/retrieve_guidelines.py` around lines 10 - 17, Replace
the hard-coded LOG_FILE and update the log() function so the log path is
configurable and created safely: make LOG_FILE a configurable variable (e.g.,
default to a project ".claude" dir or the user state dir) and resolve it at
module init, ensure the parent directory exists (create it if missing) with
proper permissions before any writes, and continue to use the existing
log(message) function to append timestamped entries; refer to LOG_FILE and log()
in retrieve_guidelines.py when making these changes.
| # 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") |
There was a problem hiding this comment.
Use a safer, configurable log file location.
Fixed /tmp log paths are predictable; keep this aligned with the retrieve script and ensure the directory exists.
🔒 Proposed fix (align with retrieve script)
-# Debug logging
-LOG_FILE = "/tmp/guidelines-plugin.log"
+# Debug logging
+LOG_FILE = os.environ.get(
+ "GUIDELINES_LOG_FILE",
+ str(
+ (
+ Path(os.environ.get("CLAUDE_PROJECT_ROOT", ""))
+ if os.environ.get("CLAUDE_PROJECT_ROOT")
+ else Path.home() / ".local" / "state" / "kaizen"
+ )
+ / ".claude"
+ / "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:
+ Path(LOG_FILE).parent.mkdir(parents=True, exist_ok=True)
+ with open(LOG_FILE, "a") as f:
f.write(f"[{timestamp}] [save] {message}\n")🧰 Tools
🪛 Ruff (0.14.14)
14-14: Probable insecure usage of temporary file or directory: "/tmp/guidelines-plugin.log"
(S108)
🤖 Prompt for AI Agents
In `@plugins/kaizen/scripts/save_guidelines.py` around lines 13 - 20, The
hard-coded LOG_FILE (LOG_FILE) and the log() function use a predictable /tmp
path; change LOG_FILE to be configurable (read from an environment variable or
shared constant used by the retrieve script) and avoid a fixed "/tmp" value, and
before opening the file ensure the parent directory exists by creating it with
os.makedirs(..., exist_ok=True) and proper permissions; update
save_guidelines.py so log() uses that configurable LOG_FILE and creates the
directory if missing to match the retrieve script's behavior.
Wire save_guidelines.py to run when conversations end, enabling automatic extraction and storage of guidelines from session context.
- 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
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@plugins/kaizen/scripts/retrieve_guidelines.py`:
- Around line 22-32: The find_guidelines_file function currently searches
.claude/guidelines.json and plugin-relative paths but misses the plain
guidelines.json that save_guidelines.py writes to the current working directory
when CLAUDE_PROJECT_ROOT is unset; update the locations list in
find_guidelines_file to include the cwd-root file (e.g., Path.cwd()/
"guidelines.json" or os.path.join(os.getcwd(), "guidelines.json")) so
retrieve_guidelines.py aligns with save_guidelines.py and will find guidelines
saved to the working directory.
| 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"), | ||
| ] |
There was a problem hiding this comment.
Align guideline search paths with save_guidelines.py to avoid “write-but-never-read”.
If CLAUDE_PROJECT_ROOT is unset and .claude/ doesn’t exist, save_guidelines.py writes guidelines.json in the cwd. retrieve_guidelines.py never searches that location, so saved guidelines won’t be retrieved.
🛠️ Suggested fix (include cwd guidelines.json)
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",
+ # CWD fallback (aligned with save_guidelines.py default)
+ "guidelines.json",
# Plugin-relative path (fallback)
str(Path(__file__).parent.parent / "guidelines.json"),
]🤖 Prompt for AI Agents
In `@plugins/kaizen/scripts/retrieve_guidelines.py` around lines 22 - 32, The
find_guidelines_file function currently searches .claude/guidelines.json and
plugin-relative paths but misses the plain guidelines.json that
save_guidelines.py writes to the current working directory when
CLAUDE_PROJECT_ROOT is unset; update the locations list in find_guidelines_file
to include the cwd-root file (e.g., Path.cwd()/ "guidelines.json" or
os.path.join(os.getcwd(), "guidelines.json")) so retrieve_guidelines.py aligns
with save_guidelines.py and will find guidelines saved to the working directory.
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.
|
I think I fixed the coderabbit feedback with the subsequent commits to this PR. |
For #36
I'll update the plugin install docs once this is merged. I can only test that once it's in the git repo.
Summary by CodeRabbit
New Features
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.