claude-dev-cli 0.16.1__py3-none-any.whl → 0.18.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of claude-dev-cli might be problematic. Click here for more details.

@@ -0,0 +1,172 @@
1
+ """Git VCS manager implementation."""
2
+
3
+ import subprocess
4
+ from pathlib import Path
5
+ from typing import Optional, List
6
+
7
+ from claude_dev_cli.vcs.manager import VCSManager, CommitInfo
8
+
9
+
10
+ class GitManager(VCSManager):
11
+ """Git version control manager.
12
+
13
+ Supports conventional commits and co-author attribution.
14
+ """
15
+
16
+ def __init__(self, repo_path: Optional[Path] = None):
17
+ """Initialize Git manager.
18
+
19
+ Args:
20
+ repo_path: Path to repository (default: current directory)
21
+ """
22
+ self.repo_path = repo_path or Path.cwd()
23
+
24
+ def is_repository(self) -> bool:
25
+ """Check if current directory is a Git repository."""
26
+ try:
27
+ result = subprocess.run(
28
+ ["git", "rev-parse", "--git-dir"],
29
+ cwd=self.repo_path,
30
+ capture_output=True,
31
+ timeout=5
32
+ )
33
+ return result.returncode == 0
34
+ except (subprocess.TimeoutExpired, FileNotFoundError):
35
+ return False
36
+
37
+ def commit(
38
+ self,
39
+ message: str,
40
+ files: Optional[List[str]] = None,
41
+ co_author: Optional[str] = None
42
+ ) -> CommitInfo:
43
+ """Create a Git commit with optional co-author."""
44
+ # Add files
45
+ if files:
46
+ for file_path in files:
47
+ subprocess.run(
48
+ ["git", "add", file_path],
49
+ cwd=self.repo_path,
50
+ timeout=10
51
+ )
52
+ else:
53
+ # Add all changes
54
+ subprocess.run(
55
+ ["git", "add", "-A"],
56
+ cwd=self.repo_path,
57
+ timeout=10
58
+ )
59
+
60
+ # Build commit message with co-author
61
+ full_message = message
62
+ if co_author:
63
+ full_message = f"{message}\n\nCo-Authored-By: {co_author}"
64
+
65
+ # Create commit
66
+ result = subprocess.run(
67
+ ["git", "commit", "-m", full_message],
68
+ cwd=self.repo_path,
69
+ capture_output=True,
70
+ text=True,
71
+ timeout=10
72
+ )
73
+
74
+ if result.returncode != 0:
75
+ raise RuntimeError(f"Commit failed: {result.stderr}")
76
+
77
+ # Get commit SHA
78
+ sha_result = subprocess.run(
79
+ ["git", "rev-parse", "HEAD"],
80
+ cwd=self.repo_path,
81
+ capture_output=True,
82
+ text=True,
83
+ timeout=5
84
+ )
85
+ sha = sha_result.stdout.strip()
86
+
87
+ # Get author
88
+ author_result = subprocess.run(
89
+ ["git", "log", "-1", "--pretty=format:%an <%ae>"],
90
+ cwd=self.repo_path,
91
+ capture_output=True,
92
+ text=True,
93
+ timeout=5
94
+ )
95
+ author = author_result.stdout.strip()
96
+
97
+ return CommitInfo(
98
+ sha=sha,
99
+ message=message,
100
+ author=author,
101
+ files=files or []
102
+ )
103
+
104
+ def create_branch(self, branch_name: str, from_branch: Optional[str] = None) -> bool:
105
+ """Create a new Git branch."""
106
+ try:
107
+ cmd = ["git", "checkout", "-b", branch_name]
108
+
109
+ if from_branch:
110
+ cmd.append(from_branch)
111
+
112
+ result = subprocess.run(
113
+ cmd,
114
+ cwd=self.repo_path,
115
+ capture_output=True,
116
+ timeout=10
117
+ )
118
+
119
+ return result.returncode == 0
120
+ except subprocess.TimeoutExpired:
121
+ return False
122
+
123
+ def checkout(self, branch_name: str) -> bool:
124
+ """Checkout a Git branch."""
125
+ try:
126
+ result = subprocess.run(
127
+ ["git", "checkout", branch_name],
128
+ cwd=self.repo_path,
129
+ capture_output=True,
130
+ timeout=10
131
+ )
132
+ return result.returncode == 0
133
+ except subprocess.TimeoutExpired:
134
+ return False
135
+
136
+ def current_branch(self) -> str:
137
+ """Get current Git branch name."""
138
+ try:
139
+ result = subprocess.run(
140
+ ["git", "branch", "--show-current"],
141
+ cwd=self.repo_path,
142
+ capture_output=True,
143
+ text=True,
144
+ timeout=5
145
+ )
146
+
147
+ if result.returncode == 0:
148
+ return result.stdout.strip()
149
+
150
+ return "unknown"
151
+ except subprocess.TimeoutExpired:
152
+ return "unknown"
153
+
154
+ def push(self, remote: str = "origin", branch: Optional[str] = None) -> bool:
155
+ """Push changes to remote."""
156
+ try:
157
+ branch_name = branch or self.current_branch()
158
+
159
+ result = subprocess.run(
160
+ ["git", "push", remote, branch_name],
161
+ cwd=self.repo_path,
162
+ capture_output=True,
163
+ timeout=30
164
+ )
165
+
166
+ return result.returncode == 0
167
+ except subprocess.TimeoutExpired:
168
+ return False
169
+
170
+ def get_vcs_name(self) -> str:
171
+ """Return VCS name."""
172
+ return "git"
@@ -0,0 +1,90 @@
1
+ """Abstract VCS manager interface."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from pathlib import Path
5
+ from typing import Optional, List
6
+ from dataclasses import dataclass
7
+
8
+
9
+ @dataclass
10
+ class CommitInfo:
11
+ """Information about a VCS commit."""
12
+ sha: str
13
+ message: str
14
+ author: str
15
+ files: List[str]
16
+
17
+
18
+ class VCSManager(ABC):
19
+ """Abstract base class for VCS operations."""
20
+
21
+ @abstractmethod
22
+ def is_repository(self) -> bool:
23
+ """Check if current directory is a VCS repository."""
24
+ pass
25
+
26
+ @abstractmethod
27
+ def commit(
28
+ self,
29
+ message: str,
30
+ files: Optional[List[str]] = None,
31
+ co_author: Optional[str] = None
32
+ ) -> CommitInfo:
33
+ """Create a commit.
34
+
35
+ Args:
36
+ message: Commit message
37
+ files: Files to commit (None = all changed files)
38
+ co_author: Co-author attribution (e.g., "Name <email>")
39
+
40
+ Returns:
41
+ CommitInfo object
42
+ """
43
+ pass
44
+
45
+ @abstractmethod
46
+ def create_branch(self, branch_name: str, from_branch: Optional[str] = None) -> bool:
47
+ """Create a new branch.
48
+
49
+ Args:
50
+ branch_name: Name of new branch
51
+ from_branch: Base branch (None = current branch)
52
+
53
+ Returns:
54
+ True if successful
55
+ """
56
+ pass
57
+
58
+ @abstractmethod
59
+ def checkout(self, branch_name: str) -> bool:
60
+ """Checkout a branch.
61
+
62
+ Args:
63
+ branch_name: Branch to checkout
64
+
65
+ Returns:
66
+ True if successful
67
+ """
68
+ pass
69
+
70
+ @abstractmethod
71
+ def current_branch(self) -> str:
72
+ """Get current branch name."""
73
+ pass
74
+
75
+ @abstractmethod
76
+ def push(self, remote: str = "origin", branch: Optional[str] = None) -> bool:
77
+ """Push changes to remote.
78
+
79
+ Args:
80
+ remote: Remote name
81
+ branch: Branch to push (None = current branch)
82
+
83
+ Returns:
84
+ True if successful
85
+ """
86
+ pass
87
+
88
+ def get_vcs_name(self) -> str:
89
+ """Get VCS system name."""
90
+ return self.__class__.__name__.replace('Manager', '').lower()