Files
gh-mhattingpete-claude-skil…/skills/code-transfer/scripts/line_insert.py
2025-11-30 08:40:11 +08:00

189 lines
5.0 KiB
Python
Executable File
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Line-based code insertion utility.
This script provides precise line-number-based code insertion,
which complements Claude's native Edit tool (which requires exact string matching).
Usage:
python line_insert.py <file_path> <line_number> <code> [--backup]
Examples:
# Insert at line 10
python line_insert.py src/main.py 10 "print('hello')"
# Insert with backup
python line_insert.py src/main.py 10 "print('hello')" --backup
"""
import argparse
import sys
from pathlib import Path
from datetime import datetime
def validate_file_path(file_path: Path) -> None:
"""
Validate that the file path is safe to use.
Args:
file_path: Path object to validate
Raises:
ValueError: If path is invalid or unsafe
"""
# Resolve to absolute path
abs_path = file_path.resolve()
# Basic security: prevent directory traversal
if ".." in str(file_path):
raise ValueError(f"Path contains '..' which is not allowed: {file_path}")
# Check parent directory exists (or can be created)
if not abs_path.parent.exists():
raise ValueError(f"Parent directory does not exist: {abs_path.parent}")
def create_backup(file_path: Path) -> Path:
"""
Create a backup of the file with timestamp.
Args:
file_path: Path to the file to backup
Returns:
Path to the backup file
"""
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = file_path.with_suffix(f"{file_path.suffix}.backup_{timestamp}")
if file_path.exists():
backup_path.write_text(file_path.read_text())
print(f"✅ Created backup: {backup_path}", file=sys.stderr)
return backup_path
def insert_code(
file_path: Path,
line_number: int,
code: str,
create_backup_flag: bool = False
) -> None:
"""
Insert code at a specific line number in a file.
Args:
file_path: Path to the target file
line_number: Line number where code should be inserted (1-based)
code: Code to insert (can be multiple lines)
create_backup_flag: Whether to create a backup before modifying
Raises:
ValueError: If line_number is invalid
IOError: If file operations fail
"""
# Validate inputs
validate_file_path(file_path)
if line_number < 1:
raise ValueError(f"Line number must be >= 1, got: {line_number}")
# Create backup if requested and file exists
if create_backup_flag and file_path.exists():
create_backup(file_path)
# Read existing content or start with empty
if file_path.exists():
with open(file_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
else:
lines = []
print(f" Creating new file: {file_path}", file=sys.stderr)
# Prepare code lines to insert
code_lines = code.splitlines(keepends=True)
# Ensure last line has newline if inserting in middle of file
if code_lines and not code_lines[-1].endswith('\n'):
code_lines[-1] += '\n'
# Insert at the specified line (1-based index)
# Line 1 means insert at the beginning
# Line len(lines)+1 means append at the end
insert_index = line_number - 1
if insert_index > len(lines):
# If line number is beyond file, pad with empty lines
lines.extend(['\\n'] * (insert_index - len(lines)))
# Insert the code
lines[insert_index:insert_index] = code_lines
# Write back to file
file_path.parent.mkdir(parents=True, exist_ok=True)
with open(file_path, 'w', encoding='utf-8') as f:
f.writelines(lines)
print(f"✅ Inserted {len(code_lines)} line(s) at line {line_number} in {file_path}", file=sys.stderr)
def main():
"""Main entry point for CLI usage."""
parser = argparse.ArgumentParser(
description="Insert code at a specific line number in a file",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Insert a single line at line 10
%(prog)s src/main.py 10 "print('hello')"
# Insert multiple lines
%(prog)s src/main.py 10 "def foo():\\n pass"
# Insert with backup
%(prog)s src/main.py 10 "print('hello')" --backup
"""
)
parser.add_argument(
'file_path',
type=Path,
help='Path to the target file'
)
parser.add_argument(
'line_number',
type=int,
help='Line number where code should be inserted (1-based)'
)
parser.add_argument(
'code',
type=str,
help='Code to insert (use \\n for newlines)'
)
parser.add_argument(
'--backup',
action='store_true',
help='Create a backup before modifying the file'
)
args = parser.parse_args()
try:
insert_code(
file_path=args.file_path,
line_number=args.line_number,
code=args.code,
create_backup_flag=args.backup
)
sys.exit(0)
except Exception as e:
print(f"❌ Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()