Coverage for src/prosemark/cli/init.py: 100%

45 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-09-24 18:08 +0000

1"""CLI command for initializing a new prosemark project.""" 

2 

3from pathlib import Path 

4from typing import Any 

5 

6import click 

7 

8from prosemark.adapters.binder_repo_fs import BinderRepoFs 

9from prosemark.adapters.clock_system import ClockSystem 

10from prosemark.adapters.console_pretty import ConsolePretty 

11from prosemark.adapters.logger_stdout import LoggerStdout 

12from prosemark.app.use_cases import InitProject 

13from prosemark.exceptions import BinderIntegrityError, FileSystemError 

14from prosemark.ports.config_port import ConfigPort, ProsemarkConfig 

15 

16 

17class FileSystemConfigPort(ConfigPort): 

18 """Temporary config port implementation.""" 

19 

20 def create_default_config(self, config_path: Path) -> None: 

21 """Create default configuration file.""" 

22 # For MVP, we don't need a config file 

23 

24 @staticmethod 

25 def config_exists(config_path: Path) -> bool: 

26 """Check if configuration file already exists.""" 

27 return config_path.exists() 

28 

29 @staticmethod 

30 def get_default_config_values() -> ProsemarkConfig: 

31 """Return default configuration values as dictionary.""" 

32 return {} 

33 

34 @staticmethod 

35 def load_config(_config_path: Path | None = None) -> dict[str, Any]: 

36 """Load configuration from file.""" 

37 return {} 

38 

39 

40@click.command() 

41@click.option('--title', '-t', required=True, help='Project title') 

42@click.option('--path', '-p', type=click.Path(path_type=Path), help='Project directory') 

43def init_command(title: str, path: Path | None) -> None: 

44 """Initialize a new prosemark project.""" 

45 try: 

46 project_path = path or Path.cwd() 

47 

48 # Wire up dependencies 

49 binder_repo = BinderRepoFs(project_path) 

50 config_port = FileSystemConfigPort() 

51 console_port = ConsolePretty() 

52 logger = LoggerStdout() 

53 clock = ClockSystem() 

54 

55 # Execute use case 

56 interactor = InitProject( 

57 binder_repo=binder_repo, 

58 config_port=config_port, 

59 console_port=console_port, 

60 logger=logger, 

61 clock=clock, 

62 ) 

63 interactor.execute(project_path) 

64 

65 # Success output matching test expectations 

66 click.echo(f'Project "{title}" initialized successfully') 

67 click.echo('Created _binder.md with project structure') 

68 

69 except BinderIntegrityError: 

70 click.echo('Error: Directory already contains a prosemark project', file=click.get_text_stream('stderr')) 

71 raise SystemExit(1) from None 

72 except FileSystemError as err: 

73 click.echo(f'Error: {err}', file=click.get_text_stream('stderr')) 

74 raise SystemExit(2) from err 

75 except Exception as err: 

76 click.echo(f'Unexpected error: {err}', file=click.get_text_stream('stderr')) 

77 raise SystemExit(3) from err