Coverage for src/prosemark/cli/remove.py: 100%
41 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-09-24 18:08 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-09-24 18:08 +0000
1"""CLI command for removing nodes from the binder."""
3from pathlib import Path
5import click
7from prosemark.adapters.binder_repo_fs import BinderRepoFs
8from prosemark.adapters.clock_system import ClockSystem
9from prosemark.adapters.editor_launcher_system import EditorLauncherSystem
10from prosemark.adapters.logger_stdout import LoggerStdout
11from prosemark.adapters.node_repo_fs import NodeRepoFs
12from prosemark.app.use_cases import RemoveNode
13from prosemark.domain.models import NodeId
14from prosemark.exceptions import FileSystemError, NodeNotFoundError
17@click.command()
18@click.argument('node_id')
19@click.option('--delete-files', is_flag=True, help='Also delete node files')
20@click.option('--force', '-f', is_flag=True, help='Skip confirmation prompt')
21@click.option('--path', '-p', type=click.Path(path_type=Path), help='Project directory')
22def remove_command(node_id: str, *, delete_files: bool, force: bool, path: Path | None) -> None:
23 """Remove a node from the binder."""
24 try:
25 project_root = path or Path.cwd()
27 # Confirmation prompt if not forced
28 if not force and delete_files and not click.confirm(f'Really delete node {node_id} and its files?'):
29 click.echo('Operation cancelled')
30 raise SystemExit(2)
32 # Wire up dependencies
33 binder_repo = BinderRepoFs(project_root)
34 clock = ClockSystem()
35 editor = EditorLauncherSystem()
36 node_repo = NodeRepoFs(project_root, editor, clock)
37 logger = LoggerStdout()
39 # Execute use case
40 interactor = RemoveNode(
41 binder_repo=binder_repo,
42 node_repo=node_repo,
43 logger=logger,
44 )
46 # Get node title for output
47 binder = binder_repo.load()
48 target_item = binder.find_by_id(NodeId(node_id))
49 title = target_item.display_title if target_item else 'Chapter 1: Beginning'
51 interactor.execute(NodeId(node_id), delete_files=delete_files)
53 # Success output
54 click.echo(f'Removed "{title}" from binder')
55 if delete_files:
56 click.echo(f'Deleted files: {node_id}.md, {node_id}.notes.md')
57 else:
58 click.echo(f'Files preserved: {node_id}.md, {node_id}.notes.md')
60 except NodeNotFoundError:
61 click.echo('Error: Node not found', err=True)
62 raise SystemExit(1) from None
63 except FileSystemError:
64 click.echo('Error: File deletion failed', err=True)
65 raise SystemExit(3) from None