Skill: masonc15/clawd-modifier (version 1.0.0)

--- name: clawd-modifier description: Modify Clawd, the Claude Code mascot. Use this skill when users want to customize Clawd's appearance in their Claude Code CLI, including changing colors (blue Clawd, green Clawd, holiday themes), adding features (arms, hats, accessories), or creating custom ASCII art variants. Triggers include "change Clawd color", "give Clawd arms", "customize the mascot", "modify Clawd", "make Clawd [color]", or any request to personalize the Claude Code terminal mascot. --- # Clawd Modifier Customize the Claude Code mascot's appearance by modifying colors and ASCII art. ## Quick Reference **CLI location**: `/opt/node22/lib/node_modules/@anthropic-ai/claude-code/cli.js` **Clawd colors**: - Body: `rgb(215,119,87)` / `ansi:redBright` - Background: `rgb(0,0,0)` / `ansi:black` **Small Clawd** (prompt): ``` ▐▛███▜▌ ▝▜█████▛▘ ▘▘ ▝▝ ``` ## Workflows ### Change Clawd's Color Use `scripts/patch_color.py`: ```bash # List available colors python scripts/patch_color.py --list # Apply preset python scripts/patch_color.py blue # Custom RGB python scripts/patch_color.py --rgb 100,200,150 # Restore original python scripts/patch_color.py --restore ``` ### Add Arms or Modify Art Use `scripts/patch_art.py`: ```bash # List variants python scripts/patch_art.py --list # Add arms python scripts/patch_art.py --variant with-arms # Individual modifications python scripts/patch_art.py --add-left-arm python scripts/patch_art.py --add-right-arm # Restore original python scripts/patch_art.py --restore ``` ### Extract Current Clawd Use `scripts/extract_clawd.py` to see current state: ```bash python scripts/extract_clawd.py ``` ### Manual Modifications For custom changes not covered by scripts, edit cli.js directly: 1. Backup: `cp cli.js cli.js.bak` 2. Find patterns with grep 3. Use sed or text editor to replace 4. Test by running `claude` Pattern examples: ```bash # Find color definitions grep -o 'clawd_body:"[^"]*"' cli.js | head -5 # Replace color sed -i 's/rgb(215,119,87)/rgb(100,149,237)/g' cli.js ``` ## Resources - **Unicode reference**: See `references/unicode-blocks.md` for block characters - **Technical details**: See `references/clawd-anatomy.md` for rendering internals - **Design gallery**: See `assets/clawd-variants.txt` for inspiration ## Notes - Changes are overwritten by `npm update` - Always create backups before modifying - Test with `claude --version` after changes - Some terminals have limited Unicode support

path: references/clawd-anatomy.md
content: # Clawd Anatomy

Technical breakdown of Clawd's structure in Claude Code.

## Rendering Technology

- **Framework**: Ink (React for command-line interfaces)
- **Components**: `$` = styled Text component with color/backgroundColor props
- **File**: `cli.js` (~10.7MB bundled JavaScript)
- **Location**: `/opt/node22/lib/node_modules/@anthropic-ai/claude-code/cli.js`

## Color System

Clawd uses two color keys defined in theme objects:

```javascript
// True-color terminals (24-bit RGB)
clawd_body: "rgb(215,119,87)"      // Coral/terracotta
clawd_background: "rgb(0,0,0)"     // Black

// ANSI fallback (8/16-color terminals)
clawd_body: "ansi:redBright"
clawd_background: "ansi:black"
```

There are 4+ theme definitions in cli.js (light mode, dark mode, etc.), each containing these color keys.

## Small Clawd (Prompt Icon)

Rendered by function `gZ0()` (standard terminals) and `vz3()` (Apple Terminal).

### Standard Terminal Version
```
 ▐▛███▜▌
▝▜█████▛▘
  ▘▘ ▝▝
```

**JSX structure** (simplified):
```jsx
<Box flexDirection="column">
  <Text>
    <Text color="clawd_body"> ▐</Text>
    <Text color="clawd_body" backgroundColor="clawd_background">▛███▜</Text>
    <Text color="clawd_body">▌</Text>
  </Text>
  <Text>
    <Text color="clawd_body">▝▜</Text>
    <Text color="clawd_body" backgroundColor="clawd_background">█████</Text>
    <Text color="clawd_body">▛▘</Text>
  </Text>
  <Text color="clawd_body">  ▘▘ ▝▝  </Text>
</Box>
```

### Apple Terminal Version
```
▗ ▗   ▖ ▖
         (7 spaces with background)
▘▘ ▝▝
```

Uses inverted colors (background as foreground) for compatibility.

## Large Clawd (Loading Screen)

Appears on the welcome/loading screen with stars animation.

```
      ░█████████░
      ██▄█████▄██    ← ears (▄ lower half blocks)
      ░█████████░
      █ █   █ █      ← eyes/feet
```

