diff --git a/pyproject.toml b/pyproject.toml index a1a9097..d93b2c3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ctxify" -version = "0.1.7" +version = "0.1.8" description = "A tool to print git repository files with tree structure" readme = "README.md" requires-python = ">=3.8" diff --git a/src/ctxify/__init__.py b/src/ctxify/__init__.py index 124e462..c3bb296 100644 --- a/src/ctxify/__init__.py +++ b/src/ctxify/__init__.py @@ -1 +1 @@ -__version__ = '0.1.7' +__version__ = '0.1.8' diff --git a/src/ctxify/main.py b/src/ctxify/main.py index b155140..579da1c 100644 --- a/src/ctxify/main.py +++ b/src/ctxify/main.py @@ -176,7 +176,7 @@ def estimate_tokens(text: str) -> int: def interactive_file_selection(root_dir: str = '.', include_md: bool = False) -> str: - """Interactively select files to include with fuzzy tab autocompletion""" + """Interactively select files or directories to include with fuzzy tab autocompletion""" if not check_git_repo(root_dir): print( f'Error: {root_dir} is not within a git repository. This tool requires a git repository.' @@ -192,40 +192,51 @@ def interactive_file_selection(root_dir: str = '.', include_md: bool = False) -> print('\n'.join(tree_lines)) return '\n'.join(tree_lines) - completer = FuzzyWordCompleter(all_files) + # Add directories to completion options + all_dirs = set() + for f in all_files: + path = Path(f) + for parent in path.parents: + if str(parent) != '.': + all_dirs.add(str(parent)) + completion_options = sorted(all_files + list(all_dirs)) + + completer = FuzzyWordCompleter(completion_options) session = PromptSession(completer=completer, complete_while_typing=True) - tree_lines.append(f'\nFiles Available in Context (from {root_dir}):') + tree_lines.append(f'\nFiles and Directories Available in Context (from {root_dir}):') print_filtered_tree(all_files, tree_lines) print('\n'.join(tree_lines)) - print('\nEnter file paths to include (press Enter twice to finish):') + print('\nEnter file or directory paths to include (press Enter twice to finish):') - selected_files: List[str] = [] + selected_items: List[str] = [] while True: try: - file_path = session.prompt('> ') - if not file_path: - if not selected_files: + input_path = session.prompt('> ') + if not input_path: + if not selected_items: continue break - if file_path in all_files: - if file_path not in selected_files: - selected_files.append(file_path) - print(f'Added: {file_path}') + if input_path in completion_options: + if input_path not in selected_items: + selected_items.append(input_path) + print(f'Added: {input_path}') else: - print(f'Already added: {file_path}') + print(f'Already added: {input_path}') else: - print(f'File not found: {file_path}') + print(f'Path not found: {input_path}') except KeyboardInterrupt: break output_lines.extend(tree_lines) output_lines.append('\n' + '-' * 50 + '\n') target_dir = Path(root_dir).resolve() - for file_path in selected_files: - full_path = target_dir / file_path + + for item_path in selected_items: + full_path = target_dir / item_path if full_path.is_file(): - output_lines.append(f'{file_path}:') + # Handle individual file + output_lines.append(f'{item_path}:') try: with open(full_path, 'r', encoding='utf-8') as f: content = f.read() @@ -233,6 +244,27 @@ def interactive_file_selection(root_dir: str = '.', include_md: bool = False) -> except Exception as e: output_lines.append(f'Error reading file: {e}') output_lines.append('') + elif full_path.is_dir(): + # Handle directory + output_lines.append(f'\nDirectory {item_path} contents:') + dir_files = [f for f in all_files if f.startswith(item_path + '/') or f == item_path] + if not dir_files: + output_lines.append(f'No tracked files found in {item_path}') + continue + # Print directory structure + print_filtered_tree(dir_files, output_lines) + output_lines.append('\nFile contents:') + for file_path in dir_files: + full_file_path = target_dir / file_path + if full_file_path.is_file(): + output_lines.append(f'\n{file_path}:') + try: + with open(full_file_path, 'r', encoding='utf-8') as f: + content = f.read() + output_lines.append(content) + except Exception as e: + output_lines.append(f'Error reading file: {e}') + output_lines.append('') full_output = '\n'.join(output_lines) token_count = estimate_tokens(full_output) @@ -244,7 +276,6 @@ def interactive_file_selection(root_dir: str = '.', include_md: bool = False) -> return full_output - def print_git_contents( root_dir: str = '.', include_md: bool = False, structure_only: bool = False ) -> str: diff --git a/uv.lock b/uv.lock index ada7c53..9f2eef9 100644 --- a/uv.lock +++ b/uv.lock @@ -25,7 +25,7 @@ wheels = [ [[package]] name = "ctxify" -version = "0.1.6" +version = "0.1.7" source = { editable = "." } dependencies = [ { name = "click" },