Skip to content

Instantly share code, notes, and snippets.

@thepushkarp
Created November 4, 2024 06:38
Show Gist options
  • Save thepushkarp/806109fc11e972227b2da7354a05be71 to your computer and use it in GitHub Desktop.
Save thepushkarp/806109fc11e972227b2da7354a05be71 to your computer and use it in GitHub Desktop.
Cursor and Copilot Instruction File Synchronization Script.
#!/usr/bin/env python3
"""
Cursor and Copilot Instruction File Synchronization Script.
Synchronizes instruction files between Cursor and GitHub Copilot by copying contents
between .cursorrules and .github/copilot-instructions.md files.
Requirements:
- Python 3.6+ (uses f-strings)
- No external dependencies required (uses only standard library)
Usage:
python sync_ai_instructions.py # Interactive usage
python sync_ai_instructions.py --dry-run # Preview changes
python sync_ai_instructions.py -y # Auto-confirm simple operations
python sync_ai_instructions.py --prefer-cursor # Always prefer .cursorrules
python sync_ai_instructions.py --prefer-copilot # Always prefer copilot-instructions.md
"""
import os
import shutil
import sys
import argparse
from enum import Enum
from typing import Optional, Tuple
class FileType(Enum):
CURSOR = ".cursorrules"
COPILOT = ".github/copilot-instructions.md"
def __str__(self):
return self.value
def check_files() -> Tuple[bool, bool]:
"""
Check for the existence of instruction files.
Returns:
Tuple of (cursor_exists: bool, copilot_exists: bool)
"""
cursor_exists = os.path.isfile(str(FileType.CURSOR))
copilot_exists = os.path.isfile(str(FileType.COPILOT))
return cursor_exists, copilot_exists
def ensure_github_dir() -> None:
"""Create .github directory if it doesn't exist."""
if not os.path.exists('.github'):
os.makedirs('.github')
def get_user_confirmation(message: str, default_yes: bool = False) -> bool:
"""
Ask for user confirmation.
Args:
message: Prompt message to display
default_yes: Whether to use yes as the default option
Returns:
bool: True if user confirms, False otherwise
"""
if default_yes:
prompt = f"{message} [Y/n]: "
response = input(prompt).strip().lower()
return response not in ('n', 'no')
else:
prompt = f"{message} [y/N]: "
response = input(prompt).strip().lower()
return response in ('y', 'yes')
def copy_file(source: FileType, target: FileType, dry_run: bool) -> None:
"""
Copy contents from source file to target file.
Args:
source: Source file type
target: Target file type
dry_run: If True, only print what would be done
"""
if dry_run:
print(f"[DRY RUN] Would copy {source} to {target}")
return
if target == FileType.COPILOT:
ensure_github_dir()
shutil.copy2(str(source), str(target))
print(f"Copied {source} to {target}")
def handle_both_files(args: argparse.Namespace) -> bool:
"""
Handle the case when both files exist.
Args:
args: Command line arguments
Returns:
bool: True if operation was handled, False otherwise
"""
if args.prefer_cursor:
copy_file(FileType.CURSOR, FileType.COPILOT, args.dry_run)
return True
if args.prefer_copilot:
copy_file(FileType.COPILOT, FileType.CURSOR, args.dry_run)
return True
while True:
choice = input("""Both files exist. Choose an action:
1. Copy .cursorrules to .github/copilot-instructions.md
2. Copy .github/copilot-instructions.md to .cursorrules
3. Leave files as they are
Enter choice (1-3): """)
if choice == '1':
copy_file(FileType.CURSOR, FileType.COPILOT, args.dry_run)
break
elif choice == '2':
copy_file(FileType.COPILOT, FileType.CURSOR, args.dry_run)
break
elif choice == '3':
print("Files left unchanged")
break
else:
print("Invalid choice. Please enter 1, 2, or 3.")
return True
def sync_instructions(args: argparse.Namespace) -> None:
"""
Synchronize instruction files based on their existence and user preferences.
Args:
args: Command line arguments containing dry_run, yes, and preference flags
"""
cursor_exists, copilot_exists = check_files()
# Neither file exists
if not cursor_exists and not copilot_exists:
print("Neither .cursorrules nor .github/copilot-instructions.md exists.")
return
# Both files exist
if cursor_exists and copilot_exists:
handle_both_files(args)
return
# Only .cursorrules exists
if cursor_exists:
print("Found .cursorrules but no .github/copilot-instructions.md")
if args.yes or get_user_confirmation(
"Copy .cursorrules to .github/copilot-instructions.md?",
args.yes
):
copy_file(FileType.CURSOR, FileType.COPILOT, args.dry_run)
return
# Only copilot-instructions.md exists
if copilot_exists:
print("Found .github/copilot-instructions.md but no .cursorrules")
if args.yes or get_user_confirmation(
"Copy .github/copilot-instructions.md to .cursorrules?",
args.yes
):
copy_file(FileType.COPILOT, FileType.CURSOR, args.dry_run)
return
def main() -> None:
parser = argparse.ArgumentParser(
description='Synchronize .cursorrules and .github/copilot-instructions.md files',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
%(prog)s --dry-run # Show what would be done without making changes
%(prog)s -y # Auto-confirm simple operations
%(prog)s --prefer-cursor # Always prefer .cursorrules when both exist
%(prog)s --prefer-copilot # Always prefer copilot-instructions.md when both exist
"""
)
parser.add_argument(
'--dry-run',
action='store_true',
help='Show what would be done without making changes'
)
parser.add_argument(
'-y', '--yes',
action='store_true',
help='Automatically answer yes to prompts (except when both files exist)'
)
preference_group = parser.add_mutually_exclusive_group()
preference_group.add_argument(
'--prefer-cursor',
action='store_true',
help='Prefer .cursorrules when both files exist'
)
preference_group.add_argument(
'--prefer-copilot',
action='store_true',
help='Prefer .github/copilot-instructions.md when both files exist'
)
args = parser.parse_args()
try:
sync_instructions(args)
except PermissionError:
print("Error: Permission denied. Please check file permissions.")
sys.exit(1)
except KeyboardInterrupt:
print("\nOperation cancelled by user")
sys.exit(1)
except Exception as e:
print(f"An error occurred: {str(e)}")
sys.exit(1)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment