Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .agents/hooks/format-python.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/usr/bin/env bash
# PostToolUse hook shared by Claude Code and Codex: format and lint the Python
# files the agent just edited. Reads the hook input JSON from stdin.
#
# Claude Code (Edit/Write) reports the file in tool_input.file_path.
# Codex (apply_patch) reports the whole patch in tool_input.command.
# Remaining lint errors are sent back to the agent via exit code 2.

set -euo pipefail

input="$(cat)"

files=()
while IFS= read -r file; do
[[ "${file}" == *.py ]] && files+=("${file}")
done < <(jq -r '
(.tool_input.file_path // empty),
((.tool_input.command // "") | scan("\\*\\*\\* (?:Add|Update) File: (.+)") | .[0])
' <<<"${input}")

# Avoid the array-length expansion here: its brace-hash prefix would start a Jinja
# comment when Copier renders this file.
[[ -n "${files[*]:-}" ]] || exit 0

cwd="$(jq -r '.cwd // empty' <<<"${input}")"
cd -- "${cwd:-$(git rev-parse --show-toplevel)}"

uv run --frozen ruff format -- "${files[@]}"
if ! uv run --frozen ruff check --fix -- "${files[@]}" >&2; then
exit 2
fi
29 changes: 29 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"permissions": {
"allow": [
"Bash(uv run --frozen *)",
"Bash(uv sync *)",
"Bash(uv lock *)",
"Bash(git status *)",
"Bash(git diff *)",
"Bash(git log *)"
],
"deny": [
"Read(.env)",
"Read(*.env)"
]
},
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "bash \"$(git rev-parse --show-toplevel)/.agents/hooks/format-python.sh\""
}
]
}
]
}
}
17 changes: 17 additions & 0 deletions .codex/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"description": "Format and lint Python files after the agent edits them.",
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "bash \"$(git rev-parse --show-toplevel)/.agents/hooks/format-python.sh\"",
"statusMessage": "Formatting Python"
}
]
}
]
}
}
3 changes: 2 additions & 1 deletion .copier-answers.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY
_commit: v1.1.0
_commit: v2.0.2
_src_path: gh:mjun0812/python-copier-template
author_email: mjun@mjunya.com
author_name: Junya Morioka
description: Modern Python Project
license: MIT
package_name: my_package
project_name: Python-Project-Template
python_version: '3.12'
35 changes: 35 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# syntax=docker/dockerfile:1

FROM ghcr.io/astral-sh/uv:0.12.10 AS uv

FROM mcr.microsoft.com/devcontainers/base:resolute

COPY --from=uv /uv /uvx /usr/local/bin/

RUN rm -f /etc/apt/apt.conf.d/docker-clean \
&& echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
curl \
fd-find \
git \
jq \
ripgrep \
wget \
&& curl -fsSL https://github.com/openai/codex/releases/latest/download/install.sh -o /tmp/install-codex.sh \
&& CODEX_INSTALL_DIR=/usr/local/bin \
CODEX_HOME=/opt/codex \
CODEX_NON_INTERACTIVE=1 \
sh /tmp/install-codex.sh \
&& rm -f /tmp/install-codex.sh

