fix(labels): add duplicate check before creating labels #116
40
CLAUDE.md
40
CLAUDE.md
@@ -1,6 +1,46 @@
|
|||||||
# CLAUDE.md
|
# CLAUDE.md
|
||||||
|
|
||||||
This file provides guidance to Claude Code when working with code in this repository.
|
This file provides guidance to Claude Code when working with code in this repository.
|
||||||
|
## ⛔ MANDATORY BEHAVIOR RULES - READ FIRST
|
||||||
|
|
||||||
|
**These rules are NON-NEGOTIABLE. Violating them wastes the user's time and money.**
|
||||||
|
|
||||||
|
### 1. WHEN USER ASKS YOU TO CHECK SOMETHING - CHECK EVERYTHING
|
||||||
|
- Search ALL locations, not just where you think it is
|
||||||
|
- Check cache directories: `~/.claude/plugins/cache/`
|
||||||
|
- Check installed: `~/.claude/plugins/marketplaces/`
|
||||||
|
- Check source: `~/claude-plugins-work/`
|
||||||
|
- **NEVER say "no" or "that's not the issue" without exhaustive verification**
|
||||||
|
|
||||||
|
### 2. WHEN USER SAYS SOMETHING IS WRONG - BELIEVE THEM
|
||||||
|
- The user knows their system better than you
|
||||||
|
- Investigate thoroughly before disagreeing
|
||||||
|
- If user suspects cache, CHECK THE CACHE
|
||||||
|
- If user suspects a file, READ THE FILE
|
||||||
|
- **Your confidence is often wrong. User's instincts are often right.**
|
||||||
|
|
||||||
|
### 3. NEVER SAY "DONE" WITHOUT VERIFICATION
|
||||||
|
- Run the actual command/script to verify
|
||||||
|
- Show the output to the user
|
||||||
|
- Check ALL affected locations
|
||||||
|
- **"Done" means VERIFIED WORKING, not "I made changes"**
|
||||||
|
|
||||||
|
### 4. SHOW EXACTLY WHAT USER ASKS FOR
|
||||||
|
- If user asks for messages, show the MESSAGES
|
||||||
|
- If user asks for code, show the CODE
|
||||||
|
- If user asks for output, show the OUTPUT
|
||||||
|
- **Don't interpret or summarize unless asked**
|
||||||
|
|
||||||
|
### 5. AFTER PLUGIN UPDATES - ALWAYS CLEAR CACHE
|
||||||
|
```bash
|
||||||
|
rm -rf ~/.claude/plugins/cache/leo-claude-mktplace/
|
||||||
|
./scripts/verify-hooks.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**FAILURE TO FOLLOW THESE RULES = WASTED USER TIME = UNACCEPTABLE**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
|
|||||||
@@ -275,6 +275,7 @@ class LabelTools:
|
|||||||
) -> Dict:
|
) -> Dict:
|
||||||
"""
|
"""
|
||||||
Create a label at the appropriate level (org or repo) based on category.
|
Create a label at the appropriate level (org or repo) based on category.
|
||||||
|
Skips if label already exists (checks both org and repo levels).
|
||||||
|
|
||||||
Organization labels: Agent, Complexity, Effort, Priority, Risk, Source, Type
|
Organization labels: Agent, Complexity, Effort, Priority, Risk, Source, Type
|
||||||
Repository labels: Component, Tech
|
Repository labels: Component, Tech
|
||||||
@@ -286,7 +287,7 @@ class LabelTools:
|
|||||||
repo: Repository in 'owner/repo' format
|
repo: Repository in 'owner/repo' format
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Created label dictionary with 'level' key indicating where it was created
|
Created label dictionary with 'level' key, or 'skipped' if already exists
|
||||||
"""
|
"""
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
|
|
||||||
@@ -294,6 +295,41 @@ class LabelTools:
|
|||||||
if not target_repo or '/' not in target_repo:
|
if not target_repo or '/' not in target_repo:
|
||||||
raise ValueError("Use 'owner/repo' format (e.g. 'org/repo-name')")
|
raise ValueError("Use 'owner/repo' format (e.g. 'org/repo-name')")
|
||||||
|
|
||||||
|
owner = target_repo.split('/')[0]
|
||||||
|
is_org = await loop.run_in_executor(
|
||||||
|
None,
|
||||||
|
lambda: self.gitea.is_org_repo(target_repo)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Fetch existing labels to check for duplicates
|
||||||
|
existing_labels = await self.get_labels(target_repo)
|
||||||
|
all_existing = existing_labels.get('organization', []) + existing_labels.get('repository', [])
|
||||||
|
existing_names = [label['name'].lower() for label in all_existing]
|
||||||
|
|
||||||
|
# Normalize the new label name for comparison
|
||||||
|
name_normalized = name.lower()
|
||||||
|
|
||||||
|
# Also check for format variations (Type/Bug vs Type: Bug)
|
||||||
|
name_variations = [name_normalized]
|
||||||
|
if '/' in name:
|
||||||
|
name_variations.append(name.replace('/', ': ').lower())
|
||||||
|
name_variations.append(name.replace('/', ':').lower())
|
||||||
|
elif ': ' in name:
|
||||||
|
name_variations.append(name.replace(': ', '/').lower())
|
||||||
|
elif ':' in name:
|
||||||
|
name_variations.append(name.replace(':', '/').lower())
|
||||||
|
|
||||||
|
# Check if label already exists in any format
|
||||||
|
for variation in name_variations:
|
||||||
|
if variation in existing_names:
|
||||||
|
logger.info(f"Label '{name}' already exists (found as '{variation}'), skipping")
|
||||||
|
return {
|
||||||
|
'name': name,
|
||||||
|
'skipped': True,
|
||||||
|
'reason': f"Label already exists",
|
||||||
|
'level': 'existing'
|
||||||
|
}
|
||||||
|
|
||||||
# Parse category from label name
|
# Parse category from label name
|
||||||
category = None
|
category = None
|
||||||
if '/' in name:
|
if '/' in name:
|
||||||
@@ -301,13 +337,6 @@ class LabelTools:
|
|||||||
elif ':' in name:
|
elif ':' in name:
|
||||||
category = name.split(':')[0].strip().lower().rstrip('s')
|
category = name.split(':')[0].strip().lower().rstrip('s')
|
||||||
|
|
||||||
# Determine level
|
|
||||||
owner = target_repo.split('/')[0]
|
|
||||||
is_org = await loop.run_in_executor(
|
|
||||||
None,
|
|
||||||
lambda: self.gitea.is_org_repo(target_repo)
|
|
||||||
)
|
|
||||||
|
|
||||||
# If it's an org repo and the category is an org-level category, create at org level
|
# If it's an org repo and the category is an org-level category, create at org level
|
||||||
if is_org and category in self.ORG_LABEL_CATEGORIES:
|
if is_org and category in self.ORG_LABEL_CATEGORIES:
|
||||||
result = await loop.run_in_executor(
|
result = await loop.run_in_executor(
|
||||||
@@ -315,6 +344,7 @@ class LabelTools:
|
|||||||
lambda: self.gitea.create_org_label(owner, name, color, description)
|
lambda: self.gitea.create_org_label(owner, name, color, description)
|
||||||
)
|
)
|
||||||
result['level'] = 'organization'
|
result['level'] = 'organization'
|
||||||
|
result['skipped'] = False
|
||||||
logger.info(f"Created organization label '{name}' in {owner}")
|
logger.info(f"Created organization label '{name}' in {owner}")
|
||||||
else:
|
else:
|
||||||
# Create at repo level
|
# Create at repo level
|
||||||
@@ -323,6 +353,7 @@ class LabelTools:
|
|||||||
lambda: self.gitea.create_label(name, color, description, target_repo)
|
lambda: self.gitea.create_label(name, color, description, target_repo)
|
||||||
)
|
)
|
||||||
result['level'] = 'repository'
|
result['level'] = 'repository'
|
||||||
|
result['skipped'] = False
|
||||||
logger.info(f"Created repository label '{name}' in {target_repo}")
|
logger.info(f"Created repository label '{name}' in {target_repo}")
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
68
plugins/claude-config-maintainer/hooks/enforce-rules.sh
Executable file
68
plugins/claude-config-maintainer/hooks/enforce-rules.sh
Executable file
@@ -0,0 +1,68 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# claude-config-maintainer: enforce mandatory behavior rules
|
||||||
|
# Checks if CLAUDE.md has the rules, adds them if missing
|
||||||
|
|
||||||
|
PREFIX="[claude-config-maintainer]"
|
||||||
|
|
||||||
|
# Find CLAUDE.md in current directory or parent
|
||||||
|
CLAUDE_MD=""
|
||||||
|
if [ -f "./CLAUDE.md" ]; then
|
||||||
|
CLAUDE_MD="./CLAUDE.md"
|
||||||
|
elif [ -f "../CLAUDE.md" ]; then
|
||||||
|
CLAUDE_MD="../CLAUDE.md"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If no CLAUDE.md found, exit silently
|
||||||
|
if [ -z "$CLAUDE_MD" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if mandatory rules exist
|
||||||
|
if grep -q "MANDATORY BEHAVIOR RULES" "$CLAUDE_MD" 2>/dev/null; then
|
||||||
|
# Rules exist, all good
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Rules missing - add them
|
||||||
|
RULES='## ⛔ MANDATORY BEHAVIOR RULES - READ FIRST
|
||||||
|
|
||||||
|
**These rules are NON-NEGOTIABLE. Violating them wastes the user'\''s time and money.**
|
||||||
|
|
||||||
|
### 1. WHEN USER ASKS YOU TO CHECK SOMETHING - CHECK EVERYTHING
|
||||||
|
- Search ALL locations, not just where you think it is
|
||||||
|
- Check cache directories: `~/.claude/plugins/cache/`
|
||||||
|
- Check installed: `~/.claude/plugins/marketplaces/`
|
||||||
|
- Check source directories
|
||||||
|
- **NEVER say "no" or "that'\''s not the issue" without exhaustive verification**
|
||||||
|
|
||||||
|
### 2. WHEN USER SAYS SOMETHING IS WRONG - BELIEVE THEM
|
||||||
|
- The user knows their system better than you
|
||||||
|
- Investigate thoroughly before disagreeing
|
||||||
|
- **Your confidence is often wrong. User'\''s instincts are often right.**
|
||||||
|
|
||||||
|
### 3. NEVER SAY "DONE" WITHOUT VERIFICATION
|
||||||
|
- Run the actual command/script to verify
|
||||||
|
- Show the output to the user
|
||||||
|
- **"Done" means VERIFIED WORKING, not "I made changes"**
|
||||||
|
|
||||||
|
### 4. SHOW EXACTLY WHAT USER ASKS FOR
|
||||||
|
- If user asks for messages, show the MESSAGES
|
||||||
|
- If user asks for code, show the CODE
|
||||||
|
- **Do not interpret or summarize unless asked**
|
||||||
|
|
||||||
|
**FAILURE TO FOLLOW THESE RULES = WASTED USER TIME = UNACCEPTABLE**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
|
# Create temp file with rules + existing content
|
||||||
|
{
|
||||||
|
head -1 "$CLAUDE_MD"
|
||||||
|
echo ""
|
||||||
|
echo "$RULES"
|
||||||
|
tail -n +2 "$CLAUDE_MD"
|
||||||
|
} > "${CLAUDE_MD}.tmp"
|
||||||
|
|
||||||
|
mv "${CLAUDE_MD}.tmp" "$CLAUDE_MD"
|
||||||
|
echo "$PREFIX Added mandatory behavior rules to CLAUDE.md"
|
||||||
10
plugins/claude-config-maintainer/hooks/hooks.json
Normal file
10
plugins/claude-config-maintainer/hooks/hooks.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"hooks": {
|
||||||
|
"SessionStart": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/enforce-rules.sh"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -78,3 +78,8 @@ main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|
||||||
|
# Clear plugin cache to ensure fresh hooks are loaded
|
||||||
|
echo "Clearing plugin cache..."
|
||||||
|
rm -rf ~/.claude/plugins/cache/leo-claude-mktplace/
|
||||||
|
echo "Cache cleared"
|
||||||
|
|||||||
44
scripts/verify-hooks.sh
Executable file
44
scripts/verify-hooks.sh
Executable file
@@ -0,0 +1,44 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Verify all hooks are command type (not prompt)
|
||||||
|
# Run this after any plugin update
|
||||||
|
|
||||||
|
echo "=== HOOK VERIFICATION ==="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
FAILED=0
|
||||||
|
|
||||||
|
# Check ALL hooks.json files in .claude directory
|
||||||
|
for f in $(find ~/.claude -name "hooks.json" 2>/dev/null); do
|
||||||
|
if grep -q '"type": "prompt"' "$f" || grep -q '"type":"prompt"' "$f"; then
|
||||||
|
echo "❌ PROMPT HOOK FOUND: $f"
|
||||||
|
FAILED=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check cache specifically
|
||||||
|
if [ -d ~/.claude/plugins/cache/leo-claude-mktplace ]; then
|
||||||
|
echo "❌ CACHE EXISTS: ~/.claude/plugins/cache/leo-claude-mktplace"
|
||||||
|
echo " Run: rm -rf ~/.claude/plugins/cache/leo-claude-mktplace/"
|
||||||
|
FAILED=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify installed hooks are command type
|
||||||
|
for plugin in doc-guardian code-sentinel projman pr-review project-hygiene; do
|
||||||
|
HOOK_FILE=~/.claude/plugins/marketplaces/leo-claude-mktplace/plugins/$plugin/hooks/hooks.json
|
||||||
|
if [ -f "$HOOK_FILE" ]; then
|
||||||
|
if grep -q '"type": "command"' "$HOOK_FILE" || grep -q '"type":"command"' "$HOOK_FILE"; then
|
||||||
|
echo "✓ $plugin: command type"
|
||||||
|
else
|
||||||
|
echo "❌ $plugin: NOT command type"
|
||||||
|
FAILED=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
if [ $FAILED -eq 0 ]; then
|
||||||
|
echo "✓ All hooks verified OK"
|
||||||
|
else
|
||||||
|
echo "❌ ISSUES FOUND - fix before using"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user