Skip to content

Commit 935fced

Browse files
committed
Improvement: Make the CLI work on windows
1 parent ecaee49 commit 935fced

File tree

2 files changed

+58
-13
lines changed

2 files changed

+58
-13
lines changed

setup.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
from setuptools import find_packages, setup
2+
from pathlib import Path
3+
4+
this_directory = Path(__file__).parent
5+
long_description = (this_directory / "README.md").read_text(encoding="utf-8")
26

37
setup(
48
name="gitingest",
@@ -19,7 +23,7 @@
1923
author="Romain Courtois",
2024
author_email="romain@coderamp.io",
2125
description="CLI tool to analyze and create text dumps of codebases for LLMs",
22-
long_description=open("README.md").read(),
26+
long_description=long_description,
2327
long_description_content_type="text/markdown",
2428
url="https://github.com/cyclotruc/gitingest",
2529
classifiers=[

src/gitingest/query_ingestion.py

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
from fnmatch import fnmatch
44
from pathlib import Path
55
from typing import Any
6+
import locale
7+
import os
8+
import platform
69

710
import tiktoken
811

@@ -16,6 +19,25 @@
1619
from gitingest.notebook_utils import process_notebook
1720
from gitingest.query_parser import ParsedQuery
1821

22+
try:
23+
locale.setlocale(locale.LC_ALL, '')
24+
except locale.Error:
25+
locale.setlocale(locale.LC_ALL, 'C')
26+
27+
def _normalize_path(path: Path) -> Path:
28+
"""Normalize path for cross-platform compatibility."""
29+
return Path(os.path.normpath(str(path)))
30+
31+
def _normalize_path_str(path: str | Path) -> str:
32+
"""Convert path to string with forward slashes for consistent output."""
33+
return str(path).replace(os.sep, '/')
34+
35+
def _get_encoding_list() -> list[str]:
36+
"""Get list of encodings to try, prioritized for the current platform."""
37+
encodings = ['utf-8', 'utf-8-sig']
38+
if platform.system() == 'Windows':
39+
encodings.extend(['cp1252', 'iso-8859-1'])
40+
return encodings + [locale.getpreferredencoding()]
1941

2042
def _should_include(path: Path, base_path: Path, include_patterns: set[str]) -> bool:
2143
"""
@@ -107,9 +129,13 @@ def _is_safe_symlink(symlink_path: Path, base_path: Path) -> bool:
107129
`True` if the symlink points within the base directory, `False` otherwise.
108130
"""
109131
try:
110-
target_path = symlink_path.resolve()
111-
base_resolved = base_path.resolve()
112-
# It's "safe" if target_path == base_resolved or is inside base_resolved
132+
if platform.system() == 'Windows':
133+
if not os.path.islink(str(symlink_path)):
134+
return False
135+
136+
target_path = _normalize_path(symlink_path.resolve())
137+
base_resolved = _normalize_path(base_path.resolve())
138+
113139
return base_resolved in target_path.parents or target_path == base_resolved
114140
except (OSError, ValueError):
115141
# If there's any error resolving the paths, consider it unsafe
@@ -162,10 +188,22 @@ def _read_file_content(file_path: Path) -> str:
162188
"""
163189
try:
164190
if file_path.suffix == ".ipynb":
165-
return process_notebook(file_path)
191+
try:
192+
return process_notebook(file_path)
193+
except Exception as e:
194+
return f"Error processing notebook: {e}"
195+
196+
for encoding in _get_encoding_list():
197+
try:
198+
with open(file_path, encoding=encoding) as f:
199+
return f.read()
200+
except UnicodeDecodeError:
201+
continue
202+
except OSError as e:
203+
return f"Error reading file: {e}"
204+
205+
return "Error: Unable to decode file with available encodings"
166206

167-
with open(file_path, encoding="utf-8", errors="ignore") as f:
168-
return f.read()
169207
except (OSError, InvalidNotebookError) as e:
170208
return f"Error reading file: {e}"
171209

@@ -531,10 +569,10 @@ def _extract_files_content(
531569
content = node["content"]
532570

533571
relative_path = Path(node["path"]).relative_to(query.local_path)
534-
572+
# Store paths with forward slashes
535573
files.append(
536574
{
537-
"path": str(relative_path),
575+
"path": _normalize_path_str(relative_path),
538576
"content": content,
539577
"size": node["size"],
540578
},
@@ -572,7 +610,8 @@ def _create_file_content_string(files: list[dict[str, Any]]) -> str:
572610
continue
573611

574612
output += separator
575-
output += f"File: {file['path']}\n"
613+
# Use forward slashes in output paths
614+
output += f"File: {_normalize_path_str(file['path'])}\n"
576615
output += separator
577616
output += f"{file['content']}\n\n"
578617

@@ -815,11 +854,13 @@ def run_ingest_query(query: ParsedQuery) -> tuple[str, str, str]:
815854
ValueError
816855
If the specified path cannot be found or if the file is not a text file.
817856
"""
818-
path = query.local_path / query.subpath.lstrip("/")
857+
subpath = _normalize_path(Path(query.subpath.strip("/"))).as_posix()
858+
path = _normalize_path(query.local_path / subpath)
859+
819860
if not path.exists():
820861
raise ValueError(f"{query.slug} cannot be found")
821862

822863
if query.type and query.type == "blob":
823-
return _ingest_single_file(path, query)
864+
return _ingest_single_file(_normalize_path(path.resolve()), query)
824865

825-
return _ingest_directory(path, query)
866+
return _ingest_directory(_normalize_path(path.resolve()), query)

0 commit comments

Comments
 (0)