devgen-cli 0.2.3__tar.gz → 0.2.5__tar.gz
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-0.2.5/PKG-INFO +187 -0
- devgen_cli-0.2.5/README.md +150 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/ai.py +11 -2
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/cli/commit.py +53 -1
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/cli/config.py +55 -2
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/cli/main.py +12 -4
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/cli/setup.py +20 -1
- devgen_cli-0.2.5/devgen/modules/changelog_generator.py +155 -0
- devgen_cli-0.2.5/devgen/modules/changelog_sections.py +79 -0
- devgen_cli-0.2.5/devgen/modules/commit_generator.py +392 -0
- devgen_cli-0.2.5/devgen/modules/diff_builder.py +220 -0
- devgen_cli-0.2.5/devgen/modules/git_ops.py +144 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/modules/license_generator.py +1 -3
- devgen_cli-0.2.5/devgen/modules/release_note_generator.py +56 -0
- devgen_cli-0.2.5/devgen/providers/__init__.py +0 -0
- devgen_cli-0.2.5/devgen/providers/anthropic.py +19 -0
- devgen_cli-0.2.5/devgen/providers/base.py +90 -0
- devgen_cli-0.2.5/devgen/providers/gemini.py +63 -0
- devgen_cli-0.2.5/devgen/providers/huggingface.py +54 -0
- devgen_cli-0.2.5/devgen/providers/ollama.py +51 -0
- devgen_cli-0.2.5/devgen/providers/openai.py +20 -0
- devgen_cli-0.2.5/devgen/providers/openrouter.py +25 -0
- devgen_cli-0.2.5/devgen/templates/commit/commit_message.tpl +14 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/utils.py +118 -10
- devgen_cli-0.2.5/devgen_cli.egg-info/PKG-INFO +187 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen_cli.egg-info/SOURCES.txt +6 -1
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen_cli.egg-info/requires.txt +1 -1
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/pyproject.toml +2 -2
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/setup.py +1 -1
- devgen_cli-0.2.3/PKG-INFO +0 -177
- devgen_cli-0.2.3/README.md +0 -140
- devgen_cli-0.2.3/devgen/modules/changelog_generator.py +0 -178
- devgen_cli-0.2.3/devgen/modules/commit_generator.py +0 -308
- devgen_cli-0.2.3/devgen/modules/release_note_generator.py +0 -66
- devgen_cli-0.2.3/devgen/providers/__init__.py +0 -24
- devgen_cli-0.2.3/devgen/providers/anthropic.py +0 -23
- devgen_cli-0.2.3/devgen/providers/gemini.py +0 -24
- devgen_cli-0.2.3/devgen/providers/huggingface.py +0 -45
- devgen_cli-0.2.3/devgen/providers/openai.py +0 -51
- devgen_cli-0.2.3/devgen/providers/openrouter.py +0 -36
- devgen_cli-0.2.3/devgen/templates/commit/commit_message.j2 +0 -15
- devgen_cli-0.2.3/devgen_cli.egg-info/PKG-INFO +0 -177
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/LICENSE +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/MANIFEST.in +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/__init__.py +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/cli/__init__.py +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/cli/changelog.py +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/cli/gitignore.py +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/cli/license.py +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/cli/release.py +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/modules/__init__.py +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/modules/gitignore_generator.py +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/templates/licenses/agpl-3.0.json +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/templates/licenses/apache-2.0.json +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/templates/licenses/bsd-2-clause.json +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/templates/licenses/bsd-3-clause.json +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/templates/licenses/bsl-1.0.json +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/templates/licenses/cc0-1.0.json +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/templates/licenses/epl-2.0.json +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/templates/licenses/gpl-2.0.json +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/templates/licenses/gpl-3.0.json +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/templates/licenses/lgpl-2.1.json +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/templates/licenses/mit.json +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/templates/licenses/mpl-2.0.json +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen/templates/licenses/unlicense.json +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen_cli.egg-info/dependency_links.txt +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen_cli.egg-info/entry_points.txt +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/devgen_cli.egg-info/top_level.txt +0 -0
- {devgen_cli-0.2.3 → devgen_cli-0.2.5}/setup.cfg +0 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: devgen-cli
|
|
3
|
+
Version: 0.2.5
|
|
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-genai>=0.1.0
|
|
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
|
+
**AI-Powered Git & Project Workflows in One CLI**
|
|
43
|
+
|
|
44
|
+
Stop wasting time on repetitive tasks. DevGen automates commits, changelogs, `.gitignore`, and license files using AI — from cloud providers or a local Ollama model.
|
|
45
|
+
|
|
46
|
+
> PyPI didn't allow the original name, so you'll find it as **devgen-cli** on PyPI
|
|
47
|
+
|
|
48
|
+
> <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>
|
|
49
|
+
> <img src="https://img.shields.io/badge/Python-3.10%2B-3776AB?logo=python&logoColor=white" alt="Python">
|
|
50
|
+
> <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>
|
|
51
|
+
> <a href="https://github.com/S4NKALP/DevGen/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/S4NKALP/DevGen/ci.yml?branch=main&label=CI" alt="CI"></a>
|
|
52
|
+
> <a href="https://github.com/S4NKALP/DevGen/actions/workflows/pre-commit.yml"><img src="https://img.shields.io/github/actions/workflow/status/S4NKALP/DevGen/pre-commit.yml?branch=main&label=Pre-Commit" alt="Pre-Commit"></a>
|
|
53
|
+
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
## Why DevGen
|
|
57
|
+
|
|
58
|
+
DevGen is a CLI for the parts of development that should be invisible. It writes Conventional Commits from your diff, drafts SemVer changelogs, fetches `.gitignore` and license templates, and routes everything through whichever AI provider you prefer — including a fully local Ollama model if you don't want to send code to the cloud.
|
|
59
|
+
|
|
60
|
+
## Features
|
|
61
|
+
|
|
62
|
+
- **AI-Powered Commits** — Conventional Commit messages generated from your staged diff, grouped by directory, with optional emoji prefixes.
|
|
63
|
+
- **Multiple Providers** — Google Gemini, OpenAI, Anthropic, OpenRouter, HuggingFace, and local Ollama, all behind one CLI.
|
|
64
|
+
- **Smart Caching** — `.gitignore` and license templates are cached for offline use; AI responses are de-duplicated.
|
|
65
|
+
- **Conventional Changelogs** — `feat`, `fix`, `refactor`, `perf`, `docs`, `test`, `build`, `ci`, `chore`, `style`, and a `BREAKING CHANGES` section.
|
|
66
|
+
- **Project Scaffolding** — Pull `.gitignore` from GitHub's collection and drop in SPDX license files (MIT, Apache-2.0, GPL-3.0, AGPL-3.0, BSD, MPL-2.0, …).
|
|
67
|
+
- **Custom Templates** — Override the commit-message prompt via a `.tpl` file in your config.
|
|
68
|
+
- **Interactive Setup** — `devgen setup` walks you through provider + API key + options.
|
|
69
|
+
- **Undo Support** — `devgen commit undo` rolls back the last AI commit while keeping changes staged.
|
|
70
|
+
- **Token-Limit Aware** — When a diff exceeds a model's context window, you get a single actionable error with `--max-groups` and `--max-diff-size` hints.
|
|
71
|
+
|
|
72
|
+
## Supported AI Providers
|
|
73
|
+
|
|
74
|
+
| Provider | Notes |
|
|
75
|
+
| ----------------- | ---------------------------------------------------- |
|
|
76
|
+
| **Google Gemini** | Default-friendly, generous free tier |
|
|
77
|
+
| **OpenAI** | GPT-4o, GPT-4.1, o-series |
|
|
78
|
+
| **Anthropic** | Claude 3.5 / 3.7 / 4 |
|
|
79
|
+
| **OpenRouter** | Single key, many models |
|
|
80
|
+
| **HuggingFace** | Inference API |
|
|
81
|
+
| **Ollama** | Fully local, no API key, no data leaves your machine |
|
|
82
|
+
|
|
83
|
+
## Installation
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# Recommended: isolated environment
|
|
87
|
+
pipx install devgen-cli
|
|
88
|
+
|
|
89
|
+
# Or use uv for speed
|
|
90
|
+
uv tool install devgen-cli
|
|
91
|
+
|
|
92
|
+
# Or plain pip
|
|
93
|
+
pip install devgen-cli
|
|
94
|
+
|
|
95
|
+
# Shell completion (bash/zsh/fish)
|
|
96
|
+
devgen --install-completion
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Requires **Python 3.10 or newer**.
|
|
100
|
+
|
|
101
|
+
## Quick Start
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# 1. Configure a provider and API key
|
|
105
|
+
devgen setup config
|
|
106
|
+
|
|
107
|
+
# 2. Stage your work as usual
|
|
108
|
+
git add .
|
|
109
|
+
|
|
110
|
+
# 3. Let DevGen write the commit message
|
|
111
|
+
devgen commit run
|
|
112
|
+
|
|
113
|
+
# 4. Or preview first, then commit
|
|
114
|
+
devgen commit run --dry-run
|
|
115
|
+
devgen commit run --push
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Commands
|
|
119
|
+
|
|
120
|
+
| Command | Description |
|
|
121
|
+
| ------------------------------- | ----------------------------------------------------------------------------- |
|
|
122
|
+
| `devgen setup config` | Interactive provider / API key / options wizard |
|
|
123
|
+
| `devgen commit run` | Stage, generate message, commit (optionally `--push`, `--check`, `--dry-run`) |
|
|
124
|
+
| `devgen commit undo` | Undo the last AI commit, keep changes staged |
|
|
125
|
+
| `devgen changelog` | Generate a Conventional Commits changelog from recent history |
|
|
126
|
+
| `devgen release-notes` | Generate release notes for a version range |
|
|
127
|
+
| `devgen gitignore list` | List available GitHub `.gitignore` templates |
|
|
128
|
+
| `devgen gitignore add <name> …` | Add `.gitignore` entries to the current project |
|
|
129
|
+
| `devgen license list` | List available SPDX licenses |
|
|
130
|
+
| `devgen license add <spdx>` | Add a LICENSE file to the current project |
|
|
131
|
+
|
|
132
|
+
Run `devgen <command> --help` for full options on any subcommand.
|
|
133
|
+
|
|
134
|
+
## Custom Templates
|
|
135
|
+
|
|
136
|
+
DevGen uses `.tpl` files for its commit prompt. To override the default, set `custom_template` in your config (see `devgen config info`) and point it at a `.tpl` file with `{{ diff_text }}`, `{{ context }}`, `{{ group_name }}`, and the conditional `{% if use_emoji %}` block.
|
|
137
|
+
|
|
138
|
+
Example minimal template:
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
{{ diff_text }}
|
|
142
|
+
Summarize the change above in one Conventional Commit line.
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Architecture
|
|
146
|
+
|
|
147
|
+
DevGen is built around a small set of composable components:
|
|
148
|
+
|
|
149
|
+
- **`BaseProvider`** — abstract base for every AI provider; handles API-key validation, token-limit detection, and error wrapping. Adding a new provider is one subclass with a single `_generate()` method.
|
|
150
|
+
- **`GitOperator`** — all `subprocess` calls to `git` live here, behind a `GitError` exception.
|
|
151
|
+
- **`DiffBuilder` / `FileGrouper` / `ManifestInspector`** — split a staged diff into per-directory groups with compact project context, so the model sees a focused slice instead of a wall of text.
|
|
152
|
+
- **`Section` enum** — single source of truth for changelog ordering and emoji, shared by `ChangelogGenerator` and `ReleaseNotesGenerator`.
|
|
153
|
+
|
|
154
|
+
The CLI entry point is `devgen.cli.main:app` (Typer), exposed as the `devgen` script.
|
|
155
|
+
|
|
156
|
+
## Development
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
git clone https://github.com/S4NKALP/DevGen.git
|
|
160
|
+
cd DevGen
|
|
161
|
+
uv sync --all-extras --dev # install runtime + dev deps
|
|
162
|
+
uv run pre-commit run --all-files
|
|
163
|
+
uv run devgen --help
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The project uses `uv` for dependency management, `ruff` for lint + format, and `pyright` for type checking. CI runs all three on Python 3.10 through 3.13 (see `.github/workflows/ci.yml`).
|
|
167
|
+
|
|
168
|
+
## Contributing
|
|
169
|
+
|
|
170
|
+
Issues and PRs welcome. Please run `uv run pre-commit run --all-files` before opening a PR so CI stays green.
|
|
171
|
+
|
|
172
|
+
## License
|
|
173
|
+
|
|
174
|
+
GPL-3.0-or-later. See [LICENSE](LICENSE).
|
|
175
|
+
|
|
176
|
+
## Acknowledgments
|
|
177
|
+
|
|
178
|
+
Built on the shoulders of:
|
|
179
|
+
|
|
180
|
+
- **Typer** & **Rich** — CLI and terminal UI
|
|
181
|
+
- **Questionary** — interactive prompts
|
|
182
|
+
- **Google Gemini**, **OpenAI**, **Anthropic**, **OpenRouter**, **HuggingFace**, and **Ollama** — AI providers
|
|
183
|
+
- **Ruff** — lint and format
|
|
184
|
+
|
|
185
|
+
<div align="center">
|
|
186
|
+
Made with ❤️ by <a href="https://github.com/S4NKALP">Sankalp</a>
|
|
187
|
+
</div>
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# DevGen
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
**AI-Powered Git & Project Workflows in One CLI**
|
|
6
|
+
|
|
7
|
+
Stop wasting time on repetitive tasks. DevGen automates commits, changelogs, `.gitignore`, and license files using AI — from cloud providers or a local Ollama model.
|
|
8
|
+
|
|
9
|
+
> PyPI didn't allow the original name, so you'll find it as **devgen-cli** on PyPI
|
|
10
|
+
|
|
11
|
+
> <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>
|
|
12
|
+
> <img src="https://img.shields.io/badge/Python-3.10%2B-3776AB?logo=python&logoColor=white" alt="Python">
|
|
13
|
+
> <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>
|
|
14
|
+
> <a href="https://github.com/S4NKALP/DevGen/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/S4NKALP/DevGen/ci.yml?branch=main&label=CI" alt="CI"></a>
|
|
15
|
+
> <a href="https://github.com/S4NKALP/DevGen/actions/workflows/pre-commit.yml"><img src="https://img.shields.io/github/actions/workflow/status/S4NKALP/DevGen/pre-commit.yml?branch=main&label=Pre-Commit" alt="Pre-Commit"></a>
|
|
16
|
+
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
## Why DevGen
|
|
20
|
+
|
|
21
|
+
DevGen is a CLI for the parts of development that should be invisible. It writes Conventional Commits from your diff, drafts SemVer changelogs, fetches `.gitignore` and license templates, and routes everything through whichever AI provider you prefer — including a fully local Ollama model if you don't want to send code to the cloud.
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
- **AI-Powered Commits** — Conventional Commit messages generated from your staged diff, grouped by directory, with optional emoji prefixes.
|
|
26
|
+
- **Multiple Providers** — Google Gemini, OpenAI, Anthropic, OpenRouter, HuggingFace, and local Ollama, all behind one CLI.
|
|
27
|
+
- **Smart Caching** — `.gitignore` and license templates are cached for offline use; AI responses are de-duplicated.
|
|
28
|
+
- **Conventional Changelogs** — `feat`, `fix`, `refactor`, `perf`, `docs`, `test`, `build`, `ci`, `chore`, `style`, and a `BREAKING CHANGES` section.
|
|
29
|
+
- **Project Scaffolding** — Pull `.gitignore` from GitHub's collection and drop in SPDX license files (MIT, Apache-2.0, GPL-3.0, AGPL-3.0, BSD, MPL-2.0, …).
|
|
30
|
+
- **Custom Templates** — Override the commit-message prompt via a `.tpl` file in your config.
|
|
31
|
+
- **Interactive Setup** — `devgen setup` walks you through provider + API key + options.
|
|
32
|
+
- **Undo Support** — `devgen commit undo` rolls back the last AI commit while keeping changes staged.
|
|
33
|
+
- **Token-Limit Aware** — When a diff exceeds a model's context window, you get a single actionable error with `--max-groups` and `--max-diff-size` hints.
|
|
34
|
+
|
|
35
|
+
## Supported AI Providers
|
|
36
|
+
|
|
37
|
+
| Provider | Notes |
|
|
38
|
+
| ----------------- | ---------------------------------------------------- |
|
|
39
|
+
| **Google Gemini** | Default-friendly, generous free tier |
|
|
40
|
+
| **OpenAI** | GPT-4o, GPT-4.1, o-series |
|
|
41
|
+
| **Anthropic** | Claude 3.5 / 3.7 / 4 |
|
|
42
|
+
| **OpenRouter** | Single key, many models |
|
|
43
|
+
| **HuggingFace** | Inference API |
|
|
44
|
+
| **Ollama** | Fully local, no API key, no data leaves your machine |
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Recommended: isolated environment
|
|
50
|
+
pipx install devgen-cli
|
|
51
|
+
|
|
52
|
+
# Or use uv for speed
|
|
53
|
+
uv tool install devgen-cli
|
|
54
|
+
|
|
55
|
+
# Or plain pip
|
|
56
|
+
pip install devgen-cli
|
|
57
|
+
|
|
58
|
+
# Shell completion (bash/zsh/fish)
|
|
59
|
+
devgen --install-completion
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Requires **Python 3.10 or newer**.
|
|
63
|
+
|
|
64
|
+
## Quick Start
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# 1. Configure a provider and API key
|
|
68
|
+
devgen setup config
|
|
69
|
+
|
|
70
|
+
# 2. Stage your work as usual
|
|
71
|
+
git add .
|
|
72
|
+
|
|
73
|
+
# 3. Let DevGen write the commit message
|
|
74
|
+
devgen commit run
|
|
75
|
+
|
|
76
|
+
# 4. Or preview first, then commit
|
|
77
|
+
devgen commit run --dry-run
|
|
78
|
+
devgen commit run --push
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Commands
|
|
82
|
+
|
|
83
|
+
| Command | Description |
|
|
84
|
+
| ------------------------------- | ----------------------------------------------------------------------------- |
|
|
85
|
+
| `devgen setup config` | Interactive provider / API key / options wizard |
|
|
86
|
+
| `devgen commit run` | Stage, generate message, commit (optionally `--push`, `--check`, `--dry-run`) |
|
|
87
|
+
| `devgen commit undo` | Undo the last AI commit, keep changes staged |
|
|
88
|
+
| `devgen changelog` | Generate a Conventional Commits changelog from recent history |
|
|
89
|
+
| `devgen release-notes` | Generate release notes for a version range |
|
|
90
|
+
| `devgen gitignore list` | List available GitHub `.gitignore` templates |
|
|
91
|
+
| `devgen gitignore add <name> …` | Add `.gitignore` entries to the current project |
|
|
92
|
+
| `devgen license list` | List available SPDX licenses |
|
|
93
|
+
| `devgen license add <spdx>` | Add a LICENSE file to the current project |
|
|
94
|
+
|
|
95
|
+
Run `devgen <command> --help` for full options on any subcommand.
|
|
96
|
+
|
|
97
|
+
## Custom Templates
|
|
98
|
+
|
|
99
|
+
DevGen uses `.tpl` files for its commit prompt. To override the default, set `custom_template` in your config (see `devgen config info`) and point it at a `.tpl` file with `{{ diff_text }}`, `{{ context }}`, `{{ group_name }}`, and the conditional `{% if use_emoji %}` block.
|
|
100
|
+
|
|
101
|
+
Example minimal template:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
{{ diff_text }}
|
|
105
|
+
Summarize the change above in one Conventional Commit line.
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Architecture
|
|
109
|
+
|
|
110
|
+
DevGen is built around a small set of composable components:
|
|
111
|
+
|
|
112
|
+
- **`BaseProvider`** — abstract base for every AI provider; handles API-key validation, token-limit detection, and error wrapping. Adding a new provider is one subclass with a single `_generate()` method.
|
|
113
|
+
- **`GitOperator`** — all `subprocess` calls to `git` live here, behind a `GitError` exception.
|
|
114
|
+
- **`DiffBuilder` / `FileGrouper` / `ManifestInspector`** — split a staged diff into per-directory groups with compact project context, so the model sees a focused slice instead of a wall of text.
|
|
115
|
+
- **`Section` enum** — single source of truth for changelog ordering and emoji, shared by `ChangelogGenerator` and `ReleaseNotesGenerator`.
|
|
116
|
+
|
|
117
|
+
The CLI entry point is `devgen.cli.main:app` (Typer), exposed as the `devgen` script.
|
|
118
|
+
|
|
119
|
+
## Development
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
git clone https://github.com/S4NKALP/DevGen.git
|
|
123
|
+
cd DevGen
|
|
124
|
+
uv sync --all-extras --dev # install runtime + dev deps
|
|
125
|
+
uv run pre-commit run --all-files
|
|
126
|
+
uv run devgen --help
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The project uses `uv` for dependency management, `ruff` for lint + format, and `pyright` for type checking. CI runs all three on Python 3.10 through 3.13 (see `.github/workflows/ci.yml`).
|
|
130
|
+
|
|
131
|
+
## Contributing
|
|
132
|
+
|
|
133
|
+
Issues and PRs welcome. Please run `uv run pre-commit run --all-files` before opening a PR so CI stays green.
|
|
134
|
+
|
|
135
|
+
## License
|
|
136
|
+
|
|
137
|
+
GPL-3.0-or-later. See [LICENSE](LICENSE).
|
|
138
|
+
|
|
139
|
+
## Acknowledgments
|
|
140
|
+
|
|
141
|
+
Built on the shoulders of:
|
|
142
|
+
|
|
143
|
+
- **Typer** & **Rich** — CLI and terminal UI
|
|
144
|
+
- **Questionary** — interactive prompts
|
|
145
|
+
- **Google Gemini**, **OpenAI**, **Anthropic**, **OpenRouter**, **HuggingFace**, and **Ollama** — AI providers
|
|
146
|
+
- **Ruff** — lint and format
|
|
147
|
+
|
|
148
|
+
<div align="center">
|
|
149
|
+
Made with ❤️ by <a href="https://github.com/S4NKALP">Sankalp</a>
|
|
150
|
+
</div>
|
|
@@ -21,8 +21,17 @@ def generate_with_ai(
|
|
|
21
21
|
provider_module = import_module(f"devgen.providers.{provider}")
|
|
22
22
|
class_name = "".join([x.capitalize() for x in provider.split("_")]) + "Provider"
|
|
23
23
|
provider_class = getattr(provider_module, class_name)
|
|
24
|
-
except
|
|
25
|
-
raise ImportError(
|
|
24
|
+
except ModuleNotFoundError as e:
|
|
25
|
+
raise ImportError(
|
|
26
|
+
f"Provider `{provider}` is not a built-in module: {e}. "
|
|
27
|
+
f"Available providers: gemini, openai, anthropic, huggingface, "
|
|
28
|
+
f"openrouter, ollama."
|
|
29
|
+
) from e
|
|
30
|
+
except AttributeError as e:
|
|
31
|
+
raise ImportError(
|
|
32
|
+
f"Provider `{provider}` exists but does not expose a "
|
|
33
|
+
f"`{class_name}` class. This is a packaging bug."
|
|
34
|
+
) from e
|
|
26
35
|
|
|
27
36
|
provider_instance = provider_class()
|
|
28
37
|
return provider_instance.generate(prompt, api_key=api_key, model=model, **kwargs)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from typing import Annotated
|
|
2
2
|
|
|
3
|
+
import questionary
|
|
3
4
|
import typer
|
|
4
5
|
|
|
5
6
|
from devgen.modules.commit_generator import run_commit_engine
|
|
@@ -50,12 +51,35 @@ def run_commit(
|
|
|
50
51
|
help="Force regeneration of commit messages.",
|
|
51
52
|
),
|
|
52
53
|
] = False,
|
|
54
|
+
check: Annotated[
|
|
55
|
+
bool,
|
|
56
|
+
typer.Option(
|
|
57
|
+
"--check",
|
|
58
|
+
help="Review/edit commit message before committing.",
|
|
59
|
+
),
|
|
60
|
+
] = False,
|
|
61
|
+
max_groups: Annotated[
|
|
62
|
+
int | None,
|
|
63
|
+
typer.Option(
|
|
64
|
+
"--max-groups",
|
|
65
|
+
help="Maximum number of commit groups. Lower this if you hit token-limit errors.",
|
|
66
|
+
),
|
|
67
|
+
] = None,
|
|
68
|
+
max_diff_size: Annotated[
|
|
69
|
+
int | None,
|
|
70
|
+
typer.Option(
|
|
71
|
+
"--max-diff-size",
|
|
72
|
+
help="Maximum diff size in chars per group (default 8000). "
|
|
73
|
+
"Lower this if you hit token-limit errors.",
|
|
74
|
+
),
|
|
75
|
+
] = None,
|
|
53
76
|
) -> None:
|
|
54
77
|
log_file = get_main_log_path()
|
|
55
78
|
logger = configure_logger("devgen.cli.commit", log_file, console=debug)
|
|
56
79
|
logger.info(f"Log file: {log_file}")
|
|
57
80
|
logger.info(
|
|
58
|
-
f"Options: dry_run={dry_run}, push={push}, debug={debug}, force={force_rebuild}"
|
|
81
|
+
f"Options: dry_run={dry_run}, push={push}, debug={debug}, force={force_rebuild}, "
|
|
82
|
+
f"check={check}, max_groups={max_groups}, max_diff_size={max_diff_size}"
|
|
59
83
|
)
|
|
60
84
|
|
|
61
85
|
run_commit_engine(
|
|
@@ -63,7 +87,10 @@ def run_commit(
|
|
|
63
87
|
push=push,
|
|
64
88
|
debug=debug,
|
|
65
89
|
force_rebuild=force_rebuild,
|
|
90
|
+
check=check,
|
|
66
91
|
logger=logger,
|
|
92
|
+
max_groups=max_groups,
|
|
93
|
+
max_diff_size=max_diff_size,
|
|
67
94
|
)
|
|
68
95
|
|
|
69
96
|
|
|
@@ -94,3 +121,28 @@ def validate() -> None:
|
|
|
94
121
|
typer.echo(f"- {f}")
|
|
95
122
|
else:
|
|
96
123
|
typer.secho("[i] No staged files.", fg=typer.colors.RED)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@app.command("undo")
|
|
127
|
+
def undo_commit() -> None:
|
|
128
|
+
"""Undoes the last commit but keeps changes staged."""
|
|
129
|
+
from devgen.utils import run_git_command
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
# Check if there's at least one commit
|
|
133
|
+
run_git_command(["git", "rev-parse", "HEAD"])
|
|
134
|
+
except Exception:
|
|
135
|
+
typer.secho("No commits found to undo.", fg=typer.colors.RED)
|
|
136
|
+
return
|
|
137
|
+
|
|
138
|
+
if questionary.confirm(
|
|
139
|
+
"Are you sure you want to undo the last commit? (Changes will remain staged)",
|
|
140
|
+
default=False,
|
|
141
|
+
).ask():
|
|
142
|
+
try:
|
|
143
|
+
run_git_command(["git", "reset", "--soft", "HEAD~1"])
|
|
144
|
+
typer.secho(
|
|
145
|
+
"Last commit undone. Changes are still staged.", fg=typer.colors.GREEN
|
|
146
|
+
)
|
|
147
|
+
except Exception as e:
|
|
148
|
+
typer.secho(f"Failed to undo commit: {e}", fg=typer.colors.RED)
|
|
@@ -62,7 +62,14 @@ def edit_config(
|
|
|
62
62
|
if key == "provider":
|
|
63
63
|
value = questionary.select(
|
|
64
64
|
"Select AI Provider:",
|
|
65
|
-
choices=[
|
|
65
|
+
choices=[
|
|
66
|
+
"gemini",
|
|
67
|
+
"openai",
|
|
68
|
+
"huggingface",
|
|
69
|
+
"openrouter",
|
|
70
|
+
"anthropic",
|
|
71
|
+
"ollama",
|
|
72
|
+
],
|
|
66
73
|
default=str(current_val) if current_val else "gemini",
|
|
67
74
|
style=style,
|
|
68
75
|
).ask()
|
|
@@ -117,7 +124,14 @@ def set_config() -> None:
|
|
|
117
124
|
# Questions
|
|
118
125
|
provider = questionary.select(
|
|
119
126
|
"Select AI Provider:",
|
|
120
|
-
choices=[
|
|
127
|
+
choices=[
|
|
128
|
+
"gemini",
|
|
129
|
+
"openai",
|
|
130
|
+
"huggingface",
|
|
131
|
+
"openrouter",
|
|
132
|
+
"anthropic",
|
|
133
|
+
"ollama",
|
|
134
|
+
],
|
|
121
135
|
default=config.get("provider", "gemini"),
|
|
122
136
|
style=style,
|
|
123
137
|
).ask()
|
|
@@ -151,12 +165,24 @@ def set_config() -> None:
|
|
|
151
165
|
raise typer.Exit(code=130)
|
|
152
166
|
emoji = emoji_choice == "Yes"
|
|
153
167
|
|
|
168
|
+
ollama_host = config.get("ollama_host", "http://localhost:11434")
|
|
169
|
+
if provider == "ollama":
|
|
170
|
+
ollama_host_input = questionary.text(
|
|
171
|
+
"Ollama server URL:",
|
|
172
|
+
default=ollama_host,
|
|
173
|
+
style=style,
|
|
174
|
+
).ask()
|
|
175
|
+
if ollama_host_input is None:
|
|
176
|
+
raise typer.Exit(code=130)
|
|
177
|
+
ollama_host = ollama_host_input.strip() or ollama_host
|
|
178
|
+
|
|
154
179
|
# Save Config
|
|
155
180
|
new_config = {
|
|
156
181
|
"provider": provider,
|
|
157
182
|
"model": model,
|
|
158
183
|
"api_key": api_key,
|
|
159
184
|
"emoji": emoji,
|
|
185
|
+
"ollama_host": ollama_host,
|
|
160
186
|
}
|
|
161
187
|
|
|
162
188
|
# Merge with existing config to preserve other keys?
|
|
@@ -167,3 +193,30 @@ def set_config() -> None:
|
|
|
167
193
|
_save_config(config)
|
|
168
194
|
typer.secho("\nConfiguration saved.", fg=typer.colors.GREEN)
|
|
169
195
|
typer.echo(yaml.dump(new_config, default_flow_style=False))
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@app.command("info")
|
|
199
|
+
def config_info() -> None:
|
|
200
|
+
"""Show information about configuration options and templates."""
|
|
201
|
+
from rich.console import Console
|
|
202
|
+
from rich.table import Table
|
|
203
|
+
|
|
204
|
+
console = Console()
|
|
205
|
+
table = Table(title="Custom Template Variables", box=None)
|
|
206
|
+
table.add_column("Variable", style="cyan")
|
|
207
|
+
table.add_column("Description", style="white")
|
|
208
|
+
|
|
209
|
+
table.add_row("{{ group_name }}", "The folder name being committed (or 'root').")
|
|
210
|
+
table.add_row("{{ diff_text }}", "The git diff of the changes.")
|
|
211
|
+
table.add_row("{{ context }}", "Project context (manifest files content).")
|
|
212
|
+
|
|
213
|
+
console.print(table)
|
|
214
|
+
console.print(
|
|
215
|
+
"\n[bold]Tip:[/bold] If you hardcode emojis in your template, the AI will likely include them regardless of the 'emoji' setting.",
|
|
216
|
+
style="yellow",
|
|
217
|
+
)
|
|
218
|
+
console.print("\n[bold]Example Template:[/bold]")
|
|
219
|
+
console.print(
|
|
220
|
+
"custom_template: |\n [type]: [desc]\n \n Diff: {{ diff_text }}\n",
|
|
221
|
+
style="dim",
|
|
222
|
+
)
|
|
@@ -15,7 +15,7 @@ from devgen.cli.setup import app as setup_app
|
|
|
15
15
|
app = typer.Typer(
|
|
16
16
|
name="devgen",
|
|
17
17
|
help="devgen-py: AI-Powered Git Commit & Release Automation.",
|
|
18
|
-
add_completion=
|
|
18
|
+
add_completion=True,
|
|
19
19
|
no_args_is_help=True,
|
|
20
20
|
rich_markup_mode="markdown",
|
|
21
21
|
)
|
|
@@ -48,13 +48,21 @@ def _version_callback(value: bool) -> None:
|
|
|
48
48
|
typer.echo(f"devgen version: {version}")
|
|
49
49
|
else:
|
|
50
50
|
typer.secho(
|
|
51
|
-
"
|
|
51
|
+
"Could not determine version: no `project.version` or "
|
|
52
|
+
"`tool.poetry.version` found in pyproject.toml.",
|
|
52
53
|
fg=typer.colors.RED,
|
|
53
54
|
err=True,
|
|
54
55
|
)
|
|
55
|
-
except
|
|
56
|
+
except FileNotFoundError:
|
|
56
57
|
typer.secho(
|
|
57
|
-
f"
|
|
58
|
+
f"pyproject.toml not found at {pyproject_path}. "
|
|
59
|
+
"The installation may be corrupt.",
|
|
60
|
+
fg=typer.colors.RED,
|
|
61
|
+
err=True,
|
|
62
|
+
)
|
|
63
|
+
except toml.TomlDecodeError as e:
|
|
64
|
+
typer.secho(
|
|
65
|
+
f"pyproject.toml is not valid TOML: {e}",
|
|
58
66
|
fg=typer.colors.RED,
|
|
59
67
|
err=True,
|
|
60
68
|
)
|
|
@@ -33,7 +33,14 @@ def setup_config() -> None:
|
|
|
33
33
|
# Questions
|
|
34
34
|
provider = questionary.select(
|
|
35
35
|
"Select AI Provider:",
|
|
36
|
-
choices=[
|
|
36
|
+
choices=[
|
|
37
|
+
"gemini",
|
|
38
|
+
"openai",
|
|
39
|
+
"huggingface",
|
|
40
|
+
"openrouter",
|
|
41
|
+
"anthropic",
|
|
42
|
+
"ollama",
|
|
43
|
+
],
|
|
37
44
|
default=current_config.get("provider", "gemini"),
|
|
38
45
|
style=style,
|
|
39
46
|
).ask()
|
|
@@ -67,12 +74,24 @@ def setup_config() -> None:
|
|
|
67
74
|
raise typer.Exit(code=130)
|
|
68
75
|
emoji = emoji_choice == "Yes"
|
|
69
76
|
|
|
77
|
+
ollama_host = current_config.get("ollama_host", "http://localhost:11434")
|
|
78
|
+
if provider == "ollama":
|
|
79
|
+
ollama_host_input = questionary.text(
|
|
80
|
+
"Ollama server URL:",
|
|
81
|
+
default=ollama_host,
|
|
82
|
+
style=style,
|
|
83
|
+
).ask()
|
|
84
|
+
if ollama_host_input is None:
|
|
85
|
+
raise typer.Exit(code=130)
|
|
86
|
+
ollama_host = ollama_host_input.strip() or ollama_host
|
|
87
|
+
|
|
70
88
|
# Save Config
|
|
71
89
|
new_config = {
|
|
72
90
|
"provider": provider,
|
|
73
91
|
"model": model,
|
|
74
92
|
"api_key": api_key,
|
|
75
93
|
"emoji": emoji,
|
|
94
|
+
"ollama_host": ollama_host,
|
|
76
95
|
}
|
|
77
96
|
|
|
78
97
|
try:
|