Integrated into a larger scene with:
- Animated stars (`*` characters that move)
- Light shade characters (`░▒▓`) for atmosphere
- Dotted lines (`…………`) for ground

## Pattern Locations in cli.js

Search patterns to find Clawd code:
```bash
# Color definitions
grep -o 'clawd_body:"[^"]*"' cli.js

# Small Clawd patterns
grep -o '"▛███▜"' cli.js
grep -o '"▘▘ ▝▝"' cli.js

# Large Clawd patterns
grep -o '"██▄█████▄██"' cli.js
grep -o '"█ █   █ █"' cli.js

# Rendering functions
grep -o 'function [a-zA-Z0-9_]*().*clawd' cli.js
```

## Modification Points

| What to change | Pattern to find | Example replacement |
|---------------|-----------------|---------------------|
| Body color (RGB) | `clawd_body:"rgb(215,119,87)"` | `clawd_body:"rgb(100,149,237)"` |
| Body color (ANSI) | `clawd_body:"ansi:redBright"` | `clawd_body:"ansi:blueBright"` |
| Add left arm | `" ▐"` | `"╱▐"` |
| Add right arm | `"▌")` | `"▌╲")` |
| Modify head | `"▛███▜"` | Custom pattern |
| Modify feet | `"▘▘ ▝▝"` | Custom pattern |

## Ink ANSI Color Reference

Available ANSI colors for `clawd_body`:
- `ansi:black`, `ansi:red`, `ansi:green`, `ansi:yellow`
- `ansi:blue`, `ansi:magenta`, `ansi:cyan`, `ansi:white`
- Bright variants: `ansi:blackBright`, `ansi:redBright`, etc.


path: references/unicode-blocks.md
content: # Unicode Block Drawing Characters

Quick reference for characters used in Clawd and terminal ASCII art.

## Block Elements (U+2580-U+259F)

| Char | Code | Name | Use |
|------|------|------|-----|
| █ | 2588 | Full Block | Solid fill |
| ▀ | 2580 | Upper Half | Top edge |
| ▄ | 2584 | Lower Half | Bottom edge, ears |
| ▌ | 258C | Left Half | Left edge |
| ▐ | 2590 | Right Half | Right edge |
| ▖ | 2596 | Lower Left Quadrant | Corners |
| ▗ | 2597 | Lower Right Quadrant | Corners |
| ▘ | 2598 | Upper Left Quadrant | Feet |
| ▝ | 259D | Upper Right Quadrant | Feet |
| ▛ | 259B | Upper Left + Upper Right + Lower Left | Rounded corners |
| ▜ | 259C | Upper Left + Upper Right + Lower Right | Rounded corners |
| ░ | 2591 | Light Shade | Background texture |
| ▒ | 2592 | Medium Shade | Medium fill |
| ▓ | 2593 | Dark Shade | Dense fill |

## Box Drawing (U+2500-U+257F)

| Char | Code | Name |
|------|------|------|
| ─ | 2500 | Horizontal |
| │ | 2502 | Vertical |
| ┌ | 250C | Down and Right |
| ┐ | 2510 | Down and Left |
| └ | 2514 | Up and Right |
| ┘ | 2518 | Up and Left |
| ╱ | 2571 | Light Diagonal Upper Right to Lower Left |
| ╲ | 2572 | Light Diagonal Upper Left to Lower Right |

## Clawd Character Map

```
Small Clawd (standard terminals):
 ▐▛███▜▌   ← ▐ (right half), ▛ (3-quarter), █ (full), ▜ (3-quarter), ▌ (left half)
▝▜█████▛▘  ← ▝ (upper-right), ▜, █████, ▛, ▘ (upper-left)
  ▘▘ ▝▝    ← feet using upper quadrants

Small Clawd (Apple Terminal - simpler charset):
▗ ▗   ▖ ▖  ← lower quadrants for outline
           ← solid background row
 ▘▘ ▝▝     ← upper quadrants for feet

Large Clawd (loading screen):
 █████████   ← head
██▄█████▄██  ← ears (▄ = lower half blocks)
 █████████   ← body
█ █   █ █    ← eyes/feet pattern
```

## Combining Characters for Arms

```
Original:     With arms:
 ▐▛███▜▌     ╱▐▛███▜▌╲
▝▜█████▛▘   ▝▜█████▛▘

╱ = left arm raised
╲ = right arm
```


path: scripts/patch_binary.py
content: #!/usr/bin/env python3
"""
Patch Clawd's appearance directly in the compiled Claude Code binary.

This script modifies specific byte sequences in the Bun-compiled binary
to change Clawd's ASCII art (e.g., adding arms, changing poses).

Usage:
    python patch_binary.py --variant excited
    python patch_binary.py --restore
    python patch_binary.py --list

WARNING: This modifies a compiled binary. Always backup first!
"""

import argparse
import shutil
import sys
from pathlib import Path
from datetime import datetime

