toolbelt-cli 0.2.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.
- toolbelt/__init__.py +4 -0
- toolbelt/cli.py +46 -0
- toolbelt/commands/__init__.py +1 -0
- toolbelt/commands/audit.py +63 -0
- toolbelt/commands/commit.py +84 -0
- toolbelt/commands/new.py +116 -0
- toolbelt/commands/version.py +47 -0
- toolbelt/services/__init__.py +1 -0
- toolbelt/services/auditor.py +295 -0
- toolbelt/services/llm.py +105 -0
- toolbelt/services/scaffolder.py +91 -0
- toolbelt/templates/java/.gitignore.j2 +7 -0
- toolbelt/templates/java/LICENSE.j2 +39 -0
- toolbelt/templates/java/README.md.j2 +26 -0
- toolbelt/templates/java/pom.xml.j2 +53 -0
- toolbelt/templates/java/src/main/java/com/{{project_name}}/App.java.j2 +7 -0
- toolbelt/templates/java/src/test/java/com/{{project_name}}/AppTest.java.j2 +13 -0
- toolbelt/templates/node/.eslintrc.json.j2 +10 -0
- toolbelt/templates/node/.gitignore.j2 +7 -0
- toolbelt/templates/node/.prettierrc.j2 +5 -0
- toolbelt/templates/node/LICENSE.j2 +39 -0
- toolbelt/templates/node/README.md.j2 +26 -0
- toolbelt/templates/node/package.json.j2 +20 -0
- toolbelt/templates/node/src/index.test.ts.j2 +6 -0
- toolbelt/templates/node/src/index.ts.j2 +7 -0
- toolbelt/templates/node/tsconfig.json.j2 +14 -0
- toolbelt/templates/python/.github/workflows/ci.yml.j2 +28 -0
- toolbelt/templates/python/.gitignore.j2 +42 -0
- toolbelt/templates/python/.pre-commit-config.yaml.j2 +13 -0
- toolbelt/templates/python/Dockerfile.j2 +10 -0
- toolbelt/templates/python/LICENSE.j2 +39 -0
- toolbelt/templates/python/Makefile.j2 +17 -0
- toolbelt/templates/python/README.md.j2 +19 -0
- toolbelt/templates/python/pyproject.toml.j2 +28 -0
- toolbelt/templates/python/src/{{project_name}}/__init__.py.j2 +1 -0
- toolbelt/templates/python/src/{{project_name}}/main.py.j2 +7 -0
- toolbelt/templates/python/tests/test_main.py.j2 +8 -0
- toolbelt/utils/__init__.py +1 -0
- toolbelt/utils/console.py +43 -0
- toolbelt/utils/git.py +54 -0
- toolbelt_cli-0.2.0.dist-info/METADATA +382 -0
- toolbelt_cli-0.2.0.dist-info/RECORD +46 -0
- toolbelt_cli-0.2.0.dist-info/WHEEL +5 -0
- toolbelt_cli-0.2.0.dist-info/entry_points.txt +3 -0
- toolbelt_cli-0.2.0.dist-info/licenses/LICENSE +21 -0
- toolbelt_cli-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "{{project_name}}"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "{{project_name}} – generated by Toolbelt"
|
|
5
|
+
authors = [{ name = "{{author}}", email = "" }]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
license = { text = "{{license}}" }
|
|
8
|
+
requires-python = ">=3.11"
|
|
9
|
+
dependencies = []
|
|
10
|
+
|
|
11
|
+
[project.optional-dependencies]
|
|
12
|
+
dev = [
|
|
13
|
+
"ruff",
|
|
14
|
+
"mypy",
|
|
15
|
+
"pytest",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[tool.ruff]
|
|
19
|
+
line-length = 88
|
|
20
|
+
select = ["E", "F", "W", "I", "N", "UP", "B", "C4"]
|
|
21
|
+
ignore = []
|
|
22
|
+
|
|
23
|
+
[tool.mypy]
|
|
24
|
+
strict = true
|
|
25
|
+
|
|
26
|
+
[tool.pytest.ini_options]
|
|
27
|
+
addopts = "-ra -q"
|
|
28
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""{{project_name}} package."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Utility subpackage for Toolbelt."""
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Rich console helpers for Toolbelt CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
from rich.panel import Panel
|
|
9
|
+
|
|
10
|
+
# Ensure UTF-8 output on Windows consoles
|
|
11
|
+
if sys.platform == "win32":
|
|
12
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
13
|
+
try:
|
|
14
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
15
|
+
except Exception:
|
|
16
|
+
pass
|
|
17
|
+
if hasattr(sys.stderr, "reconfigure"):
|
|
18
|
+
try:
|
|
19
|
+
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
|
|
20
|
+
except Exception:
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
_console = Console()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_console() -> Console:
|
|
27
|
+
"""Return a shared singleton Rich console."""
|
|
28
|
+
return _console
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def success_panel(message: str, title: str = "Success") -> Panel:
|
|
32
|
+
"""Create a green success panel."""
|
|
33
|
+
return Panel(message, title=f"✅ {title}", border_style="green")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def error_panel(message: str, title: str = "Error") -> Panel:
|
|
37
|
+
"""Create a red error panel."""
|
|
38
|
+
return Panel(message, title=f"❌ {title}", border_style="red")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def info_panel(message: str, title: str = "Info") -> Panel:
|
|
42
|
+
"""Create a cyan info panel."""
|
|
43
|
+
return Panel(message, title=f"ℹ️ {title}", border_style="cyan")
|
toolbelt/utils/git.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Git wrapper utilities for Toolbelt CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def run_git(args: list[str], cwd: Path | None = None) -> str:
|
|
11
|
+
"""Run a git command, returning stdout as text."""
|
|
12
|
+
result = subprocess.run(
|
|
13
|
+
["git", *args],
|
|
14
|
+
cwd=cwd,
|
|
15
|
+
text=True,
|
|
16
|
+
capture_output=True,
|
|
17
|
+
check=False,
|
|
18
|
+
)
|
|
19
|
+
if result.returncode != 0:
|
|
20
|
+
err = result.stderr.strip() or result.stdout.strip()
|
|
21
|
+
raise RuntimeError(f"Git command failed: git {' '.join(args)}\n{err}")
|
|
22
|
+
return result.stdout.strip()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_git_user_name(cwd: Path | None = None) -> str:
|
|
26
|
+
"""Return user.name from git config, environment, or empty string."""
|
|
27
|
+
try:
|
|
28
|
+
name = run_git(["config", "user.name"], cwd=cwd)
|
|
29
|
+
if name:
|
|
30
|
+
return name
|
|
31
|
+
except Exception:
|
|
32
|
+
pass
|
|
33
|
+
return (
|
|
34
|
+
os.getenv("GIT_AUTHOR_NAME") or os.getenv("USER") or os.getenv("USERNAME") or ""
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def git_diff_staged(cwd: Path | None = None) -> str:
|
|
39
|
+
"""Return diff of staged changes."""
|
|
40
|
+
return run_git(["diff", "--staged"], cwd=cwd)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def git_commit(message: str, cwd: Path | None = None) -> None:
|
|
44
|
+
"""Create a commit with the given message."""
|
|
45
|
+
run_git(["commit", "-m", message], cwd=cwd)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def is_git_repo(cwd: Path | None = None) -> bool:
|
|
49
|
+
"""Check if the directory is inside a git repository."""
|
|
50
|
+
try:
|
|
51
|
+
run_git(["rev-parse", "--is-inside-work-tree"], cwd=cwd)
|
|
52
|
+
return True
|
|
53
|
+
except Exception:
|
|
54
|
+
return False
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: toolbelt-cli
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A developer productivity CLI that scaffolds projects, generates conventional commit messages with Claude API, and audits repos.
|
|
5
|
+
Author-email: Christopher Njihia <crnjihia@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/crnjihia/toolbelt-cli
|
|
8
|
+
Project-URL: Documentation, https://crnjihia.github.io/toolbelt-cli/
|
|
9
|
+
Project-URL: Repository, https://github.com/crnjihia/toolbelt-cli
|
|
10
|
+
Project-URL: Issues, https://github.com/crnjihia/toolbelt-cli/issues
|
|
11
|
+
Keywords: cli,productivity,scaffold,git,conventional-commits,audit,anthropic,claude
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
21
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
22
|
+
Classifier: Topic :: Utilities
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: typer>=0.9.0
|
|
27
|
+
Requires-Dist: rich>=13.0.0
|
|
28
|
+
Requires-Dist: jinja2>=3.1.0
|
|
29
|
+
Requires-Dist: httpx>=0.24.0
|
|
30
|
+
Requires-Dist: anthropic>=0.4.0
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-mock>=3.10; extra == "dev"
|
|
34
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
35
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
36
|
+
Requires-Dist: types-jinja2; extra == "dev"
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
# Toolbelt CLI
|
|
40
|
+
|
|
41
|
+
<p align="center">
|
|
42
|
+
<img src="assets/demo.png" alt="Toolbelt CLI in action" width="800" />
|
|
43
|
+
</p>
|
|
44
|
+
|
|
45
|
+
*The screenshot above demonstrates live execution of Toolbelt CLI in Windows Terminal:*
|
|
46
|
+
1. **`toolbelt --help`**: Rich command overview, argument formatting, and shell completion options.
|
|
47
|
+
2. **`toolbelt version`**: Fast local package version readout and automated PyPI release checking.
|
|
48
|
+
3. **`toolbelt audit .`**: 10-point repository quality audit checking README sections, license files, `.gitignore` entries, test directories, and CI workflows.
|
|
49
|
+
|
|
50
|
+
<p align="center">
|
|
51
|
+
<strong>Opinionated Developer Productivity CLI: Multi-Language Scaffolding, AI Git Commits, and Repository Auditing.</strong>
|
|
52
|
+
</p>
|
|
53
|
+
|
|
54
|
+
<p align="center">
|
|
55
|
+
<a href="https://crnjihia.github.io/toolbelt-cli/"><img src="https://img.shields.io/badge/Docs-GitHub%20Pages-10b981?style=flat&logo=github" alt="Documentation" /></a>
|
|
56
|
+
<a href="https://github.com/crnjihia/toolbelt-cli/actions/workflows/ci.yml"><img src="https://github.com/crnjihia/toolbelt-cli/actions/workflows/ci.yml/badge.svg" alt="CI Status" /></a>
|
|
57
|
+
<a href="https://pypi.org/project/toolbelt-cli/"><img src="https://img.shields.io/pypi/v/toolbelt-cli.svg?color=blue" alt="PyPI Version" /></a>
|
|
58
|
+
<a href="https://pypi.org/project/toolbelt-cli/"><img src="https://img.shields.io/pypi/pyversions/toolbelt-cli.svg" alt="Python Versions" /></a>
|
|
59
|
+
<a href="https://github.com/astral-sh/ruff"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff" /></a>
|
|
60
|
+
<a href="https://mypy.readthedocs.io/"><img src="https://img.shields.io/badge/types-Mypy-blue.svg" alt="Type Checked" /></a>
|
|
61
|
+
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT" /></a>
|
|
62
|
+
</p>
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Table of Contents
|
|
67
|
+
- [Overview](#overview)
|
|
68
|
+
- [Installation](#installation)
|
|
69
|
+
- [Command Reference](#command-reference)
|
|
70
|
+
- [Feature Tour & Usage Examples](#feature-tour)
|
|
71
|
+
- [1. Project Scaffolding (`toolbelt new`)](#1-toolbelt-new-type-name)
|
|
72
|
+
- [2. AI-Powered Commits (`toolbelt commit`)](#2-toolbelt-commit)
|
|
73
|
+
- [3. Repository Auditing (`toolbelt audit`)](#3-toolbelt-audit-path)
|
|
74
|
+
- [4. Version & Upstream Sync (`toolbelt version`)](#4-toolbelt-version)
|
|
75
|
+
- [Configuration & Environment Variables](#configuration--environment-variables)
|
|
76
|
+
- [Architecture & Tech Stack](#architecture--tech-stack)
|
|
77
|
+
- [Development & Testing](#development--testing)
|
|
78
|
+
- [GitHub Setup & Publication Guide](#github-setup--publication-guide)
|
|
79
|
+
- [Publishing to PyPI](#publishing-to-pypi)
|
|
80
|
+
- [Contributing](#contributing)
|
|
81
|
+
- [License](#license)
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Overview
|
|
86
|
+
|
|
87
|
+
**Toolbelt CLI** (`toolbelt`) is a command-line tool built with Python 3.11+, Typer, Rich, and Jinja2 that automates repetitive developer workflows:
|
|
88
|
+
|
|
89
|
+
1. 🚀 **Scaffold New Projects (`toolbelt new`)**: Instantly generate clean, production-ready project templates for **Python**, **Node.js / TypeScript**, and **Java** with pre-configured linters, test harnesses, CI pipelines, and Dockerfiles.
|
|
90
|
+
2. 🤖 **AI-Powered Commits (`toolbelt commit`)**: Automatically analyze staged git diffs and draft structured Conventional Commit messages using **Claude** (`claude-sonnet-4`), with an offline rule-based fallback when no API key is set.
|
|
91
|
+
3. 🔍 **Repository Auditing (`toolbelt audit`)**: Scan any codebase for missing documentation sections (`Installation`, `Usage`, `License`), required configuration files (`.gitignore`, `pyproject.toml`, test suites, CI workflows), and automatically repair missing files with `--fix`.
|
|
92
|
+
4. 📦 **Version & PyPI Sync (`toolbelt version`)**: Inspect currently installed version and verify release availability against the Python Package Index.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Installation
|
|
97
|
+
|
|
98
|
+
### With `pipx` (Recommended)
|
|
99
|
+
Isolate CLI tools in their own dedicated virtual environments:
|
|
100
|
+
```bash
|
|
101
|
+
pipx install toolbelt-cli
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### With `uv`
|
|
105
|
+
Fast single-step tool installation:
|
|
106
|
+
```bash
|
|
107
|
+
uv tool install toolbelt-cli
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### With standard `pip`
|
|
111
|
+
```bash
|
|
112
|
+
pip install toolbelt-cli
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Local Development Installation
|
|
116
|
+
```bash
|
|
117
|
+
git clone https://github.com/crnjihia/toolbelt-cli.git
|
|
118
|
+
cd toolbelt-cli
|
|
119
|
+
pip install -e ".[dev]"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Command Reference
|
|
125
|
+
|
|
126
|
+
| Command | Arguments | Flags | Description |
|
|
127
|
+
|:---|:---|:---|:---|
|
|
128
|
+
| `toolbelt new` | `<type>` `<name>` | `--author`, `--license`, `--ci/--no-ci`, `--docker/--no-docker`, `-y/--yes` | Scaffold an opinionated Python, Node, or Java project. |
|
|
129
|
+
| `toolbelt commit` | *(none)* | `--dry-run`, `-t/--type`, `-s/--scope`, `-y/--yes` | Inspect staged diff and generate Conventional Commit message. |
|
|
130
|
+
| `toolbelt audit` | `[path=.]` | `--json`, `--fix` | Run 10-point repository audit with Rich table or JSON output. |
|
|
131
|
+
| `toolbelt version` | *(none)* | *(none)* | Display installed version and check PyPI for updates. |
|
|
132
|
+
| `toolbelt --help` | *(none)* | *(none)* | Show global options and available commands. |
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Feature Tour
|
|
137
|
+
|
|
138
|
+
### 1. `toolbelt new <type> <name>`
|
|
139
|
+
Scaffolds a new project directory with interactive prompts powered by Typer and Rich:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
toolbelt new python my_service
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```text
|
|
146
|
+
Author name [Jane Doe]: Jane Doe
|
|
147
|
+
License (MIT | Apache-2.0 | none) [MIT]: MIT
|
|
148
|
+
Include CI? (y/n) [True]: y
|
|
149
|
+
Include Docker? (y/n) [False]: y
|
|
150
|
+
|
|
151
|
+
┌──────────────────────────── 🎉 Toolbelt Scaffold ────────────────────────────┐
|
|
152
|
+
│ ✨ Project successfully scaffolded! │
|
|
153
|
+
│ │
|
|
154
|
+
│ Next steps: │
|
|
155
|
+
│ cd my_service && make install && make test │
|
|
156
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
#### Supported Stacks & Project Layouts
|
|
160
|
+
|
|
161
|
+
- **Python (`toolbelt new python <name>`)**:
|
|
162
|
+
- `src/<name>/__init__.py`, `main.py`
|
|
163
|
+
- `tests/test_main.py`
|
|
164
|
+
- `pyproject.toml` (configured with Ruff, MyPy, and Pytest)
|
|
165
|
+
- `.pre-commit-config.yaml`
|
|
166
|
+
- `Makefile` (`install`, `test`, `lint`, `format`, `clean`)
|
|
167
|
+
- `README.md`, `.gitignore`, `LICENSE`
|
|
168
|
+
- `.github/workflows/ci.yml` (optional)
|
|
169
|
+
- `Dockerfile` (optional)
|
|
170
|
+
|
|
171
|
+
- **Node.js / TypeScript (`toolbelt new node <name>`)**:
|
|
172
|
+
- `package.json` (TypeScript, ESLint, Prettier, Vitest)
|
|
173
|
+
- `tsconfig.json`
|
|
174
|
+
- `src/index.ts`, `src/index.test.ts`
|
|
175
|
+
- `.eslintrc.json`, `.prettierrc`
|
|
176
|
+
- `README.md`, `.gitignore`, `LICENSE`
|
|
177
|
+
|
|
178
|
+
- **Java (`toolbelt new java <name>`)**:
|
|
179
|
+
- `pom.xml` (Maven, JUnit 5 Jupiter, compiler plugins)
|
|
180
|
+
- `src/main/java/com/<name>/App.java`
|
|
181
|
+
- `src/test/java/com/<name>/AppTest.java`
|
|
182
|
+
- `README.md`, `.gitignore`, `LICENSE`
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
### 2. `toolbelt commit`
|
|
187
|
+
Reads your staged git diff (`git diff --staged`) and drafts a Conventional Commit message:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
git add src/
|
|
191
|
+
toolbelt commit
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
```text
|
|
195
|
+
┌────────────────────────── Proposed Commit Message ──────────────────────────┐
|
|
196
|
+
│ feat(auth): add bearer token validator and refresh middleware │
|
|
197
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
198
|
+
Apply commit? [y/n/edit] (y): y
|
|
199
|
+
✅ Commit created successfully!
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
- **Claude Sonnet 4 Integration**: Export your Anthropic API key (`export ANTHROPIC_API_KEY="sk-ant-..."`) for contextual messages following standard types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`.
|
|
203
|
+
- **Smart Fallback**: If no key is set, Toolbelt falls back to an offline heuristic analyzer that inspects diff sizes, lines added/removed, and modified file types.
|
|
204
|
+
- **Dry-run Mode**: Preview generated messages without applying commits using `toolbelt commit --dry-run`.
|
|
205
|
+
- **Safety**: Rejects any commit message shorter than 10 characters and offers interactive `[y/n/edit]` revision.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
### 3. `toolbelt audit [path]`
|
|
210
|
+
Performs a structured quality check on any repository:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
toolbelt audit .
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
```text
|
|
217
|
+
Repository Audit: Toolbelt-CLI
|
|
218
|
+
┌────────────────────────────┬──────────┬──────────────────────────┐
|
|
219
|
+
│ Check │ Status │ Details │
|
|
220
|
+
├────────────────────────────┼──────────┼──────────────────────────┤
|
|
221
|
+
│ README.md exists │ ✅ │ 227 lines │
|
|
222
|
+
│ README has Install │ ✅ │ found "## Installation" │
|
|
223
|
+
│ README has Usage │ ✅ │ found "## Usage" │
|
|
224
|
+
│ README has License │ ✅ │ found "## License" │
|
|
225
|
+
│ LICENSE file │ ✅ │ MIT │
|
|
226
|
+
│ .gitignore exists │ ✅ │ 29 entries │
|
|
227
|
+
│ .gitignore has __pycache__ │ ✅ │ │
|
|
228
|
+
│ Tests directory │ ✅ │ tests/ found │
|
|
229
|
+
│ CI config │ ✅ │ .github/workflows/ci.yml │
|
|
230
|
+
│ pyproject.toml │ ✅ │ │
|
|
231
|
+
└────────────────────────────┴──────────┴──────────────────────────┘
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
- **Exit Code**: Returns `0` if all checks pass, and `1` if any check fails (ready for pre-commit hooks and CI gates).
|
|
235
|
+
- **Auto-Fix (`--fix`)**: Automatically prompts to create missing `.gitignore` files and `LICENSE` files (MIT or Apache-2.0).
|
|
236
|
+
- **JSON Mode (`--json`)**: Outputs machine-readable JSON for integration into reporting tools or CI/CD pipelines.
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
### 4. `toolbelt version`
|
|
241
|
+
Displays installed package version and checks whether updates are available on PyPI:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
toolbelt version
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Configuration & Environment Variables
|
|
250
|
+
|
|
251
|
+
Copy `.env.example` to `.env` to configure your environment:
|
|
252
|
+
|
|
253
|
+
```ini
|
|
254
|
+
# Optional: Set Claude API key for AI-assisted git commit message generation
|
|
255
|
+
ANTHROPIC_API_KEY=sk-ant-api03-...
|
|
256
|
+
|
|
257
|
+
# Optional: Set debug logging
|
|
258
|
+
LOG_LEVEL=INFO
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Architecture & Tech Stack
|
|
264
|
+
|
|
265
|
+
```text
|
|
266
|
+
toolbelt-cli/
|
|
267
|
+
├── src/toolbelt/
|
|
268
|
+
│ ├── cli.py # Typer application definition and routing
|
|
269
|
+
│ ├── commands/ # Subcommand handlers (new, commit, audit, version)
|
|
270
|
+
│ ├── services/ # Scaffolder, LLM Commit Generator, and Auditor
|
|
271
|
+
│ ├── templates/ # Jinja2 project templates (python, node, java)
|
|
272
|
+
│ └── utils/ # Git interface, Rich console helpers
|
|
273
|
+
├── tests/ # Full offline pytest test suite with CliRunner
|
|
274
|
+
└── .github/workflows/ # CI matrix & PyPI Trusted Publishing release
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
- **CLI Framework**: [Typer](https://typer.tiangolo.com/)
|
|
278
|
+
- **Terminal UI**: [Rich](https://rich.readthedocs.io/)
|
|
279
|
+
- **Templating**: [Jinja2](https://jinja.palletsprojects.com/)
|
|
280
|
+
- **HTTP Client**: [HTTPX](https://www.python-httpx.org/)
|
|
281
|
+
- **LLM Integration**: [Anthropic Python SDK](https://github.com/anthropics/anthropic-sdk-python)
|
|
282
|
+
- **Quality**: [Ruff](https://astral.sh/ruff) & [MyPy](https://mypy.readthedocs.io/)
|
|
283
|
+
- **Testing**: [Pytest](https://docs.pytest.org/) & `CliRunner`
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Development & Testing
|
|
288
|
+
|
|
289
|
+
Run the automated test suite locally:
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
# Run unit and integration tests (100% offline)
|
|
293
|
+
pytest -v
|
|
294
|
+
|
|
295
|
+
# Check linting and style
|
|
296
|
+
ruff check .
|
|
297
|
+
ruff format --check .
|
|
298
|
+
|
|
299
|
+
# Run static type analysis
|
|
300
|
+
mypy src
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## GitHub Setup & Publication Guide
|
|
306
|
+
|
|
307
|
+
Follow these steps to publish this repository to your personal or organization GitHub account:
|
|
308
|
+
|
|
309
|
+
### 1. Initialize & Link GitHub Remote
|
|
310
|
+
Create a new repository named `toolbelt-cli` on [GitHub](https://github.com/new) (leave "Initialize with README" unchecked). Then connect your local repository:
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
# Add your GitHub remote
|
|
314
|
+
git remote add origin https://github.com/crnjihia/toolbelt-cli.git
|
|
315
|
+
|
|
316
|
+
# Ensure default branch is main
|
|
317
|
+
git branch -M main
|
|
318
|
+
|
|
319
|
+
# Push code, tags, and complete history to GitHub
|
|
320
|
+
git push -u origin main
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### 2. Configure PyPI Trusted Publishing (OIDC)
|
|
324
|
+
Toolbelt CLI uses GitHub Actions **Trusted Publishing** (OIDC) so no API tokens or passwords need to be saved in repository secrets:
|
|
325
|
+
|
|
326
|
+
1. Log into your [PyPI](https://pypi.org/) account.
|
|
327
|
+
2. Navigate to **Account settings** → **Publishing** → **Add a GitHub publisher**.
|
|
328
|
+
3. Set:
|
|
329
|
+
- **PyPI Project Name**: `toolbelt-cli`
|
|
330
|
+
- **Owner**: `crnjihia`
|
|
331
|
+
- **Repository name**: `toolbelt-cli`
|
|
332
|
+
- **Workflow name**: `release.yml`
|
|
333
|
+
- **Environment name**: `pypi`
|
|
334
|
+
4. In your GitHub repository, ensure the `pypi` environment is enabled under **Settings** → **Environments**.
|
|
335
|
+
5. When ready to release, simply tag and push:
|
|
336
|
+
```bash
|
|
337
|
+
git tag v0.1.0
|
|
338
|
+
git push origin v0.1.0
|
|
339
|
+
```
|
|
340
|
+
GitHub Actions will automatically build source distributions and wheels, run `twine check`, and publish to PyPI!
|
|
341
|
+
|
|
342
|
+
### 3. Continuous Integration Checks
|
|
343
|
+
On every push and pull request, [`.github/workflows/ci.yml`](.github/workflows/ci.yml) automatically executes:
|
|
344
|
+
- Matrix testing across Python **3.11** and **3.12**.
|
|
345
|
+
- Fast code hygiene validation via `ruff check .`.
|
|
346
|
+
- Code format verification via `ruff format --check .`.
|
|
347
|
+
- Static type analysis via `mypy src`.
|
|
348
|
+
- Complete test suite execution via `pytest -v` (19 offline tests with 100% pass guarantee).
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## Publishing to PyPI
|
|
353
|
+
|
|
354
|
+
This project is configured with GitHub Actions **Trusted Publishing** using OpenID Connect (OIDC). No long-lived API tokens are stored in repository secrets.
|
|
355
|
+
|
|
356
|
+
To release a new version:
|
|
357
|
+
1. Bump the version in `pyproject.toml` and `src/toolbelt/__init__.py`.
|
|
358
|
+
2. Update `CHANGELOG.md`.
|
|
359
|
+
3. Create and push a git tag:
|
|
360
|
+
```bash
|
|
361
|
+
git tag v0.1.0
|
|
362
|
+
git push origin v0.1.0
|
|
363
|
+
```
|
|
364
|
+
4. The `.github/workflows/release.yml` workflow will automatically build wheels and sdist distributions and publish them directly to PyPI.
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Contributing
|
|
369
|
+
|
|
370
|
+
Contributions are welcome! Please follow these steps:
|
|
371
|
+
|
|
372
|
+
1. Fork the repository on GitHub.
|
|
373
|
+
2. Create a feature branch: `git checkout -b feat/my-new-feature`
|
|
374
|
+
3. Ensure all tests and linters pass: `pytest && ruff check . && mypy src`
|
|
375
|
+
4. Commit your changes using conventional commits: `git commit -m "feat(scope): describe change"`
|
|
376
|
+
5. Open a Pull Request.
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## License
|
|
381
|
+
|
|
382
|
+
Distributed under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
toolbelt/__init__.py,sha256=7C_ocpOuOSVLtBIFZilziHtQ6pLiOJqiWvwFRJBvi_4,77
|
|
2
|
+
toolbelt/cli.py,sha256=2f_R7J-54DR5Xakm-gHiXZCH-OGSZ0YouBK63pga174,1014
|
|
3
|
+
toolbelt/commands/__init__.py,sha256=K2gu6gdttGhyZqslqX8tWhb14370D4rk3VWw6RXYJuI,32
|
|
4
|
+
toolbelt/commands/audit.py,sha256=Ax8FgPhQbCiScxYs7lJErzs201qyh_lQPQlfCttLxLw,1910
|
|
5
|
+
toolbelt/commands/commit.py,sha256=TstWbSb_IUosq9YkpMgxHmG92A109an5rtESe7FXPuk,2740
|
|
6
|
+
toolbelt/commands/new.py,sha256=N5vsIb4C43ThKipE6u17C5tSNHC2v-1uy7VbTpaJ5CI,3750
|
|
7
|
+
toolbelt/commands/version.py,sha256=9NVvWQGKJrrdE_ORFXrI6LYTf-B3dMx-WggeofF-YSM,1375
|
|
8
|
+
toolbelt/services/__init__.py,sha256=BAvx6dL13R68P071qY5QhHMd1r8pLPHz3B8SrmOGD-Q,38
|
|
9
|
+
toolbelt/services/auditor.py,sha256=HHndihgoFkEQLCZBBCT7oVHco4_zgkkEilmaaJCOi_M,10044
|
|
10
|
+
toolbelt/services/llm.py,sha256=Z40lJoAH6Vo_TGCPqdcAa5bZgFhABBMF4rj6qG3B7sQ,3660
|
|
11
|
+
toolbelt/services/scaffolder.py,sha256=A7Srq9ZGyTObfkF2KWp6c6I11zPebH7xm34aVew22dA,3117
|
|
12
|
+
toolbelt/templates/java/.gitignore.j2,sha256=cPHdQz80VRzOX7omm056_MUiwm5u1spBoQfAlv918Yw,47
|
|
13
|
+
toolbelt/templates/java/LICENSE.j2,sha256=cPi2WOgT92Szay92rtTGBuw-k0i4jiS4HGRVjXg6mKc,1854
|
|
14
|
+
toolbelt/templates/java/README.md.j2,sha256=AVsv7giQU-oqw_rNCYhIFHvfyvNmL4WCPZ0F2sUIM2g,294
|
|
15
|
+
toolbelt/templates/java/pom.xml.j2,sha256=uIrloGB0x_i76xt55fHBrY0u-Cjsav1s8skWNVCeIwg,1596
|
|
16
|
+
toolbelt/templates/java/src/main/java/com/{{project_name}}/App.java.j2,sha256=naslGyofwCh2CuiWlqD4Qt4fEwfs1jZQffkfh9RLGMU,163
|
|
17
|
+
toolbelt/templates/java/src/test/java/com/{{project_name}}/AppTest.java.j2,sha256=dJBcmKI87J8kvkks3eznlrgcZuM1GBKWd0_qsaaZq6k,292
|
|
18
|
+
toolbelt/templates/node/.eslintrc.json.j2,sha256=r76ij7koqRHDLG8F9X96ili5u45FBk4cRKPpESs0cdo,227
|
|
19
|
+
toolbelt/templates/node/.gitignore.j2,sha256=q9CldtaXHT9iSLDS75VRCGulzsgV0CovIdFWNBQtztM,62
|
|
20
|
+
toolbelt/templates/node/.prettierrc.j2,sha256=F21YNA7dWxeDiwM--szJ_sfkgjcNbc9dXelvduinFv0,72
|
|
21
|
+
toolbelt/templates/node/LICENSE.j2,sha256=cPi2WOgT92Szay92rtTGBuw-k0i4jiS4HGRVjXg6mKc,1854
|
|
22
|
+
toolbelt/templates/node/README.md.j2,sha256=za0vAhYbr2Si6zKd6Yzv3VWLq7hu6Xyvm9oHtqW-kJE,242
|
|
23
|
+
toolbelt/templates/node/package.json.j2,sha256=21cUATMkEBqL8NnvX0cDIjzgg5N-jQuOBmMZTHYC008,464
|
|
24
|
+
toolbelt/templates/node/tsconfig.json.j2,sha256=fA_Racz6ySewM7_z0LVgIU74zQZys1mYVYFbIBB6gB0,297
|
|
25
|
+
toolbelt/templates/node/src/index.test.ts.j2,sha256=VhCNbUp4fvc7bfm24QoqXxvf3kwVdUEQrRIFHkPTCLk,172
|
|
26
|
+
toolbelt/templates/node/src/index.ts.j2,sha256=gwhklqTDxdMwn5WS-lN9Mscri3tJuI8VXtI8iWYMJNM,128
|
|
27
|
+
toolbelt/templates/python/.gitignore.j2,sha256=jModVa-BWMlQHP62UkaFMrjnTVHnNYpVhEaK5O4E5SQ,435
|
|
28
|
+
toolbelt/templates/python/.pre-commit-config.yaml.j2,sha256=U6eDE-3S-Wn8u8HcV6o_iqZvoJ2-Tn1HlHLKpPmrlXw,323
|
|
29
|
+
toolbelt/templates/python/Dockerfile.j2,sha256=sQ2oCmeW90uflZH1D3otU0fYuqTBVEzEDDbvsddNaqk,190
|
|
30
|
+
toolbelt/templates/python/LICENSE.j2,sha256=cPi2WOgT92Szay92rtTGBuw-k0i4jiS4HGRVjXg6mKc,1854
|
|
31
|
+
toolbelt/templates/python/Makefile.j2,sha256=ZO-F8zuqv005QXvfuDoIze2bjkhbmk1F1UpWdR2mtGc,214
|
|
32
|
+
toolbelt/templates/python/README.md.j2,sha256=IIrQ5Hedyy6ROnCVaRHu199MuAd2qKop97kGWsXoeqs,213
|
|
33
|
+
toolbelt/templates/python/pyproject.toml.j2,sha256=9gnuf9LSvUxHEuYL_OULscxYDiKCXCS9Uq7FzbsLjQU,532
|
|
34
|
+
toolbelt/templates/python/.github/workflows/ci.yml.j2,sha256=seK05QMvzX_YTxQdFMBlYlqNgvVe_df4wx9lNGp3KqA,611
|
|
35
|
+
toolbelt/templates/python/src/{{project_name}}/__init__.py.j2,sha256=xwAn7i-xKpi1XV4MiXZ7ZI_V-sw46Lq8x13a8MSqSGs,32
|
|
36
|
+
toolbelt/templates/python/src/{{project_name}}/main.py.j2,sha256=bZ3s_JIgJv9WxcIg1EmJG0jKV20fFwCOOARedp8uTQA,142
|
|
37
|
+
toolbelt/templates/python/tests/test_main.py.j2,sha256=GvnfrPsASxwF9x_hrY23ziBFsT8XidV_8t60ZCg2QDI,202
|
|
38
|
+
toolbelt/utils/__init__.py,sha256=074WeHxVbTQbj-mI3ZPBL0JZjPP5m7FxF9nNlrUmwug,39
|
|
39
|
+
toolbelt/utils/console.py,sha256=SaM8ewjP3IBjRx5AvWcjDi_wUoFylEiFIUPq1gHAejE,1207
|
|
40
|
+
toolbelt/utils/git.py,sha256=GCvamllyXiM8yXUEOO9ymrO_DMbXoAz0K8rBZScQM08,1544
|
|
41
|
+
toolbelt_cli-0.2.0.dist-info/licenses/LICENSE,sha256=qB843A6bxH8-nRMggSBNFKfq7fbyM9IhIKRok7y1--I,1075
|
|
42
|
+
toolbelt_cli-0.2.0.dist-info/METADATA,sha256=gcOH92KG_d3aMlS_bdWK7m-1PLSp6TZMBcReDLpy6qo,16619
|
|
43
|
+
toolbelt_cli-0.2.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
44
|
+
toolbelt_cli-0.2.0.dist-info/entry_points.txt,sha256=LGDf3smiUW3v8Ypd2vxKmY2-5HDsfmixKErjrDJXK8M,70
|
|
45
|
+
toolbelt_cli-0.2.0.dist-info/top_level.txt,sha256=P6o2NssSE16C02AmOtztlzYQGM_U-pk2VyPqEHOrnoE,9
|
|
46
|
+
toolbelt_cli-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Christopher Njihia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
toolbelt
|