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.
Files changed (46) hide show
  1. toolbelt/__init__.py +4 -0
  2. toolbelt/cli.py +46 -0
  3. toolbelt/commands/__init__.py +1 -0
  4. toolbelt/commands/audit.py +63 -0
  5. toolbelt/commands/commit.py +84 -0
  6. toolbelt/commands/new.py +116 -0
  7. toolbelt/commands/version.py +47 -0
  8. toolbelt/services/__init__.py +1 -0
  9. toolbelt/services/auditor.py +295 -0
  10. toolbelt/services/llm.py +105 -0
  11. toolbelt/services/scaffolder.py +91 -0
  12. toolbelt/templates/java/.gitignore.j2 +7 -0
  13. toolbelt/templates/java/LICENSE.j2 +39 -0
  14. toolbelt/templates/java/README.md.j2 +26 -0
  15. toolbelt/templates/java/pom.xml.j2 +53 -0
  16. toolbelt/templates/java/src/main/java/com/{{project_name}}/App.java.j2 +7 -0
  17. toolbelt/templates/java/src/test/java/com/{{project_name}}/AppTest.java.j2 +13 -0
  18. toolbelt/templates/node/.eslintrc.json.j2 +10 -0
  19. toolbelt/templates/node/.gitignore.j2 +7 -0
  20. toolbelt/templates/node/.prettierrc.j2 +5 -0
  21. toolbelt/templates/node/LICENSE.j2 +39 -0
  22. toolbelt/templates/node/README.md.j2 +26 -0
  23. toolbelt/templates/node/package.json.j2 +20 -0
  24. toolbelt/templates/node/src/index.test.ts.j2 +6 -0
  25. toolbelt/templates/node/src/index.ts.j2 +7 -0
  26. toolbelt/templates/node/tsconfig.json.j2 +14 -0
  27. toolbelt/templates/python/.github/workflows/ci.yml.j2 +28 -0
  28. toolbelt/templates/python/.gitignore.j2 +42 -0
  29. toolbelt/templates/python/.pre-commit-config.yaml.j2 +13 -0
  30. toolbelt/templates/python/Dockerfile.j2 +10 -0
  31. toolbelt/templates/python/LICENSE.j2 +39 -0
  32. toolbelt/templates/python/Makefile.j2 +17 -0
  33. toolbelt/templates/python/README.md.j2 +19 -0
  34. toolbelt/templates/python/pyproject.toml.j2 +28 -0
  35. toolbelt/templates/python/src/{{project_name}}/__init__.py.j2 +1 -0
  36. toolbelt/templates/python/src/{{project_name}}/main.py.j2 +7 -0
  37. toolbelt/templates/python/tests/test_main.py.j2 +8 -0
  38. toolbelt/utils/__init__.py +1 -0
  39. toolbelt/utils/console.py +43 -0
  40. toolbelt/utils/git.py +54 -0
  41. toolbelt_cli-0.2.0.dist-info/METADATA +382 -0
  42. toolbelt_cli-0.2.0.dist-info/RECORD +46 -0
  43. toolbelt_cli-0.2.0.dist-info/WHEEL +5 -0
  44. toolbelt_cli-0.2.0.dist-info/entry_points.txt +3 -0
  45. toolbelt_cli-0.2.0.dist-info/licenses/LICENSE +21 -0
  46. toolbelt_cli-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,105 @@
