1111from forklet .core import DownloadOrchestrator
1212from forklet .services import GitHubAPIService , DownloadService
1313from forklet .infrastructure import (
14- RateLimiter , RetryManager , DownloadError ,
15- RateLimitError , AuthenticationError , RepositoryNotFoundError
14+ RateLimiter ,
15+ RetryManager ,
16+ DownloadError ,
17+ RateLimitError ,
18+ AuthenticationError ,
19+ RepositoryNotFoundError ,
1620)
1721from forklet .infrastructure .logger import logger
1822from forklet .models import (
19- DownloadRequest , DownloadStrategy , FilterCriteria ,
20- DownloadResult
23+ DownloadRequest ,
24+ DownloadStrategy ,
25+ FilterCriteria ,
26+ DownloadResult ,
2127)
2228
2329
2632#####
2733class ForkletCLI :
2834 """Main CLI application class."""
29-
35+
3036 def __init__ (self ):
3137 self .rate_limiter = RateLimiter ()
3238 self .retry_manager = RetryManager ()
3339 self .github_service : Optional [GitHubAPIService ] = None
3440 self .download_service : Optional [DownloadService ] = None
3541 self .orchestrator : Optional [DownloadOrchestrator ] = None
36-
37- def initialize_services (
38- self , auth_token : Optional [str ] = None
39- ) -> None :
42+
43+ def initialize_services (self , auth_token : Optional [str ] = None ) -> None :
4044 """Initialize all services with optional authentication."""
4145
4246 self .github_service = GitHubAPIService (
@@ -46,34 +50,30 @@ def initialize_services(
4650 self .orchestrator = DownloadOrchestrator (
4751 self .github_service , self .download_service
4852 )
49-
53+
5054 def parse_repository_string (self , repo_str : str ) -> Tuple [str , str ]:
5155 """
5256 Parse repository string in format owner/repo.
53-
57+
5458 Args:
5559 repo_str: Repository string
56-
60+
5761 Returns:
5862 Tuple of (owner, repo)
59-
63+
6064 Raises:
6165 click.BadParameter: If format is invalid
6266 """
6367
64- if '/' not in repo_str :
65- raise click .BadParameter (
66- "Repository must be in format 'owner/repo'"
67- )
68-
69- parts = repo_str .split ('/' )
68+ if "/" not in repo_str :
69+ raise click .BadParameter ("Repository must be in format 'owner/repo'" )
70+
71+ parts = repo_str .split ("/" )
7072 if len (parts ) != 2 :
71- raise click .BadParameter (
72- "Repository must be in format 'owner/repo'"
73- )
74-
73+ raise click .BadParameter ("Repository must be in format 'owner/repo'" )
74+
7575 return parts [0 ], parts [1 ]
76-
76+
7777 def create_filter_criteria (
7878 self ,
7979 include : List [str ],
@@ -84,11 +84,11 @@ def create_filter_criteria(
8484 exclude_extensions : List [str ],
8585 include_hidden : bool ,
8686 include_binary : bool ,
87- target_paths : List [str ]
87+ target_paths : List [str ],
8888 ) -> FilterCriteria :
8989 """
9090 Create filter criteria from CLI options.
91-
91+
9292 Args:
9393 include: Include patterns
9494 exclude: Exclude patterns
@@ -99,23 +99,23 @@ def create_filter_criteria(
9999 include_hidden: Include hidden files
100100 include_binary: Include binary files
101101 target_paths: Specific paths to download
102-
102+
103103 Returns:
104104 FilterCriteria object
105105 """
106106
107107 return FilterCriteria (
108- include_patterns = include ,
109- exclude_patterns = exclude ,
110- max_file_size = max_size ,
111- min_file_size = min_size ,
112- file_extensions = set (extensions ),
113- excluded_extensions = set (exclude_extensions ),
114- include_hidden = include_hidden ,
115- include_binary = include_binary ,
116- target_paths = target_paths
108+ include_patterns = include ,
109+ exclude_patterns = exclude ,
110+ max_file_size = max_size ,
111+ min_file_size = min_size ,
112+ file_extensions = set (extensions ),
113+ excluded_extensions = set (exclude_extensions ),
114+ include_hidden = include_hidden ,
115+ include_binary = include_binary ,
116+ target_paths = target_paths ,
117117 )
118-
118+
119119 async def execute_download (
120120 self ,
121121 repository : str ,
@@ -132,7 +132,7 @@ async def execute_download(
132132 ) -> None :
133133 """
134134 Execute the download operation.
135-
135+
136136 Args:
137137 repository: Repository string (owner/repo)
138138 destination: Destination directory
@@ -147,66 +147,66 @@ async def execute_download(
147147 try :
148148 # Initialize services
149149 self .initialize_services (token )
150-
150+
151151 # Parse repository
152152 owner , repo_name = self .parse_repository_string (repository )
153-
153+
154154 # Get repository info
155155 click .echo (f"📦 Fetching repository information for { owner } /{ repo_name } ..." )
156156 repo_info = await self .github_service .get_repository_info (owner , repo_name )
157-
157+
158158 # Resolve Git reference
159159 click .echo (f"🔍 Resolving reference '{ ref } '..." )
160160 git_ref = await self .github_service .resolve_reference (owner , repo_name , ref )
161-
161+
162162 # Create download request
163163 request = DownloadRequest (
164- repository = repo_info ,
165- git_ref = git_ref ,
166- destination = Path (destination ),
167- strategy = strategy ,
168- filters = filters ,
169- token = token ,
170- max_concurrent_downloads = concurrent ,
171- overwrite_existing = overwrite ,
172- show_progress_bars = progress
173- , dry_run = dry_run
164+ repository = repo_info ,
165+ git_ref = git_ref ,
166+ destination = Path (destination ),
167+ strategy = strategy ,
168+ filters = filters ,
169+ token = token ,
170+ max_concurrent_downloads = concurrent ,
171+ overwrite_existing = overwrite ,
172+ show_progress_bars = progress ,
173+ dry_run = dry_run ,
174174 )
175-
175+
176176 # Execute download
177- click .echo (
178- f"🚀 Starting download with { concurrent } concurrent workers..."
179- )
177+ click .echo (f"🚀 Starting download with { concurrent } concurrent workers..." )
180178 result = await self .orchestrator .execute_download (request )
181179
182180 # Display results (pass through verbose flag)
183181 self .display_results (result , verbose = verbose )
184-
182+
185183 except (
186- RateLimitError , AuthenticationError ,
187- RepositoryNotFoundError , DownloadError
184+ RateLimitError ,
185+ AuthenticationError ,
186+ RepositoryNotFoundError ,
187+ DownloadError ,
188188 ) as e :
189- click .echo (f"❌ Error: { e } " , err = True )
189+ click .echo (f"💥 Error: { e } " , err = True )
190190 sys .exit (1 )
191191
192192 except Exception as e :
193193 click .echo (f"💥 Unexpected error: { e } " , err = True )
194- logger .exception ("Unexpected error in download operation" )
194+ logger .debug ("Unexpected error in download operation" , exc_info = True )
195195 sys .exit (1 )
196-
196+
197197 def display_results (self , result : DownloadResult , verbose : bool = False ) -> None :
198198 """
199199 Display download results in a user-friendly format.
200-
200+
201201 Args:
202202 result: Download result
203203 """
204204
205- if hasattr (result , ' is_successful' ) and result .is_successful :
205+ if hasattr (result , " is_successful" ) and result .is_successful :
206206 click .echo ("✅ Download completed successfully!" )
207207 click .echo (f" 📁 Files: { len (result .downloaded_files )} downloaded" )
208208 click .echo (f" 💾 Size: { result .progress .downloaded_bytes } bytes" )
209-
209+
210210 if result .average_speed is not None :
211211 click .echo (f" ⚡ Speed: { result .average_speed :.2f} bytes/sec" )
212212
@@ -216,7 +216,7 @@ def display_results(self, result: DownloadResult, verbose: bool = False) -> None
216216 # When verbose, display file paths (matched / downloaded / skipped)
217217 if verbose :
218218 # Matched files (available in dry-run and set by orchestrator)
219- if hasattr (result , ' matched_files' ) and result .matched_files :
219+ if hasattr (result , " matched_files" ) and result .matched_files :
220220 click .echo (" 🔎 Matched files:" )
221221 for p in result .matched_files :
222222 click .echo (f" { p } " )
@@ -231,17 +231,19 @@ def display_results(self, result: DownloadResult, verbose: bool = False) -> None
231231 click .echo (" ⏭️ Skipped paths:" )
232232 for p in result .skipped_files :
233233 click .echo (f" { p } " )
234-
235- elif hasattr (result , ' failed_files' ) and result .failed_files :
234+
235+ elif hasattr (result , " failed_files" ) and result .failed_files :
236236 click .echo ("⚠️ Download completed with errors:" )
237237 click .echo (f" ✅ Successful: { len (result .downloaded_files )} " )
238238 click .echo (f" ❌ Failed: { len (result .failed_files )} " )
239-
239+
240240 # Show first few errors
241- for i , (filename , error ) in enumerate (list (result .failed_files .items ())[:3 ]):
241+ for i , (filename , error ) in enumerate (
242+ list (result .failed_files .items ())[:3 ]
243+ ):
242244 click .echo (f" { filename } : { error } " )
243245 if len (result .failed_files ) > 3 :
244246 click .echo (f" ... and { len (result .failed_files ) - 3 } more errors" )
245-
247+
246248 else :
247249 click .echo ("❌ Download failed completely" )
0 commit comments