mirror of
https://github.com/PR0M3TH3AN/RePrompt.git
synced 2025-09-07 14:38:43 +00:00
271 lines
9.8 KiB
Python
Executable File
271 lines
9.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
"""
|
|
Script Name: generate_repo-context.py
|
|
Description: Generates a context file (`repo-context.txt`) for AI coding assistants.
|
|
Includes an overview, important information, a directory tree with exclusions,
|
|
content of important files with syntax highlighting, and a to-do list.
|
|
|
|
Usage:
|
|
1. Ensure you have Python 3.7 or higher installed.
|
|
2. (Optional) Set up a Python virtual environment:
|
|
python3 -m venv venv
|
|
source venv/bin/activate # On Unix or MacOS
|
|
venv\Scripts\activate.bat # On Windows (Command Prompt)
|
|
venv\Scripts\Activate.ps1 # On Windows (PowerShell)
|
|
3. Install the required Python packages:
|
|
pip install -r requirements.txt
|
|
4. Configure `config.yaml` as needed.
|
|
5. Place `overview.txt`, `important_info.txt`, and `to-do_list.txt` in the script directory.
|
|
6. Run the script:
|
|
./generate_repo-context.py # Unix-like systems
|
|
python generate_repo-context.py # Windows
|
|
|
|
The script will create `repo-context.txt` with the specified structure.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import yaml
|
|
from pathlib import Path
|
|
import mimetypes
|
|
import logging
|
|
from typing import List, Dict
|
|
|
|
# Configuration Constants
|
|
CONFIG_FILE = "config.yaml"
|
|
OUTPUT_FILE = "repo-context.txt"
|
|
|
|
# Static Text Files and Their Corresponding Section Titles
|
|
STATIC_FILES = [
|
|
{"file": "overview.txt", "section_title": "Overview"},
|
|
{"file": "important_info.txt", "section_title": "Important Information"},
|
|
{"file": "to-do_list.txt", "section_title": "To-Do List"}
|
|
]
|
|
|
|
# Mapping of File Extensions to Programming Languages for Syntax Highlighting
|
|
LANGUAGE_MAP = {
|
|
'.py': 'python',
|
|
'.json': 'json',
|
|
'.env': 'bash',
|
|
'.js': 'javascript',
|
|
'.html': 'html',
|
|
'.css': 'css',
|
|
'.csv': 'csv',
|
|
'.md': 'markdown',
|
|
'.txt': '', # Plain text
|
|
# Add more mappings as needed
|
|
}
|
|
|
|
# Extensions of Binary Files to Skip
|
|
BINARY_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico', '.db', '.exe', '.bin']
|
|
|
|
def setup_logging():
|
|
"""Configures the logging format and level."""
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format='[%(levelname)s] %(message)s'
|
|
)
|
|
|
|
def load_config(config_path: Path) -> Dict:
|
|
"""
|
|
Loads configuration from a YAML file.
|
|
|
|
Args:
|
|
config_path (Path): Path to the YAML configuration file.
|
|
|
|
Returns:
|
|
dict: Configuration dictionary containing 'exclude_dirs' and 'important_files'.
|
|
"""
|
|
if not config_path.exists():
|
|
logging.error(f"Configuration file {config_path} not found.")
|
|
sys.exit(1)
|
|
try:
|
|
with open(config_path, 'r') as f:
|
|
config = yaml.safe_load(f)
|
|
logging.info(f"Loaded configuration from {config_path}.")
|
|
return config
|
|
except yaml.YAMLError as e:
|
|
logging.error(f"Error parsing configuration file: {e}")
|
|
sys.exit(1)
|
|
|
|
def generate_directory_tree(start_path: Path, exclude_dirs: List[str]) -> List[str]:
|
|
"""
|
|
Generates a directory tree as a list of strings, excluding specified directories.
|
|
|
|
Args:
|
|
start_path (Path): The root directory to start generating the tree from.
|
|
exclude_dirs (list): List of directory patterns to exclude.
|
|
|
|
Returns:
|
|
list: List of strings representing the directory tree.
|
|
"""
|
|
tree_lines = []
|
|
root = start_path.resolve()
|
|
for dirpath, dirnames, filenames in os.walk(start_path):
|
|
current_path = Path(dirpath)
|
|
rel_path = current_path.relative_to(root)
|
|
|
|
# Skip excluded directories
|
|
if any(current_path.match(excl) or excl in rel_path.parts for excl in exclude_dirs):
|
|
dirnames[:] = [] # Don't traverse further into subdirectories
|
|
continue
|
|
|
|
# Determine the indentation level
|
|
depth = len(rel_path.parts)
|
|
indent = " " * depth
|
|
connector = "├── " if depth > 0 else "."
|
|
tree_lines.append(f"{indent}{connector}{current_path.name}/" if depth > 0 else f"{connector}")
|
|
|
|
# Add files in the current directory
|
|
for filename in sorted(filenames):
|
|
file_rel_path = rel_path / filename
|
|
if any(file_rel_path.match(excl) or excl in file_rel_path.parts for excl in exclude_dirs):
|
|
continue
|
|
file_indent = " " * (depth + 1)
|
|
tree_lines.append(f"{file_indent}├── {filename}")
|
|
|
|
logging.info("Directory tree generated.")
|
|
return tree_lines
|
|
|
|
def write_directory_tree(tree_lines: List[str], output_file: Path):
|
|
"""
|
|
Writes the directory tree to the output file within markdown code blocks.
|
|
|
|
Args:
|
|
tree_lines (list): List of strings representing the directory tree.
|
|
output_file (Path): Path to the output file where the tree will be written.
|
|
"""
|
|
output_file.write_text("## Directory Tree with Exclusions\n\n```\n")
|
|
output_file.write("\n".join(tree_lines))
|
|
output_file.write("\n```\n\n")
|
|
logging.info("Directory tree written to the context file.")
|
|
|
|
def write_file_content(file_path: Path, output_file: Path):
|
|
"""
|
|
Writes the content of a file to the output file within markdown code blocks with syntax highlighting.
|
|
|
|
Args:
|
|
file_path (Path): Path to the file whose content is to be written.
|
|
output_file (Path): Path to the output file where the content will be written.
|
|
"""
|
|
ext = file_path.suffix
|
|
language = LANGUAGE_MAP.get(ext, '')
|
|
relative_display_path = file_path.relative_to(file_path.parents[1])
|
|
|
|
output_file.write(f"## {relative_display_path}\n")
|
|
if language:
|
|
output_file.write(f"```{language}\n")
|
|
else:
|
|
output_file.write("```\n")
|
|
try:
|
|
if ext in BINARY_EXTENSIONS:
|
|
output_file.write(f"*Binary file ({ext}) cannot be displayed.*\n")
|
|
else:
|
|
content = file_path.read_text(encoding='utf-8', errors='ignore')
|
|
output_file.write(content)
|
|
except Exception as e:
|
|
output_file.write(f"*Error reading file: {e}*\n")
|
|
output_file.write("\n```\n\n")
|
|
logging.info(f"Included content from {file_path}.")
|
|
|
|
def write_static_file(file_path: Path, output_file: Path, section_title: str):
|
|
"""
|
|
Writes the content of a static text file to the output file with a section header.
|
|
|
|
Args:
|
|
file_path (Path): Path to the static text file.
|
|
output_file (Path): Path to the output file where the content will be written.
|
|
section_title (str): Title of the section to be added before the content.
|
|
"""
|
|
if not file_path.exists():
|
|
logging.warning(f"Static file {file_path} not found, skipping...")
|
|
return
|
|
output_file.write(f"## {section_title}\n\n")
|
|
try:
|
|
content = file_path.read_text(encoding='utf-8', errors='ignore')
|
|
output_file.write(content + "\n\n")
|
|
logging.info(f"Included static section: {section_title}.")
|
|
except Exception as e:
|
|
output_file.write(f"*Error reading {file_path.name}: {e}*\n\n")
|
|
logging.error(f"Error reading {file_path}: {e}")
|
|
|
|
def write_custom_sections(custom_sections: List[Dict], script_dir: Path, output_file: Path):
|
|
"""
|
|
Writes custom sections to the output file based on configuration.
|
|
|
|
Args:
|
|
custom_sections (list): List of dictionaries with 'file' and 'section_title'.
|
|
script_dir (Path): Directory where the script is located.
|
|
output_file (Path): Path to the output file.
|
|
"""
|
|
for section in custom_sections:
|
|
file_name = section.get('file')
|
|
section_title = section.get('section_title', 'Custom Section')
|
|
file_path = script_dir / file_name
|
|
write_static_file(file_path, output_file, section_title)
|
|
|
|
def main():
|
|
"""Main function that orchestrates the generation of the repository context file."""
|
|
setup_logging()
|
|
|
|
# Determine the script's directory
|
|
script_dir = Path(__file__).parent.resolve()
|
|
|
|
# Load configuration
|
|
config_path = script_dir / CONFIG_FILE
|
|
config = load_config(config_path)
|
|
exclude_dirs = config.get("exclude_dirs", [])
|
|
important_files = config.get("important_files", [])
|
|
custom_sections = config.get("custom_sections", [])
|
|
|
|
# Define the starting path (default to 'src' directory or as specified)
|
|
source_dir = config.get("source_directory", "src")
|
|
start_path = script_dir.parent / source_dir
|
|
if not start_path.exists():
|
|
logging.error(f"Source directory {start_path} does not exist.")
|
|
sys.exit(1)
|
|
|
|
output_file = script_dir / OUTPUT_FILE
|
|
output_file.unlink(missing_ok=True) # Remove if exists
|
|
|
|
# Write a header to the output file
|
|
with output_file.open('w', encoding='utf-8') as f:
|
|
f.write(f"# Repository Context\n\n")
|
|
f.write(f"Generated on: {Path.now().strftime('%Y-%m-%d')}\n\n")
|
|
|
|
# Write static sections
|
|
for static in STATIC_FILES:
|
|
static_path = script_dir / static["file"]
|
|
write_static_file(static_path, output_file, static["section_title"])
|
|
|
|
# Generate and write the directory tree
|
|
tree_lines = generate_directory_tree(start_path, exclude_dirs)
|
|
write_directory_tree(tree_lines, output_file)
|
|
|
|
# Write important files
|
|
output_file.write("## Important Files\n\n")
|
|
for relative_file in important_files:
|
|
file_path = start_path / relative_file
|
|
if file_path.exists():
|
|
write_file_content(file_path, output_file)
|
|
else:
|
|
output_file.write(f"*File `{relative_file}` not found, skipping...*\n\n")
|
|
logging.warning(f"Important file {relative_file} not found, skipping...")
|
|
|
|
# Write custom sections if any
|
|
if custom_sections:
|
|
write_custom_sections(custom_sections, script_dir, output_file)
|
|
|
|
# Write to-do list
|
|
for static in STATIC_FILES:
|
|
if static["section_title"] == "To-Do List":
|
|
continue # Already written
|
|
todo_path = script_dir / "to-do_list.txt"
|
|
write_static_file(todo_path, output_file, "To-Do List")
|
|
|
|
logging.info(f"Context file created: {output_file}")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|