Created
November 18, 2024 20:09
-
-
Save Mockapapella/b752d6ea4db5d05b1ae797a3d781c41c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Writing Code and Documentation | |
## ⚠️ CRITICAL: ALL CODE MUST BE IN ARTIFACTS ⚠️ | |
The single most important rule: NEVER write code directly in the conversation. ALL code MUST be provided as a unified diff file within an artifact. No exceptions. | |
## Before You Start | |
1. Read the user's request | |
2. Plan your response | |
3. Create a SINGLE unified diff file containing ALL code changes | |
4. Place this diff file in an artifact | |
## Unified Diff File Format | |
Every code response MUST follow this exact format: | |
``` | |
diff --git a/[filename] b/[filename] | |
--- a/[filename] | |
+++ b/[filename] | |
@@ 1,1 +1,1 @@ | |
[changes with + and - markers] | |
``` | |
## Multiple Files | |
If you need to provide multiple files: | |
- DO NOT create multiple artifacts | |
- DO NOT paste code into the conversation | |
- DO combine all changes into a single unified diff file | |
- DO use proper diff headers to separate files | |
Example of multiple files in one diff: | |
``` | |
diff --git a/src/main.py b/src/main.py | |
[changes for main.py] | |
diff --git a/src/utils.py b/src/utils.py | |
[changes for utils.py] | |
``` | |
## Code Standards | |
For Python: | |
- Use type hints | |
- Add docstrings | |
- Keep code stateless when possible | |
- Use clear names over comments | |
For Documentation: | |
- Write at middle-school level | |
- Keep it simple | |
- Break down complex concepts | |
## Required Process | |
1. Plan your work in <thought_process> tags: | |
- User requirements | |
- Code/doc types needed | |
- Component breakdown | |
- Design choices | |
- Potential issues | |
- Solutions | |
2. Create artifact with unified diff: | |
- ALL code goes in the diff | |
- ONE diff file per response | |
- Proper diff formatting | |
- NO code in conversation | |
3. Explain in <explanation> tags: | |
- Changes made | |
- Design rationale | |
- Usage notes | |
## Example Structure | |
``` | |
<thought_process> | |
[Your detailed plan] | |
</thought_process> | |
[Artifact containing unified diff file] | |
<explanation> | |
[Your explanation] | |
</explanation> | |
``` | |
## Common Mistakes to Avoid | |
❌ Writing code in the conversation | |
❌ Creating multiple artifacts for different files | |
❌ Using code blocks instead of diff format | |
❌ Skipping the artifact creation | |
❌ Providing incomplete diffs | |
✅ DO put ALL code in ONE unified diff file | |
✅ DO put the diff file in an artifact | |
✅ DO use proper diff formatting | |
✅ DO combine multiple files into one diff | |
✅ DO follow this process for EVERY code request |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
quinten@quinten-desktop-ubuntu:~/Documents/Software_Development/blitz (dev)$ python patch_validator.py -f ai.patch --fix --backup | |
Validation failed | |
Errors: | |
- Hunk header corrected from: @@ -25,13 +25,22 @@ class Migration(migrations.Migration): | |
to: @@ -25,12 +25,22 @@ | |
Backup created: ai.patch.bak | |
Patch file has been fixed! | |
quinten@quinten-desktop-ubuntu:~/Documents/Software_Development/blitz (dev)$ git apply ai.patch | |
quinten@quinten-desktop-ubuntu:~/Documents/Software_Development/blitz (dev)$ python patch_validator.py --help | |
usage: patch_validator.py [-h] --file FILE [--quiet] [--fix] [--backup] | |
Validate and fix a patch file format | |
options: | |
-h, --help show this help message and exit | |
--file FILE, -f FILE Path to the patch file to validate | |
--quiet, -q Only output errors | |
--fix Fix the patch file and save changes | |
--backup Create a backup before fixing |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import argparse | |
import re | |
import sys | |
from dataclasses import dataclass | |
from typing import List, Optional, Tuple | |
from pathlib import Path | |
@dataclass | |
class ValidationResults: | |
is_valid: bool | |
errors: List[str] | |
warnings: List[str] | |
fixed_content: Optional[str] = None | |
@dataclass | |
class DiffSection: | |
header: str | |
old_file: str | |
new_file: str | |
hunks: List[Tuple[str, List[str]]] # (hunk header, hunk content) | |
class PatchValidator: | |
def __init__(self): | |
self.hunk_header_regex = re.compile(r"^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@") | |
self.diff_header_regex = re.compile(r"^diff --git (a/.+) (b/.+)$") | |
def count_hunk_lines(self, content_lines: List[str]) -> Tuple[int, int]: | |
"""Count the number of old and new lines in a hunk.""" | |
old_lines = 0 | |
new_lines = 0 | |
for line in content_lines: | |
if line.startswith("-"): | |
old_lines += 1 | |
elif line.startswith("+"): | |
new_lines += 1 | |
elif line.startswith(" "): | |
old_lines += 1 | |
new_lines += 1 | |
return old_lines, new_lines | |
def parse_patch(self, content: str) -> List[DiffSection]: | |
"""Parse patch content into sections.""" | |
lines = content.splitlines(keepends=True) | |
sections = [] | |
current_section = None | |
current_hunk_header = None | |
current_hunk_lines = [] | |
line_number = 0 | |
while line_number < len(lines): | |
line = lines[line_number] | |
# Start of a new diff section | |
if line.startswith("diff --git"): | |
if current_section is not None: | |
if current_hunk_lines: | |
current_section.hunks.append((current_hunk_header, current_hunk_lines)) | |
sections.append(current_section) | |
match = self.diff_header_regex.match(line) | |
if not match: | |
raise ValueError( | |
f"Invalid diff header at line {line_number + 1}: {line.strip()}" | |
) | |
old_path, new_path = match.groups() | |
# Look ahead for file headers | |
old_file = None | |
new_file = None | |
next_line = line_number + 1 | |
while next_line < len(lines): | |
next_line_content = lines[next_line].strip() | |
if next_line_content.startswith("---"): | |
old_file = lines[next_line] | |
next_line += 1 | |
elif next_line_content.startswith("+++"): | |
new_file = lines[next_line] | |
next_line += 1 | |
elif next_line_content.startswith("@@ "): | |
break | |
elif next_line_content.startswith("diff --git"): | |
break | |
else: | |
next_line += 1 | |
# Generate correct file headers if missing or incorrect | |
if old_path == "/dev/null": | |
expected_old_file = "--- /dev/null\n" | |
else: | |
expected_old_file = f"--- {old_path}\n" | |
if new_path == "/dev/null": | |
expected_new_file = "+++ /dev/null\n" | |
else: | |
expected_new_file = f"+++ {new_path}\n" | |
current_section = DiffSection( | |
header=line, old_file=expected_old_file, new_file=expected_new_file, hunks=[] | |
) | |
current_hunk_header = None | |
current_hunk_lines = [] | |
# Skip past the file headers we just processed | |
line_number = next_line - 1 | |
elif line.strip() and self.hunk_header_regex.match(line.strip()): | |
if current_hunk_lines: | |
current_section.hunks.append((current_hunk_header, current_hunk_lines)) | |
current_hunk_lines = [] | |
current_hunk_header = line | |
elif current_section is not None and current_hunk_header is not None: | |
current_hunk_lines.append(line) | |
line_number += 1 | |
# Add the last section and hunk | |
if current_section is not None: | |
if current_hunk_lines: | |
current_section.hunks.append((current_hunk_header, current_hunk_lines)) | |
sections.append(current_section) | |
return sections | |
def validate_and_fix_patch(self, content: str) -> ValidationResults: | |
results = ValidationResults(is_valid=True, errors=[], warnings=[], fixed_content=None) | |
if not content.strip(): | |
results.errors.append("Patch file is empty") | |
results.is_valid = False | |
return results | |
try: | |
# Parse the patch into sections | |
sections = self.parse_patch(content) | |
if not sections: | |
results.errors.append("No valid diff sections found") | |
results.is_valid = False | |
return results | |
# Process sections and build fixed content | |
fixed_lines = [] | |
for i, section in enumerate(sections): | |
# Add blank line between sections if needed | |
if i > 0: | |
fixed_lines.append("\n") | |
# Add diff header | |
fixed_lines.append(section.header) | |
# Add file headers | |
fixed_lines.append(section.old_file) | |
fixed_lines.append(section.new_file) | |
# Process hunks | |
for hunk_header, hunk_lines in section.hunks: | |
old_count, new_count = self.count_hunk_lines(hunk_lines) | |
old_start, _, new_start, _ = self.parse_hunk_header(hunk_header) | |
correct_header = f"@@ -{old_start},{old_count} +{new_start},{new_count} @@\n" | |
if correct_header.strip() != hunk_header.strip(): | |
results.errors.append( | |
f"Hunk header corrected from: {hunk_header.strip()}\n" | |
f"to: {correct_header.strip()}" | |
) | |
fixed_lines.append(correct_header) | |
else: | |
fixed_lines.append(hunk_header) | |
fixed_lines.extend(hunk_lines) | |
# Ensure proper line endings | |
results.fixed_content = "".join(fixed_lines) | |
if not results.fixed_content.endswith("\n"): | |
results.fixed_content += "\n" | |
except ValueError as e: | |
results.errors.append(str(e)) | |
results.is_valid = False | |
return results | |
results.is_valid = not results.errors | |
return results | |
def parse_hunk_header(self, header: str) -> Tuple[int, int, int, int]: | |
"""Parse a hunk header into its components.""" | |
match = self.hunk_header_regex.match(header.strip()) | |
if not match: | |
return 1, 1, 1, 1 | |
old_start, old_count, new_start, new_count = match.groups() | |
return (int(old_start), int(old_count or "1"), int(new_start), int(new_count or "1")) | |
def main(): | |
parser = argparse.ArgumentParser(description="Validate and fix a patch file format") | |
parser.add_argument("--file", "-f", required=True, help="Path to the patch file to validate") | |
parser.add_argument("--quiet", "-q", action="store_true", help="Only output errors") | |
parser.add_argument("--fix", action="store_true", help="Fix the patch file and save changes") | |
parser.add_argument("--backup", action="store_true", help="Create a backup before fixing") | |
args = parser.parse_args() | |
try: | |
with open(args.file, "r", encoding="utf-8") as f: | |
content = f.read() | |
except Exception as e: | |
print(f"Error reading file: {e}", file=sys.stderr) | |
sys.exit(1) | |
validator = PatchValidator() | |
results = validator.validate_and_fix_patch(content) | |
if not args.quiet: | |
print(f"Validation {'succeeded' if results.is_valid else 'failed'}") | |
if results.errors: | |
print("\nErrors:", file=sys.stderr) | |
for error in results.errors: | |
print(f" - {error}", file=sys.stderr) | |
if not args.quiet and results.warnings: | |
print("\nWarnings:") | |
for warning in results.warnings: | |
print(f" - {warning}") | |
if args.fix and results.fixed_content is not None: | |
try: | |
if args.backup: | |
backup_path = Path(args.file).with_suffix(".patch.bak") | |
with open(backup_path, "w", encoding="utf-8") as f: | |
f.write(content) | |
if not args.quiet: | |
print(f"\nBackup created: {backup_path}") | |
with open(args.file, "w", encoding="utf-8") as f: | |
f.write(results.fixed_content) | |
if not args.quiet: | |
print("\nPatch file has been fixed!") | |
except Exception as e: | |
print(f"Error fixing file: {e}", file=sys.stderr) | |
sys.exit(1) | |
sys.exit(0 if results.is_valid else 1) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
to do it straight from Claude :)