1
+ """Anthropic Claude LLM wrapper with rule-based fallback commit generator."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import re
7
+ from typing import Any
8
+
9
+ import anthropic
10
+
11
+ DEFAULT_MODEL = "claude-sonnet-4"
12
+
13
+ SYSTEM_PROMPT = (
14
+ "You generate Conventional Commits messages. Given a git diff, output ONLY "
15
+ "the commit message. Format: <type>(<scope>): <subject>\n\n<body if needed>. "
16
+ "Types: feat, fix, docs, style, refactor, test, chore. Max 72 chars for subject."
17
+ )
18
+
19
+
20
+ class CommitMessageGenerator:
21
+ """Generate Conventional Commit messages from a git diff."""
22
+
23
+ def __init__(self, api_key: str | None = None) -> None:
24
+ self.api_key = api_key or os.getenv("ANTHROPIC_API_KEY")
25
+
26
+ def generate(
27
+ self,
28
+ diff: str,
29
+ commit_type: str | None = None,
30
+ scope: str | None = None,
31
+ ) -> str:
32
+ """Generate a conventional commit message."""
33
+ if self.api_key and self.api_key.strip():
34
+ return self._call_claude(diff, commit_type, scope)
35
+ return self._fallback(diff, commit_type, scope)
36
+
37
+ def _call_claude(
38
+ self,
39
+ diff: str,
40
+ commit_type: str | None,
41
+ scope: str | None,
42
+ ) -> str:
43
+ client = anthropic.Anthropic(api_key=self.api_key)
44
+ user_prompt = diff
45
+ instructions = []
46
+ if commit_type:
47
+ instructions.append(f"Commit type MUST be: {commit_type}")
48
+ if scope:
49
+ instructions.append(f"Commit scope MUST be: {scope}")
50
+ if instructions:
51
+ user_prompt = f"[{'; '.join(instructions)}]\n\n{diff}"
52
+
53
+ response = client.messages.create(
54
+ model=DEFAULT_MODEL,
55
+ max_tokens=256,
56
+ system=SYSTEM_PROMPT,
57
+ messages=[{"role": "user", "content": user_prompt}],
58
+ )
59
+
60
+ content: Any = response.content
61
+ if isinstance(content, list) and content:
62
+ item = content[0]
63
+ if hasattr(item, "text"):
64
+ return str(item.text).strip()
65
+ if isinstance(item, dict) and "text" in item:
66
+ return str(item["text"]).strip()
67
+ return str(item).strip()
68
+ if hasattr(content, "text"):
69
+ return str(content.text).strip()
70
+ return str(content).strip()
71
+
72
+ def _fallback(
73
+ self,
74
+ diff: str,
75
+ commit_type: str | None = None,
76
+ scope: str | None = None,
77
+ ) -> str:
78
+ """Rule-based generator when no Anthropic API key is available."""
79
+ lines = diff.splitlines()
80
+ added = sum(
81
+ 1 for line in lines if line.startswith("+") and not line.startswith("+++")
82
+ )
83
+ removed = sum(
84
+ 1 for line in lines if line.startswith("-") and not line.startswith("---")
85
+ )
86
+ files = set(re.findall(r"^\+\+\+ b/(.+)$", diff, flags=re.MULTILINE))
87
+ files_count = max(len(files), 1)
88
+
89
+ if commit_type:
90
+ guess_type = commit_type.strip()
91
+ else:
92
+ diff_lower = diff.lower()
93
+ if any(term in diff_lower for term in ["test", "tests/"]):
94
+ guess_type = "test"
95
+ elif any(term in diff_lower for term in ["doc", "docs/", "readme"]):
96
+ guess_type = "docs"
97
+ elif any(term in diff_lower for term in [".css", ".scss", "style"]):
98
+ guess_type = "style"
99
+ else:
100
+ guess_type = "chore"
101
+
102
+ scope_part = f"({scope.strip()})" if scope and scope.strip() else ""
103
+ file_label = "file" if files_count == 1 else "files"
104
+ stats = f"(+{added}/-{removed} lines)"
105
+ return f"{guess_type}{scope_part}: update {files_count} {file_label} {stats}"
@@ -0,0 +1,91 @@
1
+ """Project scaffolding service using Jinja2 templates."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ import jinja2
10
+
11
+ TEMPLATE_ROOT = Path(__file__).parent.parent / "templates"
12
+
13
+
14
+ class Scaffolder:
15
+ """Render project templates and write files to disk."""
16
+
17
+ def __init__(
18
+ self,
19
+ project_type: str,
20
+ project_name: str,
21
+ author: str,
22
+ license: str,
23
+ include_ci: bool,
24
+ include_docker: bool,
25
+ destination: Path,
26
+ ) -> None:
27
+ self.project_type = project_type.lower()
28
+ self.project_name = project_name
29
+ self.author = author
30
+ self.license = license
31
+ self.include_ci = include_ci
32
+ self.include_docker = include_docker
33
+ self.destination = destination
34
+
35
+ tmpl_dir = TEMPLATE_ROOT / self.project_type
36
+ self.env = jinja2.Environment(
37
+ loader=jinja2.FileSystemLoader(str(tmpl_dir)),
38
+ autoescape=False,
39
+ keep_trailing_newline=True,
40
+ )
41
+ self.context: dict[str, Any] = {
42
+ "project_name": project_name,
43
+ "author": author,
44
+ "license": license,
45
+ "include_ci": include_ci,
46
+ "include_docker": include_docker,
47
+ }
48
+
49
+ def _render_template(self, template_posix_path: str) -> str:
50
+ template = self.env.get_template(template_posix_path)
51
+ return template.render(**self.context)
52
+
53
+ def _write_file(self, relative_path: Path, content: str) -> None:
54
+ target = self.destination / relative_path
55
+ target.parent.mkdir(parents=True, exist_ok=True)
56
+ target.write_text(content, encoding="utf-8")
57
+
58
+ def run(self) -> None:
59
+ """Run the scaffolding process."""
60
+ tmpl_root = TEMPLATE_ROOT / self.project_type
61
+ if not tmpl_root.exists():
62
+ raise FileNotFoundError(f"Template directory not found: {tmpl_root}")
63
+
64
+ for root, _, files in os.walk(tmpl_root):
65
+ for fname in files:
66
+ full_src = Path(root) / fname
67
+ rel_src = full_src.relative_to(tmpl_root)
68
+ rel_posix = rel_src.as_posix()
69
+
70
+ # Filter optional files
71
+ if "ci.yml" in rel_posix and not self.include_ci:
72
+ continue
73
+ if "Dockerfile" in rel_posix and not self.include_docker:
74
+ continue
75
+ if "LICENSE" in rel_posix and self.license.lower() == "none":
76
+ continue
77
+
78
+ # Render content
79
+ if fname.endswith(".j2"):
80
+ content = self._render_template(rel_posix)
81
+ else:
82
+ content = full_src.read_text(encoding="utf-8")
83
+
84
+ # Resolve path placeholders like {{project_name}}
85
+ rendered_rel_path = rel_posix.replace(
86
+ "{{project_name}}", self.project_name
87
+ )
88
+ if rendered_rel_path.endswith(".j2"):
89
+ rendered_rel_path = rendered_rel_path[:-3]
90
+
91
+ self._write_file(Path(rendered_rel_path), content)
@@ -0,0 +1,7 @@
1
+ target/
2
+ *.class
3
+ *.jar
4
+ *.war
5
+ *.ear
6
+ *.iml
7
+ .idea/
@@ -0,0 +1,39 @@
1
+ {% if license == "Apache-2.0" %} Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Copyright (c) {{author}}
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
18
+ {% else %}MIT License
19
+
20
+ Copyright (c) {{author}}
21
+
22
+ Permission is hereby granted, free of charge, to any person obtaining a copy
23
+ of this software and associated documentation files (the "Software"), to deal
24
+ in the Software without restriction, including without limitation the rights
25
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
26
+ copies of the Software, and to permit persons to whom the Software is
27
+ furnished to do so, subject to the following conditions:
28
+
29
+ The above copyright notice and this permission notice shall be included in all
30
+ copies or substantial portions of the Software.
31
+
32
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38
+ SOFTWARE.
39
+ {% endif %}
@@ -0,0 +1,26 @@
1
+ # {{project_name}}
2
+
3
+ A Java Maven project scaffolded by Toolbelt CLI.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ mvn clean install -DskipTests
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```bash
14
+ mvn clean package
15
+ java -cp target/{{project_name}}-0.1.0.jar com.{{project_name}}.App
16
+ ```
17
+
18
+ ## Test
19
+
20
+ ```bash
21
+ mvn test
22
+ ```
23
+
24
+ ## License
25
+
26
+ {{license}}
@@ -0,0 +1,53 @@
1
+ <project xmlns="http://maven.apache.org/POM/4.0.0"
2
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
4
+ https://maven.apache.org/xsd/maven-4.0.0.xsd">
5
+ <modelVersion>4.0.0</modelVersion>
6
+
7
+ <groupId>com.{{project_name}}</groupId>
8
+ <artifactId>{{project_name}}</artifactId>
9
+ <version>0.1.0</version>
10
+ <name>{{project_name}}</name>
11
+ <description>Java project scaffolded by Toolbelt CLI</description>
12
+ <licenses>
13
+ <license>
14
+ <name>{{license}}</name>
15
+ <url>https://opensource.org/licenses/{{license}}</url>
16
+ </license>
17
+ </licenses>
18
+
19
+ <properties>
20
+ <maven.compiler.source>17</maven.compiler.source>
21
+ <maven.compiler.target>17</maven.compiler.target>
22
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
23
+ </properties>
24
+
25
+ <dependencies>
26
+ <dependency>
27
+ <groupId>org.junit.jupiter</groupId>
28
+ <artifactId>junit-jupiter</artifactId>
29
+ <version>5.10.0</version>
30
+ <scope>test</scope>
31
+ </dependency>
32
+ </dependencies>
33
+
34
+ <build>
35
+ <plugins>
36
+ <plugin>
37
+ <groupId>org.apache.maven.plugins</groupId>
38
+ <artifactId>maven-compiler-plugin</artifactId>
39
+ <version>3.10.1</version>
40
+ <configuration>
41
+ <source>17</source>
42
+ <target>17</target>
43
+ </configuration>
44
+ </plugin>
45
+ <plugin>
46
+ <groupId>org.apache.maven.plugins</groupId>
47
+ <artifactId>maven-surefire-plugin</artifactId>
48
+ <version>3.1.2</version>
49
+ </plugin>
50
+ </plugins>
51
+ </build>
52
+
53
+ </project>
@@ -0,0 +1,7 @@
1
+ package com.{{project_name}};
2
+
3
+ public class App {
4
+ public static void main(String[] args) {
5
+ System.out.println("Hello from {{project_name}}!");
6
+ }
7
+ }
@@ -0,0 +1,13 @@
1
+ package com.{{project_name}};
2
+
3
+ import org.junit.jupiter.api.Test;
4
+ import static org.junit.jupiter.api.Assertions.*;
5
+
6
+ class AppTest {
7
+
8
+ @Test
9
+ void testMainOutputsGreeting() {
10
+ // Simple sanity test – in a real project you would capture stdout
11
+ assertTrue(true);
12
+ }
13
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "env": {
3
+ "node": true,
4
+ "es2022": true
5
+ },
6
+ "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
7
+ "parser": "@typescript-eslint/parser",
8
+ "plugins": ["@typescript-eslint"],
9
+ "rules": {}
10
+ }
@@ -0,0 +1,7 @@
1
+ node_modules/
2
+ dist/
3
+ .env
4
+ .env.local
5
+ .DS_Store
6
+ coverage/
7
+ *.log
@@ -0,0 +1,5 @@
1
+ {
2
+ "singleQuote": true,
3
+ "trailingComma": "all",
4
+ "printWidth": 80
5
+ }
@@ -0,0 +1,39 @@
1
+ {% if license == "Apache-2.0" %} Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Copyright (c) {{author}}
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
18
+ {% else %}MIT License
19
+
20
+ Copyright (c) {{author}}
21
+
22
+ Permission is hereby granted, free of charge, to any person obtaining a copy
23
+ of this software and associated documentation files (the "Software"), to deal
24
+ in the Software without restriction, including without limitation the rights
25
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
26
+ copies of the Software, and to permit persons to whom the Software is
27
+ furnished to do so, subject to the following conditions:
28
+
29
+ The above copyright notice and this permission notice shall be included in all
30
+ copies or substantial portions of the Software.
31
+
32
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38
+ SOFTWARE.
39
+ {% endif %}
@@ -0,0 +1,26 @@
1
+ # {{project_name}}
2
+
3
+ A modern Node.js and TypeScript project scaffolded by Toolbelt CLI.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```bash
14
+ npm run build
15
+ node dist/index.js
16
+ ```
17
+
18
+ ## Test
19
+
20
+ ```bash
21
+ npm test
22
+ ```
23
+
24
+ ## License
25
+
26
+ {{license}}
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "{{project_name}}",
3
+ "version": "0.1.0",
4
+ "description": "{{project_name}} – scaffolded by Toolbelt CLI",
5
+ "main": "dist/index.js",
6
+ "scripts": {
7
+ "build": "tsc",
8
+ "test": "vitest",
9
+ "lint": "eslint . --ext .ts",
10
+ "format": "prettier --write ."
11
+ },
12
+ "author": "{{author}}",
13
+ "license": "{{license}}",
14
+ "devDependencies": {
15
+ "typescript": "^5.4.0",
16
+ "eslint": "^9.0.0",
17
+ "prettier": "^3.0.0",
18
+ "vitest": "^2.0.0"
19
+ }
20
+ }
@@ -0,0 +1,6 @@
1
+ import { expect, test } from "vitest";
2
+ import { hello } from "./index";
3
+
4
+ test("hello returns greeting", () => {
5
+ expect(hello()).toBe("Hello from {{project_name}}!");
6
+ });
@@ -0,0 +1,7 @@
1
+ export function hello(): string {
2
+ return "Hello from {{project_name}}!";
3
+ }
4
+
5
+ if (import.meta.main) {
6
+ console.log(hello());
7
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Node",
6
+ "outDir": "dist",
7
+ "rootDir": "src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true
12
+ },
13
+ "include": ["src"]
14
+ }
@@ -0,0 +1,28 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - name: Set up Python
15
+ uses: actions/setup-python@v5
16
+ with:
17
+ python-version: "3.11"
18
+ - name: Install dependencies
19
+ run: |
20
+ python -m pip install --upgrade pip
21
+ pip install -e .
22
+ pip install ruff mypy pytest
23
+ - name: Lint with ruff
24
+ run: ruff check .
25
+ - name: Type check with mypy
26
+ run: mypy .
27
+ - name: Run tests
28
+ run: pytest --cov=src
@@ -0,0 +1,42 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ Dist/
14
+ Downloads/
15
+ Eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ *.egg-info/
23
+ .installed.cfg
24
+ *.egg
25
+
26
+ # PyInstaller
27
+ *.manifest
28
+ *.spec
29
+
30
+ # Unit test / coverage reports
31
+ htmlcov/
32
+ .tox/
33
+ .nox/
34
+ .coverage
35
+ .coverage.*
36
+ .cache
37
+ nosetests.xml
38
+ coverage.xml
39
+ *.cover
40
+ *.py,cover
41
+ .hypothesis/
42
+ .pytest_cache/
@@ -0,0 +1,13 @@
1
+ repos:
2
+ - repo: https://github.com/charliermarsh/ruff-pre-commit
3
+ rev: v0.1.0
4
+ hooks:
5
+ - id: ruff
6
+ - repo: https://github.com/pre-commit/mirrors-mypy
7
+ rev: v0.9.0
8
+ hooks:
9
+ - id: mypy
10
+ - repo: https://github.com/pre-commit/pre-commit-hooks
11
+ rev: v4.4.0
12
+ hooks:
13
+ - id: trailing-whitespace
@@ -0,0 +1,10 @@
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+ COPY . /app
5
+
6
+ RUN pip install --upgrade pip && \
7
+ pip install -e . && \
8
+ pip install ruff mypy pytest
9
+
10
+ CMD ["python", "-m", "{{project_name}}.main"]
@@ -0,0 +1,39 @@
1
+ {% if license == "Apache-2.0" %} Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Copyright (c) {{author}}
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
18
+ {% else %}MIT License
19
+
20
+ Copyright (c) {{author}}
21
+
22
+ Permission is hereby granted, free of charge, to any person obtaining a copy
23
+ of this software and associated documentation files (the "Software"), to deal
24
+ in the Software without restriction, including without limitation the rights
25
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
26
+ copies of the Software, and to permit persons to whom the Software is
27
+ furnished to do so, subject to the following conditions:
28
+
29
+ The above copyright notice and this permission notice shall be included in all
30
+ copies or substantial portions of the Software.
31
+
32
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38
+ SOFTWARE.
39
+ {% endif %}
@@ -0,0 +1,17 @@
1
+ .PHONY: install test lint format clean
2
+
3
+ install:
4
+ pip install -e ".[dev]"
5
+
6
+ test:
7
+ pytest
8
+
9
+ lint:
10
+ ruff check .
11
+ mypy src tests
12
+
13
+ format:
14
+ ruff format .
15
+
16
+ clean:
17
+ rm -rf build dist *.egg-info .pytest_cache .mypy_cache
@@ -0,0 +1,19 @@
1
+ # {{project_name}}
2
+
3
+ {{project_name}} – a Python project scaffolded by Toolbelt CLI.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install -e .
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```bash
14
+ python -m {{project_name}}.main
15
+ ```
16
+
17
+ ## License
18
+
19
+ {{license}}