Skip to content

refactor(template): overall improvements#8

Merged
hasansezertasan merged 4 commits intomainfrom
refactor/template-improvements
Dec 15, 2025
Merged

refactor(template): overall improvements#8
hasansezertasan merged 4 commits intomainfrom
refactor/template-improvements

Conversation

@hasansezertasan
Copy link
Owner

@hasansezertasan hasansezertasan commented Dec 14, 2025

High-level PR Summary

This PR introduces major architectural improvements to a Python project template (Copier-based), adding extensive configurability through conditional includes. The template now supports optional components like CLI (typer), web (fastapi), TUI (textual), and GUI (tkinter) interfaces, along with configurable dependency management (Renovate/Dependabot), release automation (release-please/release-it/release-drafter), and optional tools like Commitizen, Poe, Pants, Trunk, and mise. The refactor separates concerns by creating a new __metadata__.py module, restructures configuration handling to support pydantic-settings, enhances documentation structure with new MkDocs pages, and adds comprehensive testing fixtures. The example project has been removed in favor of an expanded .example-input.yml configuration file, and new agent-oriented documentation (AGENTS.md, CLAUDE.md) has been added for AI code assistants.

⏱️ Estimated Review Time: 1-3 hours

💡 Review Order Suggestion
Order File Path
1 copier.yml
2 README.md
3 .example-input.yml
4 AGENTS.md
5 CLAUDE.md
6 .github/workflows/opencode.yml
7 template/pyproject.toml.jinja
8 template/src/{{github_repo_name}}/__metadata__.py.jinja
9 template/src/{{github_repo_name}}/config.py.jinja
10 template/src/{{github_repo_name}}/{% if include_cli %}cli.py{% endif %}.jinja
11 template/src/{{github_repo_name}}/{% if include_web %}web.py{% endif %}.jinja
12 template/src/{{github_repo_name}}/{% if include_tui %}tui.py{% endif %}.jinja
13 template/src/{{github_repo_name}}/{% if include_gui %}gui.py{% endif %}.jinja
14 template/docs/index.md.jinja
15 template/docs/installation.md.jinja
16 template/docs/usage.md.jinja
17 template/docs/modules.md.jinja
18 template/README.md.jinja
19 template/mkdocs.yml.jinja
20 template/.github/workflows/{% if release_automation == "release-drafter" %}release-drafter.yml{% endif %}.jinja
21 template/.github/workflows/{% if release_automation == "release-please" %}release-please.yml{% endif %}.jinja
22 template/.github/{% if dependency_management == 'dependabot' %}dependabot.yml{% endif %}.jinja
23 template/.github/{% if dependency_management == 'renovate' %}renovate.json{% endif %}.jinja
24 template/.pre-commit-config.yaml.jinja
25 template/tests/{% if include_cli %}test_cli.py{% endif %}.jinja
26 template/tests/{% if include_web %}test_web.py{% endif %}.jinja
27 template/tests/{% if include_tui %}test_tui.py{% endif %}.jinja
28 template/tests/{% if include_gui %}test_gui.py{% endif %}.jinja
29 template/.vscode/launch.json.jinja
30 template/.zed/debug.json.jinja
31 template/.editorconfig
32 template/.markdownlint-cli2.jsonc
33 template/.devcontainer/docker-compose.dbeaver.yml
34 template/.devcontainer/docker-compose.vpn.yml
35 template/examples/simple/main.py
36 template/examples/simple/README.md
37 template/examples/advanced/main.py
38 template/examples/advanced/README.md
39 template/{% if include_mise %}mise.toml{% endif %}.jinja
40 template/{% if include_citation %}CITATION.cff{% endif %}.jinja

Need help? Join our Discord

@hasansezertasan hasansezertasan self-assigned this Dec 14, 2025
@hasansezertasan hasansezertasan added the enhancement New feature or request label Dec 14, 2025
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Dec 14, 2025

Reviewer's Guide

Refactors the Copier template into a more modular, option-driven scaffold with feature toggles for CLI/web/GUI/TUI, configuration and tooling choices, and aligns the generated project’s config, docs, CI/CD, and tests with these options while adding new TUI functionality and dev tooling integrations.

Sequence diagram for CLI commands dispatching to GUI,TUI,Web

sequenceDiagram
    actor User
    participant TyperApp as Typer_app
    participant CLI as cli_module
    participant GUI as gui_main
    participant TUI as tui_main
    participant Web as web_app
    participant Uvicorn as uvicorn_server

    User->>TyperApp: run "{{github_repo_name}} gui"
    TyperApp->>CLI: invoke gui()
    CLI->>GUI: main()
    GUI-->>User: show_window

    User->>TyperApp: run "{{github_repo_name}} interactive"
    TyperApp->>CLI: invoke interactive()
    CLI->>TUI: main(show_tui=True)
    TUI->>TUI: build_info_message()
    TUI->>TUI: _display_tui(message)
    TUI-->>User: interactive_terminal_ui

    User->>TyperApp: run "{{github_repo_name}} web"
    TyperApp->>CLI: invoke web()
    CLI->>Web: import app
    CLI->>Uvicorn: run(app, host, port)
    Uvicorn-->>User: serve_http_api