# Claude Code's native installer installs under $HOME, so run it as the container user
USER vscode
RUN curl -fsSL https://claude.ai/install.sh | bash \
&& rm -rf /home/vscode/.claude/downloads
USER root
RUN ln -s /home/vscode/.local/bin/claude /usr/local/bin/claude
30 changes: 13 additions & 17 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
{
"name": "python-devcontainer",
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"build": {
"dockerfile": "Dockerfile",
"context": ".."
},
"containerEnv": {
"DISPLAY": "${localEnv:DISPLAY}",
"PYTHONUNBUFFERED": "1",
"PYTHONDONTWRITEBYTECODE": "1",
"UV_CACHE_DIR": "/home/vscode/.cache/uv",
"UV_LINK_MODE": "copy",
"UV_PYTHON_PREFERENCE": "only-managed",
"UV_PROJECT_ENVIRONMENT": "/home/vscode/.venv",
"UV_COMPILE_BYTECODE": "1",
"CLAUDE_CONFIG_DIR": "/home/vscode/.claude"
Expand All @@ -15,15 +19,7 @@
"ghcr.io/devcontainers/features/github-cli:1": {},
"ghcr.io/devcontainers/features/common-utils:2": {
"configureZshAsDefaultShell": true
},
"ghcr.io/rocker-org/devcontainer-features/apt-packages:1": {
"packages": "curl,wget,git,jq,ca-certificates,build-essential,ripgrep,fd-find"
},
"ghcr.io/va-h/devcontainers-features/uv:1": {
"shellAutocompletion": true
},
"ghcr.io/devcontainers/features/node:1": {},
"ghcr.io/anthropics/devcontainer-features/claude-code:1.0": {}
}
},
"runArgs": [
"--init",
Expand All @@ -40,6 +36,7 @@
"extensions": [
"ms-python.python",
"charliermarsh.ruff",
"astral-sh.ty",
"eamodio.gitlens",
"tamasfe.even-better-toml",
"ms-toolsai.jupyter",
Expand All @@ -49,14 +46,13 @@
},
"remoteUser": "vscode",
"containerUser": "vscode",
"initializeCommand": "mkdir -p ${localEnv:HOME}/.claude ${localEnv:HOME}/.codex && touch ${localEnv:HOME}/.claude/CLAUDE.md ${localEnv:HOME}/.codex/config.toml",
"initializeCommand": "mkdir -p ${localEnv:HOME}/.claude ${localEnv:HOME}/.codex",
"mounts": [
"source=shell_history-${devcontainerId},target=/shell_history,type=volume",
"source=uv-cache-${devcontainerId},target=/home/vscode/.cache/uv,type=volume",
"source=${localEnv:HOME}/.claude,target=/home/vscode/.claude,type=bind",
"source=${localEnv:HOME}/.claude/CLAUDE.md,target=/home/vscode/.claude/CLAUDE.md,type=bind",
"source=${localEnv:HOME}/.codex,target=/home/vscode/.codex,type=bind",
"source=${localEnv:HOME}/.codex/config.toml,target=/home/vscode/.codex/config.toml,type=bind"
"source=${localEnv:HOME}/.codex,target=/home/vscode/.codex,type=bind"
],
"postCreateCommand": "npm install -g @openai/codex@latest && uv sync",
"postStartCommand": "uv run pre-commit install"
}
"postCreateCommand": "sudo chown -R vscode:vscode /home/vscode/.cache && uv sync --locked",
"postStartCommand": "uv run prek install"
}
32 changes: 32 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
groups:
github-actions:
patterns: ["*"]
- package-ecosystem: "pre-commit"
directory: "/"
schedule:
interval: "weekly"
groups:
pre-commit:
patterns: ["*"]
- package-ecosystem: "uv"
directory: "/"
schedule:
interval: "weekly"
groups:
uv:
patterns: ["*"]
- package-ecosystem: "docker"
directories:
- "/docker"
- "/.devcontainer"
schedule:
interval: "weekly"
groups:
docker:
patterns: ["*"]
34 changes: 30 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,42 @@ on:
branches:
- main

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
ci:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@v6
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
- name: Install shell tools
run: sudo apt-get update && sudo apt-get install --yes shellcheck shfmt

- name: Sync dependencies
run: uv sync --locked