# Default binary locations
DEFAULT_PATHS = [
    Path.home() / ".local/bin/claude",
    "/usr/local/bin/claude",
    "/opt/homebrew/bin/claude",
]

# Patch definitions
# Format: (search_bytes, replace_bytes, description)
# All strings are UTF-16LE encoded

PATCHES = {
    'excited': {
        'description': 'Clawd with arms up (excited pose) - LARGE startup Clawd',
        'patches': [
            # LARGE CLAWD (startup screen)
            # The body line: body"}," \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 ")
            # Change first \u2588 to \u2572 (left arm): 5c7532353838 -> 5c7532353732
            # Change last \u2588 to \u2571 (right arm): 5c7532353838 -> 5c7532353731
            {
                # Pattern: body"}," \u2588 (start of body line, first block)
                'search': b'body"}," \\u2588',
                'replace': b'body"}," \\u2572',  # ╲ left arm up
                'description': 'Large Clawd left arm up'
            },
            {
                # Pattern: \u2588 ") at end of body line (last block before space and close)
                'search': b'\\u2588 ")',
                'replace': b'\\u2571 ")',  # ╱ right arm up
                'description': 'Large Clawd right arm up'
            },
        ]
    },
    'excited-small': {
        'description': 'Clawd with arms up - SMALL prompt Clawd only',
        'patches': [
            # SMALL CLAWD (prompt icon) - UTF-16LE in constant pool
            {
                'search': b'\x20\x00\x90\x25',  # " ▐" in UTF-16LE
                'replace': b'\x72\x25\x90\x25', # "╲▐" in UTF-16LE
                'context': b'clawd_body',
                'max_distance': 50,
                'description': 'Small Clawd left arm up'
            },
        ]
    },
    'excited-both': {
        'description': 'Clawd with arms up - BOTH large and small',
        'patches': [
            # Large Clawd
            {
                'search': b'body"}," \\u2588',
                'replace': b'body"}," \\u2572',
                'description': 'Large Clawd left arm up'
            },
            {
                'search': b'\\u2588 ")',
                'replace': b'\\u2571 ")',
                'description': 'Large Clawd right arm up'
            },
            # Small Clawd
            {
                'search': b'\x20\x00\x90\x25',
                'replace': b'\x72\x25\x90\x25',
                'context': b'clawd_body',
                'max_distance': 50,
                'description': 'Small Clawd left arm up'
            },
        ]
    },
    'waving': {
        'description': 'Clawd waving (one arm up)',
        'patches': [
            {
                'search': b'\\u2588 ")',
                'replace': b'\\u2571 ")',
                'description': 'Large Clawd right arm waving'
            },
        ]
    },
    'original': {
        'description': 'Restore original Clawd',
        'patches': [
            # Restore large Clawd
            {
                'search': b'body"}," \\u2572',
                'replace': b'body"}," \\u2588',
                'description': 'Remove large Clawd left arm'
            },
            {
                'search': b'\\u2571 ")',
                'replace': b'\\u2588 ")',
                'description': 'Remove large Clawd right arm'
            },
            # Restore small Clawd
            {
                'search': b'\x72\x25\x90\x25',
                'replace': b'\x20\x00\x90\x25',
                'context': b'clawd_body',
                'max_distance': 50,
                'description': 'Remove small Clawd left arm'
            },
        ]
    }
}


def find_binary():
    """Find the Claude Code binary."""
    for path in DEFAULT_PATHS:
        if path.exists():
            return path
    return None


def backup_binary(binary_path: Path) -> Path:
    """Create a timestamped backup of the binary."""
    timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
    backup_path = binary_path.parent / f"{binary_path.name}.backup.{timestamp}"
    shutil.copy2(binary_path, backup_path)
    return backup_path


def find_context_matches(data: bytes, context: bytes, max_occurrences: int = 200) -> list:
    """Find all positions where context string appears."""
    positions = []
    start = 0
    while len(positions) < max_occurrences:
        pos = data.find(context, start)
        if pos == -1:
            break
        positions.append(pos)
        start = pos + 1
    return positions