Loading

Class diagram for new Settings and TUI components

classDiagram
    class Settings {
        <<pydantic_settings>>
        +SettingsConfigDict model_config
        +bool debug
        +str log_level
        +Path config_dir
    }

    class BaseSettings {
    }

    Settings --|> BaseSettings

    class TuiDisplayError {
        <<exception>>
        +TuiDisplayError(message)
    }

    class TuiModule {
        <<module>>
        +build_info_message() str
        +_display_tui(message) None
        +main(show_tui) int
    }

    class InfoApp {
        <<TextualApp>>
        +str CSS
        +list BINDINGS
        +compose() ComposeResult
        +on_mount() None
        +action_quit() None
    }

    class App {
    }

    InfoApp --|> App

    TuiModule ..> TuiDisplayError : raises
    TuiModule ..> InfoApp : instantiates
    TuiModule ..> Settings : uses indirect_config

    class LoggingSetup {
        <<module>>
        +setup_logger() Logger
    }

    class Logger {
    }

    LoggingSetup ..> Logger : returns
    TuiModule ..> Logger : logs

    class Metadata {
        <<module>>
        +str PROJECT_NAME
    }

    TuiModule ..> Metadata : reads_PROJECT_NAME
    Settings ..> Metadata : reads_PROJECT_NAME

    class Dirs {
        <<module>>
        +Path ROOT_FOLDER_PATH
        +Path LOG_FILE_PATH
    }

    LoggingSetup ..> Dirs : uses_paths
Loading

Flow diagram for template options driving generated components

flowchart TD
    subgraph CopierTemplate
        CY[copier_yml]
        Answers[User_answers]
    end

    CY --> Answers

    subgraph FeatureToggles
        include_cli
        include_web
        include_gui
        include_tui
        use_pydantic_settings
        include_changelog
        include_contributing
        include_code_of_conduct
        include_citation
        include_pants
        include_codecov
        include_mise
        include_trunk
        include_poe
        include_commitizen
        use_precommit
        release_automation
        dependency_management
    end

    Answers --> FeatureToggles

    subgraph GeneratedCode
        subgraph AppLayers
            CLIFile[cli_py_jinja]
            WebFile[web_py_jinja]
            GUIFile[gui_py_jinja]
            TUIFile[tui_py_jinja]
            MainFile[__main__py_jinja]
            MetadataFile[__metadata__py_jinja]
            DirsFile[dirs_py_jinja]
            ConfigFile[config_py_jinja]
        end

        subgraph Tests
            TestCLI[test_cli_py_jinja]
            TestWeb[test_web_py_jinja]
            TestGUI[test_gui_py_jinja]
            TestTUI[test_tui_py_jinja]
        end

        subgraph Docs
            Mkdocs[mkdocs_yml_jinja]
            DocsIndex[docs_index_md_jinja]
            DocsInstall[docs_installation_md_jinja]
            DocsUsage[docs_usage_md_jinja]
            DocsModules[docs_modules_md_jinja]
            ProjectReadme[README_md_jinja]
            Changelog[CHANGELOG_md_jinja]
            Contributing[CONTRIBUTING_md_jinja]
            Citation[CITATION_cff_jinja]
        end

        subgraph ToolingConfigs
            Pyproject[pyproject_toml_jinja]
            PreCommit[pre_commit_config_yaml_jinja]
            CDWorkflow[cd_yml_jinja]
            OpencodeWorkflow[opencode_yml]
            ReleaseDrafter[release_drafter_yml_jinja]
            ReleasePlease[release_please_config_json_jinja]
            Dependabot[dependabot_yml_jinja]
            Renovate[renovate_json]
            Mise[mise_toml_jinja]
            Pants[pants_toml]
            Trunk[trunk_yaml]
            Codecov[codecov_yml]
            PoeConfig[tool_poe_in_pyproject]
            CommitizenConfig[tool_commitizen_in_pyproject]
        end
    end

    include_cli --> CLIFile
    include_cli --> TestCLI
    include_cli --> MainFile
    include_cli --> Pyproject

    include_web --> WebFile
    include_web --> TestWeb
    include_web --> Pyproject

    include_gui --> GUIFile
    include_gui --> TestGUI

    include_tui --> TUIFile
    include_tui --> TestTUI
    include_tui --> CLIFile

    use_pydantic_settings --> ConfigFile
    use_pydantic_settings --> Pyproject

    include_changelog --> Changelog
    include_changelog --> ProjectReadme
    include_changelog --> CommitizenConfig

    include_contributing --> Contributing

    include_code_of_conduct --> DocsIndex

    include_citation --> Citation

    include_mise --> Mise

    include_trunk --> Trunk

    include_pants --> Pants

    include_codecov --> Codecov

    include_poe --> PoeConfig

    include_commitizen --> CommitizenConfig

    use_precommit --> PreCommit

    release_automation --> ReleaseDrafter
    release_automation --> ReleasePlease

    dependency_management --> Dependabot
    dependency_management --> Renovate

    MetadataFile --> DirsFile
    MetadataFile --> CLIFile
    MetadataFile --> WebFile
    MetadataFile --> GUIFile
    MetadataFile --> TUIFile

    DirsFile --> Pyproject
