|
1 | 1 | # Project Q&A |
2 | 2 |
|
3 | | -## Project Overview |
| 3 | +## Overview |
4 | 4 |
|
5 | | -**quickforge** is a modern Python project bootstrapper that creates production-ready Python projects with a single command. It solves the problem of repetitive project setup by generating complete project structures with 2025's best toolchain already configured: uv for package management, ruff for linting/formatting, basedpyright for type checking, and pytest for testing. The target users are Python developers who want to start new projects quickly without spending hours configuring build tools, CI/CD pipelines, and development environments. |
| 5 | +**quickforge** is a Python CLI that bootstraps a complete, modern Python project with one command. It solves the recurring tax of wiring up a package manager, linter, formatter, type checker, test runner, CI config, and editor settings every time you start something new. The target user is a Python developer who wants a fresh project on uv + ruff + basedpyright + pytest without spending an afternoon assembling boilerplate. |
6 | 6 |
|
7 | | -## Key Features |
8 | | - |
9 | | -### 1. One-Command Project Creation |
10 | | - |
11 | | -Run `quickforge new myproject` and get a complete, working project with: |
12 | | -- Proper package structure (src layout) |
13 | | -- All tools configured in pyproject.toml |
14 | | -- GitHub Actions CI/CD pipeline |
15 | | -- Pre-commit hooks ready to install |
16 | | -- VS Code settings optimized for the toolchain |
17 | | -- Test skeleton with pytest and coverage |
18 | | - |
19 | | -### 2. Multiple Project Types |
20 | | - |
21 | | -I support five project types tailored to different use cases: |
22 | | -- **library**: PyPI-publishable packages with src layout |
23 | | -- **cli**: Command-line tools with Typer integration |
24 | | -- **api**: FastAPI/Flask web services |
25 | | -- **app**: Standalone applications |
26 | | -- **script**: Single-file scripts with PEP 723 inline dependencies |
27 | | - |
28 | | -### 3. Interactive and Scriptable |
| 7 | +## Problem Solved |
29 | 8 |
|
30 | | -The CLI works in two modes: |
31 | | -- **Interactive**: Prompts guide you through all options with descriptions |
32 | | -- **Non-interactive**: Pass flags for CI/CD usage (`--yes` skips all prompts) |
| 9 | +Most Python scaffolding tools either ship outdated defaults (setup.py, black + isort + flake8, mypy) or require so much customization up front that you might as well configure things by hand. quickforge picks one opinionated stack — the one I'd configure manually in 2025 — and emits it correctly in seconds. It also goes the other direction: `audit` and `upgrade` modernize existing projects without trashing user-authored configuration. |
33 | 10 |
|
34 | | -### 4. Project Auditing |
| 11 | +## Target Users |
35 | 12 |
|
36 | | -The `quickforge audit` command analyzes existing projects and identifies: |
37 | | -- Legacy tooling (black, flake8, mypy) that could be modernized |
38 | | -- Missing type annotations and coverage |
39 | | -- Configuration improvements |
40 | | -- Security best practices |
| 13 | +- **Python developers starting a new project** — get a working library, CLI, FastAPI service, app, or PEP 723 script with sensible defaults already wired |
| 14 | +- **Maintainers of legacy projects** — migrate from Poetry/pip/setuptools to uv and from black/isort/flake8/mypy to ruff + basedpyright without rewriting pyproject.toml by hand |
41 | 15 |
|
42 | | -### 5. Automated Migration |
| 16 | +## Key Features |
43 | 17 |
|
44 | | -The `quickforge upgrade` command migrates projects from: |
45 | | -- Poetry/pip/pipenv/setuptools to uv |
46 | | -- black/isort/flake8 to ruff |
47 | | -- mypy to basedpyright |
| 18 | +### One-command project creation |
| 19 | +`quickforge new myproject` produces a `src/` layout package, configured `pyproject.toml`, pytest skeleton, GitHub Actions CI, pre-commit hooks, and VS Code settings. Both interactive prompts and a fully scriptable `--yes` mode are supported. |
48 | 20 |
|
49 | | -It preserves comments and formatting in existing configuration files. |
| 21 | +### Five project archetypes |
| 22 | +`library`, `cli` (Typer), `api` (FastAPI), `app`, and `script` (PEP 723 inline dependencies). Each archetype renders only the templates relevant to it, so a CLI project doesn't ship empty FastAPI scaffolding. |
50 | 23 |
|
51 | | -### 6. Feature Addition |
| 24 | +### Audit and upgrade |
| 25 | +`quickforge audit` detects the existing toolchain by signature (lock files, pyproject sections, tool-specific configs) and scores the project from 100. `quickforge upgrade` migrates Poetry/pip/pipenv/setuptools to uv and black/isort/flake8/mypy to ruff + basedpyright, while preserving inline comments via `tomlkit`. |
52 | 26 |
|
53 | | -Add optional features to existing projects with `quickforge add`: |
54 | | -- github-actions: CI/CD workflow |
55 | | -- docker: Dockerfile and docker-compose.yml |
56 | | -- docs: MkDocs with Material theme |
57 | | -- pre-commit: Git hooks configuration |
58 | | -- vscode: IDE settings and extensions |
59 | | -- devcontainer: Dev container for VS Code |
| 27 | +### Modular feature add-ons |
| 28 | +`quickforge add` injects Docker, GitHub Actions, MkDocs, pre-commit, VS Code settings, or a devcontainer into an existing project. Each feature has its own template set and is independently testable. |
60 | 29 |
|
61 | 30 | ## Technical Highlights |
62 | 31 |
|
63 | | -### Challenge: Comment-Preserving TOML Updates |
64 | | - |
65 | | -When upgrading projects, I needed to modify pyproject.toml without destroying users' carefully written comments and documentation. I solved this by using `tomlkit` instead of standard TOML libraries. tomlkit parses TOML into a document model that preserves whitespace, comments, and formatting, allowing surgical modifications. |
| 32 | +### Comment-preserving TOML edits during upgrades |
| 33 | +Standard `tomli`/`tomli-w` round-trips destroy comments and reflow whitespace, which is unacceptable when modifying a user's hand-written `pyproject.toml`. The upgrader uses `tomlkit`, which parses TOML into a document model that retains formatting, so migrations land as surgical edits rather than rewrites. See `src/quickforge/upgrader.py`. |
66 | 34 |
|
67 | | -### Challenge: Cross-Platform Path Handling |
| 35 | +### Signature-based toolchain detection |
| 36 | +The auditor identifies the active package manager, linter, formatter, and type checker by looking for `poetry.lock`, `Pipfile.lock`, `[tool.black]` sections, `.flake8` files, etc. — not by asking the user. This makes audit and upgrade work on any project without configuration. Detection logic lives in `src/quickforge/auditor.py`. |
68 | 37 |
|
69 | | -Template paths needed to work across macOS, Linux, and Windows. I used `pathlib.Path` throughout and carefully handled path separators in templates. The src layout path is dynamically computed based on project type. |
| 38 | +### Atomic generation with cleanup on failure |
| 39 | +Project creation is a linear pipeline (`validate -> create dirs -> render templates -> write files -> git init -> validate output`). Each stage tracks what it created; on failure, the generator unwinds and removes partial directories rather than leaving a half-formed project on disk. See `src/quickforge/generator.py`. |
70 | 40 |
|
71 | | -### Challenge: Atomic Project Generation |
| 41 | +### Conditional template rendering |
| 42 | +Templates are tagged by archetype and feature flags. `cli.py.j2` is rendered only for `--type cli`; the FastAPI router only for `--type api`; Docker, MkDocs, devcontainer files only when their flags are set. The generated tree contains only files the user actually asked for. |
72 | 43 |
|
73 | | -If generation fails midway, users shouldn't be left with a broken partial project. I implemented cleanup logic that removes partially created directories on failure, and validation that verifies the generated project is syntactically correct before reporting success. |
| 44 | +## Engineering Decisions |
74 | 45 |
|
75 | | -### Innovation: Detection-Based Auditing |
| 46 | +### tomlkit vs tomli for upgrades |
| 47 | +- **Constraint**: Upgrading a project must not destroy comments or formatting in `pyproject.toml` |
| 48 | +- **Options**: `tomli` + `tomli-w` (lossy round-trip), regex-based edits (fragile), `tomlkit` (round-trip preserving) |
| 49 | +- **Choice**: `tomlkit` for the upgrader's write path; `tomli` for read-only parsing where round-tripping isn't needed |
| 50 | +- **Why**: `tomlkit` keeps the user's documentation intact, which is the whole point of in-place migration |
76 | 51 |
|
77 | | -Rather than requiring users to declare their tooling, the auditor automatically detects what tools are in use by checking for lock files, configuration sections, and tool-specific files. This zero-configuration approach means it works on any Python project. |
| 52 | +### Typer vs Click vs argparse |
| 53 | +- **Constraint**: Need a CLI with subcommands, rich help, and good DX, without writing parser boilerplate |
| 54 | +- **Options**: argparse (verbose), Click (mature but decorator-heavy), Typer (Click underneath, infers from type hints) |
| 55 | +- **Choice**: Typer |
| 56 | +- **Why**: Function signatures double as the CLI definition, Rich integration is built in, and Click's ecosystem is still available underneath when needed |
78 | 57 |
|
79 | | -### Innovation: Template Condition System |
| 58 | +### Detection-based audit vs explicit declaration |
| 59 | +- **Constraint**: Need to audit arbitrary user projects, not just ones generated by this tool |
| 60 | +- **Options**: Require a `quickforge.toml` config file, infer from `pyproject.toml` alone, fingerprint by files on disk |
| 61 | +- **Choice**: File fingerprinting plus `pyproject.toml` parsing |
| 62 | +- **Why**: Zero-configuration UX; works on projects that have never heard of quickforge, including legacy setup.py / Pipfile / Poetry repos |
80 | 63 |
|
81 | | -Templates are conditionally rendered based on project configuration. For example, `cli.py.j2` is only rendered for CLI projects, while `main.py.j2` is rendered for app and API projects. This keeps the generated code minimal and relevant. |
| 64 | +### src/ layout for the generated default |
| 65 | +- **Constraint**: New projects should resist a known Python footgun (importing the in-tree package instead of the installed one during testing) |
| 66 | +- **Options**: Flat layout (simpler), src/ layout (industry standard, immune to in-tree shadowing) |
| 67 | +- **Choice**: src/ layout |
| 68 | +- **Why**: PyPA recommends it, tests run against the installed package, and IDE refactoring is more reliable |
82 | 69 |
|
83 | 70 | ## Frequently Asked Questions |
84 | 71 |
|
85 | | -### Q1: Why create another project scaffolding tool? |
86 | | - |
87 | | -I found that existing tools either generate outdated structures (using setup.py, black, mypy) or require significant configuration. I wanted a tool that embodies 2025 best practices out of the box, with zero configuration needed for the common case. quickforge generates exactly what I would set up manually for a new project. |
88 | | - |
89 | | -### Q2: Why uv instead of Poetry or pip? |
90 | | - |
91 | | -uv is 10-100x faster than pip and Poetry because it's written in Rust. It handles dependency resolution, virtual environments, and Python version management in one tool. The speed difference is noticeable on every operation, making development more pleasant. |
92 | | - |
93 | | -### Q3: Why ruff instead of black and flake8? |
94 | | - |
95 | | -ruff combines linting (flake8, pylint) and formatting (black, isort) into one tool that's 10-100x faster. Having one configuration section instead of three separate tools simplifies maintenance. ruff also has more rules and better autofix capabilities. |
96 | | - |
97 | | -### Q4: Why basedpyright instead of mypy? |
98 | | - |
99 | | -basedpyright is faster (3-5x), has better error messages, and integrates seamlessly with VS Code. It's stricter by default, which catches more bugs. The "based" fork adds additional checks beyond standard pyright. |
100 | | - |
101 | | -### Q5: Can I customize the generated templates? |
102 | | - |
103 | | -Currently, templates are bundled with the package and not user-customizable. This is intentional to ensure consistency and correctness. If you need significant customization, you can use quickforge as a starting point and modify the generated files, or fork the project. |
104 | | - |
105 | | -### Q6: Does quickforge support monorepos? |
106 | | - |
107 | | -Not currently. Each invocation creates a single project. For monorepos, I'd recommend using uv workspaces directly. This might be added in a future version. |
108 | | - |
109 | | -### Q7: How does the audit scoring work? |
110 | | - |
111 | | -The audit starts at 100 points and deducts based on findings: |
112 | | -- Critical issues: -20 points |
113 | | -- Errors: -10 points |
114 | | -- Warnings: -5 points |
115 | | -- Info: -1 point |
| 72 | +### Why another Python scaffolder? |
| 73 | +Most existing tools encode a 2018-era stack (setup.py, black, isort, flake8, mypy, pip) and treat modern tools as add-ons. quickforge inverts that: uv, ruff, basedpyright, and pytest are the defaults; legacy tools are not options. The generated project is what I'd produce manually if I had unlimited time on day one. |
116 | 74 |
|
117 | | -A score above 80 is considered healthy, 60-80 needs attention, and below 60 indicates significant technical debt. |
| 75 | +### Why uv instead of Poetry or pip? |
| 76 | +uv is written in Rust and resolves dependencies an order of magnitude faster, manages Python versions itself, and produces a portable `uv.lock`. It also accepts the same `pyproject.toml` standards that Poetry helped establish, so the migration cost is small. |
118 | 77 |
|
119 | | -### Q8: What happens if the upgrade fails? |
| 78 | +### Why ruff instead of black + isort + flake8? |
| 79 | +One tool replaces three, with a single config block in `pyproject.toml` and substantially faster runs. The autofix surface is wider, and rule coverage is broader than the combined set it replaces. |
120 | 80 |
|
121 | | -Before making any changes, `quickforge upgrade` creates a timestamped backup of all configuration files in `.quickforge_backup_TIMESTAMP/`. If something goes wrong, you can restore from this backup. The upgrade also supports `--dry-run` to preview changes without applying them. |
| 81 | +### Why basedpyright instead of mypy? |
| 82 | +basedpyright (a fork of pyright) is significantly faster, stricter by default, and produces clearer error messages with better editor integration. For new projects starting fresh, the stricter defaults catch bugs the day they're written. |
122 | 83 |
|
123 | | -### Q9: Why is the coverage requirement set at 80%? |
| 84 | +### Can I customize the generated templates? |
| 85 | +Not at the moment — templates are bundled with the package. The intent is that the generated output is opinionated. If you need a different stack, fork the project or modify the generated files after creation. |
124 | 86 |
|
125 | | -I believe 80% is a pragmatic target that encourages good testing habits without becoming burdensome. It allows for untested edge cases and configuration code while ensuring core functionality is covered. The threshold is configurable in the generated pyproject.toml. |
| 87 | +### Does it support monorepos? |
| 88 | +No. Each `quickforge new` invocation produces a single project. For monorepos, use uv workspaces directly on top of the generated projects. |
126 | 89 |
|
127 | | -### Q10: How do I contribute to quickforge? |
| 90 | +### How does the audit score work? |
| 91 | +The auditor starts at 100 and deducts per finding by severity (critical -20, error -10, warning -5, info -1). 80+ is healthy, 60–80 needs attention, under 60 indicates real technical debt. The threshold is configurable. |
128 | 92 |
|
129 | | -The project is open source on GitHub. To contribute: |
130 | | -1. Clone the repository |
131 | | -2. Run `uv sync --extra dev` to install dependencies |
132 | | -3. Run `uv run pre-commit install` to set up hooks |
133 | | -4. Make changes and ensure `uv run pytest` passes |
134 | | -5. Submit a pull request |
| 93 | +### What happens if an upgrade fails partway? |
| 94 | +`quickforge upgrade` writes a timestamped backup to `.quickforge_backup_<timestamp>/` before changing anything. `--dry-run` prints the planned changes without writing. If the migration aborts mid-way, the backup is the recovery path. |
135 | 95 |
|
136 | | -I welcome contributions for new project types, additional features, and bug fixes. |
| 96 | +### Why 80% as the default coverage gate? |
| 97 | +80% is the inflection point where coverage starts catching real regressions without becoming theater. It leaves room for untested edge cases and config glue while requiring meaningful tests for core paths. The number lives in the generated `pyproject.toml` and is easy to change. |
0 commit comments