- name: Check shell scripts
run: |
shellcheck docker/*.sh
test -z "$(shfmt --list docker/*.sh)"

- name: Format
run: uvx ruff format . --check --diff
run: uv run --frozen ruff format . --check --diff

- name: Lint
run: uvx ruff check --output-format=github .
run: uv run --frozen ruff check --output-format=github .

- name: Type check
run: uv run --frozen ty check

- name: Test
run: uv run --frozen pytest --cov
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -287,4 +287,8 @@ tags
.history
.ionide

# End of https://www.toptal.com/developers/gitignore/api/linux,macos,visualstudiocode,vim,python,jupyternotebooks,venv,virtualenv
# End of https://www.toptal.com/developers/gitignore/api/linux,macos,visualstudiocode,vim,python,jupyternotebooks,venv,virtualenv

### AI coding agents ###
# Personal Claude Code overrides (machine-specific settings, never shared)
.claude/settings.local.json
10 changes: 8 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
repos:
- repo: https://github.com/astral-sh/uv-pre-commit
# uv version.
rev: 0.8.22
rev: 0.12.10
hooks:
- id: uv-lock
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.13.2
rev: v0.16.6
hooks:
# Run the linter.
- id: ruff-check
args: [--fix]
# Run the formatter.
- id: ruff-format
- repo: https://github.com/astral-sh/ty-pre-commit
# ty version.
rev: v0.0.78
hooks:
# Run the type checker.
- id: ty
22 changes: 18 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,43 @@ Follow these guidelines precisely.
- ONLY use uv, NEVER pip
- Installation: `uv add package`
- Upgrading: `uv add --dev package --upgrade-package package`
- FORBIDDEN: `uv pip install`, `@latest` syntax
- FORBIDDEN: `uv pip install`, `@latest` syntax, editing `uv.lock` by hand

2. Code Quality
- Type hints required for all code
- Imports used only in type annotations go under `if TYPE_CHECKING:` with
`from __future__ import annotations` at the top of the module (Ruff `TC` rules)
- Follow existing patterns exactly
- Use Google style for docstring

3. Testing Requirements
- Framework: `uv run --frozen pytest`
- Framework: `uv run --frozen pytest` (runs in parallel; use `-n 0` for `--pdb`)
- Coverage: test edge cases and errors
- Coverage report: `uv run --frozen pytest --cov` (CI fails below 80% branch coverage of `src/`)
- New features require tests
- Bug fixes require regression tests

4. Git
- Follow the Conventional Commits style on commit messages.
- NEVER use `git commit --no-verify`; fix what the hooks report instead.

5. Running
- Application: `uv run Python-Project-Template` (or `python -m my_package`)

## Code Formatting and Linting

1. Ruff
- Format: `uv run --frozen ruff format .`
- Check: `uv run --frozen ruff check .`
- Fix: `uv run --frozen ruff check . --fix`
2. Pre-commit
2. Type Check
- Check: `uv run --frozen ty check`
3. Git Hooks (prek)
- Config: `.pre-commit-config.yaml`
- Install: `uv run prek install`
- Runs: on git commit
- Tools: Ruff (Python)
- Tools: uv lock, Ruff, ty
4. Agent Hooks
- `.agents/hooks/format-python.sh` formats and lints every Python file right
after Claude Code or Codex edits it (wired in `.claude/settings.json` and
`.codex/hooks.json`). Do not re-run the formatter manually after edits.
38 changes: 1 addition & 37 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,37 +1 @@
# Development Guidelines

This document contains critical information about working with this codebase.
Follow these guidelines precisely.

## Rules

1. Package Management
- ONLY use uv, NEVER pip
- Installation: `uv add package`
- Upgrading: `uv add --dev package --upgrade-package package`
- FORBIDDEN: `uv pip install`, `@latest` syntax

2. Code Quality
- Type hints required for all code
- Follow existing patterns exactly
- Use Google style for docstring

3. Testing Requirements
- Framework: `uv run --frozen pytest`
- Coverage: test edge cases and errors
- New features require tests
- Bug fixes require regression tests

4. Git
- Follow the Conventional Commits style on commit messages.

## Code Formatting and Linting

1. Ruff
- Format: `uv run --frozen ruff format .`
- Check: `uv run --frozen ruff check .`
- Fix: `uv run --frozen ruff check . --fix`
2. Pre-commit
- Config: `.pre-commit-config.yaml`
- Runs: on git commit
- Tools: Ruff (Python)
@AGENTS.md
Loading