Loading

File-Level Changes

Change Details Files
Make template features optional and driven by new Copier inputs (CLI/web/GUI/TUI, tooling, docs, automation).
  • Extend copier.yml with many boolean/enum options (include_cli/web/gui/tui, use_pydantic_settings, release/dependency tooling, docs and infra toggles).
  • Gate template files and filenames with Jinja conditionals so components like CLI, web app, GUI, TUI, changelog, contributing, citation, Pants, Codecov, mise, Trunk, Poe, Commitizen, Dependabot, release-please, and release-drafter are included only when selected.
  • Update README.md and .example-input.yml to document and provide defaults for all new options, and switch example to a minimal configuration by default.
copier.yml
README.md
template/README.md.jinja
template/CHANGELOG.md.jinja
template/CONTRIBUTING.md.jinja
template/CITATION.cff.jinja
template/.github/ISSUE_TEMPLATE/config.yml.jinja
.example-input.yml
template/.github/dependabot.yml.jinja
template/.github/release-drafter.yml.jinja
template/release-please-config.json.jinja
Rework pyproject template to support optional extras, scripts, and tooling based on selected features.
  • Expand keywords and classifiers with richer metadata and conditional entries for CLI, web, TUI, and tooling ecosystems.
  • Introduce optional-dependencies sections keyed by feature flags (cli/web/tui/config) and an aggregate 'all' extra; update dev dependency-group to use the 'all' extra.
  • Conditionally configure project.urls, project.scripts, and project.gui-scripts for changelog, docs site, CLI/GUI/TUI/Web entry points.
  • Make Commitizen and Poe configuration optional and controlled by include_commitizen/include_poe, and wire Commitizen’s changelog behavior to include_changelog.
  • Adjust tox env_list to include feature-specific environments and switch test env extras to use the consolidated 'all' extra; add docs-build/docs-server tox envs and make CLI runner env conditional.
  • Add pyproject metadata tweaks: new style dependencies, version bumps (ruff, ty), optional tool dependencies, package=true in hatch, and pyright configuration (exclude venv, pythonVersion, venv settings).
template/pyproject.toml.jinja
Restructure runtime package layout: introduce metadata/dirs modules, optional pydantic settings, and align all components to use them.
  • Replace the former config module with an optional pydantic-settings-based Settings class (behind use_pydantic_settings) that manages env-based configuration and paths.
  • Introduce a new dirs module (from example/config.py) to centralize PROJECT_NAME, root directory, log and config paths using platform-aware utilities, and switch logging_setup to consume it.
  • Create a metadata module for a single PROJECT_NAME constant and update CLI, GUI, web app, TUI, and tests to use it instead of the old config constant.
  • Add simple init, core, utils, and test package stubs to the template to give generated projects a more complete starting structure.
template/src/{{github_repo_name}}/config.py.jinja
template/src/{{github_repo_name}}/dirs.py.jinja
template/src/{{github_repo_name}}/__metadata__.py.jinja
template/src/{{github_repo_name}}/logging_setup.py.jinja
template/src/{{github_repo_name}}/__init__.py
template/src/{{github_repo_name}}/core.py
template/src/{{github_repo_name}}/utils.py
template/tests/__init__.py
Enhance application frontends (CLI, web, GUI) and introduce a new TUI frontend with corresponding tests and docs.
  • Rename FastAPI stub from app.py to web.py gated on include_web, update imports accordingly, and keep version/info endpoints behavior intact.
  • Make CLI, GUI, and web entry modules conditional in filenames, wire main to run CLI only when included, and extend CLI with subcommands to launch TUI, GUI, and web server when those features are enabled.
  • Add a new TUI module using Textual that can display project info or fallback to stdout, with error handling via a custom exception and a main() entry that supports non-interactive runs.
  • Introduce tests for CLI, web, GUI, and TUI that use pytest fixtures and type hints, and conditionally include them based on the corresponding feature flags.
  • Update README template usage section to show CLI/GUI/TUI/Web commands conditionally, and add docs pages (installation, usage, modules, index) that describe usage patterns and extras according to enabled features.