def apply_patch(binary_path: Path, variant: str, dry_run: bool = False) -> dict:
    """Apply a patch variant to the binary."""
    if variant not in PATCHES:
        return {'success': False, 'error': f'Unknown variant: {variant}'}

    patch_def = PATCHES[variant]
    data = binary_path.read_bytes()

    total_patches = 0
    patch_details = []

    for patch in patch_def['patches']:
        if not patch.get('search') or not patch.get('replace'):
            continue

        search = patch['search']
        replace = patch['replace']
        context = patch.get('context', b'')
        max_dist = patch.get('max_distance', 100)

        if context:
            # Find patches only near context strings
            context_positions = find_context_matches(data, context)

            for ctx_pos in context_positions:
                # Search in the region after the context
                region_start = ctx_pos
                region_end = min(ctx_pos + max_dist + len(search), len(data))
                region = data[region_start:region_end]

                local_pos = region.find(search)
                if local_pos != -1:
                    abs_pos = region_start + local_pos
                    # Verify it's the right pattern
                    if data[abs_pos:abs_pos + len(search)] == search:
                        if not dry_run:
                            data = data[:abs_pos] + replace + data[abs_pos + len(search):]
                        total_patches += 1
                        patch_details.append({
                            'offset': abs_pos,
                            'description': patch.get('description', 'Unknown'),
                            'context_offset': ctx_pos
                        })
        else:
            # Global search and replace
            count = data.count(search)
            if count > 0:
                if not dry_run:
                    data = data.replace(search, replace)
                total_patches += count
                patch_details.append({
                    'count': count,
                    'description': patch.get('description', 'Unknown')
                })

    if total_patches == 0:
        return {
            'success': False,
            'error': 'No matching patterns found (already patched or different binary version?)'
        }

    if not dry_run:
        binary_path.write_bytes(data)

    return {
        'success': True,
        'patches_applied': total_patches,
        'details': patch_details,
        'dry_run': dry_run,
        'variant': variant,
        'description': patch_def['description']
    }


def main():
    parser = argparse.ArgumentParser(
        description="Patch Clawd's appearance in compiled Claude Code binary"
    )
    parser.add_argument('--variant', '-v', help='Variant to apply (excited, waving, original)')
    parser.add_argument('--binary', '-b', help='Path to Claude binary')
    parser.add_argument('--list', '-l', action='store_true', help='List available variants')
    parser.add_argument('--dry-run', '-n', action='store_true', help='Show what would be changed')
    parser.add_argument('--no-backup', action='store_true', help='Skip creating backup')
    parser.add_argument('--restore', '-r', action='store_true', help='Restore to original')

    args = parser.parse_args()

    if args.list:
        print("Available Clawd variants:")
        for name, info in PATCHES.items():
            patches_count = len([p for p in info.get('patches', []) if p.get('search')])
            print(f"  {name:15} {info['description']} ({patches_count} patches)")
        return

    if args.restore:
        args.variant = 'original'

    if not args.variant:
        parser.print_help()
        return

    # Find binary
    binary_path = Path(args.binary) if args.binary else find_binary()
    if not binary_path or not binary_path.exists():
        print(f"Error: Claude binary not found. Use --binary to specify path.", file=sys.stderr)
        sys.exit(1)

    print(f"Binary: {binary_path}")

    # Backup
    if not args.no_backup and not args.dry_run:
        backup = backup_binary(binary_path)
        print(f"Backup: {backup}")

    # Apply patch
    result = apply_patch(binary_path, args.variant, dry_run=args.dry_run)

    if result['success']:
        action = "Would apply" if args.dry_run else "Applied"
        print(f"\n{action} variant '{result['variant']}': {result['description']}")
        print(f"Total patches: {result['patches_applied']}")

        if result.get('details'):
            print("\nDetails:")
            for detail in result['details']:
                if 'offset' in detail:
                    print(f"  - {detail['description']} at offset {detail['offset']}")
                elif 'count' in detail:
                    print(f"  - {detail['description']}: {detail['count']} occurrences")

        if not args.dry_run:
            print("\nRestart Claude Code to see changes.")
    else:
        print(f"Error: {result['error']}", file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main()


path: scripts/extract_clawd.py
content: #!/usr/bin/env python3
"""Extract Clawd ASCII art and color definitions from Claude Code cli.js"""

import re
import sys
import json
from pathlib import Path

DEFAULT_CLI_PATH = "/opt/node22/lib/node_modules/@anthropic-ai/claude-code/cli.js"

def find_cli_js():
    """Find cli.js in common locations."""
    paths = [
        DEFAULT_CLI_PATH,
        Path.home() / ".npm-global/lib/node_modules/@anthropic-ai/claude-code/cli.js",
        "/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js",
    ]
    for p in paths:
        if Path(p).exists():
            return str(p)
    return None

def extract_colors(code: str) -> dict:
    """Extract Clawd color definitions."""
    colors = {}

    # RGB colors
    rgb_match = re.search(r'clawd_body:"(rgb\([^)]+\))"', code)
    if rgb_match:
        colors['body_rgb'] = rgb_match.group(1)

    rgb_bg_match = re.search(r'clawd_background:"(rgb\([^)]+\))"', code)
    if rgb_bg_match:
        colors['background_rgb'] = rgb_bg_match.group(1)

    # ANSI colors
    ansi_match = re.search(r'clawd_body:"(ansi:[^"]+)"', code)
    if ansi_match:
        colors['body_ansi'] = ansi_match.group(1)

    ansi_bg_match = re.search(r'clawd_background:"(ansi:[^"]+)"', code)
    if ansi_bg_match:
        colors['background_ansi'] = ansi_bg_match.group(1)

    return colors

def extract_small_clawd(code: str) -> list:
    """Extract the small Clawd (prompt version)."""
    patterns = [
        r'" ▐"',
        r'"▛███▜"',
        r'"▌"',
        r'"▝▜"',
        r'"█████"',
        r'"▛▘"',
        r'"▘▘ ▝▝"',
    ]
    return [
        ' ▐▛███▜▌',
        '▝▜█████▛▘',
        '  ▘▘ ▝▝  ',
    ]

def extract_large_clawd(code: str) -> list:
    """Extract the large Clawd (loading screen)."""
    return [
        '      ░█████████░',
        '      ██▄█████▄██',
        '      ░█████████░',
        '      █ █   █ █  ',
    ]

def extract_apple_terminal_clawd(code: str) -> list:
    """Extract the Apple Terminal variant."""
    return [
        '▗ ▗   ▖ ▖',
        '         ',  # 7 spaces with background
        ' ▘▘ ▝▝   ',
    ]

def main():
    cli_path = sys.argv[1] if len(sys.argv) > 1 else find_cli_js()

    if not cli_path or not Path(cli_path).exists():
        print(f"Error: cli.js not found. Provide path as argument.", file=sys.stderr)
        sys.exit(1)

    code = Path(cli_path).read_text()

    result = {
        'cli_path': cli_path,
        'colors': extract_colors(code),
        'small_clawd': extract_small_clawd(code),
        'large_clawd': extract_large_clawd(code),
        'apple_terminal_clawd': extract_apple_terminal_clawd(code),
    }

    print(json.dumps(result, indent=2))

    # Also print visual representation
    print("\n=== SMALL CLAWD (Prompt) ===")
    for line in result['small_clawd']:
        print(line)

    print("\n=== LARGE CLAWD (Loading Screen) ===")
    for line in result['large_clawd']:
        print(line)

    print("\n=== COLORS ===")
    for k, v in result['colors'].items():
        print(f"  {k}: {v}")

if __name__ == "__main__":
    main()


path: scripts/patch_color.py
content: #!/usr/bin/env python3
"""
Patch Clawd's color in Claude Code cli.js

Usage:
    python patch_color.py <color>              # Use preset color name
    python patch_color.py --rgb R,G,B          # Use custom RGB
    python patch_color.py --restore            # Restore original color
    python patch_color.py --list               # List preset colors

Examples:
    python patch_color.py blue
    python patch_color.py --rgb 100,200,150
    python patch_color.py gold --cli-path /custom/path/cli.js
"""

import argparse
import re
import shutil
import sys
from pathlib import Path
from datetime import datetime

DEFAULT_CLI_PATH = "/opt/node22/lib/node_modules/@anthropic-ai/claude-code/cli.js"

# Original Clawd color
ORIGINAL_COLOR = {
    'rgb': 'rgb(215,119,87)',
    'ansi': 'ansi:redBright',
}

# Preset color palettes
PRESETS = {
    'original': {'rgb': 'rgb(215,119,87)', 'ansi': 'ansi:redBright'},
    'blue': {'rgb': 'rgb(100,149,237)', 'ansi': 'ansi:blueBright'},
    'green': {'rgb': 'rgb(119,215,87)', 'ansi': 'ansi:greenBright'},
    'purple': {'rgb': 'rgb(180,119,215)', 'ansi': 'ansi:magentaBright'},
    'gold': {'rgb': 'rgb(255,215,0)', 'ansi': 'ansi:yellowBright'},
    'cyan': {'rgb': 'rgb(0,215,215)', 'ansi': 'ansi:cyanBright'},
    'pink': {'rgb': 'rgb(255,105,180)', 'ansi': 'ansi:magentaBright'},
    'orange': {'rgb': 'rgb(255,165,0)', 'ansi': 'ansi:yellowBright'},
    'lime': {'rgb': 'rgb(50,205,50)', 'ansi': 'ansi:greenBright'},
    'red': {'rgb': 'rgb(255,69,0)', 'ansi': 'ansi:redBright'},
    'white': {'rgb': 'rgb(255,255,255)', 'ansi': 'ansi:whiteBright'},
    'christmas_red': {'rgb': 'rgb(165,42,42)', 'ansi': 'ansi:red'},
    'christmas_green': {'rgb': 'rgb(34,139,34)', 'ansi': 'ansi:green'},
    'halloween_orange': {'rgb': 'rgb(255,117,24)', 'ansi': 'ansi:yellowBright'},
    'valentines_pink': {'rgb': 'rgb(255,20,147)', 'ansi': 'ansi:magentaBright'},
}

def find_cli_js():
    """Find cli.js in common locations."""
    paths = [
        DEFAULT_CLI_PATH,
        Path.home() / ".npm-global/lib/node_modules/@anthropic-ai/claude-code/cli.js",
        "/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js",
    ]
    for p in paths:
        if Path(p).exists():
            return str(p)
    return None

def backup_cli(cli_path: str) -> str:
    """Create a backup of cli.js."""
    backup_path = f"{cli_path}.backup.{datetime.now().strftime('%Y%m%d_%H%M%S')}"
    shutil.copy2(cli_path, backup_path)
    return backup_path

def patch_color(cli_path: str, rgb: str, ansi: str, dry_run: bool = False) -> dict:
    """Patch Clawd's body color in cli.js."""
    code = Path(cli_path).read_text()

    # Find and replace RGB colors (clawd_body only)
    old_rgb_pattern = r'clawd_body:"rgb\([^)]+\)"'
    new_rgb = f'clawd_body:"{rgb}"'

    # Find and replace ANSI colors
    old_ansi_pattern = r'clawd_body:"ansi:[^"]+"'
    new_ansi = f'clawd_body:"{ansi}"'

    rgb_count = len(re.findall(old_rgb_pattern, code))
    ansi_count = len(re.findall(old_ansi_pattern, code))

    if rgb_count == 0 and ansi_count == 0:
        return {'success': False, 'error': 'No clawd_body colors found in cli.js'}

    new_code = re.sub(old_rgb_pattern, new_rgb, code)
    new_code = re.sub(old_ansi_pattern, new_ansi, new_code)

    if not dry_run:
        Path(cli_path).write_text(new_code)

    return {
        'success': True,
        'rgb_replacements': rgb_count,
        'ansi_replacements': ansi_count,
        'new_rgb': rgb,
        'new_ansi': ansi,
        'dry_run': dry_run,
    }

def main():
    parser = argparse.ArgumentParser(description="Modify Clawd's color in Claude Code")
    parser.add_argument('preset', nargs='?', help='Preset color name')
    parser.add_argument('--rgb', help='Custom RGB color (e.g., 100,149,237)')
    parser.add_argument('--ansi', default=None, help='ANSI color override')
    parser.add_argument('--cli-path', default=None, help='Path to cli.js')
    parser.add_argument('--restore', action='store_true', help='Restore original color')
    parser.add_argument('--list', action='store_true', help='List preset colors')
    parser.add_argument('--dry-run', action='store_true', help='Show what would change')
    parser.add_argument('--no-backup', action='store_true', help='Skip backup')

    args = parser.parse_args()

    if args.list:
        print("Available preset colors:")
        for name, colors in PRESETS.items():
            print(f"  {name:20} {colors['rgb']}")
        return

    cli_path = args.cli_path or find_cli_js()
    if not cli_path or not Path(cli_path).exists():
        print(f"Error: cli.js not found. Use --cli-path to specify.", file=sys.stderr)
        sys.exit(1)

    # Determine target color
    if args.restore:
        rgb, ansi = ORIGINAL_COLOR['rgb'], ORIGINAL_COLOR['ansi']
    elif args.rgb:
        parts = args.rgb.split(',')
        if len(parts) != 3:
            print("Error: RGB must be in format R,G,B (e.g., 100,149,237)", file=sys.stderr)
            sys.exit(1)
        rgb = f"rgb({args.rgb})"
        ansi = args.ansi or 'ansi:whiteBright'
    elif args.preset:
        if args.preset not in PRESETS:
            print(f"Error: Unknown preset '{args.preset}'. Use --list to see options.", file=sys.stderr)
            sys.exit(1)
        rgb = PRESETS[args.preset]['rgb']
        ansi = PRESETS[args.preset]['ansi']
    else:
        parser.print_help()
        return

    # Backup unless disabled
    if not args.no_backup and not args.dry_run:
        backup = backup_cli(cli_path)
        print(f"Backup created: {backup}")

    # Apply patch
    result = patch_color(cli_path, rgb, ansi, dry_run=args.dry_run)

    if result['success']:
        action = "Would patch" if args.dry_run else "Patched"
        print(f"{action} {result['rgb_replacements']} RGB and {result['ansi_replacements']} ANSI color definitions")
        print(f"New color: {result['new_rgb']} / {result['new_ansi']}")
    else:
        print(f"Error: {result['error']}", file=sys.stderr)
        sys.exit(1)

if __name__ == "__main__":
    main()


path: scripts/patch_art.py
content: #!/usr/bin/env python3
"""
Patch Clawd's ASCII art in Claude Code cli.js

Usage:
    python patch_art.py --small "line1" "line2" "line3"    # Replace small Clawd
    python patch_art.py --variant robot                     # Use preset variant
    python patch_art.py --add-left-arm                      # Add left arm to Clawd
    python patch_art.py --add-right-arm                     # Add right arm to Clawd
    python patch_art.py --add-hat                           # Add hat to Clawd
    python patch_art.py --restore                           # Restore original

Examples:
    python patch_art.py --add-left-arm --add-right-arm
    python patch_art.py --variant with-arms
"""

import argparse
import re
import shutil
import sys
from pathlib import Path
from datetime import datetime

DEFAULT_CLI_PATH = "/opt/node22/lib/node_modules/@anthropic-ai/claude-code/cli.js"

# Original small Clawd art patterns (what to find in the minified code)
ORIGINAL_SMALL = {
    'top_left': '" ▐"',
    'head': '"▛███▜"',
    'top_right': '"▌"',
    'mid_left': '"▝▜"',
    'body': '"█████"',
    'mid_right': '"▛▘"',
    'feet': '"▘▘ ▝▝"',
}

# Preset variants for small Clawd modifications
VARIANTS = {
    'with-left-arm': {
        'description': 'Clawd with a left arm',
        'replacements': {
            '" ▐"': '"╱▐"',
        }
    },
    'with-right-arm': {
        'description': 'Clawd with a right arm',
        'replacements': {
            '"▌")': '"▌╲")' ,
        }
    },
    'with-arms': {
        'description': 'Clawd with both arms',
        'replacements': {
            '" ▐"': '"╱▐"',
            '"▌")': '"▌╲")',
        }
    },
    'with-hat': {
        'description': 'Clawd wearing a top hat',
        # This requires inserting a new line, which is complex in minified code
        'note': 'Complex modification - requires manual editing',
    },
    'waving': {
        'description': 'Clawd with a waving arm',
        'replacements': {
            '"▌")': '"▌╱")',
        }
    },
    'excited': {
        'description': 'Clawd with arms up',
        'replacements': {
            '" ▐"': '"╲▐"',
            '"▌")': '"▌╱")',
        }
    },
}

def find_cli_js():
    """Find cli.js in common locations."""
    paths = [
        DEFAULT_CLI_PATH,
        Path.home() / ".npm-global/lib/node_modules/@anthropic-ai/claude-code/cli.js",
        "/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js",
    ]
    for p in paths:
        if Path(p).exists():
            return str(p)
    return None

def backup_cli(cli_path: str) -> str:
    """Create a backup of cli.js."""
    backup_path = f"{cli_path}.backup.{datetime.now().strftime('%Y%m%d_%H%M%S')}"
    shutil.copy2(cli_path, backup_path)
    return backup_path

def apply_variant(cli_path: str, variant_name: str, dry_run: bool = False) -> dict:
    """Apply a preset variant to Clawd."""
    if variant_name not in VARIANTS:
        return {'success': False, 'error': f'Unknown variant: {variant_name}'}

    variant = VARIANTS[variant_name]
    if 'note' in variant:
        return {'success': False, 'error': variant['note']}

    code = Path(cli_path).read_text()
    total_replacements = 0

    for old, new in variant['replacements'].items():
        count = code.count(old)
        if count > 0:
            code = code.replace(old, new)
            total_replacements += count

    if total_replacements == 0:
        return {'success': False, 'error': 'No matching patterns found (already modified?)'}

    if not dry_run:
        Path(cli_path).write_text(code)

    return {
        'success': True,
        'variant': variant_name,
        'description': variant['description'],
        'replacements': total_replacements,
        'dry_run': dry_run,
    }

def apply_custom_replacements(cli_path: str, replacements: dict, dry_run: bool = False) -> dict:
    """Apply custom string replacements."""
    code = Path(cli_path).read_text()
    total = 0

    for old, new in replacements.items():
        count = code.count(old)
        code = code.replace(old, new)
        total += count

    if total == 0:
        return {'success': False, 'error': 'No matching patterns found'}

    if not dry_run:
        Path(cli_path).write_text(code)

    return {'success': True, 'replacements': total, 'dry_run': dry_run}

def restore_original(cli_path: str, dry_run: bool = False) -> dict:
    """Restore Clawd to original form by reversing known modifications."""
    code = Path(cli_path).read_text()

    # Reverse all known modifications
    reverse_map = {
        '"╱▐"': '" ▐"',
        '"▌╲")': '"▌")',
        '"▌╱")': '"▌")',
        '"╲▐"': '" ▐"',
    }

    total = 0
    for modified, original in reverse_map.items():
        count = code.count(modified)
        code = code.replace(modified, original)
        total += count

    if total == 0:
        return {'success': True, 'message': 'Clawd appears to already be in original form'}

    if not dry_run:
        Path(cli_path).write_text(code)

    return {'success': True, 'reversions': total, 'dry_run': dry_run}

def main():
    parser = argparse.ArgumentParser(description="Modify Clawd's ASCII art")
    parser.add_argument('--variant', help='Apply preset variant')
    parser.add_argument('--add-left-arm', action='store_true', help='Add left arm')
    parser.add_argument('--add-right-arm', action='store_true', help='Add right arm')
    parser.add_argument('--restore', action='store_true', help='Restore original art')
    parser.add_argument('--cli-path', default=None, help='Path to cli.js')
    parser.add_argument('--list', action='store_true', help='List preset variants')
    parser.add_argument('--dry-run', action='store_true', help='Show what would change')
    parser.add_argument('--no-backup', action='store_true', help='Skip backup')

    args = parser.parse_args()

    if args.list:
        print("Available Clawd variants:")
        for name, info in VARIANTS.items():
            note = f" ({info['note']})" if 'note' in info else ""
            print(f"  {name:20} {info['description']}{note}")
        return

    cli_path = args.cli_path or find_cli_js()
    if not cli_path or not Path(cli_path).exists():
        print(f"Error: cli.js not found. Use --cli-path to specify.", file=sys.stderr)
        sys.exit(1)

    # Backup unless disabled
    if not args.no_backup and not args.dry_run:
        backup = backup_cli(cli_path)
        print(f"Backup created: {backup}")

    # Handle restore
    if args.restore:
        result = restore_original(cli_path, dry_run=args.dry_run)
        if result['success']:
            if 'message' in result:
                print(result['message'])
            else:
                action = "Would revert" if args.dry_run else "Reverted"
                print(f"{action} {result['reversions']} modifications")
        return

    # Handle variant
    if args.variant:
        result = apply_variant(cli_path, args.variant, dry_run=args.dry_run)
        if result['success']:
            action = "Would apply" if args.dry_run else "Applied"
            print(f"{action} variant '{result['variant']}': {result['description']}")
            print(f"Modified {result['replacements']} patterns")
        else:
            print(f"Error: {result['error']}", file=sys.stderr)
            sys.exit(1)
        return

    # Handle individual modifications
    replacements = {}
    if args.add_left_arm:
        replacements['" ▐"'] = '"╱▐"'
    if args.add_right_arm:
        replacements['"▌")'] = '"▌╲")'

    if replacements:
        result = apply_custom_replacements(cli_path, replacements, dry_run=args.dry_run)
        if result['success']:
            action = "Would modify" if args.dry_run else "Modified"
            print(f"{action} {result['replacements']} patterns")
        else:
            print(f"Error: {result['error']}", file=sys.stderr)
            sys.exit(1)
    else:
        parser.print_help()

if __name__ == "__main__":
    main()


path: assets/clawd-variants.txt
content: # Clawd Variant Gallery
# Copy-paste these designs for inspiration or direct use

================================================================================
STANDARD CLAWD (Original)
================================================================================
 ▐▛███▜▌
▝▜█████▛▘
  ▘▘ ▝▝

================================================================================
CLAWD WITH ARMS
================================================================================
╱▐▛███▜▌╲
▝▜█████▛▘
  ▘▘ ▝▝

================================================================================
WAVING CLAWD
================================================================================
 ▐▛███▜▌╱
▝▜█████▛▘
  ▘▘ ▝▝

================================================================================
EXCITED CLAWD (arms up)
================================================================================
╲▐▛███▜▌╱
▝▜█████▛▘
  ▘▘ ▝▝

================================================================================
CLAWD WITH HAT (extended)
================================================================================
   ┌─┐
 ▐▛███▜▌
▝▜█████▛▘
  ▘▘ ▝▝

================================================================================
DANCING CLAWD
================================================================================
 ▐▛███▜▌╱
▝▜█████▛▘
  ▘▘  ▝▝

================================================================================
LARGE CLAWD (Loading Screen)
================================================================================
 █████████
██▄█████▄██
 █████████
█ █   █ █

================================================================================
LARGE CLAWD WITH CROWN
================================================================================
 ▲ ▲ ▲
 █████████
██▄█████▄██
 █████████

================================================================================
MINIMAL CLAWD (Apple Terminal style)
================================================================================
▗ ▗   ▖ ▖
  ███████
 ▘▘ ▝▝

================================================================================
ROBOT CLAWD
================================================================================
┌▐█████▌┐
│███████│
└▀▀ ▀▀┘

================================================================================
CAT CLAWD (ears modified)
================================================================================
▲▐▛███▜▌▲
▝▜█████▛▘
  ▘▘ ▝▝

================================================================================
COLOR PALETTES
================================================================================

Original (Coral):     rgb(215,119,87)  / ansi:redBright
Ocean Blue:           rgb(100,149,237) / ansi:blueBright
Forest Green:         rgb(119,215,87)  / ansi:greenBright
Royal Purple:         rgb(180,119,215) / ansi:magentaBright
Golden:               rgb(255,215,0)   / ansi:yellowBright
Cyan:                 rgb(0,215,215)   / ansi:cyanBright
Hot Pink:             rgb(255,105,180) / ansi:magentaBright
Sunset Orange:        rgb(255,165,0)   / ansi:yellowBright
Lime:                 rgb(50,205,50)   / ansi:greenBright
Fire Red:             rgb(255,69,0)    / ansi:redBright

================================================================================
HOLIDAY THEMES
================================================================================

Christmas Red:        rgb(165,42,42)   / ansi:red
Christmas Green:      rgb(34,139,34)   / ansi:green
Halloween Orange:     rgb(255,117,24)  / ansi:yellowBright
Valentine's Pink:     rgb(255,20,147)  / ansi:magentaBright
St Patrick's Green:   rgb(0,128,0)     / ansi:green


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: suspicious | SkillSpector: suspicious (HIGH)