From f22c5cb19977db54d68c96992a51f970cf2c32a1 Mon Sep 17 00:00:00 2001 From: "Edward Z. Yang" Date: Mon, 14 Apr 2025 12:30:39 +0800 Subject: [PATCH 1/3] Update [ghstack-poisoned] --- codemcp/main.py | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/codemcp/main.py b/codemcp/main.py index 478a37e..badf0ba 100644 --- a/codemcp/main.py +++ b/codemcp/main.py @@ -7,6 +7,7 @@ from typing import List, Optional import click +import pathspec import uvicorn from fastapi.middleware.cors import CORSMiddleware from mcp.server.fastmcp import FastMCP @@ -524,6 +525,47 @@ def init_codemcp_project(path: str, python: bool = False) -> str: # Track which files we need to add to git files_to_add = [] + # Function to get files respecting .gitignore + def get_files_respecting_gitignore( + dir_path: Path, pattern: str = "**/*" + ) -> List[Path]: + """Get files in a directory respecting .gitignore rules. + + Args: + dir_path: The directory path to search in + pattern: The glob pattern to match files against (default: "**/*") + + Returns: + A list of Path objects for files that match the pattern and respect .gitignore + """ + # Check for .gitignore file + gitignore_path = dir_path / ".gitignore" + ignore_spec = None + + if gitignore_path.exists(): + with open(gitignore_path, "r") as ignore_file: + ignore_lines = ignore_file.readlines() + ignore_spec = pathspec.GitIgnoreSpec.from_lines(ignore_lines) + + # First get all files using glob + all_files = list(dir_path.glob(pattern)) + + # If there's no .gitignore, return all files + if ignore_spec is None: + return [f for f in all_files if f.is_file()] + + # Filter files that match .gitignore patterns + result = [] + for file_path in all_files: + if file_path.is_file(): + # Get relative path from the directory + rel_path = str(file_path.relative_to(dir_path)) + # Check if file should be included + if not ignore_spec.match_file(rel_path): + result.append(file_path) + + return result + # Function to replace placeholders in a string def replace_placeholders(text): for placeholder, value in replacements.items(): @@ -567,9 +609,10 @@ def process_file(template_file, template_root, output_root): print(f"Created file: {rel_path}") return rel_path - # Recursively process template directory - for template_file in templates_dir.glob("**/*"): - if template_file.is_file() and template_file.name != ".gitkeep": + # Recursively process template directory respecting .gitignore + template_files = get_files_respecting_gitignore(templates_dir, "**/*") + for template_file in template_files: + if template_file.name != ".gitkeep": # Process template file try: rel_path = process_file(template_file, templates_dir, project_path) From 47959a55609ad8b48fb9cb9d452ba8c8f5250086 Mon Sep 17 00:00:00 2001 From: "Edward Z. Yang" Date: Mon, 14 Apr 2025 12:46:22 +0800 Subject: [PATCH 2/3] Update [ghstack-poisoned] --- codemcp/main.py | 81 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 24 deletions(-) diff --git a/codemcp/main.py b/codemcp/main.py index badf0ba..08a58b9 100644 --- a/codemcp/main.py +++ b/codemcp/main.py @@ -529,7 +529,7 @@ def init_codemcp_project(path: str, python: bool = False) -> str: def get_files_respecting_gitignore( dir_path: Path, pattern: str = "**/*" ) -> List[Path]: - """Get files in a directory respecting .gitignore rules. + """Get files in a directory respecting .gitignore rules in all subdirectories. Args: dir_path: The directory path to search in @@ -538,32 +538,65 @@ def get_files_respecting_gitignore( Returns: A list of Path objects for files that match the pattern and respect .gitignore """ - # Check for .gitignore file - gitignore_path = dir_path / ".gitignore" - ignore_spec = None - - if gitignore_path.exists(): - with open(gitignore_path, "r") as ignore_file: - ignore_lines = ignore_file.readlines() - ignore_spec = pathspec.GitIgnoreSpec.from_lines(ignore_lines) - - # First get all files using glob - all_files = list(dir_path.glob(pattern)) - - # If there's no .gitignore, return all files - if ignore_spec is None: + # First collect all files and directories + all_paths = list(dir_path.glob(pattern)) + all_files = [p for p in all_paths if p.is_file()] + all_dirs = [dir_path] + [p for p in all_paths if p.is_dir()] + + # Find all .gitignore files in the directory and subdirectories + gitignore_specs = {} + + # Process .gitignore files from root to leaf directories + for directory in sorted(all_dirs, key=lambda d: str(d)): + gitignore_path = directory / ".gitignore" + if gitignore_path.exists() and gitignore_path.is_file(): + try: + with open(gitignore_path, "r") as ignore_file: + ignore_lines = ignore_file.readlines() + gitignore_specs[directory] = pathspec.GitIgnoreSpec.from_lines( + ignore_lines + ) + except Exception as e: + # Log error but continue processing + logging.warning(f"Error reading .gitignore in {directory}: {e}") + + # If no .gitignore files found, return all files + if not gitignore_specs: return [f for f in all_files if f.is_file()] - # Filter files that match .gitignore patterns - result = [] - for file_path in all_files: - if file_path.is_file(): - # Get relative path from the directory - rel_path = str(file_path.relative_to(dir_path)) - # Check if file should be included - if not ignore_spec.match_file(rel_path): - result.append(file_path) + # Helper function to check if a path is ignored by any relevant .gitignore + def is_ignored(path: Path) -> bool: + """ + Check if a path should be ignored according to .gitignore rules. + + This checks the path against all .gitignore files in its parent directories. + """ + # For files, we need to check if any parent directory is ignored first + if path.is_file(): + # Check if any parent directory is ignored + current_dir = path.parent + while current_dir.is_relative_to(dir_path): + if is_ignored(current_dir): + return True + current_dir = current_dir.parent + + # Now check the path against all relevant .gitignore specs + for spec_dir, spec in gitignore_specs.items(): + # Only apply specs from parent directories of the path + if path.is_relative_to(spec_dir): + # Get the path relative to the directory containing the .gitignore + rel_path = str(path.relative_to(spec_dir)) + # Empty string means the directory itself + if not rel_path: + rel_path = "." + # Check if path matches any pattern in the .gitignore + if spec.match_file(rel_path): + return True + + return False + # Filter out ignored files + result = [f for f in all_files if not is_ignored(f)] return result # Function to replace placeholders in a string From 7fca6b8f261242f45135d72321f8024f2b63e33f Mon Sep 17 00:00:00 2001 From: "Edward Z. Yang" Date: Mon, 14 Apr 2025 16:16:23 +0800 Subject: [PATCH 3/3] Update [ghstack-poisoned] --- codemcp/main.py | 146 ++++++++++++++++++++++++------------------------ 1 file changed, 72 insertions(+), 74 deletions(-) diff --git a/codemcp/main.py b/codemcp/main.py index 08a58b9..d485398 100644 --- a/codemcp/main.py +++ b/codemcp/main.py @@ -397,6 +397,78 @@ def normalize_newlines(s: object) -> object: return "Unknown subtool or operation" +def get_files_respecting_gitignore(dir_path: Path, pattern: str = "**/*") -> List[Path]: + """Get files in a directory respecting .gitignore rules in all subdirectories. + + Args: + dir_path: The directory path to search in + pattern: The glob pattern to match files against (default: "**/*") + + Returns: + A list of Path objects for files that match the pattern and respect .gitignore + """ + # First collect all files and directories + all_paths = list(dir_path.glob(pattern)) + all_files = [p for p in all_paths if p.is_file()] + all_dirs = [dir_path] + [p for p in all_paths if p.is_dir()] + + # Find all .gitignore files in the directory and subdirectories + gitignore_specs = {} + + # Process .gitignore files from root to leaf directories + for directory in sorted(all_dirs, key=lambda d: str(d)): + gitignore_path = directory / ".gitignore" + if gitignore_path.exists() and gitignore_path.is_file(): + try: + with open(gitignore_path, "r") as ignore_file: + ignore_lines = ignore_file.readlines() + gitignore_specs[directory] = pathspec.GitIgnoreSpec.from_lines( + ignore_lines + ) + except Exception as e: + # Log error but continue processing + logging.warning(f"Error reading .gitignore in {directory}: {e}") + + # If no .gitignore files found, return all files + if not gitignore_specs: + return [f for f in all_files if f.is_file()] + + # Helper function to check if a path is ignored by any relevant .gitignore + def is_ignored(path: Path) -> bool: + """ + Check if a path should be ignored according to .gitignore rules. + + This checks the path against all .gitignore files in its parent directories. + """ + # For files, we need to check if any parent directory is ignored first + if path.is_file(): + # Check if any parent directory is ignored + current_dir = path.parent + while current_dir.is_relative_to(dir_path): + if is_ignored(current_dir): + return True + current_dir = current_dir.parent + + # Now check the path against all relevant .gitignore specs + for spec_dir, spec in gitignore_specs.items(): + # Only apply specs from parent directories of the path + if path.is_relative_to(spec_dir): + # Get the path relative to the directory containing the .gitignore + rel_path = str(path.relative_to(spec_dir)) + # Empty string means the directory itself + if not rel_path: + rel_path = "." + # Check if path matches any pattern in the .gitignore + if spec.match_file(rel_path): + return True + + return False + + # Filter out ignored files + result = [f for f in all_files if not is_ignored(f)] + return result + + def configure_logging(log_file: str = "codemcp.log") -> None: """Configure logging to write to both a file and the console. @@ -525,80 +597,6 @@ def init_codemcp_project(path: str, python: bool = False) -> str: # Track which files we need to add to git files_to_add = [] - # Function to get files respecting .gitignore - def get_files_respecting_gitignore( - dir_path: Path, pattern: str = "**/*" - ) -> List[Path]: - """Get files in a directory respecting .gitignore rules in all subdirectories. - - Args: - dir_path: The directory path to search in - pattern: The glob pattern to match files against (default: "**/*") - - Returns: - A list of Path objects for files that match the pattern and respect .gitignore - """ - # First collect all files and directories - all_paths = list(dir_path.glob(pattern)) - all_files = [p for p in all_paths if p.is_file()] - all_dirs = [dir_path] + [p for p in all_paths if p.is_dir()] - - # Find all .gitignore files in the directory and subdirectories - gitignore_specs = {} - - # Process .gitignore files from root to leaf directories - for directory in sorted(all_dirs, key=lambda d: str(d)): - gitignore_path = directory / ".gitignore" - if gitignore_path.exists() and gitignore_path.is_file(): - try: - with open(gitignore_path, "r") as ignore_file: - ignore_lines = ignore_file.readlines() - gitignore_specs[directory] = pathspec.GitIgnoreSpec.from_lines( - ignore_lines - ) - except Exception as e: - # Log error but continue processing - logging.warning(f"Error reading .gitignore in {directory}: {e}") - - # If no .gitignore files found, return all files - if not gitignore_specs: - return [f for f in all_files if f.is_file()] - - # Helper function to check if a path is ignored by any relevant .gitignore - def is_ignored(path: Path) -> bool: - """ - Check if a path should be ignored according to .gitignore rules. - - This checks the path against all .gitignore files in its parent directories. - """ - # For files, we need to check if any parent directory is ignored first - if path.is_file(): - # Check if any parent directory is ignored - current_dir = path.parent - while current_dir.is_relative_to(dir_path): - if is_ignored(current_dir): - return True - current_dir = current_dir.parent - - # Now check the path against all relevant .gitignore specs - for spec_dir, spec in gitignore_specs.items(): - # Only apply specs from parent directories of the path - if path.is_relative_to(spec_dir): - # Get the path relative to the directory containing the .gitignore - rel_path = str(path.relative_to(spec_dir)) - # Empty string means the directory itself - if not rel_path: - rel_path = "." - # Check if path matches any pattern in the .gitignore - if spec.match_file(rel_path): - return True - - return False - - # Filter out ignored files - result = [f for f in all_files if not is_ignored(f)] - return result - # Function to replace placeholders in a string def replace_placeholders(text): for placeholder, value in replacements.items():