devgen-cli 0.2.1__py3-none-any.whl → 0.2.3__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.
devgen/cli/commit.py CHANGED
@@ -52,7 +52,7 @@ def run_commit(
52
52
  ] = False,
53
53
  ) -> None:
54
54
  log_file = get_main_log_path()
55
- logger = configure_logger("devgen.cli.commit", log_file)
55
+ logger = configure_logger("devgen.cli.commit", log_file, console=debug)
56
56
  logger.info(f"Log file: {log_file}")
57
57
  logger.info(
58
58
  f"Options: dry_run={dry_run}, push={push}, debug={debug}, force={force_rebuild}"
@@ -5,7 +5,7 @@ from datetime import datetime
5
5
  from pathlib import Path
6
6
  from typing import Dict, List, Optional
7
7
 
8
- from devgen.utils import configure_logger
8
+ from devgen.utils import configure_logger, run_git_command
9
9
 
10
10
 
11
11
  class ChangelogGenerator:
@@ -14,22 +14,6 @@ class ChangelogGenerator:
14
14
  def __init__(self, logger=None):
15
15
  self.logger = logger or configure_logger("devgen.changelog")
16
16
 
17
- def _exec_git(self, args: List[str]) -> str:
18
- """Executes a git command."""
19
- try:
20
- res = subprocess.run(
21
- args,
22
- capture_output=True,
23
- text=True,
24
- encoding="utf-8",
25
- errors="replace",
26
- check=True,
27
- )
28
- return res.stdout.strip()
29
- except subprocess.CalledProcessError as e:
30
- self.logger.error(f"Git command failed: {e}")
31
- raise RuntimeError(f"Git command failed: {e}")
32
-
33
17
  def get_commits(self, from_ref: str = "", to_ref: str = "HEAD") -> List[str]:
34
18
  """Fetches commit messages in the specified range."""
35
19
  range_spec = f"{from_ref}..{to_ref}" if from_ref else to_ref
@@ -40,7 +24,7 @@ class ChangelogGenerator:
40
24
  if not from_ref:
41
25
  # If no start ref, try to find the last tag
42
26
  try:
43
- last_tag = self._exec_git(["git", "describe", "--tags", "--abbrev=0"])
27
+ last_tag = run_git_command(["git", "describe", "--tags", "--abbrev=0"])
44
28
  cmd = [
45
29
  "git",
46
30
  "log",
@@ -49,11 +33,15 @@ class ChangelogGenerator:
49
33
  f"{last_tag}..HEAD",
50
34
  ]
51
35
  self.logger.info(f"Generating changelog from last tag: {last_tag}")
52
- except RuntimeError:
36
+ except (RuntimeError, subprocess.CalledProcessError):
53
37
  self.logger.info("No tags found, generating for all commits.")
54
38
  cmd = ["git", "log", f"--format={fmt}", "--date=short"]
55
39
 
56
- return self._exec_git(cmd).split("\n")
40
+ try:
41
+ return run_git_command(cmd).split("\n")
42
+ except subprocess.CalledProcessError as e:
43
+ self.logger.error(f"Git command failed: {e}")
44
+ raise RuntimeError(f"Git command failed: {e}")
57
45
 
58
46
  def parse_commits(self, raw_commits: List[str]) -> Dict[str, List[Dict]]:
59
47
  """Parses raw commit strings into structured data."""
@@ -11,8 +11,13 @@ from devgen.utils import (
11
11
  get_commit_dry_run_path,
12
12
  is_file_recent,
13
13
  load_template_env,
14
+ run_git_command,
14
15
  sanitize_ai_commit_message,
15
16
  )
17
+ from rich.console import Console
18
+ from rich.panel import Panel
19
+ from rich.markdown import Markdown
20
+ from rich.theme import Theme
16
21
 
17
22
 
18
23
  class CommitEngineError(Exception):
@@ -45,48 +50,44 @@ class CommitEngine:
45
50
  self.provider = provider
46
51
  self.model = model
47
52
  self.logger = logger or configure_logger(
48
- "devgen.commit", Path.home() / ".cache" / "devgen" / "commit.log"
53
+ "devgen.commit",
54
+ Path.home() / ".cache" / "devgen" / "commit.log",
55
+ console=debug,
49
56
  )
50
57
  self.kwargs = kwargs
51
58
  self.dry_run_path = get_commit_dry_run_path()
52
59
  self.template_env = load_template_env("commit")
53
60
 
61
+ self.console = Console(
62
+ theme=Theme(
63
+ {"info": "dim cyan", "warning": "magenta", "danger": "bold red"}
64
+ )
65
+ )
66
+
54
67
  # Load config from ~/.devgen.yaml
55
68
  from devgen.utils import load_config
56
69
 
57
70
  self.config = load_config()
58
71
 
59
- def _exec_git(self, args: List[str], allow_error: bool = False) -> str:
60
- """Executes a git command."""
72
+ def detect_changes(self) -> List[str]:
73
+ """Detects changed, deleted, or untracked files."""
61
74
  try:
62
- res = subprocess.run(
63
- args,
64
- capture_output=True,
65
- text=True,
66
- encoding="utf-8",
67
- errors="replace",
68
- check=not allow_error,
75
+ out = run_git_command(
76
+ [
77
+ "git",
78
+ "ls-files",
79
+ "--deleted",
80
+ "--modified",
81
+ "--others",
82
+ "--exclude-standard",
83
+ ]
69
84
  )
70
- return res.stdout.strip()
85
+ return [f.strip() for f in out.split("\n") if f.strip()]
71
86
  except subprocess.CalledProcessError as e:
72
87
  msg = f"Git command failed: {' '.join(e.cmd)}\nError: {e.stderr.strip()}"
73
88
  self.logger.error(msg)
74
89
  raise CommitEngineError(msg) from e
75
90
 
76
- def detect_changes(self) -> List[str]:
77
- """Detects changed, deleted, or untracked files."""
78
- out = self._exec_git(
79
- [
80
- "git",
81
- "ls-files",
82
- "--deleted",
83
- "--modified",
84
- "--others",
85
- "--exclude-standard",
86
- ]
87
- )
88
- return [f.strip() for f in out.split("\n") if f.strip()]
89
-
90
91
  def group_files(self, files: List[str]) -> Dict[str, List[str]]:
91
92
  """Groups files by their parent directory."""
92
93
  groups = defaultdict(list)
@@ -98,7 +99,14 @@ class CommitEngine:
98
99
 
99
100
  def generate_diff(self, files: List[str]) -> str:
100
101
  """Generates diff for specific files."""
101
- return self._exec_git(["git", "--no-pager", "diff", "--staged", "--", *files])
102
+ try:
103
+ return run_git_command(
104
+ ["git", "--no-pager", "diff", "--staged", "--", *files]
105
+ )
106
+ except subprocess.CalledProcessError as e:
107
+ msg = f"Git command failed: {' '.join(e.cmd)}\nError: {e.stderr.strip()}"
108
+ self.logger.error(msg)
109
+ raise CommitEngineError(msg) from e
102
110
 
103
111
  def _init_dry_run(self):
104
112
  """Initializes the dry-run file."""
@@ -108,7 +116,15 @@ class CommitEngine:
108
116
  f.write(f"# Dry Run: Commit Messages\n_Generated: {ts}_\n\n")
109
117
 
110
118
  def _log_dry_run(self, group: str, msg: str):
111
- """Appends a dry-run entry."""
119
+ """Appends a dry-run entry and prints to console."""
120
+ self.console.print(
121
+ Panel(
122
+ Markdown(msg),
123
+ title=f"Dry Run: {group}",
124
+ border_style="yellow",
125
+ expand=False,
126
+ )
127
+ )
112
128
  with self.dry_run_path.open("a", encoding="utf-8") as f:
113
129
  f.write(f"## Group: `{group}`\n\n```md\n{msg}\n```\n\n---\n\n")
114
130
 
@@ -117,18 +133,40 @@ class CommitEngine:
117
133
  if not files:
118
134
  return
119
135
  self.logger.info(f"Staging: {files}")
120
- self._exec_git(["git", "add", *files])
136
+ self.console.print(f"[info]Staging {len(files)} files...[/info]")
137
+ try:
138
+ run_git_command(["git", "add", *files])
139
+ except subprocess.CalledProcessError as e:
140
+ msg = f"Git command failed: {' '.join(e.cmd)}\nError: {e.stderr.strip()}"
141
+ self.logger.error(msg)
142
+ raise CommitEngineError(msg) from e
121
143
 
122
144
  def commit_staged(self, msg: str):
123
145
  """Commits staged changes."""
124
146
  self.logger.info(f"Committing:\n{msg}")
125
- self._exec_git(["git", "commit", "-m", msg])
147
+ self.console.print(
148
+ Panel(Markdown(msg), title="Commit Message", border_style="green")
149
+ )
150
+ try:
151
+ run_git_command(["git", "commit", "-m", msg])
152
+ except subprocess.CalledProcessError as e:
153
+ msg = f"Git command failed: {' '.join(e.cmd)}\nError: {e.stderr.strip()}"
154
+ self.logger.error(msg)
155
+ raise CommitEngineError(msg) from e
126
156
 
127
157
  def push_commits(self):
128
158
  """Pushes commits to remote."""
129
159
  self.logger.info("Pushing to remote...")
130
- self._exec_git(["git", "push"])
131
- self.logger.info("Push successful.")
160
+ with self.console.status("[bold green]Pushing to remote...[/bold green]"):
161
+ try:
162
+ run_git_command(["git", "push"])
163
+ except subprocess.CalledProcessError as e:
164
+ msg = (
165
+ f"Git command failed: {' '.join(e.cmd)}\nError: {e.stderr.strip()}"
166
+ )
167
+ self.logger.error(msg)
168
+ raise CommitEngineError(msg) from e
169
+ self.console.print("[bold green]Push successful.[/bold green]")
132
170
 
133
171
  def generate_message(self, group: str, diff: str, cache: Dict[str, str]) -> str:
134
172
  """Generates a commit message using AI or cache."""
@@ -147,28 +185,32 @@ class CommitEngine:
147
185
  template = self.template_env.get_template("commit_message.j2")
148
186
  prompt = template.render(group_name=group, diff_text=diff, use_emoji=use_emoji)
149
187
 
150
- raw = generate_with_ai(
151
- prompt,
152
- provider=provider,
153
- model=model,
154
- api_key=api_key,
155
- debug=self.debug,
156
- **self.kwargs,
157
- )
188
+ with self.console.status("[bold blue]Generating commit message...[/bold blue]"):
189
+ raw = generate_with_ai(
190
+ prompt,
191
+ provider=provider,
192
+ model=model,
193
+ api_key=api_key,
194
+ debug=self.debug,
195
+ **self.kwargs,
196
+ )
158
197
  return sanitize_ai_commit_message(raw)
159
198
 
160
199
  def is_ahead_of_remote(self) -> bool:
161
200
  """Checks if local branch has unpushed commits."""
162
201
  try:
163
- self._exec_git(["git", "fetch", "origin"])
164
- count = self._exec_git(
165
- ["git", "rev-list", "--count", "@{u}..HEAD"], allow_error=True
202
+ run_git_command(["git", "fetch", "origin"])
203
+ count = run_git_command(
204
+ ["git", "rev-list", "--count", "@{u}..HEAD"], check=False
166
205
  )
167
206
  if count and int(count) > 0:
168
207
  return True
169
- except CommitEngineError:
208
+ except (subprocess.CalledProcessError, CommitEngineError):
170
209
  # Maybe no upstream
171
- return bool(self._exec_git(["git", "rev-parse", "HEAD"], allow_error=True))
210
+ try:
211
+ return bool(run_git_command(["git", "rev-parse", "HEAD"], check=False))
212
+ except subprocess.CalledProcessError:
213
+ return False
172
214
  return False
173
215
 
174
216
  def load_cache(self) -> Dict[str, str]:
@@ -190,7 +232,10 @@ class CommitEngine:
190
232
 
191
233
  if not diff.strip():
192
234
  self.logger.info(f"Skipping empty diff for {group}")
193
- self._exec_git(["git", "reset", "HEAD", "--", *files])
235
+ try:
236
+ run_git_command(["git", "reset", "HEAD", "--", *files])
237
+ except subprocess.CalledProcessError:
238
+ pass # Ignore reset errors
194
239
  return True
195
240
 
196
241
  msg = self.generate_message(group, diff, cache)
@@ -200,7 +245,10 @@ class CommitEngine:
200
245
 
201
246
  if self.dry_run:
202
247
  self._log_dry_run(group, msg)
203
- self._exec_git(["git", "reset", "HEAD", "--", *files])
248
+ try:
249
+ run_git_command(["git", "reset", "HEAD", "--", *files])
250
+ except subprocess.CalledProcessError:
251
+ pass
204
252
  else:
205
253
  self.commit_staged(msg)
206
254
 
@@ -237,16 +285,19 @@ class CommitEngine:
237
285
  self.logger.error("Push aborted due to failed commits.")
238
286
 
239
287
  if self.dry_run:
240
- self.logger.info(f"Dry run done. See {self.dry_run_path}")
288
+ self.console.print(
289
+ f"[bold green]Dry run done.[/bold green] See {self.dry_run_path}"
290
+ )
241
291
  else:
242
- self.logger.info("Done.")
292
+ self.console.print("[bold green]Done.[/bold green]")
243
293
  if failed:
244
- self.logger.warning(f"Failed groups: {failed}")
294
+ self.console.print(f"[bold red]Failed groups: {failed}[/bold red]")
245
295
 
246
296
 
247
297
  def run_commit_engine(**kwargs):
248
298
  """Entry point for the commit engine."""
249
- logger = configure_logger("devgen.commit")
299
+ debug = kwargs.get("debug", False)
300
+ logger = configure_logger("devgen.commit", console=debug)
250
301
  try:
251
302
  engine = CommitEngine(**kwargs)
252
303
  engine.execute()
@@ -1,21 +1,24 @@
1
- from devgen.providers.anthropic import AnthropicProvider
2
- from devgen.providers.gemini import GeminiProvider
3
- from devgen.providers.huggingface import HuggingfaceProvider
4
- from devgen.providers.openai import OpenaiProvider
5
- from devgen.providers.openrouter import OpenrouterProvider
6
-
7
-
8
1
  def get_provider(name):
9
2
  name_lower = name.lower()
10
3
  if name_lower == "gemini":
4
+ from devgen.providers.gemini import GeminiProvider
5
+
11
6
  return GeminiProvider()
12
7
  elif name_lower == "openai":
8
+ from devgen.providers.openai import OpenaiProvider
9
+
13
10
  return OpenaiProvider()
14
11
  elif name_lower == "huggingface":
12
+ from devgen.providers.huggingface import HuggingfaceProvider
13
+
15
14
  return HuggingfaceProvider()
16
15
  elif name_lower == "openrouter":
16
+ from devgen.providers.openrouter import OpenrouterProvider
17
+
17
18
  return OpenrouterProvider()
18
19
  elif name_lower == "anthropic":
20
+ from devgen.providers.anthropic import AnthropicProvider
21
+
19
22
  return AnthropicProvider()
20
23
 
21
24
  raise NotImplementedError(f"Provider '{name}' is not implemented yet.")
@@ -39,6 +39,9 @@ class OpenaiProvider:
39
39
  # Add error handling if the client fails to initialize
40
40
  raise RuntimeError(f"Failed to initialize OpenAI client: {e}")
41
41
 
42
+ # Remove debug from kwargs if present
43
+ kwargs.pop("debug", None)
44
+
42
45
  # 2. Use the modern API syntax: client.chat.completions.create
43
46
  response = client.chat.completions.create(
44
47
  model=model or self.DEFAULT_MODEL,
@@ -19,6 +19,9 @@ class OpenrouterProvider:
19
19
  api_key=api_key,
20
20
  )
21
21
 
22
+ # Remove debug from kwargs if present
23
+ kwargs.pop("debug", None)
24
+
22
25
  response = client.chat.completions.create(
23
26
  model=model,
24
27
  messages=[{"role": "user", "content": prompt}],
devgen/utils.py CHANGED
@@ -75,7 +75,7 @@ def extract_commit_messages(filepath: Path | str) -> dict[str, str]:
75
75
 
76
76
 
77
77
  def configure_logger(
78
- name: str = "devgen", log_file: Optional[Path | str] = None
78
+ name: str = "devgen", log_file: Optional[Path | str] = None, console: bool = True
79
79
  ) -> logging.Logger:
80
80
  logger = logging.getLogger(name)
81
81
  logger.setLevel(logging.INFO)
@@ -88,9 +88,10 @@ def configure_logger(
88
88
  )
89
89
 
90
90
  # Console handler
91
- ch = logging.StreamHandler()
92
- ch.setFormatter(formatter)
93
- logger.addHandler(ch)
91
+ if console:
92
+ ch = logging.StreamHandler()
93
+ ch.setFormatter(formatter)
94
+ logger.addHandler(ch)
94
95
 
95
96
  # File handler
96
97
  if log_file:
@@ -103,15 +104,52 @@ def configure_logger(
103
104
  return logger
104
105
 
105
106
 
106
- def get_git_staged_files() -> list[str]:
107
+ def run_git_command(
108
+ args: list[str],
109
+ check: bool = True,
110
+ cwd: Optional[Path] = None,
111
+ encoding: str = "utf-8",
112
+ errors: str = "replace",
113
+ ) -> str:
114
+ """
115
+ Executes a git command and returns the output.
116
+
117
+ Args:
118
+ args: List of command arguments (e.g., ["git", "status"]).
119
+ check: Whether to raise an exception on non-zero exit code.
120
+ cwd: Current working directory for the command.
121
+ encoding: Output encoding.
122
+ errors: Error handling strategy for encoding.
123
+
124
+ Returns:
125
+ The standard output of the command, stripped of leading/trailing whitespace.
126
+
127
+ Raises:
128
+ subprocess.CalledProcessError: If the command fails and check is True.
129
+ """
107
130
  try:
108
131
  res = subprocess.run(
109
- ["git", "diff", "--name-only", "--cached"],
132
+ args,
110
133
  capture_output=True,
111
134
  text=True,
112
- check=True,
135
+ encoding=encoding,
136
+ errors=errors,
137
+ check=check,
138
+ cwd=cwd,
113
139
  )
114
- return [f for f in res.stdout.splitlines() if f.strip()]
140
+ return res.stdout.strip()
141
+ except subprocess.CalledProcessError as e:
142
+ # Log the error if a logger is configured, but re-raise
143
+ # We don't have access to a specific logger here easily without passing it in,
144
+ # so we rely on the caller to handle logging if needed, or we could log to a default one.
145
+ # For now, just re-raise as the caller expects.
146
+ raise e
147
+
148
+
149
+ def get_git_staged_files() -> list[str]:
150
+ try:
151
+ output = run_git_command(["git", "diff", "--name-only", "--cached"])
152
+ return [f for f in output.splitlines() if f.strip()]
115
153
  except subprocess.CalledProcessError:
116
154
  return []
117
155
 
@@ -170,6 +208,7 @@ __all__ = [
170
208
  "sanitize_ai_commit_message",
171
209
  "extract_commit_messages",
172
210
  "configure_logger",
211
+ "run_git_command",
173
212
  "get_git_staged_files",
174
213
  "read_file_content",
175
214
  "delete_file",
@@ -0,0 +1,177 @@
1
+ Metadata-Version: 2.4
2
+ Name: devgen-cli
3
+ Version: 0.2.3
4
+ Summary: A collection of developer tools
5
+ Home-page: https://github.com/S4NKALP/devgen
6
+ Author: Sankalp Tharu
7
+ Author-email: Sankalp Tharu <sankalptharu50028@gmail.com>
8
+ License: GPL-3.0-or-later
9
+ Project-URL: Homepage, https://github.com/S4NKALP/devgen
10
+ Keywords: devgen,cli,git,changelog,gitignore,license,commit-generator,commit
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.7
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: anthropic>=0.75.0
24
+ Requires-Dist: google-generativeai>=0.8.5
25
+ Requires-Dist: jinja2>=3.1.6
26
+ Requires-Dist: openai>=2.11.0
27
+ Requires-Dist: pyyaml>=6.0.3
28
+ Requires-Dist: questionary>=2.1.1
29
+ Requires-Dist: requests>=2.32.5
30
+ Requires-Dist: rich>=14.2.0
31
+ Requires-Dist: toml>=0.10.2
32
+ Requires-Dist: typer>=0.20.0
33
+ Dynamic: author
34
+ Dynamic: home-page
35
+ Dynamic: license-file
36
+ Dynamic: requires-python
37
+
38
+ # DevGen
39
+
40
+ <div align="center">
41
+
42
+ > **Your AI Powerhouse for Git & Project Management.**
43
+ > Stop wasting time on repetitive tasks. Automate your commits, changelogs, and project essentials with a single CLI.
44
+ >
45
+ > PyPI didn't allow the original name, so you'll find it as **devgen-cli** on PyPI
46
+
47
+ <a href="https://pypi.org/project/devgen-cli"><img src="https://img.shields.io/pypi/v/devgen-cli?color=blue&label=PyPI&logo=pypi&logoColor=white" alt="PyPI"></a>
48
+ <img src="https://img.shields.io/badge/Python-3.10%2B-3776AB?logo=python&logoColor=white" alt="Python">
49
+ <a href="https://github.com/S4NKALP/DevGen/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-GPL--3.0--or--later-blue.svg" alt="License"></a>
50
+
51
+ </div>
52
+
53
+ ---
54
+
55
+ **DevGen** isn't just another CLI tool it's your development workflow on steroids. By leveraging state of the art AI models, DevGen turns tedious manual tasks into one click magic. From semantic commit messages to comprehensive release notes, DevGen handles the boring stuff so you can focus on building.
56
+
57
+ ## ⚡ Why DevGen?
58
+
59
+ **🧠 AI Brain**
60
+ Semantic commit messages powered by Gemini, OpenAI, Claude, HuggingFace, and OpenRouter. It reads your diffs and understands your code.
61
+
62
+ **🚀 Battle Tested**
63
+ Generates **Conventional Commits** and **Semantic Versioning** compliant changelogs that actually make sense.
64
+
65
+ **⚡ Lightning Fast**
66
+ Smart caching and async operations mean you never wait longer than necessary.
67
+
68
+ **Project Essentials**
69
+ Quickly add `.gitignore` and license files to your existing projects. Access cached templates instantly, even without internet.
70
+
71
+ **🛠️ Zero Friction**
72
+ Interactive setup gets you running in seconds.
73
+
74
+ ## 📦 Installation
75
+
76
+ Get started in seconds.
77
+
78
+ ```bash
79
+ # Recommended: Install via pipx for an isolated environment
80
+ pipx install devgen-cli
81
+
82
+ # Or use uv for blazing speed
83
+ uv tool install devgen-cli
84
+
85
+ # Standard pip install
86
+ pip install devgen-cli
87
+ ```
88
+
89
+ ## 🚀 Quick Start
90
+
91
+ **1. Initialize & Configure**
92
+ Tell DevGen which AI provider to use.
93
+
94
+ ```bash
95
+ devgen setup config
96
+ ```
97
+
98
+ **2. Stage & Commit**
99
+ Stage your files and let AI write the message.
100
+
101
+ ```bash
102
+ git add .
103
+ devgen commit run
104
+ ```
105
+
106
+ _Boom. Done._
107
+
108
+ ## 💡 Feature Deep Dive
109
+
110
+ ### 🤖 AI Powered Commits
111
+
112
+ Stop writing "fix bug" or "wip". DevGen analyzes your staged changes, groups them by component, and generates meaningful, semantic commit messages.
113
+
114
+ ```bash
115
+ # Preview what DevGen will generate
116
+ devgen commit run --dry-run
117
+
118
+ # Commit and push in one go
119
+ devgen commit run --push
120
+ ```
121
+
122
+ ### 📝 Changelogs & Release Notes
123
+
124
+ Turn your git history into beautiful, readable documentation.
125
+
126
+ ```bash
127
+ # Generate a changelog from the last tag
128
+ devgen changelog generate
129
+
130
+ # Create release notes for v2.0.0
131
+ devgen release notes --version 2.0.0
132
+ ```
133
+
134
+ ### 🛡️ Essential Files
135
+
136
+ Don't waste time searching for templates. Generate the essential files for your project instantly.
137
+
138
+ ```bash
139
+ # Interactive search for gitignore templates
140
+ devgen gitignore generate
141
+
142
+ # Generate a license interactively
143
+ devgen license generate
144
+ ```
145
+
146
+ ## ⚙️ Configuration
147
+
148
+ Your settings live in `~/.devgen.yaml`. You can tweak your AI provider, model, and preferences there.
149
+
150
+ | Option | Description |
151
+ | :--------- | :----------------------------------------------------------- |
152
+ | `provider` | `gemini`, `openai`, `anthropic`, `huggingface`, `openrouter` |
153
+ | `model` | Specific model name (e.g., `gemini-2.5-flash`, `gpt-4o`) |
154
+ | `emoji` | Enable/disable gitmojis in commits (`true`/`false`) |
155
+
156
+ ## 🤝 Contributing
157
+
158
+ We love contributions! Found a bug? Want a new feature? Open an issue or submit a PR.
159
+
160
+ ## � Acknowledgments
161
+
162
+ DevGen wouldn't be possible without these amazing open-source projects and AI providers:
163
+
164
+ - **[Typer](https://typer.tiangolo.com/)** & **[Rich](https://rich.readthedocs.io/)** for building the beautiful, intuitive, and responsive CLI interface.
165
+ - **[Questionary](https://github.com/tmbo/questionary)** for creating interactive, user-friendly prompts and selection menus.
166
+ - **[Jinja2](https://jinja.palletsprojects.com/)** for the powerful template engine used to generate files and messages.
167
+ - **[Google Gemini](https://deepmind.google/technologies/gemini/)**, **[OpenAI](https://openai.com/)**, **[Anthropic](https://www.anthropic.com/)**, **[HuggingFace](https://huggingface.co/)**, and **[OpenRouter](https://openrouter.ai/)** for providing the advanced AI models that power the semantic generation features.
168
+
169
+ ## �📝 License
170
+
171
+ Proudly open source under the [GPL-3.0-or-later](LICENSE) License.
172
+
173
+ ---
174
+
175
+ <div align="center">
176
+ Made with ❤️ by <a href="https://github.com/S4NKALP">Sankalp</a>
177
+ </div>
@@ -1,9 +1,9 @@
1
1
  devgen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  devgen/ai.py,sha256=e4aVFfa1atwmIBI9QSot4EnEiDG9SBwtGOMx91mh1ow,1494
3
- devgen/utils.py,sha256=7xjfWVHVHRm7Qe30Z9hV2MOqUKJwJ8hpuqzMb-0hYZo,5541
3
+ devgen/utils.py,sha256=l2mFzKD5nuF3DPZ7ORPQINboROwQiHCVS3YgqZOrac0,6857
4
4
  devgen/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  devgen/cli/changelog.py,sha256=V8HhhzsIcI0CZQnJeUv06Zns4oCVS51ndFsnQC78HZ8,960
6
- devgen/cli/commit.py,sha256=3hBohdAW1dY1QzWIXj1TxL661eLd-2nAmY8dI5SvTMM,2460
6
+ devgen/cli/commit.py,sha256=MhEXxRKXYB1v9MmHjwZ7XCEb9G2Hn5Dv33VSOEQ5KHU,2475
7
7
  devgen/cli/config.py,sha256=l1ip4Rw7k1rph3HIxT9rXF0WTUecP_ptUfy2-aC6JKI,4960
8
8
  devgen/cli/gitignore.py,sha256=Nl7t_EZDhsOs5y-YkbOiuFLxHuAw48QTpw4_m9qJR2w,4446
9
9
  devgen/cli/license.py,sha256=LAwro2Mwf7PEQWORMFqwp__Mn0Kx3BPhqZR-ttuKAbk,2830
@@ -11,17 +11,17 @@ devgen/cli/main.py,sha256=xmuZH3m7ckl14fjTnjtys65MD_aXA2JW4jjo0vfmZaQ,3356
11
11
  devgen/cli/release.py,sha256=X0PfH7Vx__-CraiCNoMvxXWDmcmKdCdX16TYNubteV4,864
12
12
  devgen/cli/setup.py,sha256=LOxYkjHDYQIwIOv7N4VJRek6QyU_pqk7B_R3qArjOm0,2477
13
13
  devgen/modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- devgen/modules/changelog_generator.py,sha256=UzKEYoWeRyDCqhCNNVWwvf91ka0lofcLc4uf9DJU8SM,6789
15
- devgen/modules/commit_generator.py,sha256=x-cM1PVPyPdS6HZrLRNpQzjl1HdyhQM4rm_iciJBpr0,8635
14
+ devgen/modules/changelog_generator.py,sha256=-3kT-nxw2MCIGbGtg-k7tsd6RsdJi5TAVqVQ5eU7Rvg,6489
15
+ devgen/modules/commit_generator.py,sha256=6hJG9uyNB2Y0MXx2VdxQ0mmBYI3zVLYMSfFpskym_Yk,10833
16
16
  devgen/modules/gitignore_generator.py,sha256=DpHAOrKIwSDklv7CLelYro478MQGJ4EcMAFi5E--348,4379
17
17
  devgen/modules/license_generator.py,sha256=nOWcyPKgMnjHIYmu6CwZ-teDr_nl0j1lWpouuMjqDQg,2665
18
18
  devgen/modules/release_note_generator.py,sha256=IiVNHV6KdpTjPCJCxuYKLWX_pVc-2ORuh82OQwZBVWg,2112
19
- devgen/providers/__init__.py,sha256=Iug5kxOGF64uF6htTHumLCPmdrtoGwZwb7j5YuTqD_E,759
19
+ devgen/providers/__init__.py,sha256=44ibMNiWX5IOm37_uTgX4OGLsUWGAlmzALF7uKOn7T8,802
20
20
  devgen/providers/anthropic.py,sha256=IeOwQTLcemOlSLAHAu7MPQVoAK2ZvjixzKuKKNq2nqs,782
21
21
  devgen/providers/gemini.py,sha256=xrFo5vMTCpWjg2eUaLDygDc949K1-esp31rMSsIUZEY,792
22
22
  devgen/providers/huggingface.py,sha256=bJjna6XDcjSWb6E-RZOYzSjP8ea5X5_zjCQAekF1iIc,1575
23
- devgen/providers/openai.py,sha256=4uiPC1NrqR6Qu_hMouLMjpZWs5kNK2ZIkb37F_y1Sek,2173
24
- devgen/providers/openrouter.py,sha256=NT7v8GujUH0Pg8TA9CG1SIkKSd3H-YEQmSfpU1Vc1GU,1107
23
+ devgen/providers/openai.py,sha256=e92AMNles5yZeDnTQHcWcqAvH-p-10Ptqxbcwuz9pWA,2254
24
+ devgen/providers/openrouter.py,sha256=Cnu8A9hwjiWzL_s2dwXbQDbhRqqXqlcnGTyqxqZKhLQ,1196
25
25
  devgen/templates/commit/commit_message.j2,sha256=gTECP-5V5rAwCpg3gT7SsLAMT9AnSibC8vHXDe80yR4,793
26
26
  devgen/templates/licenses/agpl-3.0.json,sha256=lYJObhc3-nBQQD2L3cw9C-Q9ceiV26qkzp6b1H7NnZ4,35832
27
27
  devgen/templates/licenses/apache-2.0.json,sha256=7U9zUdz8kbmrtL0hDgOgtYMhRnA7Qi7WuDxSMNQ0MIU,11989
@@ -36,9 +36,9 @@ devgen/templates/licenses/lgpl-2.1.json,sha256=M1uxVajrZBccW3gX5cYwMdZ0DyBLbxnmP
36
36
  devgen/templates/licenses/mit.json,sha256=NfRzL86O8TZOY2ZRdelQFEVX0t1o3bUJtqmib-pXM0s,1426
37
37
  devgen/templates/licenses/mpl-2.0.json,sha256=Fq-oj2-z5TZS2Qr9bQHea-Nm_baC2_7DQtuSKT476Yk,17729
38
38
  devgen/templates/licenses/unlicense.json,sha256=uWl_NnBn_Daqu-0df7y4E5H0q7KUdDby3TK1IHBDr4Y,1553
39
- devgen_cli-0.2.1.dist-info/licenses/LICENSE,sha256=-JHhLXXB2RRUeojKiRRTDHyJ1QiOCtrDfwbahUOb6Yc,35150
40
- devgen_cli-0.2.1.dist-info/METADATA,sha256=MkiBID2hBa2dpPIoxRbX8O47Gyqd02VgxHAR8K81VX4,7934
41
- devgen_cli-0.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
42
- devgen_cli-0.2.1.dist-info/entry_points.txt,sha256=NdqXSz3M3_0x0I3tFrXEF7r0mwCDmMfUNZbh2HDZSCg,47
43
- devgen_cli-0.2.1.dist-info/top_level.txt,sha256=u-4SzOUkP7II9PqLZ4PCpIAsxv4tuxuJ4gtdApYG4Bw,7
44
- devgen_cli-0.2.1.dist-info/RECORD,,
39
+ devgen_cli-0.2.3.dist-info/licenses/LICENSE,sha256=-JHhLXXB2RRUeojKiRRTDHyJ1QiOCtrDfwbahUOb6Yc,35150
40
+ devgen_cli-0.2.3.dist-info/METADATA,sha256=BmEV-sUK8-s3V4K_Gx2mSirHbNRsT6Iu-MZiqfps0jE,6020
41
+ devgen_cli-0.2.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
42
+ devgen_cli-0.2.3.dist-info/entry_points.txt,sha256=NdqXSz3M3_0x0I3tFrXEF7r0mwCDmMfUNZbh2HDZSCg,47
43
+ devgen_cli-0.2.3.dist-info/top_level.txt,sha256=u-4SzOUkP7II9PqLZ4PCpIAsxv4tuxuJ4gtdApYG4Bw,7
44
+ devgen_cli-0.2.3.dist-info/RECORD,,
@@ -1,299 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: devgen-cli
3
- Version: 0.2.1
4
- Summary: A collection of developer tools
5
- Home-page: https://github.com/S4NKALP/devgen
6
- Author: Sankalp Tharu
7
- Author-email: Sankalp Tharu <sankalptharu50028@gmail.com>
8
- License: GPL-3.0-or-later
9
- Project-URL: Homepage, https://github.com/S4NKALP/devgen
10
- Keywords: devgen,cli,git,changelog,gitignore,license,commit-generator,commit
11
- Classifier: Development Status :: 3 - Alpha
12
- Classifier: Intended Audience :: Developers
13
- Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
14
- Classifier: Operating System :: OS Independent
15
- Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3.7
17
- Classifier: Programming Language :: Python :: 3.8
18
- Classifier: Programming Language :: Python :: 3.9
19
- Classifier: Programming Language :: Python :: 3.10
20
- Requires-Python: >=3.10
21
- Description-Content-Type: text/markdown
22
- License-File: LICENSE
23
- Requires-Dist: anthropic>=0.75.0
24
- Requires-Dist: google-generativeai>=0.8.5
25
- Requires-Dist: jinja2>=3.1.6
26
- Requires-Dist: openai>=2.11.0
27
- Requires-Dist: pyyaml>=6.0.3
28
- Requires-Dist: questionary>=2.1.1
29
- Requires-Dist: requests>=2.32.5
30
- Requires-Dist: rich>=14.2.0
31
- Requires-Dist: toml>=0.10.2
32
- Requires-Dist: typer>=0.20.0
33
- Dynamic: author
34
- Dynamic: home-page
35
- Dynamic: license-file
36
- Dynamic: requires-python
37
-
38
- # DevGen
39
-
40
- <div align="center">
41
-
42
- <img src="https://img.shields.io/pypi/v/devgen?color=blue&label=PyPI&logo=pypi&logoColor=white" alt="PyPI">
43
- <img src="https://img.shields.io/badge/Python-3.10%2B-3776AB?logo=python&logoColor=white" alt="Python">
44
- <a href="https://github.com/S4NKALP/devgen/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-GPL--3.0--or--later-blue.svg" alt="License"></a>
45
-
46
- </div>
47
-
48
- **DevGen** is an AI-powered developer toolkit that automates common Git and project management tasks. Generate semantic commit messages, changelogs, release notes, `.gitignore` files, and licenses—all from a single unified CLI.
49
-
50
- ## ✨ Features
51
-
52
- - 🚀 **AI-Powered Commit Messages** - Automatically generate semantic commit messages from your staged changes using AI
53
- - 📝 **Changelog Generation** - Create changelogs from git history using conventional commits
54
- - 🚀 **Release Notes** - Generate clean, emoji-enhanced release notes for your releases
55
- - 🙈 **Gitignore Templates** - Generate `.gitignore` files from GitHub's official templates
56
- - 📄 **License Generation** - Quickly generate open source licenses with interactive setup
57
- - ⚙️ **Multi-Provider AI Support** - Works with Gemini, OpenAI, Anthropic, HuggingFace, and OpenRouter
58
-
59
- ## 📦 Installation
60
-
61
- ```bash
62
- # Stable release from PyPI
63
- pip install devgen-cli
64
-
65
- # Using pipx (recommended for isolated CLI)
66
- pipx install devgen-cli
67
-
68
- # Using uv (fast Python package manager)
69
- uv tool install devgen-cli
70
- ```
71
-
72
- ## 🚀 Quick Start
73
-
74
- ### 1. Initial Setup
75
-
76
- Configure your AI provider and API key:
77
-
78
- ```bash
79
- devgen setup config
80
- ```
81
-
82
- This will create a configuration file at `~/.devgen.yaml` with your preferred AI provider, model, and API key.
83
-
84
- ### 2. Generate Commit Messages
85
-
86
- Stage your changes and let DevGen generate semantic commit messages:
87
-
88
- ```bash
89
- # Dry run to preview commit messages
90
- devgen commit run --dry-run
91
-
92
- # Actually commit with AI-generated messages
93
- devgen commit run
94
-
95
- # Commit and automatically push
96
- devgen commit run --push
97
-
98
- # Force regeneration of commit messages
99
- devgen commit run --force-rebuild
100
- ```
101
-
102
- **How it works:**
103
-
104
- - DevGen detects all staged and unstaged changes
105
- - Groups files by directory
106
- - Generates semantic commit messages for each group using AI
107
- - Follows [Conventional Commits](https://www.conventionalcommits.org/) format
108
-
109
- **Additional commit commands:**
110
-
111
- ```bash
112
- # View cached commit messages
113
- devgen commit list-cached
114
-
115
- # Clear the commit cache
116
- devgen commit clear-cache
117
-
118
- # Check what files are staged
119
- devgen commit validate
120
- ```
121
-
122
- ### 3. Generate Changelogs
123
-
124
- Create changelogs from your git history:
125
-
126
- ```bash
127
- # Generate changelog from last tag to HEAD
128
- devgen changelog generate
129
-
130
- # Generate from a specific reference
131
- devgen changelog generate --from v1.0.0
132
-
133
- # Output to a custom file
134
- devgen changelog generate --output CHANGES.md
135
- ```
136
-
137
- The changelog follows Semantic Release conventions, categorizing commits into:
138
-
139
- - **BREAKING CHANGES**
140
- - **Features**
141
- - **Bug Fixes**
142
- - **Documentation**
143
- - **Other Changes**
144
-
145
- ### 4. Generate Release Notes
146
-
147
- Create release notes for your releases:
148
-
149
- ```bash
150
- # Generate release notes
151
- devgen release notes --version 1.4.0
152
-
153
- # Custom output file
154
- devgen release notes --version 1.4.0 --output RELEASE.md
155
-
156
- # From a specific reference
157
- devgen release notes --version 1.4.0 --from v1.3.0
158
- ```
159
-
160
- ### 5. Generate .gitignore Files
161
-
162
- Create `.gitignore` files from GitHub templates:
163
-
164
- ```bash
165
- # Interactive mode (search and select templates)
166
- devgen gitignore generate
167
-
168
- # Specify templates directly
169
- devgen gitignore generate Python Node Docker
170
-
171
- # List available templates
172
- devgen gitignore list
173
-
174
- # Use cached templates (offline mode)
175
- devgen gitignore generate --offline Python
176
- ```
177
-
178
- **Options:**
179
-
180
- - `--append` / `--overwrite` - Append to or overwrite existing `.gitignore`
181
- - `--output` - Specify output file path (default: `.gitignore`)
182
- - `--offline` - Use only cached templates
183
-
184
- ### 6. Generate Licenses
185
-
186
- Generate open source licenses interactively:
187
-
188
- ```bash
189
- devgen license generate
190
- ```
191
-
192
- This will:
193
-
194
- 1. Show available licenses (MIT, Apache-2.0, GPL-3.0, etc.)
195
- 2. Prompt for author name
196
- 3. Prompt for year (defaults to current year)
197
- 4. Generate the license file
198
-
199
- **Available licenses:**
200
-
201
- - MIT
202
- - Apache-2.0
203
- - GPL-2.0, GPL-3.0
204
- - LGPL-2.1
205
- - AGPL-3.0
206
- - BSD-2-Clause, BSD-3-Clause
207
- - MPL-2.0
208
- - EPL-2.0
209
- - BSL-1.0
210
- - CC0-1.0
211
- - Unlicense
212
-
213
- ## ⚙️ Configuration
214
-
215
- Configuration is stored in `~/.devgen.yaml`. You can edit it manually or use the interactive setup:
216
-
217
- ```bash
218
- devgen setup config
219
- ```
220
-
221
- **Configuration options:**
222
-
223
- - `provider` - AI provider: `gemini`, `openai`, `anthropic`, `huggingface`, or `openrouter`
224
- - `model` - Model name (e.g., `gemini-2.5-flash`, `gpt-4`, `claude-3-opus`)
225
- - `api_key` - Your API key
226
- - `emoji` - Enable/disable emojis in commit messages (default: `true`)
227
-
228
- ## 📋 Command Reference
229
-
230
- ### Commit Commands
231
-
232
- ```bash
233
- devgen commit run [--dry-run] [--push] [--debug] [--force-rebuild]
234
- devgen commit validate
235
- devgen commit list-cached
236
- devgen commit clear-cache
237
- ```
238
-
239
- ### Changelog Commands
240
-
241
- ```bash
242
- devgen changelog generate [--output FILE] [--from REF]
243
- ```
244
-
245
- ### Release Commands
246
-
247
- ```bash
248
- devgen release notes [--version VERSION] [--output FILE] [--from REF]
249
- ```
250
-
251
- ### Gitignore Commands
252
-
253
- ```bash
254
- devgen gitignore generate [TEMPLATES...] [--output FILE] [--append/--overwrite] [--offline]
255
- devgen gitignore list [--cached]
256
- ```
257
-
258
- ### License Commands
259
-
260
- ```bash
261
- devgen license generate [--output FILE]
262
- ```
263
-
264
- ### Setup Commands
265
-
266
- ```bash
267
- devgen setup config
268
- ```
269
-
270
- ## 🎯 Use Cases
271
-
272
- - **Automated Commit Workflow**: Let AI analyze your changes and generate meaningful commit messages
273
- - **Release Preparation**: Automatically generate changelogs and release notes from git history
274
- - **Project Setup**: Quickly add `.gitignore` and license files to new projects
275
- - **CI/CD Integration**: Use in scripts to automate documentation generation
276
-
277
- ## 🔧 Requirements
278
-
279
- - Python 3.10 or higher
280
- - Git repository (for commit/changelog/release features)
281
- - API key for your chosen AI provider
282
-
283
- ## 📝 License
284
-
285
- This project is licensed under the GPL-3.0-or-later License - see the [LICENSE](LICENSE) file for details.
286
-
287
- ## 🤝 Contributing
288
-
289
- Contributions are welcome! Please feel free to submit a Pull Request.
290
-
291
- ## 📚 Links
292
-
293
- - **Homepage**: [https://github.com/S4NKALP/devgen](https://github.com/S4NKALP/devgen)
294
- - **PyPI**: [https://pypi.org/project/devgen](https://pypi.org/project/devgen)
295
-
296
- ## 🙏 Acknowledgments
297
-
298
- - Uses GitHub's official [gitignore templates](https://github.com/github/gitignore)
299
- - Follows [Conventional Commits](https://www.conventionalcommits.org/) specification