template/src/{{github_repo_name}}/cli.py.jinja
template/src/{{github_repo_name}}/__main__.py.jinja
template/src/{{github_repo_name}}/web.py.jinja
template/src/{{github_repo_name}}/gui.py.jinja
template/src/{{github_repo_name}}/tui.py.jinja
template/tests/test_cli.py.jinja
template/tests/test_web.py.jinja
template/tests/test_gui.py.jinja
template/tests/test_tui.py.jinja
template/README.md.jinja
template/docs/index.md.jinja
template/docs/installation.md.jinja
template/docs/usage.md.jinja
template/docs/modules.md.jinja
Refine tooling configuration for linting, type checking, pre-commit, docs, and tox workflows to better match the new template structure.
  • Convert .pre-commit-config into a Jinja template that conditionally adds mypy extra dependencies (fastapi/typer/textual) and enables additional hooks (pyright, black, isort, codespell, commitizen, local pytest runner).
  • Adjust ruff configuration to normalize per-file-ignore paths and add ignores for examples; set copyright author from template variables while removing redundant TODO.
  • Configure pyright to be stricter and venv-aware (exclude .venv, specify pythonVersion, venv, venvPath).
  • Extend tox config with feature-specific envs (cli/web/tui/config), docs-build/docs-server, and change docs env naming; ensure envs use the new extras and dependency groups consistently.
  • Add mkdocs nav entries for new docs pages and update Zed debug config to only include CLI debug profile when CLI is part of the project.
template/.pre-commit-config.yaml.jinja
template/pyproject.toml.jinja
template/.github/actionlint.yaml
template/tests/test_cli.py.jinja
template/tests/test_web.py.jinja
template/mkdocs.yml.jinja
template/.zed/debug.json.jinja
Rework CI/CD and automation templates to be more configurable and robust, plus add opencode integration.
  • Rename CD workflow job to a more generic Continuous Deployment job, fix templating/escaping of GitHub expressions, and keep trusted publishing flow via uv publish and gh release upload.
  • Make release-drafter template conditional and sensitive to dependency_management choice when excluding bot contributors and in regex replacers.
  • Introduce optional Dependabot and release-please config templates controlled by dependency_management/release_automation, and remove always-on variants from the template root.
  • Add opencode GitHub Actions workflow both to the template and this repo to allow AI-assisted code review/changes triggered by comments.
template/.github/workflows/cd.yml.jinja
template/.github/release-drafter.yml.jinja
template/.github/workflows/opencode.yml
template/.github/workflows/opencode.yml.jinja
template/.github/dependabot.yml.jinja
template/release-please-config.json.jinja
Clean up and generalize repository meta-docs and dev tooling, adding guidance files for AI agents and VPN/db devcontainer helpers.
  • Add CLAUDE.md and AGENTS.md with detailed instructions for AI tools (Claude, generic agents) on project architecture, commands, and style requirements.
  • Introduce optional devcontainer docker-compose overrides for VPN and DBeaver/CloudBeaver services to support more complex dev environments.
  • Adjust template answers file to be valid YAML with leading document marker, and enrich docs index/nav and examples tree with simple and advanced usage examples.
  • Remove the pre-rendered example project directory from the repo, relying instead on Copier + .example-input.yml for generating examples on demand.
