SuperClaude/plugins/superclaude/scripts/clean_command_names.py
BlackBear 18c0e4e127
Add missing install.sh script (#483)
* feat: add missing install.sh script referenced in README\n\n- Create comprehensive installation script with POSIX compatibility\n- Add interactive and non-interactive installation modes\n- Include prerequisites checking and MCP server setup guidance\n- Replace echo -e with printf for better POSIX compliance

* fix: resolve linting errors in install_mcp.py and clean_command_names.py

Fix multiple ruff linting errors to ensure CI/CD pipeline passes:

- install_mcp.py: Remove unused pathlib.Path import, replace bare except
  with specific exception types (ValueError, IndexError), remove
  extraneous f-string prefixes on lines without placeholders
- clean_command_names.py: Remove unused os import, convert f-strings
  without placeholders to regular strings
- pyproject.toml: Exclude docs/ directory from ruff checks to avoid
  N999 module naming violations in documentation templates

All linting checks now pass successfully.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* style: apply ruff format to Python source files

Apply ruff formatting rules to CLI and scripts modules to ensure
consistent code style across the codebase.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(ci): remove incompatible pip cache from quick-check workflow

## Problem
GitHub Actions was failing with error:
"Cache folder path is retrieved for pip but doesn't exist on disk:
/home/runner/.cache/pip. This likely indicates that there are no
dependencies to cache."

## Root Cause
The quick-check.yml workflow specified `cache: 'pip'` in the Python
setup step, but the workflow uses UV (not pip) for package management
via `uv pip install --system -e ".[dev]"`.

UV uses its own cache directory (~/.cache/uv), so the pip cache path
was never created, causing the error.

This was a migration oversight:
- When UV was adopted as the project standard (commit 00706f0), the
  CLAUDE.md established "CRITICAL: Never use pip directly" rule
- The test.yml workflow was created correctly without pip cache
- The quick-check.yml workflow incorrectly included pip cache from
  initial creation (commit 8c0559c) and was not updated during migration

## Solution
Remove `cache: 'pip'` line to align with:
- Project's UV-first architecture (CLAUDE.md)
- test.yml workflow (which runs successfully without pip cache)
- readme-quality-check.yml workflow (no cache needed)

Note: publish-pypi.yml intentionally uses pip cache as it directly
runs `python -m pip install` commands, which is correct for that workflow.

## Impact
-  Eliminates GitHub Actions cache warning
-  Aligns all UV-based workflows consistently
-  Follows project standards documented in CLAUDE.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-14 08:03:04 +05:30

168 lines
4.5 KiB
Python

#!/usr/bin/env python3
"""
SuperClaude Plugin - Command Name Attribute Cleanup Script
This script automatically removes redundant 'name:' attributes from command
frontmatter in markdown files. The plugin naming system derives command names
from the plugin name + filename, making explicit name attributes unnecessary.
Usage:
python scripts/clean_command_names.py
Exit Codes:
0 - Success (files modified or no changes needed)
1 - Error (directory not found or processing failed)
"""
import re
import sys
from pathlib import Path
from typing import Tuple
def find_project_root() -> Path:
"""
Find the project root directory by locating plugin.json.
Returns:
Path: Project root directory
Raises:
FileNotFoundError: If project root cannot be determined
"""
script_dir = Path(__file__).parent.absolute()
current_dir = script_dir.parent
# Look for plugin.json up to 3 levels up
for _ in range(3):
if (current_dir / "plugin.json").exists():
return current_dir
current_dir = current_dir.parent
raise FileNotFoundError("Could not find project root (plugin.json not found)")
def clean_name_attributes(content: str) -> Tuple[str, bool]:
"""
Remove 'name:' attributes from YAML frontmatter.
Args:
content: File content as string
Returns:
Tuple of (cleaned content, was_modified)
"""
# Pattern to match 'name: value' in frontmatter
# Matches: name: value, name : value, NAME: value (case-insensitive)
name_pattern = re.compile(r'^\s*name\s*:\s*.*$', re.MULTILINE | re.IGNORECASE)
# Check if modification is needed
if not name_pattern.search(content):
return content, False
# Remove name attributes
cleaned = name_pattern.sub('', content)
# Clean up multiple consecutive newlines (max 2)
cleaned = re.sub(r'\n{3,}', '\n\n', cleaned)
# Remove trailing whitespace while preserving final newline
cleaned = cleaned.rstrip() + '\n' if cleaned.strip() else ''
return cleaned, True
def process_commands_directory(commands_dir: Path) -> int:
"""
Process all command markdown files in directory.
Args:
commands_dir: Path to commands directory
Returns:
Number of files modified
"""
if not commands_dir.is_dir():
print(f"Error: Directory not found: {commands_dir}", file=sys.stderr)
return -1
modified_count = 0
processed_count = 0
error_count = 0
print(f"🔍 Scanning: {commands_dir}")
print(f"{'='*60}")
# Process all .md files
for md_file in sorted(commands_dir.glob("*.md")):
processed_count += 1
try:
# Read file content
content = md_file.read_text(encoding='utf-8')
# Clean name attributes
cleaned_content, was_modified = clean_name_attributes(content)
if was_modified:
# Write cleaned content back
md_file.write_text(cleaned_content, encoding='utf-8')
modified_count += 1
print(f"✅ Modified: {md_file.name}")
else:
print(f"⏭️ Skipped: {md_file.name} (no name attribute)")
except Exception as e:
error_count += 1
print(f"❌ Error: {md_file.name} - {e}", file=sys.stderr)
print("="*60)
print("📊 Summary:")
print(f" • Processed: {processed_count} files")
print(f" • Modified: {modified_count} files")
print(f" • Errors: {error_count} files")
return modified_count if error_count == 0 else -1
def main() -> int:
"""
Main execution function.
Returns:
Exit code (0 for success, 1 for error)
"""
print("🚀 SuperClaude Plugin - Command Name Cleanup")
print()
try:
# Find project root and commands directory
project_root = find_project_root()
commands_dir = project_root / "commands"
print(f"📁 Project root: {project_root}")
print()
# Process commands directory
result = process_commands_directory(commands_dir)
if result < 0:
print("\n❌ Cleanup failed with errors", file=sys.stderr)
return 1
print("\n✅ Cleanup completed successfully")
return 0
except FileNotFoundError as e:
print(f"❌ Error: {e}", file=sys.stderr)
return 1
except Exception as e:
print(f"❌ Unexpected error: {e}", file=sys.stderr)
import traceback
traceback.print_exc()
return 1
if __name__ == "__main__":
sys.exit(main())