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.
- claude_dev_cli/__init__.py +1 -1
- claude_dev_cli/cli.py +424 -0
- claude_dev_cli/logging/__init__.py +6 -0
- claude_dev_cli/logging/logger.py +84 -0
- claude_dev_cli/logging/markdown_logger.py +131 -0
- claude_dev_cli/notifications/__init__.py +6 -0
- claude_dev_cli/notifications/notifier.py +69 -0
- claude_dev_cli/notifications/ntfy.py +87 -0
- claude_dev_cli/project/__init__.py +10 -0
- claude_dev_cli/project/bug_tracker.py +458 -0
- claude_dev_cli/project/context_gatherer.py +535 -0
- claude_dev_cli/project/executor.py +370 -0
- claude_dev_cli/tickets/__init__.py +7 -0
- claude_dev_cli/tickets/backend.py +229 -0
- claude_dev_cli/tickets/markdown.py +309 -0
- claude_dev_cli/tickets/repo_tickets.py +361 -0
- claude_dev_cli/vcs/__init__.py +6 -0
- claude_dev_cli/vcs/git.py +172 -0
- claude_dev_cli/vcs/manager.py +90 -0
- {claude_dev_cli-0.16.1.dist-info → claude_dev_cli-0.18.0.dist-info}/METADATA +600 -10
- {claude_dev_cli-0.16.1.dist-info → claude_dev_cli-0.18.0.dist-info}/RECORD +25 -8
- {claude_dev_cli-0.16.1.dist-info → claude_dev_cli-0.18.0.dist-info}/WHEEL +0 -0
- {claude_dev_cli-0.16.1.dist-info → claude_dev_cli-0.18.0.dist-info}/entry_points.txt +0 -0
- {claude_dev_cli-0.16.1.dist-info → claude_dev_cli-0.18.0.dist-info}/licenses/LICENSE +0 -0
- {claude_dev_cli-0.16.1.dist-info → claude_dev_cli-0.18.0.dist-info}/top_level.txt +0 -0
|
@@ -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()
|