CLAUDE.md
AGENTS.md
template/.devcontainer/docker-compose.vpn.yml
template/.devcontainer/docker-compose.dbeaver.yml
template/{{_copier_conf.answers_file}}.jinja
template/examples/simple/*
template/examples/advanced/*
example/*

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes and found some issues that need to be addressed.

  • In template/docs/installation.md.jinja, the extras list uses {% if config %} but there is no config template variable (only use_pydantic_settings / the config extra name), so this condition will never be true and should be updated to use the appropriate flag.
  • The example answers file .example-input.yml defines use_pre_commit, but copier.yml declares use_precommit; this mismatch means the example value will be ignored and should be aligned with the actual question name.
  • The local pytest pre-commit hook in template/.pre-commit-config.yaml.jinja hardcodes ./venv/bin/pytest, while the rest of the template uses uv and a .venv virtualenv; consider updating this path or using uv so it works out of the box with generated projects.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `template/docs/installation.md.jinja`, the extras list uses `{% if config %}` but there is no `config` template variable (only `use_pydantic_settings` / the `config` extra name), so this condition will never be true and should be updated to use the appropriate flag.
- The example answers file `.example-input.yml` defines `use_pre_commit`, but `copier.yml` declares `use_precommit`; this mismatch means the example value will be ignored and should be aligned with the actual question name.
- The local `pytest` pre-commit hook in `template/.pre-commit-config.yaml.jinja` hardcodes `./venv/bin/pytest`, while the rest of the template uses `uv` and a `.venv` virtualenv; consider updating this path or using `uv` so it works out of the box with generated projects.

## Individual Comments

### Comment 1
<location> `template/pyproject.toml.jinja:20-24` </location>
<code_context>
+    "tox", "pytest", "ruff", "mypy", "pyright",
+    "type-hints", "typed", "modern-python",
+    "ci-cd", "github-actions", "automation", "mkdocs",
+    "pre-commit", "linting", "testing", "coverage", "release-automation"
+    {% if include_cli %}"typer",{% endif %}
+    {% if include_web %}"fastapi", "docker",{% endif %}
+    {% if include_tui %}"textual", "tui",{% endif %}
+    {% if use_pydantic_settings %}"pydantic",{% endif %}
+]
+# TODO @{{github_user}}: Fill in the missing classifiers...
</code_context>

<issue_to_address>
**issue (bug_risk):** Conditional keyword entries can render invalid TOML due to missing commas between elements.

The last fixed entry (`"release-automation"`) has no trailing comma, and each conditional entry starts on a new line without a leading comma. When any conditional is enabled, this yields adjacent string literals without a separator. Please either add a trailing comma after the last fixed element or start each conditional entry with a comma (e.g. `{% if include_cli %}, "typer"{% endif %}`) so all flag combinations produce valid TOML.
</issue_to_address>

### Comment 2
<location> `template/pyproject.toml.jinja:102` </location>
<code_context>
 [dependency-groups]
 dev = [
-  "{{github_repo_name}}[cli,web]",
+  "{{github_repo_name}}[all]",
   "tox==4.31.0",
   "tox-uv==1.29.0",
</code_context>

<issue_to_address>
**issue (bug_risk):** The `dev` dependency group always references the `all` extra even when no optional extras are defined.

`[dependency-groups].dev` now always depends on `"{{github_repo_name}}[all]"`, but `[project.optional-dependencies]` (and thus the `all` extra) is only generated when at least one of `include_cli`, `include_web`, `include_tui`, or `use_pydantic_settings` is true. In configs like `.example-input.yml` where all are false, `dev` installs will fail because `all` is undefined. Please either conditionally include `"{{github_repo_name}}[all]"` under the same condition as `[project.optional-dependencies]`, or use `"{{github_repo_name}}"` when no extras are enabled.
</issue_to_address>

### Comment 3
<location> `template/docs/installation.md.jinja:8` </location>
<code_context>
+> - {% if include_cli %}cli: Installs typer and adds {{github_repo_name}} as a command.{% endif %}
+> - {% if include_tui %}tui: Installs textual and adds {{github_repo_name}}-tui as a command.{% endif %}
+> - {% if include_web %}web: Installs FastAPI and adds {{github_repo_name}}-web as a command.{% endif %}
+> - {% if config %}config: Installs pydantic-settings.{% endif %}
+> - all: Installs all extras.
+{% endif %}
</code_context>

<issue_to_address>
**issue (bug_risk):** The conditional for the `config` extra references an undefined template variable.

Other conditions use the `use_pydantic_settings` flag, but this line checks `config`, which isn’t defined in `copier.yml`. That means this extra may render incorrectly or error at runtime. Please switch this to use `use_pydantic_settings` and align the description with that flag.
</issue_to_address>

### Comment 4
<location> `template/docs/index.md.jinja:13-15` </location>
<code_context>
+
+## Indices and tables
+
+- [Index](genindex)
+- [Module Index](modindex)
+- [Search](search)
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Index/search links look like Sphinx artifacts and won’t resolve under MkDocs.

As-is, these will render as broken links. Please either remove them or replace them with links to real MkDocs pages (e.g., existing Markdown docs or MkDocs’ built-in search UI).
</issue_to_address>

### Comment 5
<location> `CLAUDE.md:56` </location>
<code_context>
+uv run --locked <repo-name> info
+
+# Run the FastAPI web app
+uv run --locked fastapi dev <repo-name>.app:app
+
+# Run pre-commit hooks
</code_context>

<issue_to_address>
**issue (bug_risk):** Update FastAPI run command to match the new `web.py` module path.

Other references (README, `web.py` rename) expose the FastAPI app as `<repo-name>.web:app`. This command should use the same path: `uv run --locked fastapi dev <repo-name>.web:app` to avoid a broken or confusing entrypoint.
</issue_to_address>

### Comment 6
<location> `CLAUDE.md:94` </location>
<code_context>
+Projects created from this template have:
+
+- **CLI Application**: Typer-based CLI in `cli.py.jinja` with `version` and `info` commands
+- **Web Application**: FastAPI stub in `app.py.jinja` with `/version` and `/info` endpoints
+- **Core Modules**:
+  - `__metadata__.py.jinja`: Project name constant
</code_context>

<issue_to_address>
**issue:** Align referenced FastAPI template filename with the new `web.py.jinja` name.

This line still mentions `app.py.jinja`, but the FastAPI template is now `web.py.jinja` (via `{% if include_web %}web.py{% endif %}.jinja`). Please update the filename here to keep the documentation accurate.
</issue_to_address>

### Comment 7
<location> `AGENTS.md:14` </location>
<code_context>
+# Pre-commit: uv run --locked tox run -e pre-commit
+# Build: uv build
+# Run CLI: uv run --locked <repo-name> version
+# Run FastAPI: uv run --locked fastapi dev <repo-name>.app:app
+# Serve docs: uv run --only-group docs mkdocs serve
+# Trunk check: trunk check (if configured)
</code_context>

<issue_to_address>
**issue (bug_risk):** FastAPI run command should use the updated `.web:app` module path.

The template and README now use `<repo-name>.web:app` as the FastAPI entrypoint, but this still references `<repo-name>.app:app`. Please update this line to `uv run --locked fastapi dev <repo-name>.web:app` so the documented command works correctly and stays consistent.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link

@recurseml recurseml bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review by RecurseML

🔍 Review performed on 73a9cfc..3c0a027

✨ No bugs found, your code is sparkling clean

✅ Files analyzed, no issues (50)

.example-input.yml
.github/workflows/opencode.yml
AGENTS.md
CLAUDE.md
README.md
copier.yml
example/.copier-answers.yml
example/.devcontainer/Dockerfile
example/.devcontainer/devcontainer.json
example/.devcontainer/docker-compose.yml
example/.dockerignore
example/.editorconfig
example/.github/FUNDING.yml
example/.github/ISSUE_TEMPLATE/bug-report.md
example/.github/ISSUE_TEMPLATE/feature-request.md
example/.github/PULL_REQUEST/pull_request_template.md
example/.github/actionlint.yaml
example/.github/labeler.yml
example/.github/linters/.codeql.yml
example/.github/linters/.codespellrc
example/.github/linters/.cspell.yml
example/.github/release-drafter.yml
example/.github/workflows/cd.yml
example/.github/workflows/codeql.yml
example/.github/workflows/first-interaction.yml
example/.github/workflows/gh-pages.yml
example/.github/workflows/label.yml
example/.github/workflows/manual.yml
example/.github/workflows/stale.yml
example/.gitignore
example/.markdownlint-cli2.jsonc
example/.markdownlint.json
example/.pre-commit-config.yaml
example/.vscode/extensions.json
example/.vscode/launch.json
example/.yamlfmt.yml
example/.yamllint.yaml
example/.zed/debug.json
example/CHANGELOG.md
example/CODE_OF_CONDUCT.md
example/CONTRIBUTING.md
example/Dockerfile
example/LICENSE
example/README.md
example/docs/index.md
example/mkdocs.yml
example/pyproject.toml
example/src/example/__init__.py
example/src/example/__main__.py
example/src/example/app.py

⏭️ Files skipped (92)
  Locations  
.python-version
example/.github/linters/.markdownlint.yml
example/.github/linters/.shellcheckrc
example/.github/linters/.yamllint.yml
example/.github/linters/actionlint.yml
example/src/example/cli.py
example/src/example/gui.py
example/src/example/logging_setup.py
example/src/example/py.typed
example/tests/__init__.py
example/tests/test_cli.py
example/tests/test_gui.py
example/tests/test_web.py
example/uv.lock
template/.codecov.yml
template/.devcontainer/docker-compose.dbeaver.yml
template/.devcontainer/docker-compose.vpn.yml
template/.editorconfig
template/.github/ISSUE_TEMPLATE/config.yml.jinja
template/.github/actionlint.yaml
template/.github/workflows/cd.yml.jinja
template/.github/workflows/check-pr-title.yml
template/.github/workflows/check-pr-title.yml.jinja
template/.github/workflows/ci.yml
template/.github/workflows/ci.yml.jinja
template/.github/workflows/opencode.yml
template/.github/workflows/release-drafter.yml.jinja
template/.github/workflows/release-please.yml
template/.github/workflows/{% if release_automation == "release-drafter" %}release-drafter.yml{% endif %}.jinja
template/.github/workflows/{% if release_automation == "release-please" %}release-please.yml{% endif %}.jinja
template/.github/{% if dependency_management == 'dependabot' %}dependabot.yml{% endif %}.jinja
template/.github/{% if release_automation == "release-drafter" %}release-drafter.yml{% endif %}.jinja
template/.markdownlint-cli2.jsonc
template/.pre-commit-config.yaml.jinja
template/.release-it.json
template/.trunk/.gitignore
template/.trunk/configs/.hadolint.yaml
template/.trunk/configs/.markdownlint.yaml
template/.trunk/trunk.yaml
template/.vscode/launch.json.jinja
template/.zed/debug.json.jinja
template/README.md.jinja
template/docs/index.md
template/docs/index.md.jinja
template/docs/installation.md.jinja
template/docs/modules.md.jinja
template/docs/usage.md.jinja
template/examples/advanced/README.md
template/examples/advanced/main.py
template/examples/simple/README.md
template/examples/simple/main.py
template/mkdocs.yml.jinja
template/pants.toml
template/pyproject.toml.jinja
template/release-please-config.json
template/renovate.json
template/src/{{github_repo_name}}/__init__.py
template/src/{{github_repo_name}}/__init__.py.jinja
template/src/{{github_repo_name}}/__main__.py.jinja
template/src/{{github_repo_name}}/__metadata__.py.jinja
template/src/{{github_repo_name}}/config.py.jinja
template/src/{{github_repo_name}}/core.py
template/src/{{github_repo_name}}/dirs.py.jinja
template/src/{{github_repo_name}}/logging_setup.py.jinja
template/src/{{github_repo_name}}/utils.py
template/src/{{github_repo_name}}/{% if include_cli %}cli.py{% endif %}.jinja
template/src/{{github_repo_name}}/{% if include_gui %}gui.py{% endif %}.jinja
template/src/{{github_repo_name}}/{% if include_tui %}tui.py{% endif %}.jinja
template/src/{{github_repo_name}}/{% if include_web %}web.py{% endif %}.jinja
template/tests/__init__.py
template/tests/__init__.py.jinja
template/tests/{% if include_cli %}test_cli.py{% endif %}.jinja
template/tests/{% if include_gui %}test_gui.py{% endif %}.jinja
template/tests/{% if include_tui %}test_tui.py{% endif %}.jinja
template/tests/{% if include_web %}test_web.py{% endif %}.jinja
template/{% if dependency_management == 'renovate' %}renovate.json{% endif %}.jinja
template/{% if include_changelog %}CHANGELOG.md{% endif %}.jinja
template/{% if include_citation %}CITATION.cff{% endif %}.jinja
template/{% if include_code_of_conduct %}CODE_OF_CONDUCT.md{% endif %}.jinja
template/{% if include_codecov %}.codecov.yml{% endif %}.jinja
template/{% if include_contributing %}CONTRIBUTING.md{% endif %}.jinja
template/{% if include_mise %}mise.toml{% endif %}.jinja
template/{% if include_pants %}pants.toml{% endif %}.jinja
template/{% if include_trunk %}.trunk{% endif %}/.gitignore
template/{% if include_trunk %}.trunk{% endif %}/configs/.hadolint.yaml
template/{% if include_trunk %}.trunk{% endif %}/configs/.markdownlint.yaml
template/{% if include_trunk %}.trunk{% endif %}/trunk.yaml
template/{% if include_web %}Dockerfile{% endif %}.jinja
template/{% if release_automation == "release-it" %}.release-it.json{% endif %}.jinja
template/{% if release_automation == "release-please" %}release-please-config.json{% endif %}.jinja
template/{{_copier_conf.answers_file}}.jinja
tests/__init__.py

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR transforms a basic Python project template into a highly configurable Copier template by introducing conditional includes for optional components. The refactor adds extensive configurability through new template variables in copier.yml, enabling users to selectively include CLI (Typer), web (FastAPI), TUI (Textual), GUI (Tkinter) interfaces, along with configurable dependency management, release automation, and optional development tools. The changes separate concerns by creating a new __metadata__.py module for project constants, restructure configuration handling to support pydantic-settings, enhance documentation structure, and remove the example project in favor of expanded configuration options.

Key Changes

  • Added 20+ new configuration options in copier.yml for conditional inclusion of components (CLI, web, TUI, GUI, C extensions, pydantic-settings, release automation, dependency management, etc.)
  • Introduced __metadata__.py module to centralize project metadata and updated all imports from config.py to use the new module
  • Restructured template files to use conditional Jinja2 blocks based on new configuration flags

Reviewed changes

Copilot reviewed 142 out of 163 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
copier.yml Added 20+ new configuration variables for conditional component inclusion
README.md Updated documentation to reflect new configurability options
.example-input.yml Expanded with all new configuration options set to defaults
AGENTS.md New AI agent guidance file for development commands and code style
CLAUDE.md Comprehensive Claude AI assistant documentation
template/pyproject.toml.jinja Heavily modified to support conditional dependencies and optional components
template/src/{{github_repo_name}}/__metadata__.py.jinja New module for project metadata constants
template/src/{{github_repo_name}}/config.py.jinja Restructured to use pydantic-settings when enabled
template/src/{{github_repo_name}}/cli.py.jinja Updated imports and added conditional commands for TUI/GUI/web
template/src/{{github_repo_name}}/logging_setup.py.jinja Updated imports to use new dirs.py module
template/tests/test_*.py.jinja Updated test files to use pytest fixtures and new metadata module
template/.pre-commit-config.yaml Enhanced with additional hooks including pyright, black, isort, codespell, commitizen
template/.github/workflows/*.yml.jinja Restructured workflows for conditional release automation and dependency management
Comments suppressed due to low confidence (13)

template/{{_copier_conf.answers_file}}.jinja:1

  • The --- YAML document separator on line 2 appears after a comment line. While technically valid, this is unconventional. Typically, if a YAML document separator is used, it should be the first line of the file. Consider either removing the separator entirely (since it's not required for single-document YAML files) or placing it before the comment on line 1.
    template/{% if include_citation %}CITATION.cff{% endif %}.jinja:1
  • The name parsing logic assumes the author's full name has exactly two parts (first and last name). This will break for authors with middle names or single-word names. For authors with names like 'John Paul Smith', split(' ')[0] will only capture 'John' and split(' ')[-1] will capture 'Smith', losing 'Paul'. Consider using a more robust parsing approach or providing separate fields for given and family names in the template variables.
    template/{% if include_citation %}CITATION.cff{% endif %}.jinja:1
  • Duplicate name parsing issue in the preferred-citation section. This has the same problem as lines 5-6 where names with middle names or single-word names will not be handled correctly.
    template/src/{{github_repo_name}}/{% if include_tui %}tui.py{% endif %}.jinja:1
  • The exception handling on line 103 catches a generic Exception but the error message on line 104 constructs a TuiDisplayError with the exception message. This loses the original exception type information which could be valuable for debugging. Consider preserving the exception chain or logging the original exception type.
    template/pyproject.toml.jinja:1
  • The trailing commas in the all dependency list may result in a syntax error if all conditions evaluate to false, leaving a list with only commas. While unlikely given default values, consider restructuring to avoid potential issues or adding a comment explaining the assumption.
    template/pyproject.toml.jinja:1
  • Unlike other dependencies in this file, these three test dependencies lack version specifiers. For consistency and reproducibility, consider adding version constraints (e.g., 'pytest-sugar==x.y.z' or 'pytest-sugar>=x.y.z').
    template/pyproject.toml.jinja:1
  • The path patterns use ./ prefix which is inconsistent with standard ruff configuration patterns. Ruff typically expects paths without the leading ./. Consider removing the prefix for consistency: tests/*.py and examples/*.py.
    template/.github/{% if dependency_management == 'dependabot' %}dependabot.yml{% endif %}.jinja:1
  • The commit message prefix 'deps' differs from the conventional commit prefix 'chore(deps)' used in many projects and shown in the release-drafter configuration. Consider using 'chore' as the prefix for consistency across the project's tooling.
    template/.github/workflows/cd.yml.jinja:1
  • Missing closing brace in the Jinja raw block. The {% raw %} block is not properly closed - it should end with {% endraw %} on line 45, but line 69 shows {% endraw %} which is supposed to close a different block. This will cause template rendering errors.
    template/.github/workflows/build-wheels.yml:1
  • The workflow uses UV_PUBLISH_TOKEN with a secret PYPI_TOKEN, but the CD workflow uses trusted publishing without tokens. This inconsistency in publishing methods could be confusing. Consider documenting why different approaches are used or standardizing on one method. If this workflow is for C extensions (as the name suggests), document the publishing strategy difference.
    template/.github/actionlint.yaml:1
  • Ignoring shellcheck warnings globally across all workflow files may hide legitimate issues. Consider being more specific about which workflows need these exceptions, or better yet, fix the underlying shell script issues that trigger SC2006 (deprecated backticks), SC2086 (unquoted variables), and SC2129 (inefficient redirection).
    template/.github/ISSUE_TEMPLATE/config.yml.jinja:1
  • Lines 19-27 appear to be duplicates of the contact_links defined earlier in the file (lines 4-18). This creates redundant entries in the GitHub issue template configuration. Remove the duplicate entries to avoid confusion.
    template/.editorconfig:1
  • Setting indent_size = unset for Makefiles is unusual. Makefiles traditionally use tabs with a specific tab width. Consider setting indent_size = 4 or indent_size = 8 (common tab widths) instead of unsetting it entirely.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@hasansezertasan hasansezertasan merged commit 61aedc8 into main Dec 15, 2025
1 check was pending
@hasansezertasan hasansezertasan deleted the refactor/template-improvements branch December 15, 2025 08:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant